Merge pull request #27 from nektos/parser

Replace parser with new parser from GitHub
This commit is contained in:
Casey Lee 2019-02-07 09:12:09 -08:00 committed by GitHub
commit 18427728ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
189 changed files with 26276 additions and 4015 deletions

View file

@ -1,4 +1,4 @@
#!/bin/sh
set -e
golangci-lint run
go test -cover ./...
go test -cover -short ./...

View file

@ -50,6 +50,6 @@ endif
git push origin $(NEW_VERSION)
vendor:
go run main.go -ra vendor
go mod vendor
.PHONY: vendor

View file

@ -2,81 +2,46 @@ package actions
import (
"fmt"
"net/http"
"net/url"
"log"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/nektos/act/common"
log "github.com/sirupsen/logrus"
"github.com/actions/workflow-parser/model"
"github.com/howeyc/gopass"
)
// imageURL is the directory where a `Dockerfile` should exist
func parseImageLocal(workingDir string, contextDir string) (contextDirOut string, tag string, ok bool) {
if !strings.HasPrefix(contextDir, "./") {
return "", "", false
}
contextDir = filepath.Join(workingDir, contextDir)
if _, err := os.Stat(filepath.Join(contextDir, "Dockerfile")); os.IsNotExist(err) {
log.Debugf("Ignoring missing Dockerfile '%s/Dockerfile'", contextDir)
return "", "", false
}
var secretCache map[string]string
sha, _, err := common.FindGitRevision(contextDir)
if err != nil {
log.Warnf("Unable to determine git revision: %v", err)
sha = "latest"
}
return contextDir, fmt.Sprintf("%s:%s", filepath.Base(contextDir), sha), true
type actionEnvironmentApplier struct {
*model.Action
}
// imageURL is the URL for a docker repo
func parseImageReference(image string) (ref string, ok bool) {
imageURL, err := url.Parse(image)
if err != nil {
log.Debugf("Unable to parse image as url: %v", err)
return "", false
}
if imageURL.Scheme != "docker" {
log.Debugf("Ignoring non-docker ref '%s'", imageURL.String())
return "", false
}
return fmt.Sprintf("%s%s", imageURL.Host, imageURL.Path), true
func newActionEnvironmentApplier(action *model.Action) environmentApplier {
return &actionEnvironmentApplier{action}
}
// imageURL is the directory where a `Dockerfile` should exist
func parseImageGithub(image string) (cloneURL *url.URL, ref string, path string, ok bool) {
re := regexp.MustCompile("^([^/@]+)/([^/@]+)(/([^@]*))?(@(.*))?$")
matches := re.FindStringSubmatch(image)
if matches == nil {
return nil, "", "", false
func (action *actionEnvironmentApplier) applyEnvironment(env map[string]string) {
for envKey, envValue := range action.Env {
env[envKey] = envValue
}
cloneURL, err := url.Parse(fmt.Sprintf("https://github.com/%s/%s", matches[1], matches[2]))
if err != nil {
log.Debugf("Unable to parse as URL: %v", err)
return nil, "", "", false
}
for _, secret := range action.Secrets {
if secretVal, ok := os.LookupEnv(secret); ok {
env[secret] = secretVal
} else {
if secretCache == nil {
secretCache = make(map[string]string)
}
resp, err := http.Head(cloneURL.String())
if resp.StatusCode >= 400 || err != nil {
log.Debugf("Unable to HEAD URL %s status=%v err=%v", cloneURL.String(), resp.StatusCode, err)
return nil, "", "", false
}
if _, ok := secretCache[secret]; !ok {
fmt.Printf("Provide value for '%s': ", secret)
val, err := gopass.GetPasswdMasked()
if err != nil {
log.Fatal("abort")
}
ref = matches[6]
if ref == "" {
ref = "master"
secretCache[secret] = string(val)
}
env[secret] = secretCache[secret]
}
}
path = matches[4]
if path == "" {
path = "."
}
return cloneURL, ref, path, true
}

64
actions/graph.go Normal file
View file

@ -0,0 +1,64 @@
package actions
import (
"log"
"github.com/actions/workflow-parser/model"
)
// return a pipeline that is run in series. pipeline is a list of steps to run in parallel
func newExecutionGraph(workflowConfig *model.Configuration, actionNames ...string) [][]string {
// first, build a list of all the necessary actions to run, and their dependencies
actionDependencies := make(map[string][]string)
for len(actionNames) > 0 {
newActionNames := make([]string, 0)
for _, aName := range actionNames {
// make sure we haven't visited this action yet
if _, ok := actionDependencies[aName]; !ok {
action := workflowConfig.GetAction(aName)
if action != nil {
actionDependencies[aName] = action.Needs
newActionNames = append(newActionNames, action.Needs...)
}
}
}
actionNames = newActionNames
}
// next, build an execution graph
graph := make([][]string, 0)
for len(actionDependencies) > 0 {
stage := make([]string, 0)
for aName, aDeps := range actionDependencies {
// make sure all deps are in the graph already
if listInLists(aDeps, graph...) {
stage = append(stage, aName)
delete(actionDependencies, aName)
}
}
if len(stage) == 0 {
log.Fatalf("Unable to build dependency graph!")
}
graph = append(graph, stage)
}
return graph
}
// return true iff all strings in srcList exist in at least one of the searchLists
func listInLists(srcList []string, searchLists ...[]string) bool {
for _, src := range srcList {
found := false
for _, searchList := range searchLists {
for _, search := range searchList {
if src == search {
found = true
}
}
}
if !found {
return false
}
}
return true
}

View file

@ -1,129 +0,0 @@
package actions
import (
"fmt"
"log"
"os"
"github.com/howeyc/gopass"
)
type workflowModel struct {
On string
Resolves []string
}
type actionModel struct {
Needs []string
Uses string
Runs []string
Args []string
Env map[string]string
Secrets []string
}
type workflowsFile struct {
Workflow map[string]workflowModel
Action map[string]actionModel
}
func (wFile *workflowsFile) getWorkflow(eventName string) (*workflowModel, string, error) {
var rtn workflowModel
for wName, w := range wFile.Workflow {
if w.On == eventName {
rtn = w
return &rtn, wName, nil
}
}
return nil, "", fmt.Errorf("unsupported event: %v", eventName)
}
func (wFile *workflowsFile) getAction(actionName string) (*actionModel, error) {
if a, ok := wFile.Action[actionName]; ok {
return &a, nil
}
return nil, fmt.Errorf("unsupported action: %v", actionName)
}
// return a pipeline that is run in series. pipeline is a list of steps to run in parallel
func (wFile *workflowsFile) newExecutionGraph(actionNames ...string) [][]string {
// first, build a list of all the necessary actions to run, and their dependencies
actionDependencies := make(map[string][]string)
for len(actionNames) > 0 {
newActionNames := make([]string, 0)
for _, aName := range actionNames {
// make sure we haven't visited this action yet
if _, ok := actionDependencies[aName]; !ok {
actionDependencies[aName] = wFile.Action[aName].Needs
newActionNames = append(newActionNames, wFile.Action[aName].Needs...)
}
}
actionNames = newActionNames
}
// next, build an execution graph
graph := make([][]string, 0)
for len(actionDependencies) > 0 {
stage := make([]string, 0)
for aName, aDeps := range actionDependencies {
// make sure all deps are in the graph already
if listInLists(aDeps, graph...) {
stage = append(stage, aName)
delete(actionDependencies, aName)
}
}
if len(stage) == 0 {
log.Fatalf("Unable to build dependency graph!")
}
graph = append(graph, stage)
}
return graph
}
// return true iff all strings in srcList exist in at least one of the searchLists
func listInLists(srcList []string, searchLists ...[]string) bool {
for _, src := range srcList {
found := false
for _, searchList := range searchLists {
for _, search := range searchList {
if src == search {
found = true
}
}
}
if !found {
return false
}
}
return true
}
var secretCache map[string]string
func (action *actionModel) applyEnvironment(env map[string]string) {
for envKey, envValue := range action.Env {
env[envKey] = envValue
}
for _, secret := range action.Secrets {
if secretVal, ok := os.LookupEnv(secret); ok {
env[secret] = secretVal
} else {
if secretCache == nil {
secretCache = make(map[string]string)
}
if _, ok := secretCache[secret]; !ok {
fmt.Printf("Provide value for '%s': ", secret)
val, err := gopass.GetPasswdMasked()
if err != nil {
log.Fatal("abort")
}
secretCache[secret] = string(val)
}
env[secret] = secretCache[secret]
}
}
}

View file

@ -1,140 +0,0 @@
package actions
import (
"bytes"
"errors"
"fmt"
"io"
"strings"
"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/ast"
"github.com/hashicorp/hcl/hcl/token"
log "github.com/sirupsen/logrus"
)
func parseWorkflowsFile(workflowReader io.Reader) (*workflowsFile, error) {
// TODO: add validation logic
// - check for circular dependencies
// - check for valid local path refs
// - check for valid dependencies
buf := new(bytes.Buffer)
_, err := buf.ReadFrom(workflowReader)
if err != nil {
log.Error(err)
}
workflows := new(workflowsFile)
astFile, err := hcl.ParseBytes(buf.Bytes())
if err != nil {
return nil, err
}
rootNode := ast.Walk(astFile.Node, cleanWorkflowsAST)
err = hcl.DecodeObject(workflows, rootNode)
if err != nil {
return nil, err
}
return workflows, nil
}
func cleanWorkflowsAST(node ast.Node) (ast.Node, bool) {
if objectItem, ok := node.(*ast.ObjectItem); ok {
key := objectItem.Keys[0].Token.Value()
// handle condition where value is a string but should be a list
switch key {
case "args", "runs":
if literalType, ok := objectItem.Val.(*ast.LiteralType); ok {
listType := new(ast.ListType)
parts, err := parseCommand(literalType.Token.Value().(string))
if err != nil {
return nil, false
}
quote := literalType.Token.Text[0]
for _, part := range parts {
part = fmt.Sprintf("%c%s%c", quote, strings.Replace(part, "\\", "\\\\", -1), quote)
listType.Add(&ast.LiteralType{
Token: token.Token{
Type: token.STRING,
Text: part,
},
})
}
objectItem.Val = listType
}
case "resolves", "needs":
if literalType, ok := objectItem.Val.(*ast.LiteralType); ok {
listType := new(ast.ListType)
listType.Add(literalType)
objectItem.Val = listType
}
}
}
return node, true
}
// reused from: https://github.com/laurent22/massren/blob/ae4c57da1e09a95d9383f7eb645a9f69790dec6c/main.go#L172
// nolint: gocyclo
func parseCommand(cmd string) ([]string, error) {
var args []string
state := "start"
current := ""
quote := "\""
for i := 0; i < len(cmd); i++ {
c := cmd[i]
if state == "quotes" {
if string(c) != quote {
current += string(c)
} else {
args = append(args, current)
current = ""
state = "start"
}
continue
}
if c == '"' || c == '\'' {
state = "quotes"
quote = string(c)
continue
}
if state == "arg" {
if c == ' ' || c == '\t' {
args = append(args, current)
current = ""
state = "start"
} else {
current += string(c)
}
continue
}
if c != ' ' && c != '\t' {
state = "arg"
current += string(c)
}
}
if state == "quotes" {
return []string{}, fmt.Errorf("unclosed quote in command line: %s", cmd)
}
if current != "" {
args = append(args, current)
}
if len(args) == 0 {
return []string{}, errors.New("empty command line")
}
log.Debugf("Parsed literal %+q to list %+q", cmd, args)
return args, nil
}

View file

@ -1,161 +0,0 @@
package actions
import (
"strings"
"testing"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
)
func TestParseWorkflowsFile(t *testing.T) {
log.SetLevel(log.DebugLevel)
conf := `
workflow "build-and-deploy" {
on = "push"
resolves = ["deploy"]
}
action "build" {
uses = "./action1"
args = "echo 'build'"
}
action "test" {
uses = "docker://ubuntu:18.04"
runs = "echo 'test'"
needs = ["build"]
}
action "deploy" {
uses = "./action2"
args = ["echo","deploy"]
needs = ["test"]
}
action "docker-login" {
uses = "docker://docker"
runs = ["sh", "-c", "echo $DOCKER_AUTH | docker login --username $REGISTRY_USER --password-stdin"]
secrets = ["DOCKER_AUTH"]
env = {
REGISTRY_USER = "username"
}
}
action "unit-tests" {
uses = "./scripts/github_actions"
runs = "yarn test:ci-unittest || echo \"Unit tests failed, but running danger to present the results!\" 2>&1"
}
action "regex-in-args" {
uses = "actions/bin/filter@master"
args = "tag v?[0-9]+\\.[0-9]+\\.[0-9]+"
}
action "regex-in-args-array" {
uses = "actions/bin/filter@master"
args = ["tag","v?[0-9]+\\.[0-9]+\\.[0-9]+"]
}
`
workflows, err := parseWorkflowsFile(strings.NewReader(conf))
assert.Nil(t, err)
assert.Equal(t, 1, len(workflows.Workflow))
w, wName, _ := workflows.getWorkflow("push")
assert.Equal(t, "build-and-deploy", wName)
assert.ElementsMatch(t, []string{"deploy"}, w.Resolves)
actions := []struct {
name string
uses string
needs []string
runs []string
args []string
secrets []string
}{
{"build",
"./action1",
nil,
nil,
[]string{"echo", "build"},
nil,
},
{"test",
"docker://ubuntu:18.04",
[]string{"build"},
[]string{"echo", "test"},
nil,
nil,
},
{"deploy",
"./action2",
[]string{"test"},
nil,
[]string{"echo", "deploy"},
nil,
},
{"docker-login",
"docker://docker",
nil,
[]string{"sh", "-c", "echo $DOCKER_AUTH | docker login --username $REGISTRY_USER --password-stdin"},
nil,
[]string{"DOCKER_AUTH"},
},
{"unit-tests",
"./scripts/github_actions",
nil,
[]string{"yarn", "test:ci-unittest", "||", "echo", "Unit tests failed, but running danger to present the results!", "2>&1"},
nil,
nil,
},
{"regex-in-args",
"actions/bin/filter@master",
nil,
nil,
[]string{"tag", `v?[0-9]+\.[0-9]+\.[0-9]+`},
nil,
},
{"regex-in-args-array",
"actions/bin/filter@master",
nil,
nil,
[]string{"tag", `v?[0-9]+\.[0-9]+\.[0-9]+`},
nil,
},
}
for _, exp := range actions {
act, _ := workflows.getAction(exp.name)
assert.Equal(t, exp.uses, act.Uses, "[%s] Uses", exp.name)
if exp.needs == nil {
assert.Nil(t, act.Needs, "[%s] Needs", exp.name)
} else {
assert.ElementsMatch(t, exp.needs, act.Needs, "[%s] Needs", exp.name)
}
if exp.runs == nil {
assert.Nil(t, act.Runs, "[%s] Runs", exp.name)
} else {
assert.ElementsMatch(t, exp.runs, act.Runs, "[%s] Runs", exp.name)
}
if exp.args == nil {
assert.Nil(t, act.Args, "[%s] Args", exp.name)
} else {
assert.ElementsMatch(t, exp.args, act.Args, "[%s] Args", exp.name)
}
/*
if exp.env == nil {
assert.Nil(t, act.Env, "[%s] Env", exp.name)
} else {
assert.ElementsMatch(t, exp.env, act.Env, "[%s] Env", exp.name)
}
*/
if exp.secrets == nil {
assert.Nil(t, act.Secrets, "[%s] Secrets", exp.name)
} else {
assert.ElementsMatch(t, exp.secrets, act.Secrets, "[%s] Secrets", exp.name)
}
}
}

View file

@ -6,15 +6,17 @@ import (
"path/filepath"
"sort"
"github.com/actions/workflow-parser/model"
"github.com/actions/workflow-parser/parser"
"github.com/nektos/act/common"
log "github.com/sirupsen/logrus"
)
type runnerImpl struct {
config *RunnerConfig
workflows *workflowsFile
tempDir string
eventJSON string
config *RunnerConfig
workflowConfig *model.Configuration
tempDir string
eventJSON string
}
// NewRunner Creates a new Runner
@ -56,7 +58,15 @@ func (runner *runnerImpl) setupWorkflows() error {
defer workflowReader.Close()
runner.workflows, err = parseWorkflowsFile(workflowReader)
runner.workflowConfig, err = parser.Parse(workflowReader)
/*
if err != nil {
parserError := err.(*parser.ParserError)
for _, e := range parserError.Errors {
fmt.Fprintln(os.Stderr, e)
}
}
*/
return err
}
@ -88,7 +98,7 @@ func (runner *runnerImpl) resolvePath(path string) string {
func (runner *runnerImpl) ListEvents() []string {
log.Debugf("Listing all events")
events := make([]string, 0)
for _, w := range runner.workflows.Workflow {
for _, w := range runner.workflowConfig.Workflows {
events = append(events, w.On)
}
@ -103,17 +113,14 @@ func (runner *runnerImpl) ListEvents() []string {
// GraphEvent builds an execution path
func (runner *runnerImpl) GraphEvent(eventName string) ([][]string, error) {
log.Debugf("Listing actions for event '%s'", eventName)
workflow, _, err := runner.workflows.getWorkflow(eventName)
if err != nil {
return nil, err
}
return runner.workflows.newExecutionGraph(workflow.Resolves...), nil
resolves := runner.resolveEvent(runner.config.EventName)
return newExecutionGraph(runner.workflowConfig, resolves...), nil
}
// RunAction runs a set of actions in parallel, and their dependencies
func (runner *runnerImpl) RunActions(actionNames ...string) error {
log.Debugf("Running actions %+q", actionNames)
graph := runner.workflows.newExecutionGraph(actionNames...)
graph := newExecutionGraph(runner.workflowConfig, actionNames...)
pipeline := make([]common.Executor, 0)
for _, actions := range graph {
@ -131,15 +138,32 @@ func (runner *runnerImpl) RunActions(actionNames ...string) error {
// RunEvent runs the actions for a single event
func (runner *runnerImpl) RunEvent() error {
log.Debugf("Running event '%s'", runner.config.EventName)
workflow, _, err := runner.workflows.getWorkflow(runner.config.EventName)
if err != nil {
return err
}
log.Debugf("Running actions %s -> %s", runner.config.EventName, workflow.Resolves)
return runner.RunActions(workflow.Resolves...)
resolves := runner.resolveEvent(runner.config.EventName)
log.Debugf("Running actions %s -> %s", runner.config.EventName, resolves)
return runner.RunActions(resolves...)
}
func (runner *runnerImpl) Close() error {
return os.RemoveAll(runner.tempDir)
}
// get list of resolves for an event
func (runner *runnerImpl) resolveEvent(eventName string) []string {
workflows := runner.workflowConfig.GetWorkflows(runner.config.EventName)
resolves := make([]string, 0)
for _, workflow := range workflows {
for _, resolve := range workflow.Resolves {
found := false
for _, r := range resolves {
if r == resolve {
found = true
break
}
}
if !found {
resolves = append(resolves, resolve)
}
}
}
return resolves
}

View file

@ -9,25 +9,96 @@ import (
"path/filepath"
"regexp"
"github.com/actions/workflow-parser/model"
"github.com/nektos/act/common"
"github.com/nektos/act/container"
log "github.com/sirupsen/logrus"
)
func (runner *runnerImpl) newActionExecutor(actionName string) common.Executor {
action, err := runner.workflows.getAction(actionName)
action := runner.workflowConfig.GetAction(actionName)
if action == nil {
return common.NewErrorExecutor(fmt.Errorf("Unable to find action named '%s'", actionName))
}
executors := make([]common.Executor, 0)
image, err := runner.addImageExecutor(action, &executors)
if err != nil {
return common.NewErrorExecutor(err)
}
env := make(map[string]string)
for _, applier := range []environmentApplier{action, runner} {
applier.applyEnvironment(env)
err = runner.addRunExecutor(action, image, &executors)
if err != nil {
return common.NewErrorExecutor(err)
}
env["GITHUB_ACTION"] = actionName
logger := newActionLogger(actionName, runner.config.Dryrun)
log.Debugf("Using '%s' for action '%s'", action.Uses, actionName)
return common.NewPipelineExecutor(executors...)
}
func (runner *runnerImpl) addImageExecutor(action *model.Action, executors *[]common.Executor) (string, error) {
var image string
logger := newActionLogger(action.Identifier, runner.config.Dryrun)
log.Debugf("Using '%s' for action '%s'", action.Uses, action.Identifier)
in := container.DockerExecutorInput{
Ctx: runner.config.Ctx,
Logger: logger,
Dryrun: runner.config.Dryrun,
}
switch uses := action.Uses.(type) {
case *model.UsesDockerImage:
image = uses.Image
*executors = append(*executors, container.NewDockerPullExecutor(container.NewDockerPullExecutorInput{
DockerExecutorInput: in,
Image: image,
}))
case *model.UsesPath:
contextDir := filepath.Join(runner.config.WorkingDir, uses.String())
sha, _, err := common.FindGitRevision(contextDir)
if err != nil {
log.Warnf("Unable to determine git revision: %v", err)
sha = "latest"
}
image = fmt.Sprintf("%s:%s", filepath.Base(contextDir), sha)
*executors = append(*executors, container.NewDockerBuildExecutor(container.NewDockerBuildExecutorInput{
DockerExecutorInput: in,
ContextDir: contextDir,
ImageTag: image,
}))
case *model.UsesRepository:
image = fmt.Sprintf("%s:%s", filepath.Base(uses.Repository), uses.Ref)
cloneURL := fmt.Sprintf("https://github.com/%s", uses.Repository)
cloneDir := filepath.Join(os.TempDir(), "act", action.Uses.String())
*executors = append(*executors, common.NewGitCloneExecutor(common.NewGitCloneExecutorInput{
URL: cloneURL,
Ref: uses.Ref,
Dir: cloneDir,
Logger: logger,
Dryrun: runner.config.Dryrun,
}))
contextDir := filepath.Join(cloneDir, uses.Path)
*executors = append(*executors, container.NewDockerBuildExecutor(container.NewDockerBuildExecutorInput{
DockerExecutorInput: in,
ContextDir: contextDir,
ImageTag: image,
}))
default:
return "", fmt.Errorf("unable to determine executor type for image '%s'", action.Uses)
}
return image, nil
}
func (runner *runnerImpl) addRunExecutor(action *model.Action, image string, executors *[]common.Executor) error {
logger := newActionLogger(action.Identifier, runner.config.Dryrun)
log.Debugf("Using '%s' for action '%s'", action.Uses, action.Identifier)
in := container.DockerExecutorInput{
Ctx: runner.config.Ctx,
@ -35,61 +106,37 @@ func (runner *runnerImpl) newActionExecutor(actionName string) common.Executor {
Dryrun: runner.config.Dryrun,
}
var image string
executors := make([]common.Executor, 0)
if imageRef, ok := parseImageReference(action.Uses); ok {
executors = append(executors, container.NewDockerPullExecutor(container.NewDockerPullExecutorInput{
DockerExecutorInput: in,
Image: imageRef,
}))
image = imageRef
} else if contextDir, imageTag, ok := parseImageLocal(runner.config.WorkingDir, action.Uses); ok {
executors = append(executors, container.NewDockerBuildExecutor(container.NewDockerBuildExecutorInput{
DockerExecutorInput: in,
ContextDir: contextDir,
ImageTag: imageTag,
}))
image = imageTag
} else if cloneURL, ref, path, ok := parseImageGithub(action.Uses); ok {
cloneDir := filepath.Join(os.TempDir(), "act", action.Uses)
executors = append(executors, common.NewGitCloneExecutor(common.NewGitCloneExecutorInput{
URL: cloneURL,
Ref: ref,
Dir: cloneDir,
Logger: logger,
Dryrun: runner.config.Dryrun,
}))
contextDir := filepath.Join(cloneDir, path)
imageTag := fmt.Sprintf("%s:%s", filepath.Base(cloneURL.Path), ref)
executors = append(executors, container.NewDockerBuildExecutor(container.NewDockerBuildExecutorInput{
DockerExecutorInput: in,
ContextDir: contextDir,
ImageTag: imageTag,
}))
image = imageTag
} else {
return common.NewErrorExecutor(fmt.Errorf("unable to determine executor type for image '%s'", action.Uses))
env := make(map[string]string)
for _, applier := range []environmentApplier{newActionEnvironmentApplier(action), runner} {
applier.applyEnvironment(env)
}
env["GITHUB_ACTION"] = action.Identifier
ghReader, err := runner.createGithubTarball()
if err != nil {
return common.NewErrorExecutor(err)
return err
}
envList := make([]string, 0)
for k, v := range env {
envList = append(envList, fmt.Sprintf("%s=%s", k, v))
}
executors = append(executors, container.NewDockerRunExecutor(container.NewDockerRunExecutorInput{
var cmd, entrypoint []string
if action.Args != nil {
cmd = action.Args.Split()
}
if action.Runs != nil {
entrypoint = action.Runs.Split()
}
*executors = append(*executors, container.NewDockerRunExecutor(container.NewDockerRunExecutorInput{
DockerExecutorInput: in,
Cmd: action.Args,
Entrypoint: action.Runs,
Cmd: cmd,
Entrypoint: entrypoint,
Image: image,
WorkingDir: "/github/workspace",
Env: envList,
Name: runner.createContainerName(actionName),
Name: runner.createContainerName(action.Identifier),
Binds: []string{
fmt.Sprintf("%s:%s", runner.config.WorkingDir, "/github/workspace"),
fmt.Sprintf("%s:%s", runner.tempDir, "/github/home"),
@ -99,13 +146,17 @@ func (runner *runnerImpl) newActionExecutor(actionName string) common.Executor {
ReuseContainers: runner.config.ReuseContainers,
}))
return common.NewPipelineExecutor(executors...)
return nil
}
func (runner *runnerImpl) applyEnvironment(env map[string]string) {
repoPath := runner.config.WorkingDir
_, workflowName, _ := runner.workflows.getWorkflow(runner.config.EventName)
workflows := runner.workflowConfig.GetWorkflows(runner.config.EventName)
if len(workflows) == 0 {
return
}
workflowName := workflows[0].Identifier
env["HOME"] = "/github/home"
env["GITHUB_ACTOR"] = "nektos/act"

View file

@ -1,91 +1,47 @@
package actions
import (
"fmt"
"path/filepath"
"context"
"testing"
"github.com/nektos/act/common"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"gotest.tools/assert"
)
func TestParseImageReference(t *testing.T) {
log.SetLevel(log.DebugLevel)
tables := []struct {
refIn string
refOut string
ok bool
}{
{"docker://myhost.com/foo/bar", "myhost.com/foo/bar", true},
{"docker://ubuntu", "ubuntu", true},
{"docker://ubuntu:18.04", "ubuntu:18.04", true},
{"docker://cibuilds/hugo:0.53", "cibuilds/hugo:0.53", true},
{"http://google.com:8080", "", false},
{"./foo", "", false},
func TestRunEvent(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}
tables := []struct {
workflowPath string
eventName string
errorMessage string
}{
{"basic.workflow", "push", ""},
{"pipe.workflow", "push", ""},
{"fail.workflow", "push", "exit with `FAILURE`: 1"},
{"regex.workflow", "push", "exit with `NEUTRAL`: 78"},
{"gitref.workflow", "push", ""},
{"env.workflow", "push", ""},
}
log.SetLevel(log.DebugLevel)
for _, table := range tables {
refOut, ok := parseImageReference(table.refIn)
assert.Equal(t, table.refOut, refOut)
assert.Equal(t, table.ok, ok)
}
runnerConfig := &RunnerConfig{
Ctx: context.Background(),
WorkflowPath: table.workflowPath,
WorkingDir: "testdata",
EventName: table.eventName,
}
runner, err := NewRunner(runnerConfig)
assert.NilError(t, err, table.workflowPath)
}
func TestParseImageLocal(t *testing.T) {
log.SetLevel(log.DebugLevel)
tables := []struct {
pathIn string
contextDir string
refTag string
ok bool
}{
{"docker://myhost.com/foo/bar", "", "", false},
{"http://google.com:8080", "", "", false},
{"example/action1", "", "", false},
{"./example/action1", "/example/action1", "action1:", true},
}
revision, _, err := common.FindGitRevision(".")
assert.Nil(t, err)
basedir, err := filepath.Abs("..")
assert.Nil(t, err)
for _, table := range tables {
contextDir, refTag, ok := parseImageLocal(basedir, table.pathIn)
assert.Equal(t, table.ok, ok, "ok match for %s", table.pathIn)
if ok {
assert.Equal(t, fmt.Sprintf("%s%s", basedir, table.contextDir), contextDir, "context dir doesn't match for %s", table.pathIn)
assert.Equal(t, fmt.Sprintf("%s%s", table.refTag, revision), refTag)
err = runner.RunEvent()
if table.errorMessage == "" {
assert.NilError(t, err, table.workflowPath)
} else {
assert.Error(t, err, table.errorMessage)
}
}
}
func TestParseImageGithub(t *testing.T) {
log.SetLevel(log.DebugLevel)
tables := []struct {
image string
cloneURL string
ref string
path string
ok bool
}{
{"nektos/act", "https://github.com/nektos/act", "master", ".", true},
{"nektos/act/foo", "https://github.com/nektos/act", "master", "foo", true},
{"nektos/act@xxxxx", "https://github.com/nektos/act", "xxxxx", ".", true},
{"nektos/act/bar/baz@zzzzz", "https://github.com/nektos/act", "zzzzz", "bar/baz", true},
{"assimovt/actions-github-deploy/github-deploy@deployment-status-metadata", "https://github.com/assimovt/actions-github-deploy", "deployment-status-metadata", "github-deploy", true},
{"nektos/zzzzundefinedzzzz", "", "", "", false},
}
for _, table := range tables {
cloneURL, ref, path, ok := parseImageGithub(table.image)
assert.Equal(t, table.ok, ok, "ok match for %s", table.image)
if ok {
assert.Equal(t, table.cloneURL, cloneURL.String())
assert.Equal(t, table.ref, ref)
assert.Equal(t, table.path, path)
}
}
}

View file

@ -10,7 +10,7 @@ action "build" {
action "test" {
uses = "docker://ubuntu:18.04"
args = "echo 'test'"
args = "env"
needs = ["build"]
}

9
actions/testdata/env.workflow vendored Normal file
View file

@ -0,0 +1,9 @@
workflow "test" {
on = "push"
resolves = ["test-action"]
}
action "test-action" {
uses = "docker://alpine:3.9"
runs = ["sh", "-c", "echo $GITHUB_REPOSITORY | grep '^nektos/act$'"]
}

13
actions/testdata/fail.workflow vendored Normal file
View file

@ -0,0 +1,13 @@
workflow "test" {
on = "push"
resolves = ["test-action"]
}
action "test-action" {
uses = "docker://alpine:3.9"
runs = ["sh", "-c", "echo $IN | grep $OUT"]
env = {
IN = "foo"
OUT = "bar"
}
}

14
actions/testdata/gitref.workflow vendored Normal file
View file

@ -0,0 +1,14 @@
workflow "New workflow" {
on = "push"
resolves = ["branch-ref","commit-ref"]
}
action "branch-ref" {
uses = "actions/docker/cli@master"
args = "version"
}
action "commit-ref" {
uses = "actions/docker/cli@c08a5fc9e0286844156fefff2c141072048141f6"
args = "version"
}

13
actions/testdata/pipe.workflow vendored Normal file
View file

@ -0,0 +1,13 @@
workflow "test" {
on = "push"
resolves = ["test-action"]
}
action "test-action" {
uses = "docker://alpine:3.9"
runs = ["sh", "-c", "echo $IN | grep $OUT"]
env = {
IN = "foo"
OUT = "foo"
}
}

9
actions/testdata/regex.workflow vendored Normal file
View file

@ -0,0 +1,9 @@
workflow "New workflow" {
on = "push"
resolves = ["filter-version-before-deploy"]
}
action "filter-version-before-deploy" {
uses = "actions/bin/filter@master"
args = "tag z?[0-9]+\\.[0-9]+\\.[0-9]+"
}

View file

@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"io/ioutil"
"net/url"
"os"
"path"
"path/filepath"
@ -185,7 +184,7 @@ func findGitDirectory(fromFile string) (string, error) {
// NewGitCloneExecutorInput the input for the NewGitCloneExecutor
type NewGitCloneExecutorInput struct {
URL *url.URL
URL string
Ref string
Dir string
Logger *log.Entry
@ -195,8 +194,8 @@ type NewGitCloneExecutorInput struct {
// NewGitCloneExecutor creates an executor to clone git repos
func NewGitCloneExecutor(input NewGitCloneExecutorInput) Executor {
return func() error {
input.Logger.Infof("git clone '%s'", input.URL.String())
input.Logger.Debugf(" cloning %s to %s", input.URL.String(), input.Dir)
input.Logger.Infof("git clone '%s' # ref=%s", input.URL, input.Ref)
input.Logger.Debugf(" cloning %s to %s", input.URL, input.Dir)
if input.Dryrun {
return nil
@ -210,11 +209,12 @@ func NewGitCloneExecutor(input NewGitCloneExecutorInput) Executor {
r, err := git.PlainOpen(input.Dir)
if err != nil {
r, err = git.PlainClone(input.Dir, false, &git.CloneOptions{
URL: input.URL.String(),
Progress: input.Logger.WriterLevel(log.DebugLevel),
ReferenceName: refName,
URL: input.URL,
Progress: input.Logger.WriterLevel(log.DebugLevel),
//ReferenceName: refName,
})
if err != nil {
input.Logger.Errorf("Unable to clone %v %s: %v", input.URL, refName, err)
return err
}
}
@ -225,17 +225,24 @@ func NewGitCloneExecutor(input NewGitCloneExecutorInput) Executor {
}
err = w.Pull(&git.PullOptions{
ReferenceName: refName,
Force: true,
//ReferenceName: refName,
Force: true,
})
if err != nil && err.Error() != "already up-to-date" {
input.Logger.Errorf("Unable to pull %s: %v", refName, err)
}
input.Logger.Debugf("Cloned %s to %s", input.URL.String(), input.Dir)
input.Logger.Debugf("Cloned %s to %s", input.URL, input.Dir)
hash, err := r.ResolveRevision(plumbing.Revision(input.Ref))
if err != nil {
input.Logger.Errorf("Unable to resolve %s: %v", input.Ref, err)
return err
}
err = w.Checkout(&git.CheckoutOptions{
Branch: refName,
Force: true,
//Branch: refName,
Hash: *hash,
Force: true,
})
if err != nil {
input.Logger.Errorf("Unable to checkout %s: %v", refName, err)

View file

@ -204,7 +204,7 @@ func waitContainer(input NewDockerRunExecutorInput, cli *client.Client, containe
if statusCode == 0 {
return nil
} else if statusCode == 78 {
return fmt.Errorf("exiting with `NEUTRAL`: 78")
return fmt.Errorf("exit with `NEUTRAL`: 78")
}
return fmt.Errorf("exit with `FAILURE`: %v", statusCode)

View file

@ -1,3 +0,0 @@
{
"foo": "bar"
}

29
go.mod
View file

@ -4,40 +4,47 @@ require (
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/Microsoft/go-winio v0.4.11 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/actions/workflow-parser v1.0.0
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 // indirect
github.com/docker/distribution v2.7.0+incompatible // indirect
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/docker/docker v1.13.1
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.3.3 // indirect
github.com/emirpasic/gods v1.12.0 // indirect
github.com/go-ini/ini v1.41.0
github.com/gogo/protobuf v1.2.0 // indirect
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
github.com/gorilla/context v1.1.1 // indirect
github.com/gorilla/mux v1.6.2 // indirect
github.com/hashicorp/hcl v1.0.0
github.com/gorilla/mux v1.7.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jtolds/gls v4.2.1+incompatible // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/opencontainers/runc v0.1.1 // indirect
github.com/pkg/errors v0.8.1 // indirect
github.com/sirupsen/logrus v1.3.0
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 // indirect
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c // indirect
github.com/soniakeys/graph v0.0.0 // indirect
github.com/spf13/cobra v0.0.3
github.com/spf13/pflag v1.0.3 // indirect
github.com/stretchr/testify v1.3.0
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc
golang.org/x/net v0.0.0-20181220203305-927f97764cc3 // indirect
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 // indirect
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect
golang.org/x/sys v0.0.0-20190102155601-82a175fd1598 // indirect
golang.org/x/sys v0.0.0-20190201152629-afcc84fd7533 // indirect
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 // indirect
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c // indirect
google.golang.org/grpc v1.17.0 // indirect
google.golang.org/genproto v0.0.0-20190128161407-8ac453e89fca // indirect
google.golang.org/grpc v1.18.0 // indirect
gopkg.in/ini.v1 v1.41.0 // indirect
gopkg.in/src-d/go-git.v4 v4.8.1
gopkg.in/src-d/go-billy.v4 v4.3.0 // indirect
gopkg.in/src-d/go-git-fixtures.v3 v3.3.0 // indirect
gopkg.in/src-d/go-git.v4 v4.9.1
gopkg.in/yaml.v2 v2.2.2
gotest.tools v2.2.0+incompatible // indirect
gotest.tools v2.2.0+incompatible
)
replace github.com/docker/docker => github.com/docker/engine v0.0.0-20181106193140-f5749085e9cb

57
go.sum
View file

@ -5,6 +5,8 @@ github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/actions/workflow-parser v1.0.0 h1:Zz2Ke31f3OMYCSzU2pqZSsk/Oz+lWXfEiXMisjxgGcc=
github.com/actions/workflow-parser v1.0.0/go.mod h1:jz9ZVl8zUIcjMfDQearQjvUHIBhx9l1ys4keDd6be34=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
@ -15,8 +17,8 @@ github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/distribution v2.7.0+incompatible h1:neUDAlf3wX6Ml4HdqTrbcOHXtfRN0TFIwt6YFL7N9RU=
github.com/docker/distribution v2.7.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/engine v0.0.0-20181106193140-f5749085e9cb h1:PyjxRdW1mqCmSoxy/6uP01P7CGbsD+woX+oOWbaUPwQ=
@ -27,6 +29,8 @@ github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/emirpasic/gods v1.9.0 h1:rUF4PuzEjMChMiNsVjdI+SyLu7rEqpQ5reNFnhC7oFo=
github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/gliderlabs/ssh v0.1.1 h1:j3L6gSLQalDETeEg/Jg0mGY0/y/N6zI2xX1978P0Uqw=
@ -37,6 +41,7 @@ github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -44,10 +49,8 @@ github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U=
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c h1:kQWxfPIHVLbgLzphqk3QUflDy9QdksZR4ygR807bpy0=
@ -72,6 +75,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
@ -90,10 +95,14 @@ github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY=
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w=
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
github.com/soniakeys/bits v1.0.0 h1:Rune9VFefdJvLE0Q5iRCVGiKdSu2iDihs2I6SCm7evw=
github.com/soniakeys/bits v1.0.0/go.mod h1:7yJHB//UizrUr64VFneewK6SX5oeCf0SMbDYe2ey1JA=
github.com/soniakeys/graph v0.0.0 h1:C/Rr8rv9wbhZIsYHcWJFoI84pkipJocMYdRteE+/PQA=
github.com/soniakeys/graph v0.0.0/go.mod h1:lxpIbor/bIzWUAqvt1Dx92Hr63uWeyuEAbPnsjYbVwM=
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
@ -111,13 +120,16 @@ github.com/xanzy/ssh-agent v0.2.0 h1:Adglfbi5p9Z0BmK2oKU9nTG+zKfniSfnaMYB+ULd+Ro
github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc h1:F5tKCVGp+MUAHhKp5MZtGqAlGX3+oCsiL1Q629FL90M=
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613 h1:MQ/ZZiDsUapFFiMS+vzwXkCTeEKaum+Do5rINYJDmxc=
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 h1:ulvT7fqt0yHWzpJwI57MezWnYDVpCAYBVuYst/L+fAY=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -127,10 +139,12 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190102155601-82a175fd1598 h1:S8GOgffXV1X3fpVG442QRfWOt0iFl79eHJ7OPt725bo=
golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190201152629-afcc84fd7533 h1:bLfqnzrpeG4usq5OvMCrwTdmMJ6aTmlCuo1eKl0mhkI=
golang.org/x/sys v0.0.0-20190201152629-afcc84fd7533/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52 h1:JG/0uqcGdTNgq7FdU+61l5Pdmb8putNZlXb65bJBROs=
@ -138,8 +152,11 @@ golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGm
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.17.0 h1:TRJYBgMclJvGYn2rIMjj+h9KtMt5r1Ij7ODVRIZkwhk=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/genproto v0.0.0-20190128161407-8ac453e89fca h1:L1odPN6KVjhk0Lbg41BKcjGjP7ELTvh/qDcyh6hEfv0=
google.golang.org/genproto v0.0.0-20190128161407-8ac453e89fca/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.18.0 h1:IZl7mfBGfbhYx2p2rKRtYgDFw6SBz+kclmxYrCksPPA=
google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
@ -148,10 +165,14 @@ gopkg.in/ini.v1 v1.41.0 h1:Ka3ViY6gNYSKiVy71zXBEqKplnV35ImDLVG+8uoIklE=
gopkg.in/ini.v1 v1.41.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/src-d/go-billy.v4 v4.2.1 h1:omN5CrMrMcQ+4I8bJ0wEhOBPanIRWzFC953IiXKdYzo=
gopkg.in/src-d/go-billy.v4 v4.2.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
gopkg.in/src-d/go-billy.v4 v4.3.0 h1:KtlZ4c1OWbIs4jCv5ZXrTqG8EQocr0g/d4DjNg70aek=
gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1 h1:XWW/s5W18RaJpmo1l0IYGqXKuJITWRFuA45iOf1dKJs=
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
gopkg.in/src-d/go-git.v4 v4.8.1 h1:aAyBmkdE1QUUEHcP4YFCGKmsMQRAuRmUcPEQR7lOAa0=
gopkg.in/src-d/go-git.v4 v4.8.1/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk=
gopkg.in/src-d/go-git-fixtures.v3 v3.3.0 h1:AxUOwLW3at53ysFqs0Lg+H+8KSQXl7AEHBvWj8wEsT8=
gopkg.in/src-d/go-git-fixtures.v3 v3.3.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
gopkg.in/src-d/go-git.v4 v4.9.1 h1:0oKHJZY8tM7B71378cfTg2c5jmWyNlXvestTT6WfY+4=
gopkg.in/src-d/go-git.v4 v4.9.1/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=

21
vendor/github.com/actions/workflow-parser/LICENSE generated vendored Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 GitHub
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.

View file

@ -0,0 +1,38 @@
package model
import (
"strings"
)
// Command represents the optional "runs" and "args" attributes.
// Each one takes one of two forms:
// - runs="entrypoint arg1 arg2 ..."
// - runs=[ "entrypoint", "arg1", "arg2", ... ]
type Command interface {
isCommand()
Split() []string
}
// StringCommand represents the string based form of the "runs" or "args"
// attribute.
// - runs="entrypoint arg1 arg2 ..."
type StringCommand struct {
Value string
}
// ListCommand represents the list based form of the "runs" or "args" attribute.
// - runs=[ "entrypoint", "arg1", "arg2", ... ]
type ListCommand struct {
Values []string
}
func (s *StringCommand) isCommand() {}
func (l *ListCommand) isCommand() {}
func (s *StringCommand) Split() []string {
return strings.Fields(s.Value)
}
func (l *ListCommand) Split() []string {
return l.Values
}

View file

@ -0,0 +1,64 @@
package model
import (
"strings"
)
// Configuration is a parsed main.workflow file
type Configuration struct {
Actions []*Action
Workflows []*Workflow
}
// Action represents a single "action" stanza in a .workflow file.
type Action struct {
Identifier string
Uses Uses
Runs, Args Command
Needs []string
Env map[string]string
Secrets []string
}
// Workflow represents a single "workflow" stanza in a .workflow file.
type Workflow struct {
Identifier string
On string
Resolves []string
}
// GetAction looks up action by identifier.
//
// If the action is not found, nil is returned.
func (c *Configuration) GetAction(id string) *Action {
for _, action := range c.Actions {
if action.Identifier == id {
return action
}
}
return nil
}
// GetWorkflow looks up a workflow by identifier.
//
// If the workflow is not found, nil is returned.
func (c *Configuration) GetWorkflow(id string) *Workflow {
for _, workflow := range c.Workflows {
if workflow.Identifier == id {
return workflow
}
}
return nil
}
// GetWorkflows gets all Workflow structures that match a given type of event.
// e.g., GetWorkflows("push")
func (c *Configuration) GetWorkflows(eventType string) []*Workflow {
var ret []*Workflow
for _, workflow := range c.Workflows {
if strings.EqualFold(workflow.On, eventType) {
ret = append(ret, workflow)
}
}
return ret
}

View file

@ -0,0 +1,57 @@
package model
import (
"fmt"
)
type Uses interface {
fmt.Stringer
isUses()
}
// UsesDockerImage represents `uses = "docker://<image>"`
type UsesDockerImage struct {
Image string
}
// UsesRepository represents `uses = "<owner>/<repo>[/<path>]@<ref>"`
type UsesRepository struct {
Repository string
Path string
Ref string
}
// UsesPath represents `uses = "./<path>"`
type UsesPath struct {
Path string
}
// UsesInvalid represents any invalid `uses = "<raw>"` value
type UsesInvalid struct {
Raw string
}
func (u *UsesDockerImage) isUses() {}
func (u *UsesRepository) isUses() {}
func (u *UsesPath) isUses() {}
func (u *UsesInvalid) isUses() {}
func (u *UsesDockerImage) String() string {
return fmt.Sprintf("docker://%s", u.Image)
}
func (u *UsesRepository) String() string {
if u.Path == "" {
return fmt.Sprintf("%s@%s", u.Repository, u.Ref)
}
return fmt.Sprintf("%s/%s@%s", u.Repository, u.Path, u.Ref)
}
func (u *UsesPath) String() string {
return fmt.Sprintf("./%s", u.Path)
}
func (u *UsesInvalid) String() string {
return u.Raw
}

View file

@ -0,0 +1,136 @@
package parser
import (
"bytes"
"fmt"
"sort"
"strconv"
"strings"
"github.com/actions/workflow-parser/model"
)
type Error struct {
message string
Errors []*ParseError
Actions []*model.Action
Workflows []*model.Workflow
}
func (e *Error) Error() string {
buffer := bytes.NewBuffer(nil)
buffer.WriteString(e.message)
for _, pe := range e.Errors {
buffer.WriteString("\n ")
buffer.WriteString(pe.Error())
}
return buffer.String()
}
// FirstError searches a Configuration for the first error at or above a
// given severity level. Checking the return value against nil is a good
// way to see if the file has any errors at or above the given severity.
// A caller intending to execute the file might check for
// `errors.FirstError(parser.WARNING)`, while a caller intending to
// display the file might check for `errors.FirstError(parser.FATAL)`.
func (e *Error) FirstError(severity Severity) error {
for _, pe := range e.Errors {
if pe.Severity >= severity {
return pe
}
}
return nil
}
// ParseError represents an error identified by the parser, either syntactic
// (HCL) or semantic (.workflow) in nature. There are fields for location
// (File, Line, Column), severity, and base error string. The `Error()`
// function on this type concatenates whatever bits of the location are
// available with the message. The severity is only used for filtering.
type ParseError struct {
message string
Pos ErrorPos
Severity Severity
}
// ErrorPos represents the location of an error in a user's workflow
// file(s).
type ErrorPos struct {
File string
Line int
Column int
}
// newFatal creates a new error at the FATAL level, indicating that the
// file is so broken it should not be displayed.
func newFatal(pos ErrorPos, format string, a ...interface{}) *ParseError {
return &ParseError{
message: fmt.Sprintf(format, a...),
Pos: pos,
Severity: FATAL,
}
}
// newError creates a new error at the ERROR level, indicating that the
// file can be displayed but cannot be run.
func newError(pos ErrorPos, format string, a ...interface{}) *ParseError {
return &ParseError{
message: fmt.Sprintf(format, a...),
Pos: pos,
Severity: ERROR,
}
}
// newWarning creates a new error at the WARNING level, indicating that
// the file might be runnable but might not execute as intended.
func newWarning(pos ErrorPos, format string, a ...interface{}) *ParseError {
return &ParseError{
message: fmt.Sprintf(format, a...),
Pos: pos,
Severity: WARNING,
}
}
func (e *ParseError) Error() string {
var sb strings.Builder
if e.Pos.Line != 0 {
sb.WriteString("Line ") // nolint: errcheck
sb.WriteString(strconv.Itoa(e.Pos.Line)) // nolint: errcheck
sb.WriteString(": ") // nolint: errcheck
}
if sb.Len() > 0 {
sb.WriteString(e.message) // nolint: errcheck
return sb.String()
}
return e.message
}
const (
_ = iota
// WARNING indicates a mistake that might affect correctness
WARNING
// ERROR indicates a mistake that prevents execution of any workflows in the file
ERROR
// FATAL indicates a mistake that prevents even drawing the file
FATAL
)
// Severity represents the level of an error encountered while parsing a
// workflow file. See the comments for WARNING, ERROR, and FATAL, above.
type Severity int
type errorList []*ParseError
func (a errorList) Len() int { return len(a) }
func (a errorList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a errorList) Less(i, j int) bool { return a[i].Pos.Line < a[j].Pos.Line }
// sortErrors sorts the errors reported by the parser. Do this after
// parsing is complete. The sort is stable, so order is preserved within
// a single line: left to right, syntax errors before validation errors.
func (errors errorList) sort() {
sort.Stable(errors)
}

View file

@ -0,0 +1,42 @@
package parser
import (
"strings"
)
// isAllowedEventType returns true if the event type is supported.
func isAllowedEventType(eventType string) bool {
_, ok := eventTypeWhitelist[strings.ToLower(eventType)]
return ok
}
// https://developer.github.com/actions/creating-workflows/workflow-configuration-options/#events-supported-in-workflow-files
var eventTypeWhitelist = map[string]struct{}{
"check_run": {},
"check_suite": {},
"commit_comment": {},
"create": {},
"delete": {},
"deployment": {},
"deployment_status": {},
"fork": {},
"gollum": {},
"issue_comment": {},
"issues": {},
"label": {},
"member": {},
"milestone": {},
"page_build": {},
"project_card": {},
"project_column": {},
"project": {},
"public": {},
"pull_request_review_comment": {},
"pull_request_review": {},
"pull_request": {},
"push": {},
"release": {},
"repository_dispatch": {},
"status": {},
"watch": {},
}

View file

@ -0,0 +1,15 @@
package parser
type OptionFunc func(*Parser)
func WithSuppressWarnings() OptionFunc {
return func(ps *Parser) {
ps.suppressSeverity = WARNING
}
}
func WithSuppressErrors() OptionFunc {
return func(ps *Parser) {
ps.suppressSeverity = ERROR
}
}

View file

@ -0,0 +1,807 @@
package parser
import (
"fmt"
"io"
"io/ioutil"
"regexp"
"strings"
"github.com/actions/workflow-parser/model"
"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/ast"
hclparser "github.com/hashicorp/hcl/hcl/parser"
"github.com/hashicorp/hcl/hcl/token"
"github.com/soniakeys/graph"
)
const minVersion = 0
const maxVersion = 0
const maxSecrets = 100
type Parser struct {
version int
actions []*model.Action
workflows []*model.Workflow
errors errorList
posMap map[interface{}]ast.Node
suppressSeverity Severity
}
// Parse parses a .workflow file and return the actions and global variables found within.
func Parse(reader io.Reader, options ...OptionFunc) (*model.Configuration, error) {
// FIXME - check context for deadline?
b, err := ioutil.ReadAll(reader)
if err != nil {
return nil, err
}
root, err := hcl.ParseBytes(b)
if err != nil {
if pe, ok := err.(*hclparser.PosError); ok {
pos := ErrorPos{File: pe.Pos.Filename, Line: pe.Pos.Line, Column: pe.Pos.Column}
errors := errorList{newFatal(pos, pe.Err.Error())}
return nil, &Error{
message: "unable to parse",
Errors: errors,
}
}
return nil, err
}
p := parseAndValidate(root.Node, options...)
if len(p.errors) > 0 {
return nil, &Error{
message: "unable to parse and validate",
Errors: p.errors,
Actions: p.actions,
Workflows: p.workflows,
}
}
return &model.Configuration{
Actions: p.actions,
Workflows: p.workflows,
}, nil
}
// parseAndValidate converts a HCL AST into a Parser and validates
// high-level structure.
// Parameters:
// - root - the contents of a .workflow file, as AST
// Returns:
// - a Parser structure containing actions and workflow definitions
func parseAndValidate(root ast.Node, options ...OptionFunc) *Parser {
p := &Parser{
posMap: make(map[interface{}]ast.Node),
}
for _, option := range options {
option(p)
}
p.parseRoot(root)
p.validate()
p.errors.sort()
return p
}
func (p *Parser) validate() {
p.analyzeDependencies()
p.checkCircularDependencies()
p.checkActions()
p.checkFlows()
}
func uniqStrings(items []string) []string {
seen := make(map[string]bool)
ret := make([]string, 0, len(items))
for _, item := range items {
if !seen[item] {
seen[item] = true
ret = append(ret, item)
}
}
return ret
}
// checkCircularDependencies finds loops in the action graph.
// It emits a fatal error for each cycle it finds, in the order (top to
// bottom, left to right) they appear in the .workflow file.
func (p *Parser) checkCircularDependencies() {
// make a map from action name to node ID, which is the index in the p.actions array
// That is, p.actions[actionmap[X]].Identifier == X
actionmap := make(map[string]graph.NI)
for i, action := range p.actions {
actionmap[action.Identifier] = graph.NI(i)
}
// make an adjacency list representation of the action dependency graph
adjList := make(graph.AdjacencyList, len(p.actions))
for i, action := range p.actions {
adjList[i] = make([]graph.NI, 0, len(action.Needs))
for _, depName := range action.Needs {
if depIdx, ok := actionmap[depName]; ok {
adjList[i] = append(adjList[i], depIdx)
}
}
}
// find cycles, and print a fatal error for each one
g := graph.Directed{AdjacencyList: adjList}
g.Cycles(func(cycle []graph.NI) bool {
node := p.posMap[&p.actions[cycle[len(cycle)-1]].Needs]
p.addFatal(node, "Circular dependency on `%s'", p.actions[cycle[0]].Identifier)
return true
})
}
// checkActions returns error if any actions are syntactically correct but
// have structural errors
func (p *Parser) checkActions() {
secrets := make(map[string]bool)
for _, t := range p.actions {
// Ensure the Action has a `uses` attribute
if t.Uses == nil {
p.addError(p.posMap[t], "Action `%s' must have a `uses' attribute", t.Identifier)
// continue, checking other actions
}
// Ensure there aren't too many secrets
for _, str := range t.Secrets {
if !secrets[str] {
secrets[str] = true
if len(secrets) == maxSecrets+1 {
p.addError(p.posMap[&t.Secrets], "All actions combined must not have more than %d unique secrets", maxSecrets)
}
}
}
// Ensure that no environment variable or secret begins with
// "GITHUB_", unless it's "GITHUB_TOKEN".
// Also ensure that all environment variable names come from the legal
// form for environment variable names.
// Finally, ensure that the same key name isn't used more than once
// between env and secrets, combined.
for k := range t.Env {
p.checkEnvironmentVariable(k, p.posMap[&t.Env])
}
secretVars := make(map[string]bool)
for _, k := range t.Secrets {
p.checkEnvironmentVariable(k, p.posMap[&t.Secrets])
if _, found := t.Env[k]; found {
p.addError(p.posMap[&t.Secrets], "Secret `%s' conflicts with an environment variable with the same name", k)
}
if secretVars[k] {
p.addWarning(p.posMap[&t.Secrets], "Secret `%s' redefined", k)
}
secretVars[k] = true
}
}
}
var envVarChecker = regexp.MustCompile(`\A[A-Za-z_][A-Za-z_0-9]*\z`)
func (p *Parser) checkEnvironmentVariable(key string, node ast.Node) {
if key != "GITHUB_TOKEN" && strings.HasPrefix(key, "GITHUB_") {
p.addWarning(node, "Environment variables and secrets beginning with `GITHUB_' are reserved")
}
if !envVarChecker.MatchString(key) {
p.addWarning(node, "Environment variables and secrets must contain only A-Z, a-z, 0-9, and _ characters, got `%s'", key)
}
}
// checkFlows appends an error if any workflows are syntactically correct but
// have structural errors
func (p *Parser) checkFlows() {
actionmap := makeActionMap(p.actions)
for _, f := range p.workflows {
// make sure there's an `on` attribute
if f.On == "" {
p.addError(p.posMap[f], "Workflow `%s' must have an `on' attribute", f.Identifier)
// continue, checking other workflows
} else if !isAllowedEventType(f.On) {
p.addError(p.posMap[&f.On], "Workflow `%s' has unknown `on' value `%s'", f.Identifier, f.On)
// continue, checking other workflows
}
// make sure that the actions that are resolved all exist
for _, actionID := range f.Resolves {
_, ok := actionmap[actionID]
if !ok {
p.addError(p.posMap[&f.Resolves], "Workflow `%s' resolves unknown action `%s'", f.Identifier, actionID)
// continue, checking other workflows
}
}
}
}
func makeActionMap(actions []*model.Action) map[string]*model.Action {
actionmap := make(map[string]*model.Action)
for _, action := range actions {
actionmap[action.Identifier] = action
}
return actionmap
}
// Fill in Action dependencies for all actions based on explicit dependencies
// declarations.
//
// p.actions is an array of Action objects, as parsed. The Action objects in
// this array are mutated, by setting Action.dependencies for each.
func (p *Parser) analyzeDependencies() {
actionmap := makeActionMap(p.actions)
for _, action := range p.actions {
// analyze explicit dependencies for each "needs" keyword
p.analyzeNeeds(action, actionmap)
}
// uniq all the dependencies lists
for _, action := range p.actions {
if len(action.Needs) >= 2 {
action.Needs = uniqStrings(action.Needs)
}
}
}
func (p *Parser) analyzeNeeds(action *model.Action, actionmap map[string]*model.Action) {
for _, need := range action.Needs {
_, ok := actionmap[need]
if !ok {
p.addError(p.posMap[&action.Needs], "Action `%s' needs nonexistent action `%s'", action.Identifier, need)
// continue, checking other actions
}
}
}
// literalToStringMap converts a object value from the AST to a
// map[string]string. For example, the HCL `{ a="b" c="d" }` becomes the
// Go expression map[string]string{ "a": "b", "c": "d" }.
// If the value doesn't adhere to that format -- e.g.,
// if it's not an object, or it has non-assignment attributes, or if any
// of its values are anything other than a string, the function appends an
// appropriate error.
func (p *Parser) literalToStringMap(node ast.Node) map[string]string {
obj, ok := node.(*ast.ObjectType)
if !ok {
p.addError(node, "Expected object, got %s", typename(node))
return nil
}
p.checkAssignmentsOnly(obj.List, "")
ret := make(map[string]string)
for _, item := range obj.List.Items {
if !isAssignment(item) {
continue
}
str, ok := p.literalToString(item.Val)
if ok {
key := p.identString(item.Keys[0].Token)
if key != "" {
if _, found := ret[key]; found {
p.addWarning(node, "Environment variable `%s' redefined", key)
}
ret[key] = str
}
}
}
return ret
}
func (p *Parser) identString(t token.Token) string {
switch t.Type {
case token.STRING:
return t.Value().(string)
case token.IDENT:
return t.Text
default:
p.addErrorFromToken(t,
"Each identifier should be a string, got %s",
strings.ToLower(t.Type.String()))
return ""
}
}
// literalToStringArray converts a list value from the AST to a []string.
// For example, the HCL `[ "a", "b", "c" ]` becomes the Go expression
// []string{ "a", "b", "c" }.
// If the value doesn't adhere to that format -- it's not a list, or it
// contains anything other than strings, the function appends an
// appropriate error.
// If promoteScalars is true, then values that are scalar strings are
// promoted to a single-entry string array. E.g., "foo" becomes the Go
// expression []string{ "foo" }.
func (p *Parser) literalToStringArray(node ast.Node, promoteScalars bool) ([]string, bool) {
literal, ok := node.(*ast.LiteralType)
if ok {
if promoteScalars && literal.Token.Type == token.STRING {
return []string{literal.Token.Value().(string)}, true
}
p.addError(node, "Expected list, got %s", typename(node))
return nil, false
}
list, ok := node.(*ast.ListType)
if !ok {
p.addError(node, "Expected list, got %s", typename(node))
return nil, false
}
ret := make([]string, 0, len(list.List))
for _, literal := range list.List {
str, ok := p.literalToString(literal)
if ok {
ret = append(ret, str)
}
}
return ret, true
}
// literalToString converts a literal value from the AST into a string.
// If the value isn't a scalar or isn't a string, the function appends an
// appropriate error and returns "", false.
func (p *Parser) literalToString(node ast.Node) (string, bool) {
val := p.literalCast(node, token.STRING)
if val == nil {
return "", false
}
return val.(string), true
}
// literalToInt converts a literal value from the AST into an int64.
// Supported number formats are: 123, 0x123, and 0123.
// Exponents (1e6) and floats (123.456) generate errors.
// If the value isn't a scalar or isn't a number, the function appends an
// appropriate error and returns 0, false.
func (p *Parser) literalToInt(node ast.Node) (int64, bool) {
val := p.literalCast(node, token.NUMBER)
if val == nil {
return 0, false
}
return val.(int64), true
}
func (p *Parser) literalCast(node ast.Node, t token.Type) interface{} {
literal, ok := node.(*ast.LiteralType)
if !ok {
p.addError(node, "Expected %s, got %s", strings.ToLower(t.String()), typename(node))
return nil
}
if literal.Token.Type != t {
p.addError(node, "Expected %s, got %s", strings.ToLower(t.String()), typename(node))
return nil
}
return literal.Token.Value()
}
// parseRoot parses the root of the AST, filling in p.version, p.actions,
// and p.workflows.
func (p *Parser) parseRoot(node ast.Node) {
objectList, ok := node.(*ast.ObjectList)
if !ok {
// It should be impossible for HCL to return anything other than an
// ObjectList as the root node. This error should never happen.
p.addError(node, "Internal error: root node must be an ObjectList")
return
}
p.actions = make([]*model.Action, 0, len(objectList.Items))
p.workflows = make([]*model.Workflow, 0, len(objectList.Items))
identifiers := make(map[string]bool)
for idx, item := range objectList.Items {
if item.Assign.IsValid() {
p.parseVersion(idx, item)
continue
}
p.parseBlock(item, identifiers)
}
}
// parseBlock parses a single, top-level "action" or "workflow" block,
// appending it to p.actions or p.workflows as appropriate.
func (p *Parser) parseBlock(item *ast.ObjectItem, identifiers map[string]bool) {
if len(item.Keys) != 2 {
p.addError(item, "Invalid toplevel declaration")
return
}
cmd := p.identString(item.Keys[0].Token)
var id string
switch cmd {
case "action":
action := p.actionifyItem(item)
if action != nil {
id = action.Identifier
p.actions = append(p.actions, action)
}
case "workflow":
workflow := p.workflowifyItem(item)
if workflow != nil {
id = workflow.Identifier
p.workflows = append(p.workflows, workflow)
}
default:
p.addError(item, "Invalid toplevel keyword, `%s'", cmd)
return
}
if identifiers[id] {
p.addError(item, "Identifier `%s' redefined", id)
}
identifiers[id] = true
}
// parseVersion parses a top-level `version=N` statement, filling in
// p.version.
func (p *Parser) parseVersion(idx int, item *ast.ObjectItem) {
if len(item.Keys) != 1 || p.identString(item.Keys[0].Token) != "version" {
// not a valid `version` declaration
p.addError(item.Val, "Toplevel declarations cannot be assignments")
return
}
if idx != 0 {
p.addError(item.Val, "`version` must be the first declaration")
return
}
version, ok := p.literalToInt(item.Val)
if !ok {
return
}
if version < minVersion || version > maxVersion {
p.addError(item.Val, "`version = %d` is not supported", version)
return
}
p.version = int(version)
}
// parseIdentifier parses the double-quoted identifier (name) for a
// "workflow" or "action" block.
func (p *Parser) parseIdentifier(key *ast.ObjectKey) string {
id := key.Token.Text
if len(id) < 3 || id[0] != '"' || id[len(id)-1] != '"' {
p.addError(key, "Invalid format for identifier `%s'", id)
return ""
}
return id[1 : len(id)-1]
}
// parseRequiredString parses a string value, setting its value into the
// out-parameter `value` and returning true if successful.
func (p *Parser) parseRequiredString(value *string, val ast.Node, nodeType, name, id string) bool {
if *value != "" {
p.addWarning(val, "`%s' redefined in %s `%s'", name, nodeType, id)
// continue, allowing the redefinition
}
newVal, ok := p.literalToString(val)
if !ok {
p.addError(val, "Invalid format for `%s' in %s `%s', expected string", name, nodeType, id)
return false
}
if newVal == "" {
p.addError(val, "`%s' value in %s `%s' cannot be blank", name, nodeType, id)
return false
}
*value = newVal
return true
}
// parseBlockPreamble parses the beginning of a "workflow" or "action"
// block.
func (p *Parser) parseBlockPreamble(item *ast.ObjectItem, nodeType string) (string, *ast.ObjectType) {
id := p.parseIdentifier(item.Keys[1])
if id == "" {
return "", nil
}
node := item.Val
obj, ok := node.(*ast.ObjectType)
if !ok {
p.addError(node, "Each %s must have an { ... } block", nodeType)
return "", nil
}
p.checkAssignmentsOnly(obj.List, id)
return id, obj
}
// actionifyItem converts an AST block to an Action object.
func (p *Parser) actionifyItem(item *ast.ObjectItem) *model.Action {
id, obj := p.parseBlockPreamble(item, "action")
if obj == nil {
return nil
}
action := &model.Action{
Identifier: id,
}
p.posMap[action] = item
for _, item := range obj.List.Items {
p.parseActionAttribute(p.identString(item.Keys[0].Token), action, item.Val)
}
return action
}
// parseActionAttribute parses a single key-value pair from an "action"
// block. This function rejects any unknown keys and enforces formatting
// requirements on all values.
// It also has higher-than-normal cyclomatic complexity, so we ask the
// gocyclo linter to ignore it.
// nolint: gocyclo
func (p *Parser) parseActionAttribute(name string, action *model.Action, val ast.Node) {
switch name {
case "uses":
p.parseUses(action, val)
case "needs":
if needs, ok := p.literalToStringArray(val, true); ok {
action.Needs = needs
p.posMap[&action.Needs] = val
}
case "runs":
if runs := p.parseCommand(action, action.Runs, name, val, false); runs != nil {
action.Runs = runs
}
case "args":
if args := p.parseCommand(action, action.Args, name, val, true); args != nil {
action.Args = args
}
case "env":
if env := p.literalToStringMap(val); env != nil {
action.Env = env
}
p.posMap[&action.Env] = val
case "secrets":
if secrets, ok := p.literalToStringArray(val, false); ok {
action.Secrets = secrets
p.posMap[&action.Secrets] = val
}
default:
p.addWarning(val, "Unknown action attribute `%s'", name)
}
}
// parseUses sets the action.Uses value based on the contents of the AST
// node. This function enforces formatting requirements on the value.
func (p *Parser) parseUses(action *model.Action, node ast.Node) {
if action.Uses != nil {
p.addWarning(node, "`uses' redefined in action `%s'", action.Identifier)
// continue, allowing the redefinition
}
strVal, ok := p.literalToString(node)
if !ok {
return
}
if strVal == "" {
action.Uses = &model.UsesInvalid{}
p.addError(node, "`uses' value in action `%s' cannot be blank", action.Identifier)
return
}
if strings.HasPrefix(strVal, "./") {
action.Uses = &model.UsesPath{Path: strings.TrimPrefix(strVal, "./")}
return
}
if strings.HasPrefix(strVal, "docker://") {
action.Uses = &model.UsesDockerImage{Image: strings.TrimPrefix(strVal, "docker://")}
return
}
tok := strings.Split(strVal, "@")
if len(tok) != 2 {
action.Uses = &model.UsesInvalid{Raw: strVal}
p.addError(node, "The `uses' attribute must be a path, a Docker image, or owner/repo@ref")
return
}
ref := tok[1]
tok = strings.SplitN(tok[0], "/", 3)
if len(tok) < 2 {
action.Uses = &model.UsesInvalid{Raw: strVal}
p.addError(node, "The `uses' attribute must be a path, a Docker image, or owner/repo@ref")
return
}
usesRepo := &model.UsesRepository{Repository: tok[0] + "/" + tok[1], Ref: ref}
action.Uses = usesRepo
if len(tok) == 3 {
usesRepo.Path = tok[2]
}
}
// parseUses sets the action.Runs or action.Args value based on the
// contents of the AST node. This function enforces formatting
// requirements on the value.
func (p *Parser) parseCommand(action *model.Action, cmd model.Command, name string, node ast.Node, allowBlank bool) model.Command {
if cmd != nil {
p.addWarning(node, "`%s' redefined in action `%s'", name, action.Identifier)
// continue, allowing the redefinition
}
// Is it a list?
if _, ok := node.(*ast.ListType); ok {
if parsed, ok := p.literalToStringArray(node, false); ok {
return &model.ListCommand{Values: parsed}
}
return nil
}
// If not, parse a whitespace-separated string into a list.
var raw string
var ok bool
if raw, ok = p.literalToString(node); !ok {
p.addError(node, "The `%s' attribute must be a string or a list", name)
return nil
}
if raw == "" && !allowBlank {
p.addError(node, "`%s' value in action `%s' cannot be blank", name, action.Identifier)
return nil
}
return &model.StringCommand{Value: raw}
}
func typename(val interface{}) string {
switch cast := val.(type) {
case *ast.ListType:
return "list"
case *ast.LiteralType:
return strings.ToLower(cast.Token.Type.String())
case *ast.ObjectType:
return "object"
default:
return fmt.Sprintf("%T", val)
}
}
// workflowifyItem converts an AST block to a Workflow object.
func (p *Parser) workflowifyItem(item *ast.ObjectItem) *model.Workflow {
id, obj := p.parseBlockPreamble(item, "workflow")
if obj == nil {
return nil
}
var ok bool
workflow := &model.Workflow{Identifier: id}
for _, item := range obj.List.Items {
name := p.identString(item.Keys[0].Token)
switch name {
case "on":
ok = p.parseRequiredString(&workflow.On, item.Val, "workflow", name, id)
if ok {
p.posMap[&workflow.On] = item
}
case "resolves":
if workflow.Resolves != nil {
p.addWarning(item.Val, "`resolves' redefined in workflow `%s'", id)
// continue, allowing the redefinition
}
workflow.Resolves, ok = p.literalToStringArray(item.Val, true)
p.posMap[&workflow.Resolves] = item
if !ok {
p.addError(item.Val, "Invalid format for `resolves' in workflow `%s', expected list of strings", id)
// continue, allowing workflow with no `resolves`
}
default:
p.addWarning(item.Val, "Unknown workflow attribute `%s'", name)
// continue, treat as no-op
}
}
p.posMap[workflow] = item
return workflow
}
func isAssignment(item *ast.ObjectItem) bool {
return len(item.Keys) == 1 && item.Assign.IsValid()
}
// checkAssignmentsOnly ensures that all elements in the object are "key =
// value" pairs.
func (p *Parser) checkAssignmentsOnly(objectList *ast.ObjectList, actionID string) {
for _, item := range objectList.Items {
if !isAssignment(item) {
var desc string
if actionID == "" {
desc = "the object"
} else {
desc = fmt.Sprintf("action `%s'", actionID)
}
p.addErrorFromObjectItem(item, "Each attribute of %s must be an assignment", desc)
continue
}
child, ok := item.Val.(*ast.ObjectType)
if ok {
p.checkAssignmentsOnly(child.List, actionID)
}
}
}
func (p *Parser) addWarning(node ast.Node, format string, a ...interface{}) {
if p.suppressSeverity < WARNING {
p.errors = append(p.errors, newWarning(posFromNode(node), format, a...))
}
}
func (p *Parser) addError(node ast.Node, format string, a ...interface{}) {
if p.suppressSeverity < ERROR {
p.errors = append(p.errors, newError(posFromNode(node), format, a...))
}
}
func (p *Parser) addErrorFromToken(t token.Token, format string, a ...interface{}) {
if p.suppressSeverity < ERROR {
p.errors = append(p.errors, newError(posFromToken(t), format, a...))
}
}
func (p *Parser) addErrorFromObjectItem(objectItem *ast.ObjectItem, format string, a ...interface{}) {
if p.suppressSeverity < ERROR {
p.errors = append(p.errors, newError(posFromObjectItem(objectItem), format, a...))
}
}
func (p *Parser) addFatal(node ast.Node, format string, a ...interface{}) {
if p.suppressSeverity < FATAL {
p.errors = append(p.errors, newFatal(posFromNode(node), format, a...))
}
}
// posFromNode returns an ErrorPos (file, line, and column) from an AST
// node, so we can report specific locations for each parse error.
func posFromNode(node ast.Node) ErrorPos {
var pos *token.Pos
switch cast := node.(type) {
case *ast.ObjectList:
if len(cast.Items) > 0 {
if len(cast.Items[0].Keys) > 0 {
pos = &cast.Items[0].Keys[0].Token.Pos
}
}
case *ast.ObjectItem:
return posFromNode(cast.Val)
case *ast.ObjectType:
pos = &cast.Lbrace
case *ast.LiteralType:
pos = &cast.Token.Pos
case *ast.ListType:
pos = &cast.Lbrack
case *ast.ObjectKey:
pos = &cast.Token.Pos
}
if pos == nil {
return ErrorPos{}
}
return ErrorPos{File: pos.Filename, Line: pos.Line, Column: pos.Column}
}
// posFromObjectItem returns an ErrorPos from an ObjectItem. This is for
// cases where posFromNode(item) would fail because the item has no Val
// set.
func posFromObjectItem(item *ast.ObjectItem) ErrorPos {
if len(item.Keys) > 0 {
return posFromNode(item.Keys[0])
}
return ErrorPos{}
}
// posFromToken returns an ErrorPos from a Token. We can't use
// posFromNode here because Tokens aren't Nodes.
func posFromToken(token token.Token) ErrorPos {
return ErrorPos{File: token.Pos.Filename, Line: token.Pos.Line, Column: token.Pos.Column}
}

View file

@ -11,7 +11,7 @@ type EnumerableWithIndex interface {
// Map invokes the given function once for each element and returns a
// container containing the values returned by the given function.
// TODO need help on how to enforce this in containers (don't want to type assert when chaining)
// TODO would appreciate help on how to enforce this in containers (don't want to type assert when chaining)
// Map(func(index int, value interface{}) interface{}) Container
// Select returns a new container containing all elements for which the given function returns a true value.

View file

@ -11,9 +11,10 @@ package arraylist
import (
"fmt"
"strings"
"github.com/emirpasic/gods/lists"
"github.com/emirpasic/gods/utils"
"strings"
)
func assertListImplementation() {
@ -31,9 +32,13 @@ const (
shrinkFactor = float32(0.25) // shrink when size is 25% of capacity (0 means never shrink)
)
// New instantiates a new empty list
func New() *List {
return &List{}
// New instantiates a new list and adds the passed values, if any, to the list
func New(values ...interface{}) *List {
list := &List{}
if len(values) > 0 {
list.Add(values...)
}
return list
}
// Add appends a value at the end of the list
@ -56,7 +61,7 @@ func (list *List) Get(index int) (interface{}, bool) {
return list.elements[index], true
}
// Remove removes one or more elements from the list with the supplied indices.
// Remove removes the element at the given index from the list.
func (list *List) Remove(index int) {
if !list.withinRange(index) {
@ -98,6 +103,19 @@ func (list *List) Values() []interface{} {
return newElements
}
//IndexOf returns index of provided element
func (list *List) IndexOf(value interface{}) int {
if list.size == 0 {
return -1
}
for index, element := range list.elements {
if element == value {
return index
}
}
return -1
}
// Empty returns true if list does not contain any elements.
func (list *List) Empty() bool {
return list.size == 0
@ -145,14 +163,24 @@ func (list *List) Insert(index int, values ...interface{}) {
l := len(values)
list.growBy(l)
list.size += l
// Shift old to right
for i := list.size - 1; i >= index+l; i-- {
list.elements[i] = list.elements[i-l]
}
// Insert new
for i, value := range values {
list.elements[index+i] = value
copy(list.elements[index+l:], list.elements[index:list.size-l])
copy(list.elements[index:], values)
}
// Set the value at specified index
// Does not do anything if position is negative or bigger than list's size
// Note: position equal to list's size is valid, i.e. append.
func (list *List) Set(index int, value interface{}) {
if !list.withinRange(index) {
// Append
if index == list.size {
list.Add(value)
}
return
}
list.elements[index] = value
}
// String returns a string representation of container

View file

@ -23,6 +23,7 @@ type List interface {
Sort(comparator utils.Comparator)
Swap(index1, index2 int)
Insert(index int, values ...interface{})
Set(index int, value interface{})
containers.Container
// Empty() bool

View file

@ -11,12 +11,12 @@ func assertSerializationImplementation() {
var _ containers.JSONDeserializer = (*Heap)(nil)
}
// ToJSON outputs the JSON representation of list's elements.
// ToJSON outputs the JSON representation of the heap.
func (heap *Heap) ToJSON() ([]byte, error) {
return heap.list.ToJSON()
}
// FromJSON populates list's elements from the input JSON representation.
// FromJSON populates the heap from the input JSON representation.
func (heap *Heap) FromJSON(data []byte) error {
return heap.list.FromJSON(data)
}

27
vendor/github.com/google/go-cmp/LICENSE generated vendored Normal file
View file

@ -0,0 +1,27 @@
Copyright (c) 2017 The Go Authors. 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 Google Inc. 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 THE COPYRIGHT
OWNER OR CONTRIBUTORS 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.

553
vendor/github.com/google/go-cmp/cmp/compare.go generated vendored Normal file
View file

@ -0,0 +1,553 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.
// Package cmp determines equality of values.
//
// This package is intended to be a more powerful and safer alternative to
// reflect.DeepEqual for comparing whether two values are semantically equal.
//
// The primary features of cmp are:
//
// • When the default behavior of equality does not suit the needs of the test,
// custom equality functions can override the equality operation.
// For example, an equality function may report floats as equal so long as they
// are within some tolerance of each other.
//
// • Types that have an Equal method may use that method to determine equality.
// This allows package authors to determine the equality operation for the types
// that they define.
//
// • If no custom equality functions are used and no Equal method is defined,
// equality is determined by recursively comparing the primitive kinds on both
// values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported
// fields are not compared by default; they result in panics unless suppressed
// by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared
// using the AllowUnexported option.
package cmp
import (
"fmt"
"reflect"
"github.com/google/go-cmp/cmp/internal/diff"
"github.com/google/go-cmp/cmp/internal/function"
"github.com/google/go-cmp/cmp/internal/value"
)
// BUG(dsnet): Maps with keys containing NaN values cannot be properly compared due to
// the reflection package's inability to retrieve such entries. Equal will panic
// anytime it comes across a NaN key, but this behavior may change.
//
// See https://golang.org/issue/11104 for more details.
var nothing = reflect.Value{}
// Equal reports whether x and y are equal by recursively applying the
// following rules in the given order to x and y and all of their sub-values:
//
// • If two values are not of the same type, then they are never equal
// and the overall result is false.
//
// • Let S be the set of all Ignore, Transformer, and Comparer options that
// remain after applying all path filters, value filters, and type filters.
// If at least one Ignore exists in S, then the comparison is ignored.
// If the number of Transformer and Comparer options in S is greater than one,
// then Equal panics because it is ambiguous which option to use.
// If S contains a single Transformer, then use that to transform the current
// values and recursively call Equal on the output values.
// If S contains a single Comparer, then use that to compare the current values.
// Otherwise, evaluation proceeds to the next rule.
//
// • If the values have an Equal method of the form "(T) Equal(T) bool" or
// "(T) Equal(I) bool" where T is assignable to I, then use the result of
// x.Equal(y) even if x or y is nil.
// Otherwise, no such method exists and evaluation proceeds to the next rule.
//
// • Lastly, try to compare x and y based on their basic kinds.
// Simple kinds like booleans, integers, floats, complex numbers, strings, and
// channels are compared using the equivalent of the == operator in Go.
// Functions are only equal if they are both nil, otherwise they are unequal.
// Pointers are equal if the underlying values they point to are also equal.
// Interfaces are equal if their underlying concrete values are also equal.
//
// Structs are equal if all of their fields are equal. If a struct contains
// unexported fields, Equal panics unless the AllowUnexported option is used or
// an Ignore option (e.g., cmpopts.IgnoreUnexported) ignores that field.
//
// Arrays, slices, and maps are equal if they are both nil or both non-nil
// with the same length and the elements at each index or key are equal.
// Note that a non-nil empty slice and a nil slice are not equal.
// To equate empty slices and maps, consider using cmpopts.EquateEmpty.
// Map keys are equal according to the == operator.
// To use custom comparisons for map keys, consider using cmpopts.SortMaps.
func Equal(x, y interface{}, opts ...Option) bool {
s := newState(opts)
s.compareAny(reflect.ValueOf(x), reflect.ValueOf(y))
return s.result.Equal()
}
// Diff returns a human-readable report of the differences between two values.
// It returns an empty string if and only if Equal returns true for the same
// input values and options. The output string will use the "-" symbol to
// indicate elements removed from x, and the "+" symbol to indicate elements
// added to y.
//
// Do not depend on this output being stable.
func Diff(x, y interface{}, opts ...Option) string {
r := new(defaultReporter)
opts = Options{Options(opts), r}
eq := Equal(x, y, opts...)
d := r.String()
if (d == "") != eq {
panic("inconsistent difference and equality results")
}
return d
}
type state struct {
// These fields represent the "comparison state".
// Calling statelessCompare must not result in observable changes to these.
result diff.Result // The current result of comparison
curPath Path // The current path in the value tree
reporter reporter // Optional reporter used for difference formatting
// dynChecker triggers pseudo-random checks for option correctness.
// It is safe for statelessCompare to mutate this value.
dynChecker dynChecker
// These fields, once set by processOption, will not change.
exporters map[reflect.Type]bool // Set of structs with unexported field visibility
opts Options // List of all fundamental and filter options
}
func newState(opts []Option) *state {
s := new(state)
for _, opt := range opts {
s.processOption(opt)
}
return s
}
func (s *state) processOption(opt Option) {
switch opt := opt.(type) {
case nil:
case Options:
for _, o := range opt {
s.processOption(o)
}
case coreOption:
type filtered interface {
isFiltered() bool
}
if fopt, ok := opt.(filtered); ok && !fopt.isFiltered() {
panic(fmt.Sprintf("cannot use an unfiltered option: %v", opt))
}
s.opts = append(s.opts, opt)
case visibleStructs:
if s.exporters == nil {
s.exporters = make(map[reflect.Type]bool)
}
for t := range opt {
s.exporters[t] = true
}
case reporter:
if s.reporter != nil {
panic("difference reporter already registered")
}
s.reporter = opt
default:
panic(fmt.Sprintf("unknown option %T", opt))
}
}
// statelessCompare compares two values and returns the result.
// This function is stateless in that it does not alter the current result,
// or output to any registered reporters.
func (s *state) statelessCompare(vx, vy reflect.Value) diff.Result {
// We do not save and restore the curPath because all of the compareX
// methods should properly push and pop from the path.
// It is an implementation bug if the contents of curPath differs from
// when calling this function to when returning from it.
oldResult, oldReporter := s.result, s.reporter
s.result = diff.Result{} // Reset result
s.reporter = nil // Remove reporter to avoid spurious printouts
s.compareAny(vx, vy)
res := s.result
s.result, s.reporter = oldResult, oldReporter
return res
}
func (s *state) compareAny(vx, vy reflect.Value) {
// TODO: Support cyclic data structures.
// Rule 0: Differing types are never equal.
if !vx.IsValid() || !vy.IsValid() {
s.report(vx.IsValid() == vy.IsValid(), vx, vy)
return
}
if vx.Type() != vy.Type() {
s.report(false, vx, vy) // Possible for path to be empty
return
}
t := vx.Type()
if len(s.curPath) == 0 {
s.curPath.push(&pathStep{typ: t})
defer s.curPath.pop()
}
vx, vy = s.tryExporting(vx, vy)
// Rule 1: Check whether an option applies on this node in the value tree.
if s.tryOptions(vx, vy, t) {
return
}
// Rule 2: Check whether the type has a valid Equal method.
if s.tryMethod(vx, vy, t) {
return
}
// Rule 3: Recursively descend into each value's underlying kind.
switch t.Kind() {
case reflect.Bool:
s.report(vx.Bool() == vy.Bool(), vx, vy)
return
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
s.report(vx.Int() == vy.Int(), vx, vy)
return
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
s.report(vx.Uint() == vy.Uint(), vx, vy)
return
case reflect.Float32, reflect.Float64:
s.report(vx.Float() == vy.Float(), vx, vy)
return
case reflect.Complex64, reflect.Complex128:
s.report(vx.Complex() == vy.Complex(), vx, vy)
return
case reflect.String:
s.report(vx.String() == vy.String(), vx, vy)
return
case reflect.Chan, reflect.UnsafePointer:
s.report(vx.Pointer() == vy.Pointer(), vx, vy)
return
case reflect.Func:
s.report(vx.IsNil() && vy.IsNil(), vx, vy)
return
case reflect.Ptr:
if vx.IsNil() || vy.IsNil() {
s.report(vx.IsNil() && vy.IsNil(), vx, vy)
return
}
s.curPath.push(&indirect{pathStep{t.Elem()}})
defer s.curPath.pop()
s.compareAny(vx.Elem(), vy.Elem())
return
case reflect.Interface:
if vx.IsNil() || vy.IsNil() {
s.report(vx.IsNil() && vy.IsNil(), vx, vy)
return
}
if vx.Elem().Type() != vy.Elem().Type() {
s.report(false, vx.Elem(), vy.Elem())
return
}
s.curPath.push(&typeAssertion{pathStep{vx.Elem().Type()}})
defer s.curPath.pop()
s.compareAny(vx.Elem(), vy.Elem())
return
case reflect.Slice:
if vx.IsNil() || vy.IsNil() {
s.report(vx.IsNil() && vy.IsNil(), vx, vy)
return
}
fallthrough
case reflect.Array:
s.compareArray(vx, vy, t)
return
case reflect.Map:
s.compareMap(vx, vy, t)
return
case reflect.Struct:
s.compareStruct(vx, vy, t)
return
default:
panic(fmt.Sprintf("%v kind not handled", t.Kind()))
}
}
func (s *state) tryExporting(vx, vy reflect.Value) (reflect.Value, reflect.Value) {
if sf, ok := s.curPath[len(s.curPath)-1].(*structField); ok && sf.unexported {
if sf.force {
// Use unsafe pointer arithmetic to get read-write access to an
// unexported field in the struct.
vx = unsafeRetrieveField(sf.pvx, sf.field)
vy = unsafeRetrieveField(sf.pvy, sf.field)
} else {
// We are not allowed to export the value, so invalidate them
// so that tryOptions can panic later if not explicitly ignored.
vx = nothing
vy = nothing
}
}
return vx, vy
}
func (s *state) tryOptions(vx, vy reflect.Value, t reflect.Type) bool {
// If there were no FilterValues, we will not detect invalid inputs,
// so manually check for them and append invalid if necessary.
// We still evaluate the options since an ignore can override invalid.
opts := s.opts
if !vx.IsValid() || !vy.IsValid() {
opts = Options{opts, invalid{}}
}
// Evaluate all filters and apply the remaining options.
if opt := opts.filter(s, vx, vy, t); opt != nil {
opt.apply(s, vx, vy)
return true
}
return false
}
func (s *state) tryMethod(vx, vy reflect.Value, t reflect.Type) bool {
// Check if this type even has an Equal method.
m, ok := t.MethodByName("Equal")
if !ok || !function.IsType(m.Type, function.EqualAssignable) {
return false
}
eq := s.callTTBFunc(m.Func, vx, vy)
s.report(eq, vx, vy)
return true
}
func (s *state) callTRFunc(f, v reflect.Value) reflect.Value {
v = sanitizeValue(v, f.Type().In(0))
if !s.dynChecker.Next() {
return f.Call([]reflect.Value{v})[0]
}
// Run the function twice and ensure that we get the same results back.
// We run in goroutines so that the race detector (if enabled) can detect
// unsafe mutations to the input.
c := make(chan reflect.Value)
go detectRaces(c, f, v)
want := f.Call([]reflect.Value{v})[0]
if got := <-c; !s.statelessCompare(got, want).Equal() {
// To avoid false-positives with non-reflexive equality operations,
// we sanity check whether a value is equal to itself.
if !s.statelessCompare(want, want).Equal() {
return want
}
fn := getFuncName(f.Pointer())
panic(fmt.Sprintf("non-deterministic function detected: %s", fn))
}
return want
}
func (s *state) callTTBFunc(f, x, y reflect.Value) bool {
x = sanitizeValue(x, f.Type().In(0))
y = sanitizeValue(y, f.Type().In(1))
if !s.dynChecker.Next() {
return f.Call([]reflect.Value{x, y})[0].Bool()
}
// Swapping the input arguments is sufficient to check that
// f is symmetric and deterministic.
// We run in goroutines so that the race detector (if enabled) can detect
// unsafe mutations to the input.
c := make(chan reflect.Value)
go detectRaces(c, f, y, x)
want := f.Call([]reflect.Value{x, y})[0].Bool()
if got := <-c; !got.IsValid() || got.Bool() != want {
fn := getFuncName(f.Pointer())
panic(fmt.Sprintf("non-deterministic or non-symmetric function detected: %s", fn))
}
return want
}
func detectRaces(c chan<- reflect.Value, f reflect.Value, vs ...reflect.Value) {
var ret reflect.Value
defer func() {
recover() // Ignore panics, let the other call to f panic instead
c <- ret
}()
ret = f.Call(vs)[0]
}
// sanitizeValue converts nil interfaces of type T to those of type R,
// assuming that T is assignable to R.
// Otherwise, it returns the input value as is.
func sanitizeValue(v reflect.Value, t reflect.Type) reflect.Value {
// TODO(dsnet): Remove this hacky workaround.
// See https://golang.org/issue/22143
if v.Kind() == reflect.Interface && v.IsNil() && v.Type() != t {
return reflect.New(t).Elem()
}
return v
}
func (s *state) compareArray(vx, vy reflect.Value, t reflect.Type) {
step := &sliceIndex{pathStep{t.Elem()}, 0, 0}
s.curPath.push(step)
// Compute an edit-script for slices vx and vy.
es := diff.Difference(vx.Len(), vy.Len(), func(ix, iy int) diff.Result {
step.xkey, step.ykey = ix, iy
return s.statelessCompare(vx.Index(ix), vy.Index(iy))
})
// Report the entire slice as is if the arrays are of primitive kind,
// and the arrays are different enough.
isPrimitive := false
switch t.Elem().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
isPrimitive = true
}
if isPrimitive && es.Dist() > (vx.Len()+vy.Len())/4 {
s.curPath.pop() // Pop first since we are reporting the whole slice
s.report(false, vx, vy)
return
}
// Replay the edit-script.
var ix, iy int
for _, e := range es {
switch e {
case diff.UniqueX:
step.xkey, step.ykey = ix, -1
s.report(false, vx.Index(ix), nothing)
ix++
case diff.UniqueY:
step.xkey, step.ykey = -1, iy
s.report(false, nothing, vy.Index(iy))
iy++
default:
step.xkey, step.ykey = ix, iy
if e == diff.Identity {
s.report(true, vx.Index(ix), vy.Index(iy))
} else {
s.compareAny(vx.Index(ix), vy.Index(iy))
}
ix++
iy++
}
}
s.curPath.pop()
return
}
func (s *state) compareMap(vx, vy reflect.Value, t reflect.Type) {
if vx.IsNil() || vy.IsNil() {
s.report(vx.IsNil() && vy.IsNil(), vx, vy)
return
}
// We combine and sort the two map keys so that we can perform the
// comparisons in a deterministic order.
step := &mapIndex{pathStep: pathStep{t.Elem()}}
s.curPath.push(step)
defer s.curPath.pop()
for _, k := range value.SortKeys(append(vx.MapKeys(), vy.MapKeys()...)) {
step.key = k
vvx := vx.MapIndex(k)
vvy := vy.MapIndex(k)
switch {
case vvx.IsValid() && vvy.IsValid():
s.compareAny(vvx, vvy)
case vvx.IsValid() && !vvy.IsValid():
s.report(false, vvx, nothing)
case !vvx.IsValid() && vvy.IsValid():
s.report(false, nothing, vvy)
default:
// It is possible for both vvx and vvy to be invalid if the
// key contained a NaN value in it. There is no way in
// reflection to be able to retrieve these values.
// See https://golang.org/issue/11104
panic(fmt.Sprintf("%#v has map key with NaNs", s.curPath))
}
}
}
func (s *state) compareStruct(vx, vy reflect.Value, t reflect.Type) {
var vax, vay reflect.Value // Addressable versions of vx and vy
step := &structField{}
s.curPath.push(step)
defer s.curPath.pop()
for i := 0; i < t.NumField(); i++ {
vvx := vx.Field(i)
vvy := vy.Field(i)
step.typ = t.Field(i).Type
step.name = t.Field(i).Name
step.idx = i
step.unexported = !isExported(step.name)
if step.unexported {
// Defer checking of unexported fields until later to give an
// Ignore a chance to ignore the field.
if !vax.IsValid() || !vay.IsValid() {
// For unsafeRetrieveField to work, the parent struct must
// be addressable. Create a new copy of the values if
// necessary to make them addressable.
vax = makeAddressable(vx)
vay = makeAddressable(vy)
}
step.force = s.exporters[t]
step.pvx = vax
step.pvy = vay
step.field = t.Field(i)
}
s.compareAny(vvx, vvy)
}
}
// report records the result of a single comparison.
// It also calls Report if any reporter is registered.
func (s *state) report(eq bool, vx, vy reflect.Value) {
if eq {
s.result.NSame++
} else {
s.result.NDiff++
}
if s.reporter != nil {
s.reporter.Report(vx, vy, eq, s.curPath)
}
}
// dynChecker tracks the state needed to periodically perform checks that
// user provided functions are symmetric and deterministic.
// The zero value is safe for immediate use.
type dynChecker struct{ curr, next int }
// Next increments the state and reports whether a check should be performed.
//
// Checks occur every Nth function call, where N is a triangular number:
// 0 1 3 6 10 15 21 28 36 45 55 66 78 91 105 120 136 153 171 190 ...
// See https://en.wikipedia.org/wiki/Triangular_number
//
// This sequence ensures that the cost of checks drops significantly as
// the number of functions calls grows larger.
func (dc *dynChecker) Next() bool {
ok := dc.curr == dc.next
if ok {
dc.curr = 0
dc.next++
}
dc.curr++
return ok
}
// makeAddressable returns a value that is always addressable.
// It returns the input verbatim if it is already addressable,
// otherwise it creates a new value and returns an addressable copy.
func makeAddressable(v reflect.Value) reflect.Value {
if v.CanAddr() {
return v
}
vc := reflect.New(v.Type()).Elem()
vc.Set(v)
return vc
}

View file

@ -0,0 +1,17 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.
// +build !debug
package diff
var debug debugger
type debugger struct{}
func (debugger) Begin(_, _ int, f EqualFunc, _, _ *EditScript) EqualFunc {
return f
}
func (debugger) Update() {}
func (debugger) Finish() {}

View file

@ -0,0 +1,122 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.
// +build debug
package diff
import (
"fmt"
"strings"
"sync"
"time"
)
// The algorithm can be seen running in real-time by enabling debugging:
// go test -tags=debug -v
//
// Example output:
// === RUN TestDifference/#34
// ┌───────────────────────────────┐
// │ \ · · · · · · · · · · · · · · │
// │ · # · · · · · · · · · · · · · │
// │ · \ · · · · · · · · · · · · · │
// │ · · \ · · · · · · · · · · · · │
// │ · · · X # · · · · · · · · · · │
// │ · · · # \ · · · · · · · · · · │
// │ · · · · · # # · · · · · · · · │
// │ · · · · · # \ · · · · · · · · │
// │ · · · · · · · \ · · · · · · · │
// │ · · · · · · · · \ · · · · · · │
// │ · · · · · · · · · \ · · · · · │
// │ · · · · · · · · · · \ · · # · │
// │ · · · · · · · · · · · \ # # · │
// │ · · · · · · · · · · · # # # · │
// │ · · · · · · · · · · # # # # · │
// │ · · · · · · · · · # # # # # · │
// │ · · · · · · · · · · · · · · \ │
// └───────────────────────────────┘
// [.Y..M.XY......YXYXY.|]
//
// The grid represents the edit-graph where the horizontal axis represents
// list X and the vertical axis represents list Y. The start of the two lists
// is the top-left, while the ends are the bottom-right. The '·' represents
// an unexplored node in the graph. The '\' indicates that the two symbols
// from list X and Y are equal. The 'X' indicates that two symbols are similar
// (but not exactly equal) to each other. The '#' indicates that the two symbols
// are different (and not similar). The algorithm traverses this graph trying to
// make the paths starting in the top-left and the bottom-right connect.
//
// The series of '.', 'X', 'Y', and 'M' characters at the bottom represents
// the currently established path from the forward and reverse searches,
// separated by a '|' character.
const (
updateDelay = 100 * time.Millisecond
finishDelay = 500 * time.Millisecond
ansiTerminal = true // ANSI escape codes used to move terminal cursor
)
var debug debugger
type debugger struct {
sync.Mutex
p1, p2 EditScript
fwdPath, revPath *EditScript
grid []byte
lines int
}
func (dbg *debugger) Begin(nx, ny int, f EqualFunc, p1, p2 *EditScript) EqualFunc {
dbg.Lock()
dbg.fwdPath, dbg.revPath = p1, p2
top := "┌─" + strings.Repeat("──", nx) + "┐\n"
row := "│ " + strings.Repeat("· ", nx) + "│\n"
btm := "└─" + strings.Repeat("──", nx) + "┘\n"
dbg.grid = []byte(top + strings.Repeat(row, ny) + btm)
dbg.lines = strings.Count(dbg.String(), "\n")
fmt.Print(dbg)
// Wrap the EqualFunc so that we can intercept each result.
return func(ix, iy int) (r Result) {
cell := dbg.grid[len(top)+iy*len(row):][len("│ ")+len("· ")*ix:][:len("·")]
for i := range cell {
cell[i] = 0 // Zero out the multiple bytes of UTF-8 middle-dot
}
switch r = f(ix, iy); {
case r.Equal():
cell[0] = '\\'
case r.Similar():
cell[0] = 'X'
default:
cell[0] = '#'
}
return
}
}
func (dbg *debugger) Update() {
dbg.print(updateDelay)
}
func (dbg *debugger) Finish() {
dbg.print(finishDelay)
dbg.Unlock()
}
func (dbg *debugger) String() string {
dbg.p1, dbg.p2 = *dbg.fwdPath, dbg.p2[:0]
for i := len(*dbg.revPath) - 1; i >= 0; i-- {
dbg.p2 = append(dbg.p2, (*dbg.revPath)[i])
}
return fmt.Sprintf("%s[%v|%v]\n\n", dbg.grid, dbg.p1, dbg.p2)
}
func (dbg *debugger) print(d time.Duration) {
if ansiTerminal {
fmt.Printf("\x1b[%dA", dbg.lines) // Reset terminal cursor
}
fmt.Print(dbg)
time.Sleep(d)
}

View file

@ -0,0 +1,363 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.
// Package diff implements an algorithm for producing edit-scripts.
// The edit-script is a sequence of operations needed to transform one list
// of symbols into another (or vice-versa). The edits allowed are insertions,
// deletions, and modifications. The summation of all edits is called the
// Levenshtein distance as this problem is well-known in computer science.
//
// This package prioritizes performance over accuracy. That is, the run time
// is more important than obtaining a minimal Levenshtein distance.
package diff
// EditType represents a single operation within an edit-script.
type EditType uint8
const (
// Identity indicates that a symbol pair is identical in both list X and Y.
Identity EditType = iota
// UniqueX indicates that a symbol only exists in X and not Y.
UniqueX
// UniqueY indicates that a symbol only exists in Y and not X.
UniqueY
// Modified indicates that a symbol pair is a modification of each other.
Modified
)
// EditScript represents the series of differences between two lists.
type EditScript []EditType
// String returns a human-readable string representing the edit-script where
// Identity, UniqueX, UniqueY, and Modified are represented by the
// '.', 'X', 'Y', and 'M' characters, respectively.
func (es EditScript) String() string {
b := make([]byte, len(es))
for i, e := range es {
switch e {
case Identity:
b[i] = '.'
case UniqueX:
b[i] = 'X'
case UniqueY:
b[i] = 'Y'
case Modified:
b[i] = 'M'
default:
panic("invalid edit-type")
}
}
return string(b)
}
// stats returns a histogram of the number of each type of edit operation.
func (es EditScript) stats() (s struct{ NI, NX, NY, NM int }) {
for _, e := range es {
switch e {
case Identity:
s.NI++
case UniqueX:
s.NX++
case UniqueY:
s.NY++
case Modified:
s.NM++
default:
panic("invalid edit-type")
}
}
return
}
// Dist is the Levenshtein distance and is guaranteed to be 0 if and only if
// lists X and Y are equal.
func (es EditScript) Dist() int { return len(es) - es.stats().NI }
// LenX is the length of the X list.
func (es EditScript) LenX() int { return len(es) - es.stats().NY }
// LenY is the length of the Y list.
func (es EditScript) LenY() int { return len(es) - es.stats().NX }
// EqualFunc reports whether the symbols at indexes ix and iy are equal.
// When called by Difference, the index is guaranteed to be within nx and ny.
type EqualFunc func(ix int, iy int) Result
// Result is the result of comparison.
// NSame is the number of sub-elements that are equal.
// NDiff is the number of sub-elements that are not equal.
type Result struct{ NSame, NDiff int }
// Equal indicates whether the symbols are equal. Two symbols are equal
// if and only if NDiff == 0. If Equal, then they are also Similar.
func (r Result) Equal() bool { return r.NDiff == 0 }
// Similar indicates whether two symbols are similar and may be represented
// by using the Modified type. As a special case, we consider binary comparisons
// (i.e., those that return Result{1, 0} or Result{0, 1}) to be similar.
//
// The exact ratio of NSame to NDiff to determine similarity may change.
func (r Result) Similar() bool {
// Use NSame+1 to offset NSame so that binary comparisons are similar.
return r.NSame+1 >= r.NDiff
}
// Difference reports whether two lists of lengths nx and ny are equal
// given the definition of equality provided as f.
//
// This function returns an edit-script, which is a sequence of operations
// needed to convert one list into the other. The following invariants for
// the edit-script are maintained:
// • eq == (es.Dist()==0)
// • nx == es.LenX()
// • ny == es.LenY()
//
// This algorithm is not guaranteed to be an optimal solution (i.e., one that
// produces an edit-script with a minimal Levenshtein distance). This algorithm
// favors performance over optimality. The exact output is not guaranteed to
// be stable and may change over time.
func Difference(nx, ny int, f EqualFunc) (es EditScript) {
// This algorithm is based on traversing what is known as an "edit-graph".
// See Figure 1 from "An O(ND) Difference Algorithm and Its Variations"
// by Eugene W. Myers. Since D can be as large as N itself, this is
// effectively O(N^2). Unlike the algorithm from that paper, we are not
// interested in the optimal path, but at least some "decent" path.
//
// For example, let X and Y be lists of symbols:
// X = [A B C A B B A]
// Y = [C B A B A C]
//
// The edit-graph can be drawn as the following:
// A B C A B B A
// ┌─────────────┐
// C │_|_|\|_|_|_|_│ 0
// B │_|\|_|_|\|\|_│ 1
// A │\|_|_|\|_|_|\│ 2
// B │_|\|_|_|\|\|_│ 3
// A │\|_|_|\|_|_|\│ 4
// C │ | |\| | | | │ 5
// └─────────────┘ 6
// 0 1 2 3 4 5 6 7
//
// List X is written along the horizontal axis, while list Y is written
// along the vertical axis. At any point on this grid, if the symbol in
// list X matches the corresponding symbol in list Y, then a '\' is drawn.
// The goal of any minimal edit-script algorithm is to find a path from the
// top-left corner to the bottom-right corner, while traveling through the
// fewest horizontal or vertical edges.
// A horizontal edge is equivalent to inserting a symbol from list X.
// A vertical edge is equivalent to inserting a symbol from list Y.
// A diagonal edge is equivalent to a matching symbol between both X and Y.
// Invariants:
// • 0 ≤ fwdPath.X ≤ (fwdFrontier.X, revFrontier.X) ≤ revPath.X ≤ nx
// • 0 ≤ fwdPath.Y ≤ (fwdFrontier.Y, revFrontier.Y) ≤ revPath.Y ≤ ny
//
// In general:
// • fwdFrontier.X < revFrontier.X
// • fwdFrontier.Y < revFrontier.Y
// Unless, it is time for the algorithm to terminate.
fwdPath := path{+1, point{0, 0}, make(EditScript, 0, (nx+ny)/2)}
revPath := path{-1, point{nx, ny}, make(EditScript, 0)}
fwdFrontier := fwdPath.point // Forward search frontier
revFrontier := revPath.point // Reverse search frontier
// Search budget bounds the cost of searching for better paths.
// The longest sequence of non-matching symbols that can be tolerated is
// approximately the square-root of the search budget.
searchBudget := 4 * (nx + ny) // O(n)
// The algorithm below is a greedy, meet-in-the-middle algorithm for
// computing sub-optimal edit-scripts between two lists.
//
// The algorithm is approximately as follows:
// • Searching for differences switches back-and-forth between
// a search that starts at the beginning (the top-left corner), and
// a search that starts at the end (the bottom-right corner). The goal of
// the search is connect with the search from the opposite corner.
// • As we search, we build a path in a greedy manner, where the first
// match seen is added to the path (this is sub-optimal, but provides a
// decent result in practice). When matches are found, we try the next pair
// of symbols in the lists and follow all matches as far as possible.
// • When searching for matches, we search along a diagonal going through
// through the "frontier" point. If no matches are found, we advance the
// frontier towards the opposite corner.
// • This algorithm terminates when either the X coordinates or the
// Y coordinates of the forward and reverse frontier points ever intersect.
//
// This algorithm is correct even if searching only in the forward direction
// or in the reverse direction. We do both because it is commonly observed
// that two lists commonly differ because elements were added to the front
// or end of the other list.
//
// Running the tests with the "debug" build tag prints a visualization of
// the algorithm running in real-time. This is educational for understanding
// how the algorithm works. See debug_enable.go.
f = debug.Begin(nx, ny, f, &fwdPath.es, &revPath.es)
for {
// Forward search from the beginning.
if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 {
break
}
for stop1, stop2, i := false, false, 0; !(stop1 && stop2) && searchBudget > 0; i++ {
// Search in a diagonal pattern for a match.
z := zigzag(i)
p := point{fwdFrontier.X + z, fwdFrontier.Y - z}
switch {
case p.X >= revPath.X || p.Y < fwdPath.Y:
stop1 = true // Hit top-right corner
case p.Y >= revPath.Y || p.X < fwdPath.X:
stop2 = true // Hit bottom-left corner
case f(p.X, p.Y).Equal():
// Match found, so connect the path to this point.
fwdPath.connect(p, f)
fwdPath.append(Identity)
// Follow sequence of matches as far as possible.
for fwdPath.X < revPath.X && fwdPath.Y < revPath.Y {
if !f(fwdPath.X, fwdPath.Y).Equal() {
break
}
fwdPath.append(Identity)
}
fwdFrontier = fwdPath.point
stop1, stop2 = true, true
default:
searchBudget-- // Match not found
}
debug.Update()
}
// Advance the frontier towards reverse point.
if revPath.X-fwdFrontier.X >= revPath.Y-fwdFrontier.Y {
fwdFrontier.X++
} else {
fwdFrontier.Y++
}
// Reverse search from the end.
if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 {
break
}
for stop1, stop2, i := false, false, 0; !(stop1 && stop2) && searchBudget > 0; i++ {
// Search in a diagonal pattern for a match.
z := zigzag(i)
p := point{revFrontier.X - z, revFrontier.Y + z}
switch {
case fwdPath.X >= p.X || revPath.Y < p.Y:
stop1 = true // Hit bottom-left corner
case fwdPath.Y >= p.Y || revPath.X < p.X:
stop2 = true // Hit top-right corner
case f(p.X-1, p.Y-1).Equal():
// Match found, so connect the path to this point.
revPath.connect(p, f)
revPath.append(Identity)
// Follow sequence of matches as far as possible.
for fwdPath.X < revPath.X && fwdPath.Y < revPath.Y {
if !f(revPath.X-1, revPath.Y-1).Equal() {
break
}
revPath.append(Identity)
}
revFrontier = revPath.point
stop1, stop2 = true, true
default:
searchBudget-- // Match not found
}
debug.Update()
}
// Advance the frontier towards forward point.
if revFrontier.X-fwdPath.X >= revFrontier.Y-fwdPath.Y {
revFrontier.X--
} else {
revFrontier.Y--
}
}
// Join the forward and reverse paths and then append the reverse path.
fwdPath.connect(revPath.point, f)
for i := len(revPath.es) - 1; i >= 0; i-- {
t := revPath.es[i]
revPath.es = revPath.es[:i]
fwdPath.append(t)
}
debug.Finish()
return fwdPath.es
}
type path struct {
dir int // +1 if forward, -1 if reverse
point // Leading point of the EditScript path
es EditScript
}
// connect appends any necessary Identity, Modified, UniqueX, or UniqueY types
// to the edit-script to connect p.point to dst.
func (p *path) connect(dst point, f EqualFunc) {
if p.dir > 0 {
// Connect in forward direction.
for dst.X > p.X && dst.Y > p.Y {
switch r := f(p.X, p.Y); {
case r.Equal():
p.append(Identity)
case r.Similar():
p.append(Modified)
case dst.X-p.X >= dst.Y-p.Y:
p.append(UniqueX)
default:
p.append(UniqueY)
}
}
for dst.X > p.X {
p.append(UniqueX)
}
for dst.Y > p.Y {
p.append(UniqueY)
}
} else {
// Connect in reverse direction.
for p.X > dst.X && p.Y > dst.Y {
switch r := f(p.X-1, p.Y-1); {
case r.Equal():
p.append(Identity)
case r.Similar():
p.append(Modified)
case p.Y-dst.Y >= p.X-dst.X:
p.append(UniqueY)
default:
p.append(UniqueX)
}
}
for p.X > dst.X {
p.append(UniqueX)
}
for p.Y > dst.Y {
p.append(UniqueY)
}
}
}
func (p *path) append(t EditType) {
p.es = append(p.es, t)
switch t {
case Identity, Modified:
p.add(p.dir, p.dir)
case UniqueX:
p.add(p.dir, 0)
case UniqueY:
p.add(0, p.dir)
}
debug.Update()
}
type point struct{ X, Y int }
func (p *point) add(dx, dy int) { p.X += dx; p.Y += dy }
// zigzag maps a consecutive sequence of integers to a zig-zag sequence.
// [0 1 2 3 4 5 ...] => [0 -1 +1 -2 +2 ...]
func zigzag(x int) int {
if x&1 != 0 {
x = ^x
}
return x >> 1
}

View file

@ -0,0 +1,49 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.
// Package function identifies function types.
package function
import "reflect"
type funcType int
const (
_ funcType = iota
ttbFunc // func(T, T) bool
tibFunc // func(T, I) bool
trFunc // func(T) R
Equal = ttbFunc // func(T, T) bool
EqualAssignable = tibFunc // func(T, I) bool; encapsulates func(T, T) bool
Transformer = trFunc // func(T) R
ValueFilter = ttbFunc // func(T, T) bool
Less = ttbFunc // func(T, T) bool
)
var boolType = reflect.TypeOf(true)
// IsType reports whether the reflect.Type is of the specified function type.
func IsType(t reflect.Type, ft funcType) bool {
if t == nil || t.Kind() != reflect.Func || t.IsVariadic() {
return false
}
ni, no := t.NumIn(), t.NumOut()
switch ft {
case ttbFunc: // func(T, T) bool
if ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType {
return true
}
case tibFunc: // func(T, I) bool
if ni == 2 && no == 1 && t.In(0).AssignableTo(t.In(1)) && t.Out(0) == boolType {
return true
}
case trFunc: // func(T) R
if ni == 1 && no == 1 {
return true
}
}
return false
}

View file

@ -0,0 +1,277 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.
// Package value provides functionality for reflect.Value types.
package value
import (
"fmt"
"reflect"
"strconv"
"strings"
"unicode"
)
var stringerIface = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
// Format formats the value v as a string.
//
// This is similar to fmt.Sprintf("%+v", v) except this:
// * Prints the type unless it can be elided
// * Avoids printing struct fields that are zero
// * Prints a nil-slice as being nil, not empty
// * Prints map entries in deterministic order
func Format(v reflect.Value, conf FormatConfig) string {
conf.printType = true
conf.followPointers = true
conf.realPointers = true
return formatAny(v, conf, nil)
}
type FormatConfig struct {
UseStringer bool // Should the String method be used if available?
printType bool // Should we print the type before the value?
PrintPrimitiveType bool // Should we print the type of primitives?
followPointers bool // Should we recursively follow pointers?
realPointers bool // Should we print the real address of pointers?
}
func formatAny(v reflect.Value, conf FormatConfig, visited map[uintptr]bool) string {
// TODO: Should this be a multi-line printout in certain situations?
if !v.IsValid() {
return "<non-existent>"
}
if conf.UseStringer && v.Type().Implements(stringerIface) && v.CanInterface() {
if (v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface) && v.IsNil() {
return "<nil>"
}
const stringerPrefix = "s" // Indicates that the String method was used
s := v.Interface().(fmt.Stringer).String()
return stringerPrefix + formatString(s)
}
switch v.Kind() {
case reflect.Bool:
return formatPrimitive(v.Type(), v.Bool(), conf)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return formatPrimitive(v.Type(), v.Int(), conf)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
if v.Type().PkgPath() == "" || v.Kind() == reflect.Uintptr {
// Unnamed uints are usually bytes or words, so use hexadecimal.
return formatPrimitive(v.Type(), formatHex(v.Uint()), conf)
}
return formatPrimitive(v.Type(), v.Uint(), conf)
case reflect.Float32, reflect.Float64:
return formatPrimitive(v.Type(), v.Float(), conf)
case reflect.Complex64, reflect.Complex128:
return formatPrimitive(v.Type(), v.Complex(), conf)
case reflect.String:
return formatPrimitive(v.Type(), formatString(v.String()), conf)
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
return formatPointer(v, conf)
case reflect.Ptr:
if v.IsNil() {
if conf.printType {
return fmt.Sprintf("(%v)(nil)", v.Type())
}
return "<nil>"
}
if visited[v.Pointer()] || !conf.followPointers {
return formatPointer(v, conf)
}
visited = insertPointer(visited, v.Pointer())
return "&" + formatAny(v.Elem(), conf, visited)
case reflect.Interface:
if v.IsNil() {
if conf.printType {
return fmt.Sprintf("%v(nil)", v.Type())
}
return "<nil>"
}
return formatAny(v.Elem(), conf, visited)
case reflect.Slice:
if v.IsNil() {
if conf.printType {
return fmt.Sprintf("%v(nil)", v.Type())
}
return "<nil>"
}
if visited[v.Pointer()] {
return formatPointer(v, conf)
}
visited = insertPointer(visited, v.Pointer())
fallthrough
case reflect.Array:
var ss []string
subConf := conf
subConf.printType = v.Type().Elem().Kind() == reflect.Interface
for i := 0; i < v.Len(); i++ {
s := formatAny(v.Index(i), subConf, visited)
ss = append(ss, s)
}
s := fmt.Sprintf("{%s}", strings.Join(ss, ", "))
if conf.printType {
return v.Type().String() + s
}
return s
case reflect.Map:
if v.IsNil() {
if conf.printType {
return fmt.Sprintf("%v(nil)", v.Type())
}
return "<nil>"
}
if visited[v.Pointer()] {
return formatPointer(v, conf)
}
visited = insertPointer(visited, v.Pointer())
var ss []string
keyConf, valConf := conf, conf
keyConf.printType = v.Type().Key().Kind() == reflect.Interface
keyConf.followPointers = false
valConf.printType = v.Type().Elem().Kind() == reflect.Interface
for _, k := range SortKeys(v.MapKeys()) {
sk := formatAny(k, keyConf, visited)
sv := formatAny(v.MapIndex(k), valConf, visited)
ss = append(ss, fmt.Sprintf("%s: %s", sk, sv))
}
s := fmt.Sprintf("{%s}", strings.Join(ss, ", "))
if conf.printType {
return v.Type().String() + s
}
return s
case reflect.Struct:
var ss []string
subConf := conf
subConf.printType = true
for i := 0; i < v.NumField(); i++ {
vv := v.Field(i)
if isZero(vv) {
continue // Elide zero value fields
}
name := v.Type().Field(i).Name
subConf.UseStringer = conf.UseStringer
s := formatAny(vv, subConf, visited)
ss = append(ss, fmt.Sprintf("%s: %s", name, s))
}
s := fmt.Sprintf("{%s}", strings.Join(ss, ", "))
if conf.printType {
return v.Type().String() + s
}
return s
default:
panic(fmt.Sprintf("%v kind not handled", v.Kind()))
}
}
func formatString(s string) string {
// Use quoted string if it the same length as a raw string literal.
// Otherwise, attempt to use the raw string form.
qs := strconv.Quote(s)
if len(qs) == 1+len(s)+1 {
return qs
}
// Disallow newlines to ensure output is a single line.
// Only allow printable runes for readability purposes.
rawInvalid := func(r rune) bool {
return r == '`' || r == '\n' || !unicode.IsPrint(r)
}
if strings.IndexFunc(s, rawInvalid) < 0 {
return "`" + s + "`"
}
return qs
}
func formatPrimitive(t reflect.Type, v interface{}, conf FormatConfig) string {
if conf.printType && (conf.PrintPrimitiveType || t.PkgPath() != "") {
return fmt.Sprintf("%v(%v)", t, v)
}
return fmt.Sprintf("%v", v)
}
func formatPointer(v reflect.Value, conf FormatConfig) string {
p := v.Pointer()
if !conf.realPointers {
p = 0 // For deterministic printing purposes
}
s := formatHex(uint64(p))
if conf.printType {
return fmt.Sprintf("(%v)(%s)", v.Type(), s)
}
return s
}
func formatHex(u uint64) string {
var f string
switch {
case u <= 0xff:
f = "0x%02x"
case u <= 0xffff:
f = "0x%04x"
case u <= 0xffffff:
f = "0x%06x"
case u <= 0xffffffff:
f = "0x%08x"
case u <= 0xffffffffff:
f = "0x%010x"
case u <= 0xffffffffffff:
f = "0x%012x"
case u <= 0xffffffffffffff:
f = "0x%014x"
case u <= 0xffffffffffffffff:
f = "0x%016x"
}
return fmt.Sprintf(f, u)
}
// insertPointer insert p into m, allocating m if necessary.
func insertPointer(m map[uintptr]bool, p uintptr) map[uintptr]bool {
if m == nil {
m = make(map[uintptr]bool)
}
m[p] = true
return m
}
// isZero reports whether v is the zero value.
// This does not rely on Interface and so can be used on unexported fields.
func isZero(v reflect.Value) bool {
switch v.Kind() {
case reflect.Bool:
return v.Bool() == false
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Complex64, reflect.Complex128:
return v.Complex() == 0
case reflect.String:
return v.String() == ""
case reflect.UnsafePointer:
return v.Pointer() == 0
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
return v.IsNil()
case reflect.Array:
for i := 0; i < v.Len(); i++ {
if !isZero(v.Index(i)) {
return false
}
}
return true
case reflect.Struct:
for i := 0; i < v.NumField(); i++ {
if !isZero(v.Field(i)) {
return false
}
}
return true
}
return false
}

View file

@ -0,0 +1,111 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.
package value
import (
"fmt"
"math"
"reflect"
"sort"
)
// SortKeys sorts a list of map keys, deduplicating keys if necessary.
// The type of each value must be comparable.
func SortKeys(vs []reflect.Value) []reflect.Value {
if len(vs) == 0 {
return vs
}
// Sort the map keys.
sort.Sort(valueSorter(vs))
// Deduplicate keys (fails for NaNs).
vs2 := vs[:1]
for _, v := range vs[1:] {
if isLess(vs2[len(vs2)-1], v) {
vs2 = append(vs2, v)
}
}
return vs2
}
// TODO: Use sort.Slice once Google AppEngine is on Go1.8 or above.
type valueSorter []reflect.Value
func (vs valueSorter) Len() int { return len(vs) }
func (vs valueSorter) Less(i, j int) bool { return isLess(vs[i], vs[j]) }
func (vs valueSorter) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] }
// isLess is a generic function for sorting arbitrary map keys.
// The inputs must be of the same type and must be comparable.
func isLess(x, y reflect.Value) bool {
switch x.Type().Kind() {
case reflect.Bool:
return !x.Bool() && y.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return x.Int() < y.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return x.Uint() < y.Uint()
case reflect.Float32, reflect.Float64:
fx, fy := x.Float(), y.Float()
return fx < fy || math.IsNaN(fx) && !math.IsNaN(fy)
case reflect.Complex64, reflect.Complex128:
cx, cy := x.Complex(), y.Complex()
rx, ix, ry, iy := real(cx), imag(cx), real(cy), imag(cy)
if rx == ry || (math.IsNaN(rx) && math.IsNaN(ry)) {
return ix < iy || math.IsNaN(ix) && !math.IsNaN(iy)
}
return rx < ry || math.IsNaN(rx) && !math.IsNaN(ry)
case reflect.Ptr, reflect.UnsafePointer, reflect.Chan:
return x.Pointer() < y.Pointer()
case reflect.String:
return x.String() < y.String()
case reflect.Array:
for i := 0; i < x.Len(); i++ {
if isLess(x.Index(i), y.Index(i)) {
return true
}
if isLess(y.Index(i), x.Index(i)) {
return false
}
}
return false
case reflect.Struct:
for i := 0; i < x.NumField(); i++ {
if isLess(x.Field(i), y.Field(i)) {
return true
}
if isLess(y.Field(i), x.Field(i)) {
return false
}
}
return false
case reflect.Interface:
vx, vy := x.Elem(), y.Elem()
if !vx.IsValid() || !vy.IsValid() {
return !vx.IsValid() && vy.IsValid()
}
tx, ty := vx.Type(), vy.Type()
if tx == ty {
return isLess(x.Elem(), y.Elem())
}
if tx.Kind() != ty.Kind() {
return vx.Kind() < vy.Kind()
}
if tx.String() != ty.String() {
return tx.String() < ty.String()
}
if tx.PkgPath() != ty.PkgPath() {
return tx.PkgPath() < ty.PkgPath()
}
// This can happen in rare situations, so we fallback to just comparing
// the unique pointer for a reflect.Type. This guarantees deterministic
// ordering within a program, but it is obviously not stable.
return reflect.ValueOf(vx.Type()).Pointer() < reflect.ValueOf(vy.Type()).Pointer()
default:
// Must be Func, Map, or Slice; which are not comparable.
panic(fmt.Sprintf("%T is not comparable", x.Type()))
}
}

453
vendor/github.com/google/go-cmp/cmp/options.go generated vendored Normal file
View file

@ -0,0 +1,453 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.
package cmp
import (
"fmt"
"reflect"
"runtime"
"strings"
"github.com/google/go-cmp/cmp/internal/function"
)
// Option configures for specific behavior of Equal and Diff. In particular,
// the fundamental Option functions (Ignore, Transformer, and Comparer),
// configure how equality is determined.
//
// The fundamental options may be composed with filters (FilterPath and
// FilterValues) to control the scope over which they are applied.
//
// The cmp/cmpopts package provides helper functions for creating options that
// may be used with Equal and Diff.
type Option interface {
// filter applies all filters and returns the option that remains.
// Each option may only read s.curPath and call s.callTTBFunc.
//
// An Options is returned only if multiple comparers or transformers
// can apply simultaneously and will only contain values of those types
// or sub-Options containing values of those types.
filter(s *state, vx, vy reflect.Value, t reflect.Type) applicableOption
}
// applicableOption represents the following types:
// Fundamental: ignore | invalid | *comparer | *transformer
// Grouping: Options
type applicableOption interface {
Option
// apply executes the option, which may mutate s or panic.
apply(s *state, vx, vy reflect.Value)
}
// coreOption represents the following types:
// Fundamental: ignore | invalid | *comparer | *transformer
// Filters: *pathFilter | *valuesFilter
type coreOption interface {
Option
isCore()
}
type core struct{}
func (core) isCore() {}
// Options is a list of Option values that also satisfies the Option interface.
// Helper comparison packages may return an Options value when packing multiple
// Option values into a single Option. When this package processes an Options,
// it will be implicitly expanded into a flat list.
//
// Applying a filter on an Options is equivalent to applying that same filter
// on all individual options held within.
type Options []Option
func (opts Options) filter(s *state, vx, vy reflect.Value, t reflect.Type) (out applicableOption) {
for _, opt := range opts {
switch opt := opt.filter(s, vx, vy, t); opt.(type) {
case ignore:
return ignore{} // Only ignore can short-circuit evaluation
case invalid:
out = invalid{} // Takes precedence over comparer or transformer
case *comparer, *transformer, Options:
switch out.(type) {
case nil:
out = opt
case invalid:
// Keep invalid
case *comparer, *transformer, Options:
out = Options{out, opt} // Conflicting comparers or transformers
}
}
}
return out
}
func (opts Options) apply(s *state, _, _ reflect.Value) {
const warning = "ambiguous set of applicable options"
const help = "consider using filters to ensure at most one Comparer or Transformer may apply"
var ss []string
for _, opt := range flattenOptions(nil, opts) {
ss = append(ss, fmt.Sprint(opt))
}
set := strings.Join(ss, "\n\t")
panic(fmt.Sprintf("%s at %#v:\n\t%s\n%s", warning, s.curPath, set, help))
}
func (opts Options) String() string {
var ss []string
for _, opt := range opts {
ss = append(ss, fmt.Sprint(opt))
}
return fmt.Sprintf("Options{%s}", strings.Join(ss, ", "))
}
// FilterPath returns a new Option where opt is only evaluated if filter f
// returns true for the current Path in the value tree.
//
// The option passed in may be an Ignore, Transformer, Comparer, Options, or
// a previously filtered Option.
func FilterPath(f func(Path) bool, opt Option) Option {
if f == nil {
panic("invalid path filter function")
}
if opt := normalizeOption(opt); opt != nil {
return &pathFilter{fnc: f, opt: opt}
}
return nil
}
type pathFilter struct {
core
fnc func(Path) bool
opt Option
}
func (f pathFilter) filter(s *state, vx, vy reflect.Value, t reflect.Type) applicableOption {
if f.fnc(s.curPath) {
return f.opt.filter(s, vx, vy, t)
}
return nil
}
func (f pathFilter) String() string {
fn := getFuncName(reflect.ValueOf(f.fnc).Pointer())
return fmt.Sprintf("FilterPath(%s, %v)", fn, f.opt)
}
// FilterValues returns a new Option where opt is only evaluated if filter f,
// which is a function of the form "func(T, T) bool", returns true for the
// current pair of values being compared. If the type of the values is not
// assignable to T, then this filter implicitly returns false.
//
// The filter function must be
// symmetric (i.e., agnostic to the order of the inputs) and
// deterministic (i.e., produces the same result when given the same inputs).
// If T is an interface, it is possible that f is called with two values with
// different concrete types that both implement T.
//
// The option passed in may be an Ignore, Transformer, Comparer, Options, or
// a previously filtered Option.
func FilterValues(f interface{}, opt Option) Option {
v := reflect.ValueOf(f)
if !function.IsType(v.Type(), function.ValueFilter) || v.IsNil() {
panic(fmt.Sprintf("invalid values filter function: %T", f))
}
if opt := normalizeOption(opt); opt != nil {
vf := &valuesFilter{fnc: v, opt: opt}
if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 {
vf.typ = ti
}
return vf
}
return nil
}
type valuesFilter struct {
core
typ reflect.Type // T
fnc reflect.Value // func(T, T) bool
opt Option
}
func (f valuesFilter) filter(s *state, vx, vy reflect.Value, t reflect.Type) applicableOption {
if !vx.IsValid() || !vy.IsValid() {
return invalid{}
}
if (f.typ == nil || t.AssignableTo(f.typ)) && s.callTTBFunc(f.fnc, vx, vy) {
return f.opt.filter(s, vx, vy, t)
}
return nil
}
func (f valuesFilter) String() string {
fn := getFuncName(f.fnc.Pointer())
return fmt.Sprintf("FilterValues(%s, %v)", fn, f.opt)
}
// Ignore is an Option that causes all comparisons to be ignored.
// This value is intended to be combined with FilterPath or FilterValues.
// It is an error to pass an unfiltered Ignore option to Equal.
func Ignore() Option { return ignore{} }
type ignore struct{ core }
func (ignore) isFiltered() bool { return false }
func (ignore) filter(_ *state, _, _ reflect.Value, _ reflect.Type) applicableOption { return ignore{} }
func (ignore) apply(_ *state, _, _ reflect.Value) { return }
func (ignore) String() string { return "Ignore()" }
// invalid is a sentinel Option type to indicate that some options could not
// be evaluated due to unexported fields.
type invalid struct{ core }
func (invalid) filter(_ *state, _, _ reflect.Value, _ reflect.Type) applicableOption { return invalid{} }
func (invalid) apply(s *state, _, _ reflect.Value) {
const help = "consider using AllowUnexported or cmpopts.IgnoreUnexported"
panic(fmt.Sprintf("cannot handle unexported field: %#v\n%s", s.curPath, help))
}
// Transformer returns an Option that applies a transformation function that
// converts values of a certain type into that of another.
//
// The transformer f must be a function "func(T) R" that converts values of
// type T to those of type R and is implicitly filtered to input values
// assignable to T. The transformer must not mutate T in any way.
//
// To help prevent some cases of infinite recursive cycles applying the
// same transform to the output of itself (e.g., in the case where the
// input and output types are the same), an implicit filter is added such that
// a transformer is applicable only if that exact transformer is not already
// in the tail of the Path since the last non-Transform step.
//
// The name is a user provided label that is used as the Transform.Name in the
// transformation PathStep. If empty, an arbitrary name is used.
func Transformer(name string, f interface{}) Option {
v := reflect.ValueOf(f)
if !function.IsType(v.Type(), function.Transformer) || v.IsNil() {
panic(fmt.Sprintf("invalid transformer function: %T", f))
}
if name == "" {
name = "λ" // Lambda-symbol as place-holder for anonymous transformer
}
if !isValid(name) {
panic(fmt.Sprintf("invalid name: %q", name))
}
tr := &transformer{name: name, fnc: reflect.ValueOf(f)}
if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 {
tr.typ = ti
}
return tr
}
type transformer struct {
core
name string
typ reflect.Type // T
fnc reflect.Value // func(T) R
}
func (tr *transformer) isFiltered() bool { return tr.typ != nil }
func (tr *transformer) filter(s *state, _, _ reflect.Value, t reflect.Type) applicableOption {
for i := len(s.curPath) - 1; i >= 0; i-- {
if t, ok := s.curPath[i].(*transform); !ok {
break // Hit most recent non-Transform step
} else if tr == t.trans {
return nil // Cannot directly use same Transform
}
}
if tr.typ == nil || t.AssignableTo(tr.typ) {
return tr
}
return nil
}
func (tr *transformer) apply(s *state, vx, vy reflect.Value) {
// Update path before calling the Transformer so that dynamic checks
// will use the updated path.
s.curPath.push(&transform{pathStep{tr.fnc.Type().Out(0)}, tr})
defer s.curPath.pop()
vx = s.callTRFunc(tr.fnc, vx)
vy = s.callTRFunc(tr.fnc, vy)
s.compareAny(vx, vy)
}
func (tr transformer) String() string {
return fmt.Sprintf("Transformer(%s, %s)", tr.name, getFuncName(tr.fnc.Pointer()))
}
// Comparer returns an Option that determines whether two values are equal
// to each other.
//
// The comparer f must be a function "func(T, T) bool" and is implicitly
// filtered to input values assignable to T. If T is an interface, it is
// possible that f is called with two values of different concrete types that
// both implement T.
//
// The equality function must be:
// • Symmetric: equal(x, y) == equal(y, x)
// • Deterministic: equal(x, y) == equal(x, y)
// • Pure: equal(x, y) does not modify x or y
func Comparer(f interface{}) Option {
v := reflect.ValueOf(f)
if !function.IsType(v.Type(), function.Equal) || v.IsNil() {
panic(fmt.Sprintf("invalid comparer function: %T", f))
}
cm := &comparer{fnc: v}
if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 {
cm.typ = ti
}
return cm
}
type comparer struct {
core
typ reflect.Type // T
fnc reflect.Value // func(T, T) bool
}
func (cm *comparer) isFiltered() bool { return cm.typ != nil }
func (cm *comparer) filter(_ *state, _, _ reflect.Value, t reflect.Type) applicableOption {
if cm.typ == nil || t.AssignableTo(cm.typ) {
return cm
}
return nil
}
func (cm *comparer) apply(s *state, vx, vy reflect.Value) {
eq := s.callTTBFunc(cm.fnc, vx, vy)
s.report(eq, vx, vy)
}
func (cm comparer) String() string {
return fmt.Sprintf("Comparer(%s)", getFuncName(cm.fnc.Pointer()))
}
// AllowUnexported returns an Option that forcibly allows operations on
// unexported fields in certain structs, which are specified by passing in a
// value of each struct type.
//
// Users of this option must understand that comparing on unexported fields
// from external packages is not safe since changes in the internal
// implementation of some external package may cause the result of Equal
// to unexpectedly change. However, it may be valid to use this option on types
// defined in an internal package where the semantic meaning of an unexported
// field is in the control of the user.
//
// For some cases, a custom Comparer should be used instead that defines
// equality as a function of the public API of a type rather than the underlying
// unexported implementation.
//
// For example, the reflect.Type documentation defines equality to be determined
// by the == operator on the interface (essentially performing a shallow pointer
// comparison) and most attempts to compare *regexp.Regexp types are interested
// in only checking that the regular expression strings are equal.
// Both of these are accomplished using Comparers:
//
// Comparer(func(x, y reflect.Type) bool { return x == y })
// Comparer(func(x, y *regexp.Regexp) bool { return x.String() == y.String() })
//
// In other cases, the cmpopts.IgnoreUnexported option can be used to ignore
// all unexported fields on specified struct types.
func AllowUnexported(types ...interface{}) Option {
if !supportAllowUnexported {
panic("AllowUnexported is not supported on purego builds, Google App Engine Standard, or GopherJS")
}
m := make(map[reflect.Type]bool)
for _, typ := range types {
t := reflect.TypeOf(typ)
if t.Kind() != reflect.Struct {
panic(fmt.Sprintf("invalid struct type: %T", typ))
}
m[t] = true
}
return visibleStructs(m)
}
type visibleStructs map[reflect.Type]bool
func (visibleStructs) filter(_ *state, _, _ reflect.Value, _ reflect.Type) applicableOption {
panic("not implemented")
}
// reporter is an Option that configures how differences are reported.
type reporter interface {
// TODO: Not exported yet.
//
// Perhaps add PushStep and PopStep and change Report to only accept
// a PathStep instead of the full-path? Adding a PushStep and PopStep makes
// it clear that we are traversing the value tree in a depth-first-search
// manner, which has an effect on how values are printed.
Option
// Report is called for every comparison made and will be provided with
// the two values being compared, the equality result, and the
// current path in the value tree. It is possible for x or y to be an
// invalid reflect.Value if one of the values is non-existent;
// which is possible with maps and slices.
Report(x, y reflect.Value, eq bool, p Path)
}
// normalizeOption normalizes the input options such that all Options groups
// are flattened and groups with a single element are reduced to that element.
// Only coreOptions and Options containing coreOptions are allowed.
func normalizeOption(src Option) Option {
switch opts := flattenOptions(nil, Options{src}); len(opts) {
case 0:
return nil
case 1:
return opts[0]
default:
return opts
}
}
// flattenOptions copies all options in src to dst as a flat list.
// Only coreOptions and Options containing coreOptions are allowed.
func flattenOptions(dst, src Options) Options {
for _, opt := range src {
switch opt := opt.(type) {
case nil:
continue
case Options:
dst = flattenOptions(dst, opt)
case coreOption:
dst = append(dst, opt)
default:
panic(fmt.Sprintf("invalid option type: %T", opt))
}
}
return dst
}
// getFuncName returns a short function name from the pointer.
// The string parsing logic works up until Go1.9.
func getFuncName(p uintptr) string {
fnc := runtime.FuncForPC(p)
if fnc == nil {
return "<unknown>"
}
name := fnc.Name() // E.g., "long/path/name/mypkg.(mytype).(long/path/name/mypkg.myfunc)-fm"
if strings.HasSuffix(name, ")-fm") || strings.HasSuffix(name, ")·fm") {
// Strip the package name from method name.
name = strings.TrimSuffix(name, ")-fm")
name = strings.TrimSuffix(name, ")·fm")
if i := strings.LastIndexByte(name, '('); i >= 0 {
methodName := name[i+1:] // E.g., "long/path/name/mypkg.myfunc"
if j := strings.LastIndexByte(methodName, '.'); j >= 0 {
methodName = methodName[j+1:] // E.g., "myfunc"
}
name = name[:i] + methodName // E.g., "long/path/name/mypkg.(mytype)." + "myfunc"
}
}
if i := strings.LastIndexByte(name, '/'); i >= 0 {
// Strip the package name.
name = name[i+1:] // E.g., "mypkg.(mytype).myfunc"
}
return name
}

309
vendor/github.com/google/go-cmp/cmp/path.go generated vendored Normal file
View file

@ -0,0 +1,309 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.
package cmp
import (
"fmt"
"reflect"
"strings"
"unicode"
"unicode/utf8"
)
type (
// Path is a list of PathSteps describing the sequence of operations to get
// from some root type to the current position in the value tree.
// The first Path element is always an operation-less PathStep that exists
// simply to identify the initial type.
//
// When traversing structs with embedded structs, the embedded struct will
// always be accessed as a field before traversing the fields of the
// embedded struct themselves. That is, an exported field from the
// embedded struct will never be accessed directly from the parent struct.
Path []PathStep
// PathStep is a union-type for specific operations to traverse
// a value's tree structure. Users of this package never need to implement
// these types as values of this type will be returned by this package.
PathStep interface {
String() string
Type() reflect.Type // Resulting type after performing the path step
isPathStep()
}
// SliceIndex is an index operation on a slice or array at some index Key.
SliceIndex interface {
PathStep
Key() int // May return -1 if in a split state
// SplitKeys returns the indexes for indexing into slices in the
// x and y values, respectively. These indexes may differ due to the
// insertion or removal of an element in one of the slices, causing
// all of the indexes to be shifted. If an index is -1, then that
// indicates that the element does not exist in the associated slice.
//
// Key is guaranteed to return -1 if and only if the indexes returned
// by SplitKeys are not the same. SplitKeys will never return -1 for
// both indexes.
SplitKeys() (x int, y int)
isSliceIndex()
}
// MapIndex is an index operation on a map at some index Key.
MapIndex interface {
PathStep
Key() reflect.Value
isMapIndex()
}
// TypeAssertion represents a type assertion on an interface.
TypeAssertion interface {
PathStep
isTypeAssertion()
}
// StructField represents a struct field access on a field called Name.
StructField interface {
PathStep
Name() string
Index() int
isStructField()
}
// Indirect represents pointer indirection on the parent type.
Indirect interface {
PathStep
isIndirect()
}
// Transform is a transformation from the parent type to the current type.
Transform interface {
PathStep
Name() string
Func() reflect.Value
// Option returns the originally constructed Transformer option.
// The == operator can be used to detect the exact option used.
Option() Option
isTransform()
}
)
func (pa *Path) push(s PathStep) {
*pa = append(*pa, s)
}
func (pa *Path) pop() {
*pa = (*pa)[:len(*pa)-1]
}
// Last returns the last PathStep in the Path.
// If the path is empty, this returns a non-nil PathStep that reports a nil Type.
func (pa Path) Last() PathStep {
return pa.Index(-1)
}
// Index returns the ith step in the Path and supports negative indexing.
// A negative index starts counting from the tail of the Path such that -1
// refers to the last step, -2 refers to the second-to-last step, and so on.
// If index is invalid, this returns a non-nil PathStep that reports a nil Type.
func (pa Path) Index(i int) PathStep {
if i < 0 {
i = len(pa) + i
}
if i < 0 || i >= len(pa) {
return pathStep{}
}
return pa[i]
}
// String returns the simplified path to a node.
// The simplified path only contains struct field accesses.
//
// For example:
// MyMap.MySlices.MyField
func (pa Path) String() string {
var ss []string
for _, s := range pa {
if _, ok := s.(*structField); ok {
ss = append(ss, s.String())
}
}
return strings.TrimPrefix(strings.Join(ss, ""), ".")
}
// GoString returns the path to a specific node using Go syntax.
//
// For example:
// (*root.MyMap["key"].(*mypkg.MyStruct).MySlices)[2][3].MyField
func (pa Path) GoString() string {
var ssPre, ssPost []string
var numIndirect int
for i, s := range pa {
var nextStep PathStep
if i+1 < len(pa) {
nextStep = pa[i+1]
}
switch s := s.(type) {
case *indirect:
numIndirect++
pPre, pPost := "(", ")"
switch nextStep.(type) {
case *indirect:
continue // Next step is indirection, so let them batch up
case *structField:
numIndirect-- // Automatic indirection on struct fields
case nil:
pPre, pPost = "", "" // Last step; no need for parenthesis
}
if numIndirect > 0 {
ssPre = append(ssPre, pPre+strings.Repeat("*", numIndirect))
ssPost = append(ssPost, pPost)
}
numIndirect = 0
continue
case *transform:
ssPre = append(ssPre, s.trans.name+"(")
ssPost = append(ssPost, ")")
continue
case *typeAssertion:
// As a special-case, elide type assertions on anonymous types
// since they are typically generated dynamically and can be very
// verbose. For example, some transforms return interface{} because
// of Go's lack of generics, but typically take in and return the
// exact same concrete type.
if s.Type().PkgPath() == "" {
continue
}
}
ssPost = append(ssPost, s.String())
}
for i, j := 0, len(ssPre)-1; i < j; i, j = i+1, j-1 {
ssPre[i], ssPre[j] = ssPre[j], ssPre[i]
}
return strings.Join(ssPre, "") + strings.Join(ssPost, "")
}
type (
pathStep struct {
typ reflect.Type
}
sliceIndex struct {
pathStep
xkey, ykey int
}
mapIndex struct {
pathStep
key reflect.Value
}
typeAssertion struct {
pathStep
}
structField struct {
pathStep
name string
idx int
// These fields are used for forcibly accessing an unexported field.
// pvx, pvy, and field are only valid if unexported is true.
unexported bool
force bool // Forcibly allow visibility
pvx, pvy reflect.Value // Parent values
field reflect.StructField // Field information
}
indirect struct {
pathStep
}
transform struct {
pathStep
trans *transformer
}
)
func (ps pathStep) Type() reflect.Type { return ps.typ }
func (ps pathStep) String() string {
if ps.typ == nil {
return "<nil>"
}
s := ps.typ.String()
if s == "" || strings.ContainsAny(s, "{}\n") {
return "root" // Type too simple or complex to print
}
return fmt.Sprintf("{%s}", s)
}
func (si sliceIndex) String() string {
switch {
case si.xkey == si.ykey:
return fmt.Sprintf("[%d]", si.xkey)
case si.ykey == -1:
// [5->?] means "I don't know where X[5] went"
return fmt.Sprintf("[%d->?]", si.xkey)
case si.xkey == -1:
// [?->3] means "I don't know where Y[3] came from"
return fmt.Sprintf("[?->%d]", si.ykey)
default:
// [5->3] means "X[5] moved to Y[3]"
return fmt.Sprintf("[%d->%d]", si.xkey, si.ykey)
}
}
func (mi mapIndex) String() string { return fmt.Sprintf("[%#v]", mi.key) }
func (ta typeAssertion) String() string { return fmt.Sprintf(".(%v)", ta.typ) }
func (sf structField) String() string { return fmt.Sprintf(".%s", sf.name) }
func (in indirect) String() string { return "*" }
func (tf transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) }
func (si sliceIndex) Key() int {
if si.xkey != si.ykey {
return -1
}
return si.xkey
}
func (si sliceIndex) SplitKeys() (x, y int) { return si.xkey, si.ykey }
func (mi mapIndex) Key() reflect.Value { return mi.key }
func (sf structField) Name() string { return sf.name }
func (sf structField) Index() int { return sf.idx }
func (tf transform) Name() string { return tf.trans.name }
func (tf transform) Func() reflect.Value { return tf.trans.fnc }
func (tf transform) Option() Option { return tf.trans }
func (pathStep) isPathStep() {}
func (sliceIndex) isSliceIndex() {}
func (mapIndex) isMapIndex() {}
func (typeAssertion) isTypeAssertion() {}
func (structField) isStructField() {}
func (indirect) isIndirect() {}
func (transform) isTransform() {}
var (
_ SliceIndex = sliceIndex{}
_ MapIndex = mapIndex{}
_ TypeAssertion = typeAssertion{}
_ StructField = structField{}
_ Indirect = indirect{}
_ Transform = transform{}
_ PathStep = sliceIndex{}
_ PathStep = mapIndex{}
_ PathStep = typeAssertion{}
_ PathStep = structField{}
_ PathStep = indirect{}
_ PathStep = transform{}
)
// isExported reports whether the identifier is exported.
func isExported(id string) bool {
r, _ := utf8.DecodeRuneInString(id)
return unicode.IsUpper(r)
}
// isValid reports whether the identifier is valid.
// Empty and underscore-only strings are not valid.
func isValid(id string) bool {
ok := id != "" && id != "_"
for j, c := range id {
ok = ok && (j > 0 || !unicode.IsDigit(c))
ok = ok && (c == '_' || unicode.IsLetter(c) || unicode.IsDigit(c))
}
return ok
}

53
vendor/github.com/google/go-cmp/cmp/reporter.go generated vendored Normal file
View file

@ -0,0 +1,53 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.
package cmp
import (
"fmt"
"reflect"
"strings"
"github.com/google/go-cmp/cmp/internal/value"
)
type defaultReporter struct {
Option
diffs []string // List of differences, possibly truncated
ndiffs int // Total number of differences
nbytes int // Number of bytes in diffs
nlines int // Number of lines in diffs
}
var _ reporter = (*defaultReporter)(nil)
func (r *defaultReporter) Report(x, y reflect.Value, eq bool, p Path) {
if eq {
return // Ignore equal results
}
const maxBytes = 4096
const maxLines = 256
r.ndiffs++
if r.nbytes < maxBytes && r.nlines < maxLines {
sx := value.Format(x, value.FormatConfig{UseStringer: true})
sy := value.Format(y, value.FormatConfig{UseStringer: true})
if sx == sy {
// Unhelpful output, so use more exact formatting.
sx = value.Format(x, value.FormatConfig{PrintPrimitiveType: true})
sy = value.Format(y, value.FormatConfig{PrintPrimitiveType: true})
}
s := fmt.Sprintf("%#v:\n\t-: %s\n\t+: %s\n", p, sx, sy)
r.diffs = append(r.diffs, s)
r.nbytes += len(s)
r.nlines += strings.Count(s, "\n")
}
}
func (r *defaultReporter) String() string {
s := strings.Join(r.diffs, "")
if r.ndiffs == len(r.diffs) {
return s
}
return fmt.Sprintf("%s... %d more differences ...", s, r.ndiffs-len(r.diffs))
}

15
vendor/github.com/google/go-cmp/cmp/unsafe_panic.go generated vendored Normal file
View file

@ -0,0 +1,15 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.
// +build purego appengine js
package cmp
import "reflect"
const supportAllowUnexported = false
func unsafeRetrieveField(reflect.Value, reflect.StructField) reflect.Value {
panic("unsafeRetrieveField is not implemented")
}

23
vendor/github.com/google/go-cmp/cmp/unsafe_reflect.go generated vendored Normal file
View file

@ -0,0 +1,23 @@
// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.
// +build !purego,!appengine,!js
package cmp
import (
"reflect"
"unsafe"
)
const supportAllowUnexported = true
// unsafeRetrieveField uses unsafe to forcibly retrieve any field from a struct
// such that the value has read-write permissions.
//
// The parent struct, v, must be addressable, while f must be a StructField
// describing the field to retrieve.
func unsafeRetrieveField(v reflect.Value, f reflect.StructField) reflect.Value {
return reflect.NewAt(f.Type, unsafe.Pointer(v.UnsafeAddr()+f.Offset)).Elem()
}

View file

@ -76,6 +76,16 @@ func Expand(path string) (string, error) {
return filepath.Join(dir, path[1:]), nil
}
// Reset clears the cache, forcing the next call to Dir to re-detect
// the home directory. This generally never has to be called, but can be
// useful in tests if you're modifying the home directory via the HOME
// env var or something.
func Reset() {
cacheLock.Lock()
defer cacheLock.Unlock()
homedirCache = ""
}
func dirUnix() (string, error) {
homeEnv := "HOME"
if runtime.GOOS == "plan9" {

9
vendor/github.com/soniakeys/bits/.travis.yml generated vendored Normal file
View file

@ -0,0 +1,9 @@
sudo: false
language: go
go: master
before_script:
- go vet
- go get github.com/client9/misspell/cmd/misspell
- misspell -error *
- go get github.com/soniakeys/vetc
- vetc

463
vendor/github.com/soniakeys/bits/bits.go generated vendored Normal file
View file

@ -0,0 +1,463 @@
// Copyright 2017 Sonia Keys
// License MIT: http://opensource.org/licenses/MIT
// Bits implements methods on a bit array type.
//
// The Bits type holds a fixed size array of bits, numbered consecutively
// from zero. Some set-like operations are possible, but the API is more
// array-like or register-like.
package bits
import (
"fmt"
mb "math/bits"
)
// Bits holds a fixed number of bits.
//
// Bit number 0 is stored in the LSB, or bit 0, of the word indexed at 0.
//
// When Num is not a multiple of 64, the last element of Bits will hold some
// bits beyond Num. These bits are undefined. They are not required to be
// zero but do not have any meaning. Bits methods are not required to leave
// them undisturbed.
type Bits struct {
Num int // number of bits
Bits []uint64
}
// New constructs a Bits value with the given number of bits.
//
// It panics if num is negative.
func New(num int) Bits {
if num < 0 {
panic("negative number of bits")
}
return Bits{num, make([]uint64, (num+63)>>6)}
}
// NewGivens constructs a Bits value with the given bits nums set to 1.
//
// The number of bits will be just enough to hold the largest bit value
// listed. That is, the number of bits will be the max bit number plus one.
//
// It panics if any bit number is negative.
func NewGivens(nums ...int) Bits {
max := -1
for _, p := range nums {
if p > max {
max = p
}
}
b := New(max + 1)
for _, p := range nums {
b.SetBit(p, 1)
}
return b
}
// AllOnes returns true if all Num bits are 1.
func (b Bits) AllOnes() bool {
last := len(b.Bits) - 1
for _, w := range b.Bits[:last] {
if w != ^uint64(0) {
return false
}
}
return ^b.Bits[last]<<uint(64*len(b.Bits)-b.Num) == 0
}
// AllZeros returns true if all Num bits are 0.
func (b Bits) AllZeros() bool {
last := len(b.Bits) - 1
for _, w := range b.Bits[:last] {
if w != 0 {
return false
}
}
return b.Bits[last]<<uint(64*len(b.Bits)-b.Num) == 0
}
// And sets z = x & y.
//
// It panics if x and y do not have the same Num.
func (z *Bits) And(x, y Bits) {
if x.Num != y.Num {
panic("arguments have different number of bits")
}
if z.Num != x.Num {
*z = New(x.Num)
}
for i, w := range y.Bits {
z.Bits[i] = x.Bits[i] & w
}
}
// AndNot sets z = x &^ y.
//
// It panics if x and y do not have the same Num.
func (z *Bits) AndNot(x, y Bits) {
if x.Num != y.Num {
panic("arguments have different number of bits")
}
if z.Num != x.Num {
*z = New(x.Num)
}
for i, w := range y.Bits {
z.Bits[i] = x.Bits[i] &^ w
}
}
// Bit returns the value of the n'th bit of receiver b.
func (b Bits) Bit(n int) int {
if n < 0 || n >= b.Num {
panic("bit number out of range")
}
return int(b.Bits[n>>6] >> uint(n&63) & 1)
}
// ClearAll sets all bits to 0.
func (b Bits) ClearAll() {
for i := range b.Bits {
b.Bits[i] = 0
}
}
// ClearBits sets the given bits to 0 in receiver b.
//
// Other bits of b are left unchanged.
//
// It panics if any bit number is out of range.
// That is, negative or >= the number of bits.
func (b Bits) ClearBits(nums ...int) {
for _, p := range nums {
b.SetBit(p, 0)
}
}
// Equal returns true if all Num bits of a and b are equal.
//
// It panics if a and b have different Num.
func (a Bits) Equal(b Bits) bool {
if a.Num != b.Num {
panic("receiver and argument have different number of bits")
}
if a.Num == 0 {
return true
}
last := len(a.Bits) - 1
for i, w := range a.Bits[:last] {
if w != b.Bits[i] {
return false
}
}
return (a.Bits[last]^b.Bits[last])<<uint(len(a.Bits)*64-a.Num) == 0
}
// IterateOnes calls visitor function v for each bit with a value of 1, in order
// from lowest bit to highest bit.
//
// Iteration continues to the highest bit as long as v returns true.
// It stops if v returns false.
//
// IterateOnes returns true normally. It returns false if v returns false.
//
// IterateOnes may not be sensitive to changes if bits are changed during
// iteration, by the vistor function for example.
// See OneFrom for an iteration method sensitive to changes during iteration.
func (b Bits) IterateOnes(v func(int) bool) bool {
for x, w := range b.Bits {
if w != 0 {
t := mb.TrailingZeros64(w)
i := t // index in w of next 1 bit
for {
n := x<<6 | i
if n >= b.Num {
return true
}
if !v(x<<6 | i) {
return false
}
w >>= uint(t + 1)
if w == 0 {
break
}
t = mb.TrailingZeros64(w)
i += 1 + t
}
}
}
return true
}
// IterateZeros calls visitor function v for each bit with a value of 0,
// in order from lowest bit to highest bit.
//
// Iteration continues to the highest bit as long as v returns true.
// It stops if v returns false.
//
// IterateZeros returns true normally. It returns false if v returns false.
//
// IterateZeros may not be sensitive to changes if bits are changed during
// iteration, by the vistor function for example.
// See ZeroFrom for an iteration method sensitive to changes during iteration.
func (b Bits) IterateZeros(v func(int) bool) bool {
for x, w := range b.Bits {
w = ^w
if w != 0 {
t := mb.TrailingZeros64(w)
i := t // index in w of next 1 bit
for {
n := x<<6 | i
if n >= b.Num {
return true
}
if !v(x<<6 | i) {
return false
}
w >>= uint(t + 1)
if w == 0 {
break
}
t = mb.TrailingZeros64(w)
i += 1 + t
}
}
}
return true
}
// Not sets receiver z to the complement of b.
func (z *Bits) Not(b Bits) {
if z.Num != b.Num {
*z = New(b.Num)
}
for i, w := range b.Bits {
z.Bits[i] = ^w
}
}
// OneFrom returns the number of the first 1 bit at or after (from) bit num.
//
// It returns -1 if there is no one bit at or after num.
//
// This provides one way to iterate over one bits.
// To iterate over the one bits, call OneFrom with n = 0 to get the the first
// one bit, then call with the result + 1 to get successive one bits.
// Unlike the Iterate method, this technique is stateless and so allows
// bits to be changed between successive calls.
//
// There is no panic for calling OneFrom with an argument >= b.Num.
// In this case OneFrom simply returns -1.
//
// See also Iterate.
func (b Bits) OneFrom(num int) int {
if num >= b.Num {
return -1
}
x := num >> 6
// test for 1 in this word at or after n
if wx := b.Bits[x] >> uint(num&63); wx != 0 {
num += mb.TrailingZeros64(wx)
if num >= b.Num {
return -1
}
return num
}
x++
for y, wy := range b.Bits[x:] {
if wy != 0 {
num = (x+y)<<6 | mb.TrailingZeros64(wy)
if num >= b.Num {
return -1
}
return num
}
}
return -1
}
// Or sets z = x | y.
//
// It panics if x and y do not have the same Num.
func (z *Bits) Or(x, y Bits) {
if x.Num != y.Num {
panic("arguments have different number of bits")
}
if z.Num != x.Num {
*z = New(x.Num)
}
for i, w := range y.Bits {
z.Bits[i] = x.Bits[i] | w
}
}
// OnesCount returns the number of 1 bits.
func (b Bits) OnesCount() (c int) {
if b.Num == 0 {
return 0
}
last := len(b.Bits) - 1
for _, w := range b.Bits[:last] {
c += mb.OnesCount64(w)
}
c += mb.OnesCount64(b.Bits[last] << uint(len(b.Bits)*64-b.Num))
return
}
// Set sets the bits of z to the bits of x.
func (z *Bits) Set(b Bits) {
if z.Num != b.Num {
*z = New(b.Num)
}
copy(z.Bits, b.Bits)
}
// SetAll sets z to have all 1 bits.
func (b Bits) SetAll() {
for i := range b.Bits {
b.Bits[i] = ^uint64(0)
}
}
// SetBit sets the n'th bit to x, where x is a 0 or 1.
//
// It panics if n is out of range
func (b Bits) SetBit(n, x int) {
if n < 0 || n >= b.Num {
panic("bit number out of range")
}
if x == 0 {
b.Bits[n>>6] &^= 1 << uint(n&63)
} else {
b.Bits[n>>6] |= 1 << uint(n&63)
}
}
// SetBits sets the given bits to 1 in receiver b.
//
// Other bits of b are left unchanged.
//
// It panics if any bit number is out of range, negative or >= the number
// of bits.
func (b Bits) SetBits(nums ...int) {
for _, p := range nums {
b.SetBit(p, 1)
}
}
// Single returns true if b has exactly one 1 bit.
func (b Bits) Single() bool {
// like OnesCount, but stop as soon as two are found
if b.Num == 0 {
return false
}
c := 0
last := len(b.Bits) - 1
for _, w := range b.Bits[:last] {
c += mb.OnesCount64(w)
if c > 1 {
return false
}
}
c += mb.OnesCount64(b.Bits[last] << uint(len(b.Bits)*64-b.Num))
return c == 1
}
// Slice returns a slice with the bit numbers of each 1 bit.
func (b Bits) Slice() (s []int) {
for x, w := range b.Bits {
if w == 0 {
continue
}
t := mb.TrailingZeros64(w)
i := t // index in w of next 1 bit
for {
n := x<<6 | i
if n >= b.Num {
break
}
s = append(s, n)
w >>= uint(t + 1)
if w == 0 {
break
}
t = mb.TrailingZeros64(w)
i += 1 + t
}
}
return
}
// String returns a readable representation.
//
// The returned string is big-endian, with the highest number bit first.
//
// If Num is 0, an empty string is returned.
func (b Bits) String() (s string) {
if b.Num == 0 {
return ""
}
last := len(b.Bits) - 1
for _, w := range b.Bits[:last] {
s = fmt.Sprintf("%064b", w) + s
}
lb := b.Num - 64*last
return fmt.Sprintf("%0*b", lb,
b.Bits[last]&(^uint64(0)>>uint(64-lb))) + s
}
// Xor sets z = x ^ y.
func (z *Bits) Xor(x, y Bits) {
if x.Num != y.Num {
panic("arguments have different number of bits")
}
if z.Num != x.Num {
*z = New(x.Num)
}
for i, w := range y.Bits {
z.Bits[i] = x.Bits[i] ^ w
}
}
// ZeroFrom returns the number of the first 0 bit at or after (from) bit num.
//
// It returns -1 if there is no zero bit at or after num.
//
// This provides one way to iterate over zero bits.
// To iterate over the zero bits, call ZeroFrom with n = 0 to get the the first
// zero bit, then call with the result + 1 to get successive zero bits.
// Unlike the IterateZeros method, this technique is stateless and so allows
// bits to be changed between successive calls.
//
// There is no panic for calling ZeroFrom with an argument >= b.Num.
// In this case ZeroFrom simply returns -1.
//
// See also IterateZeros.
func (b Bits) ZeroFrom(num int) int {
// code much like OneFrom except words are negated before testing
if num >= b.Num {
return -1
}
x := num >> 6
// negate word to test for 0 at or after n
if wx := ^b.Bits[x] >> uint(num&63); wx != 0 {
num += mb.TrailingZeros64(wx)
if num >= b.Num {
return -1
}
return num
}
x++
for y, wy := range b.Bits[x:] {
wy = ^wy
if wy != 0 {
num = (x+y)<<6 | mb.TrailingZeros64(wy)
if num >= b.Num {
return -1
}
return num
}
}
return -1
}

1
vendor/github.com/soniakeys/bits/go.mod generated vendored Normal file
View file

@ -0,0 +1 @@
module "github.com/soniakeys/bits"

38
vendor/github.com/soniakeys/bits/readme.adoc generated vendored Normal file
View file

@ -0,0 +1,38 @@
= Bits
Bits provides methods on a bit array type.
The Bits type holds a fixed size array of bits, numbered consecutively
from zero. Some set-like operations are possible, but the API is more
array-like or register-like.
image:https://godoc.org/github.com/soniakeys/bits?status.svg[link=https://godoc.org/github.com/soniakeys/bits] image:https://travis-ci.org/soniakeys/bits.svg[link=https://travis-ci.org/soniakeys/bits]
== Motivation and history
This package evolved from needs of my library of
https://github.com/soniakeys/graph[graph algorithms]. For graph algorithms
a common need is to store a single bit of information per node in a way that
is both fast and memory efficient. I began by using `big.Int` from the standard
library, then wrapped big.Int in a type. From time to time I considered
other publicly available bit array or bit set packages, such as Will
Fitzgerald's popular https://github.com/willf/bitset[bitset], but there were
always little reasons I preferred my own type and methods. My type that
wrapped `big.Int` met my needs until some simple benchmarks indicated it
might be causing performance problems. Some further experiments supported
this hypothesis so I ran further tests with a prototype bit array written
from scratch. Then satisfied that my custom bit array was solving the graph
performance problems, I decided to move it to a separate package with the
idea it might have more general utility. For the initial version of this
package I did the following:
- implemented a few tests to demonstrate fundamental correctness
- brought over most methods of my type that wrapped big.Int
- changed the index type from the graph-specific node index to a general `int`
- replaced some custom bit-twiddling with use of the new `math/bits` package
in the standard library
- renamed a few methods for clarity
- added a few methods for symmetry
- added a few new methods I had seen a need for in my graph library
- added doc, examples, tests, and more tests for 100% coverage
- added this readme

2
vendor/github.com/soniakeys/graph/.gitignore generated vendored Normal file
View file

@ -0,0 +1,2 @@
*.dot
anecdote/anecdote

11
vendor/github.com/soniakeys/graph/.travis.yml generated vendored Normal file
View file

@ -0,0 +1,11 @@
sudo: false
language: go
go:
- "1.9.x"
- master
before_script:
- go tool vet -composites=false -printf=false -shift=false .
- go get github.com/client9/misspell/cmd/misspell
- go get github.com/soniakeys/vetc
- misspell -error * */* */*/*
- vetc

406
vendor/github.com/soniakeys/graph/adj.go generated vendored Normal file
View file

@ -0,0 +1,406 @@
// Copyright 2014 Sonia Keys
// License MIT: https://opensource.org/licenses/MIT
package graph
// adj.go contains methods on AdjacencyList and LabeledAdjacencyList.
//
// AdjacencyList methods are placed first and are alphabetized.
// LabeledAdjacencyList methods follow, also alphabetized.
// Only exported methods need be alphabetized; non-exported methods can
// be left near their use.
import (
"sort"
"github.com/soniakeys/bits"
)
// NI is a "node int"
//
// It is a node number or node ID. NIs are used extensively as slice indexes.
// NIs typically account for a significant fraction of the memory footprint of
// a graph.
type NI int32
// AnyParallel identifies if a graph contains parallel arcs, multiple arcs
// that lead from a node to the same node.
//
// If the graph has parallel arcs, the results fr and to represent an example
// where there are parallel arcs from node `fr` to node `to`.
//
// If there are no parallel arcs, the method returns false -1 -1.
//
// Multiple loops on a node count as parallel arcs.
//
// See also alt.AnyParallelMap, which can perform better for some large
// or dense graphs.
func (g AdjacencyList) AnyParallel() (has bool, fr, to NI) {
var t []NI
for n, to := range g {
if len(to) == 0 {
continue
}
// different code in the labeled version, so no code gen.
t = append(t[:0], to...)
sort.Slice(t, func(i, j int) bool { return t[i] < t[j] })
t0 := t[0]
for _, to := range t[1:] {
if to == t0 {
return true, NI(n), t0
}
t0 = to
}
}
return false, -1, -1
}
// Complement returns the arc-complement of a simple graph.
//
// The result will have an arc for every pair of distinct nodes where there
// is not an arc in g. The complement is valid for both directed and
// undirected graphs. If g is undirected, the complement will be undirected.
// The result will always be a simple graph, having no loops or parallel arcs.
func (g AdjacencyList) Complement() AdjacencyList {
c := make(AdjacencyList, len(g))
b := bits.New(len(g))
for n, to := range g {
b.ClearAll()
for _, to := range to {
b.SetBit(int(to), 1)
}
b.SetBit(n, 1)
ct := make([]NI, len(g)-b.OnesCount())
i := 0
b.IterateZeros(func(to int) bool {
ct[i] = NI(to)
i++
return true
})
c[n] = ct
}
return c
}
// IsUndirected returns true if g represents an undirected graph.
//
// Returns true when all non-loop arcs are paired in reciprocal pairs.
// Otherwise returns false and an example unpaired arc.
func (g AdjacencyList) IsUndirected() (u bool, from, to NI) {
// similar code in dot/writeUndirected
unpaired := make(AdjacencyList, len(g))
for fr, to := range g {
arc: // for each arc in g
for _, to := range to {
if to == NI(fr) {
continue // loop
}
// search unpaired arcs
ut := unpaired[to]
for i, u := range ut {
if u == NI(fr) { // found reciprocal
last := len(ut) - 1
ut[i] = ut[last]
unpaired[to] = ut[:last]
continue arc
}
}
// reciprocal not found
unpaired[fr] = append(unpaired[fr], to)
}
}
for fr, to := range unpaired {
if len(to) > 0 {
return false, NI(fr), to[0]
}
}
return true, -1, -1
}
// SortArcLists sorts the arc lists of each node of receiver g.
//
// Nodes are not relabeled and the graph remains equivalent.
func (g AdjacencyList) SortArcLists() {
for _, to := range g {
sort.Slice(to, func(i, j int) bool { return to[i] < to[j] })
}
}
// ------- Labeled methods below -------
// ArcsAsEdges constructs an edge list with an edge for each arc, including
// reciprocals.
//
// This is a simple way to construct an edge list for algorithms that allow
// the duplication represented by the reciprocal arcs. (e.g. Kruskal)
//
// See also LabeledUndirected.Edges for the edge list without this duplication.
func (g LabeledAdjacencyList) ArcsAsEdges() (el []LabeledEdge) {
for fr, to := range g {
for _, to := range to {
el = append(el, LabeledEdge{Edge{NI(fr), to.To}, to.Label})
}
}
return
}
// DistanceMatrix constructs a distance matrix corresponding to the arcs
// of graph g and weight function w.
//
// An arc from f to t with WeightFunc return w is represented by d[f][t] == w.
// In case of parallel arcs, the lowest weight is stored. The distance from
// any node to itself d[n][n] is 0, unless the node has a loop with a negative
// weight. If g has no arc from f to distinct t, +Inf is stored for d[f][t].
//
// The returned DistanceMatrix is suitable for DistanceMatrix.FloydWarshall.
func (g LabeledAdjacencyList) DistanceMatrix(w WeightFunc) (d DistanceMatrix) {
d = newDM(len(g))
for fr, to := range g {
for _, to := range to {
// < to pick min of parallel arcs (also nicely ignores NaN)
if wt := w(to.Label); wt < d[fr][to.To] {
d[fr][to.To] = wt
}
}
}
return
}
// HasArcLabel returns true if g has any arc from node `fr` to node `to`
// with label `l`.
//
// Also returned is the index within the slice of arcs from node `fr`.
// If no arc from `fr` to `to` with label `l` is present, HasArcLabel returns
// false, -1.
func (g LabeledAdjacencyList) HasArcLabel(fr, to NI, l LI) (bool, int) {
t := Half{to, l}
for x, h := range g[fr] {
if h == t {
return true, x
}
}
return false, -1
}
// AnyParallel identifies if a graph contains parallel arcs, multiple arcs
// that lead from a node to the same node.
//
// If the graph has parallel arcs, the results fr and to represent an example
// where there are parallel arcs from node `fr` to node `to`.
//
// If there are no parallel arcs, the method returns -1 -1.
//
// Multiple loops on a node count as parallel arcs.
//
// See also alt.AnyParallelMap, which can perform better for some large
// or dense graphs.
func (g LabeledAdjacencyList) AnyParallel() (has bool, fr, to NI) {
var t []NI
for n, to := range g {
if len(to) == 0 {
continue
}
// slightly different code needed here compared to AdjacencyList
t = t[:0]
for _, to := range to {
t = append(t, to.To)
}
sort.Slice(t, func(i, j int) bool { return t[i] < t[j] })
t0 := t[0]
for _, to := range t[1:] {
if to == t0 {
return true, NI(n), t0
}
t0 = to
}
}
return false, -1, -1
}
// AnyParallelLabel identifies if a graph contains parallel arcs with the same
// label.
//
// If the graph has parallel arcs with the same label, the results fr and to
// represent an example where there are parallel arcs from node `fr`
// to node `to`.
//
// If there are no parallel arcs, the method returns false -1 Half{}.
//
// Multiple loops on a node count as parallel arcs.
func (g LabeledAdjacencyList) AnyParallelLabel() (has bool, fr NI, to Half) {
var t []Half
for n, to := range g {
if len(to) == 0 {
continue
}
// slightly different code needed here compared to AdjacencyList
t = t[:0]
for _, to := range to {
t = append(t, to)
}
sort.Slice(t, func(i, j int) bool {
return t[i].To < t[j].To ||
t[i].To == t[j].To && t[i].Label < t[j].Label
})
t0 := t[0]
for _, to := range t[1:] {
if to == t0 {
return true, NI(n), t0
}
t0 = to
}
}
return false, -1, Half{}
}
// IsUndirected returns true if g represents an undirected graph.
//
// Returns true when all non-loop arcs are paired in reciprocal pairs with
// matching labels. Otherwise returns false and an example unpaired arc.
//
// Note the requirement that reciprocal pairs have matching labels is
// an additional test not present in the otherwise equivalent unlabeled version
// of IsUndirected.
func (g LabeledAdjacencyList) IsUndirected() (u bool, from NI, to Half) {
// similar code in LabeledAdjacencyList.Edges
unpaired := make(LabeledAdjacencyList, len(g))
for fr, to := range g {
arc: // for each arc in g
for _, to := range to {
if to.To == NI(fr) {
continue // loop
}
// search unpaired arcs
ut := unpaired[to.To]
for i, u := range ut {
if u.To == NI(fr) && u.Label == to.Label { // found reciprocal
last := len(ut) - 1
ut[i] = ut[last]
unpaired[to.To] = ut[:last]
continue arc
}
}
// reciprocal not found
unpaired[fr] = append(unpaired[fr], to)
}
}
for fr, to := range unpaired {
if len(to) > 0 {
return false, NI(fr), to[0]
}
}
return true, -1, to
}
// ArcLabels constructs the multiset of LIs present in g.
func (g LabeledAdjacencyList) ArcLabels() map[LI]int {
s := map[LI]int{}
for _, to := range g {
for _, to := range to {
s[to.Label]++
}
}
return s
}
// NegativeArc returns true if the receiver graph contains a negative arc.
func (g LabeledAdjacencyList) NegativeArc(w WeightFunc) bool {
for _, nbs := range g {
for _, nb := range nbs {
if w(nb.Label) < 0 {
return true
}
}
}
return false
}
// ParallelArcsLabel identifies all arcs from node `fr` to node `to` with label `l`.
//
// The returned slice contains an element for each arc from node `fr` to node `to`
// with label `l`. The element value is the index within the slice of arcs from node
// `fr`.
//
// See also the method HasArcLabel, which stops after finding a single arc.
func (g LabeledAdjacencyList) ParallelArcsLabel(fr, to NI, l LI) (p []int) {
t := Half{to, l}
for x, h := range g[fr] {
if h == t {
p = append(p, x)
}
}
return
}
// Unlabeled constructs the unlabeled graph corresponding to g.
func (g LabeledAdjacencyList) Unlabeled() AdjacencyList {
a := make(AdjacencyList, len(g))
for n, nbs := range g {
to := make([]NI, len(nbs))
for i, nb := range nbs {
to[i] = nb.To
}
a[n] = to
}
return a
}
// WeightedArcsAsEdges constructs a WeightedEdgeList object from the receiver.
//
// Internally it calls g.ArcsAsEdges() to obtain the Edges member.
// See LabeledAdjacencyList.ArcsAsEdges().
func (g LabeledAdjacencyList) WeightedArcsAsEdges(w WeightFunc) *WeightedEdgeList {
return &WeightedEdgeList{
Order: g.Order(),
WeightFunc: w,
Edges: g.ArcsAsEdges(),
}
}
// WeightedInDegree computes the weighted in-degree of each node in g
// for a given weight function w.
//
// The weighted in-degree of a node is the sum of weights of arcs going to
// the node.
//
// A weighted degree of a node is often termed the "strength" of a node.
//
// See note for undirected graphs at LabeledAdjacencyList.WeightedOutDegree.
func (g LabeledAdjacencyList) WeightedInDegree(w WeightFunc) []float64 {
ind := make([]float64, len(g))
for _, to := range g {
for _, to := range to {
ind[to.To] += w(to.Label)
}
}
return ind
}
// WeightedOutDegree computes the weighted out-degree of the specified node
// for a given weight function w.
//
// The weighted out-degree of a node is the sum of weights of arcs going from
// the node.
//
// A weighted degree of a node is often termed the "strength" of a node.
//
// Note for undirected graphs, the WeightedOutDegree result for a node will
// equal the WeightedInDegree for the node. You can use WeightedInDegree if
// you have need for the weighted degrees of all nodes or use WeightedOutDegree
// to compute the weighted degrees of individual nodes. In either case loops
// are counted just once, unlike the (unweighted) UndirectedDegree methods.
func (g LabeledAdjacencyList) WeightedOutDegree(n NI, w WeightFunc) (d float64) {
for _, to := range g[n] {
d += w(to.Label)
}
return
}
// More about loops and strength: I didn't see consensus on this especially
// in the case of undirected graphs. Some sources said to add in-degree and
// out-degree, which would seemingly double both loops and non-loops.
// Some said to double loops. Some said sum the edge weights and had no
// comment on loops. R of course makes everything an option. The meaning
// of "strength" where loops exist is unclear. So while I could write an
// UndirectedWeighted degree function that doubles loops but not edges,
// I'm going to just leave this for now.

417
vendor/github.com/soniakeys/graph/adj_RO.go generated vendored Normal file
View file

@ -0,0 +1,417 @@
// Copyright 2014 Sonia Keys
// License MIT: http://opensource.org/licenses/MIT
package graph
// adj_RO.go is code generated from adj_cg.go by directives in graph.go.
// Editing adj_cg.go is okay.
// DO NOT EDIT adj_RO.go. The RO is for Read Only.
import (
"errors"
"fmt"
"math/rand"
"github.com/soniakeys/bits"
)
// ArcDensity returns density for an simple directed graph.
//
// See also ArcDensity function.
//
// There are equivalent labeled and unlabeled versions of this method.
func (g AdjacencyList) ArcDensity() float64 {
return ArcDensity(len(g), g.ArcSize())
}
// ArcSize returns the number of arcs in g.
//
// Note that for an undirected graph without loops, the number of undirected
// edges -- the traditional meaning of graph size -- will be ArcSize()/2.
// On the other hand, if g is an undirected graph that has or may have loops,
// g.ArcSize()/2 is not a meaningful quantity.
//
// There are equivalent labeled and unlabeled versions of this method.
func (g AdjacencyList) ArcSize() int {
m := 0
for _, to := range g {
m += len(to)
}
return m
}
// BoundsOk validates that all arcs in g stay within the slice bounds of g.
//
// BoundsOk returns true when no arcs point outside the bounds of g.
// Otherwise it returns false and an example arc that points outside of g.
//
// Most methods of this package assume the BoundsOk condition and may
// panic when they encounter an arc pointing outside of the graph. This
// function can be used to validate a graph when the BoundsOk condition
// is unknown.
//
// There are equivalent labeled and unlabeled versions of this method.
func (g AdjacencyList) BoundsOk() (ok bool, fr NI, to NI) {
for fr, to := range g {
for _, to := range to {
if to < 0 || to >= NI(len(g)) {
return false, NI(fr), to
}
}
}
return true, -1, to
}
// BreadthFirst traverses a directed or undirected graph in breadth
// first order.
//
// Traversal starts at node start and visits the nodes reachable from
// start. The function visit is called for each node visited. Nodes
// not reachable from start are not visited.
//
// There are equivalent labeled and unlabeled versions of this method.
//
// See also alt.BreadthFirst, a variant with more options, and
// alt.BreadthFirst2, a direction optimizing variant.
func (g AdjacencyList) BreadthFirst(start NI, visit func(NI)) {
v := bits.New(len(g))
v.SetBit(int(start), 1)
visit(start)
var next []NI
for frontier := []NI{start}; len(frontier) > 0; {
for _, n := range frontier {
for _, nb := range g[n] {
if v.Bit(int(nb)) == 0 {
v.SetBit(int(nb), 1)
visit(nb)
next = append(next, nb)
}
}
}
frontier, next = next, frontier[:0]
}
}
// Copy makes a deep copy of g.
// Copy also computes the arc size ma, the number of arcs.
//
// There are equivalent labeled and unlabeled versions of this method.
func (g AdjacencyList) Copy() (c AdjacencyList, ma int) {
c = make(AdjacencyList, len(g))
for n, to := range g {
c[n] = append([]NI{}, to...)
ma += len(to)
}
return
}
// DepthFirst traverses a directed or undirected graph in depth
// first order.
//
// Traversal starts at node start and visits the nodes reachable from
// start. The function visit is called for each node visited. Nodes
// not reachable from start are not visited.
//
// There are equivalent labeled and unlabeled versions of this method.
//
// See also alt.DepthFirst, a variant with more options.
func (g AdjacencyList) DepthFirst(start NI, visit func(NI)) {
v := bits.New(len(g))
var f func(NI)
f = func(n NI) {
visit(n)
v.SetBit(int(n), 1)
for _, to := range g[n] {
if v.Bit(int(to)) == 0 {
f(to)
}
}
}
f(start)
}
// HasArc returns true if g has any arc from node `fr` to node `to`.
//
// Also returned is the index within the slice of arcs from node `fr`.
// If no arc from `fr` to `to` is present, HasArc returns false, -1.
//
// There are equivalent labeled and unlabeled versions of this method.
//
// See also the method ParallelArcs, which finds all parallel arcs from
// `fr` to `to`.
func (g AdjacencyList) HasArc(fr, to NI) (bool, int) {
for x, h := range g[fr] {
if h == to {
return true, x
}
}
return false, -1
}
// AnyLoop identifies if a graph contains a loop, an arc that leads from a
// a node back to the same node.
//
// If g contains a loop, the method returns true and an example of a node
// with a loop. If there are no loops in g, the method returns false, -1.
//
// There are equivalent labeled and unlabeled versions of this method.
func (g AdjacencyList) AnyLoop() (bool, NI) {
for fr, to := range g {
for _, to := range to {
if NI(fr) == to {
return true, to
}
}
}
return false, -1
}
// AddNode maps a node in a supergraph to a subgraph node.
//
// Argument p must be an NI in supergraph s.Super. AddNode panics if
// p is not a valid node index of s.Super.
//
// AddNode is idempotent in that it does not add a new node to the subgraph if
// a subgraph node already exists mapped to supergraph node p.
//
// The mapped subgraph NI is returned.
func (s *Subgraph) AddNode(p NI) (b NI) {
if int(p) < 0 || int(p) >= s.Super.Order() {
panic(fmt.Sprint("AddNode: NI ", p, " not in supergraph"))
}
if b, ok := s.SubNI[p]; ok {
return b
}
a := s.AdjacencyList
b = NI(len(a))
s.AdjacencyList = append(a, nil)
s.SuperNI = append(s.SuperNI, p)
s.SubNI[p] = b
return
}
// AddArc adds an arc to a subgraph.
//
// Arguments fr, to must be NIs in supergraph s.Super. As with AddNode,
// AddArc panics if fr and to are not valid node indexes of s.Super.
//
// The arc specfied by fr, to must exist in s.Super. Further, the number of
// parallel arcs in the subgraph cannot exceed the number of corresponding
// parallel arcs in the supergraph. That is, each arc already added to the
// subgraph counts against the arcs available in the supergraph. If a matching
// arc is not available, AddArc returns an error.
//
// If a matching arc is available, subgraph nodes are added as needed, the
// subgraph arc is added, and the method returns nil.
func (s *Subgraph) AddArc(fr NI, to NI) error {
// verify supergraph NIs first, but without adding subgraph nodes just yet.
if int(fr) < 0 || int(fr) >= s.Super.Order() {
panic(fmt.Sprint("AddArc: NI ", fr, " not in supergraph"))
}
if int(to) < 0 || int(to) >= s.Super.Order() {
panic(fmt.Sprint("AddArc: NI ", to, " not in supergraph"))
}
// count existing matching arcs in subgraph
n := 0
a := s.AdjacencyList
if bf, ok := s.SubNI[fr]; ok {
if bt, ok := s.SubNI[to]; ok {
// both NIs already exist in subgraph, need to count arcs
bTo := to
bTo = bt
for _, t := range a[bf] {
if t == bTo {
n++
}
}
}
}
// verify matching arcs are available in supergraph
for _, t := range (*s.Super)[fr] {
if t == to {
if n > 0 {
n-- // match existing arc
continue
}
// no more existing arcs need to be matched. nodes can finally
// be added as needed and then the arc can be added.
bf := s.AddNode(fr)
to = s.AddNode(to)
s.AdjacencyList[bf] = append(s.AdjacencyList[bf], to)
return nil // success
}
}
return errors.New("arc not available in supergraph")
}
func (super AdjacencyList) induceArcs(sub map[NI]NI, sup []NI) AdjacencyList {
s := make(AdjacencyList, len(sup))
for b, p := range sup {
var a []NI
for _, to := range super[p] {
if bt, ok := sub[to]; ok {
to = bt
a = append(a, to)
}
}
s[b] = a
}
return s
}
// InduceList constructs a node-induced subgraph.
//
// The subgraph is induced on receiver graph g. Argument l must be a list of
// NIs in receiver graph g. Receiver g becomes the supergraph of the induced
// subgraph.
//
// Duplicate NIs are allowed in list l. The duplicates are effectively removed
// and only a single corresponding node is created in the subgraph. Subgraph
// NIs are mapped in the order of list l, execpt for ignoring duplicates.
// NIs in l that are not in g will panic.
//
// Returned is the constructed Subgraph object containing the induced subgraph
// and the mappings to the supergraph.
func (g *AdjacencyList) InduceList(l []NI) *Subgraph {
sub, sup := mapList(l)
return &Subgraph{
Super: g,
SubNI: sub,
SuperNI: sup,
AdjacencyList: g.induceArcs(sub, sup)}
}
// InduceBits constructs a node-induced subgraph.
//
// The subgraph is induced on receiver graph g. Argument t must be a bitmap
// representing NIs in receiver graph g. Receiver g becomes the supergraph
// of the induced subgraph. NIs in t that are not in g will panic.
//
// Returned is the constructed Subgraph object containing the induced subgraph
// and the mappings to the supergraph.
func (g *AdjacencyList) InduceBits(t bits.Bits) *Subgraph {
sub, sup := mapBits(t)
return &Subgraph{
Super: g,
SubNI: sub,
SuperNI: sup,
AdjacencyList: g.induceArcs(sub, sup)}
}
// IsSimple checks for loops and parallel arcs.
//
// A graph is "simple" if it has no loops or parallel arcs.
//
// IsSimple returns true, -1 for simple graphs. If a loop or parallel arc is
// found, simple returns false and a node that represents a counterexample
// to the graph being simple.
//
// See also separate methods AnyLoop and AnyParallel.
//
// There are equivalent labeled and unlabeled versions of this method.
func (g AdjacencyList) IsSimple() (ok bool, n NI) {
if lp, n := g.AnyLoop(); lp {
return false, n
}
if pa, n, _ := g.AnyParallel(); pa {
return false, n
}
return true, -1
}
// IsolatedNodes returns a bitmap of isolated nodes in receiver graph g.
//
// An isolated node is one with no arcs going to or from it.
//
// There are equivalent labeled and unlabeled versions of this method.
func (g AdjacencyList) IsolatedNodes() (i bits.Bits) {
i = bits.New(len(g))
i.SetAll()
for fr, to := range g {
if len(to) > 0 {
i.SetBit(fr, 0)
for _, to := range to {
i.SetBit(int(to), 0)
}
}
}
return
}
// Order is the number of nodes in receiver g.
//
// It is simply a wrapper method for the Go builtin len().
//
// There are equivalent labeled and unlabeled versions of this method.
func (g AdjacencyList) Order() int {
// Why a wrapper for len()? Mostly for Directed and Undirected.
// u.Order() is a little nicer than len(u.LabeledAdjacencyList).
return len(g)
}
// ParallelArcs identifies all arcs from node `fr` to node `to`.
//
// The returned slice contains an element for each arc from node `fr` to node `to`.
// The element value is the index within the slice of arcs from node `fr`.
//
// There are equivalent labeled and unlabeled versions of this method.
//
// See also the method HasArc, which stops after finding a single arc.
func (g AdjacencyList) ParallelArcs(fr, to NI) (p []int) {
for x, h := range g[fr] {
if h == to {
p = append(p, x)
}
}
return
}
// Permute permutes the node labeling of receiver g.
//
// Argument p must be a permutation of the node numbers of the graph,
// 0 through len(g)-1. A permutation returned by rand.Perm(len(g)) for
// example is acceptable.
//
// The graph is permuted in place. The graph keeps the same underlying
// memory but values of the graph representation are permuted to produce
// an isomorphic graph. The node previously labeled 0 becomes p[0] and so on.
// See example (or the code) for clarification.
//
// There are equivalent labeled and unlabeled versions of this method.
func (g AdjacencyList) Permute(p []int) {
old := append(AdjacencyList{}, g...) // shallow copy
for fr, arcs := range old {
for i, to := range arcs {
arcs[i] = NI(p[to])
}
g[p[fr]] = arcs
}
}
// ShuffleArcLists shuffles the arc lists of each node of receiver g.
//
// For example a node with arcs leading to nodes 3 and 7 might have an
// arc list of either [3 7] or [7 3] after calling this method. The
// connectivity of the graph is not changed. The resulting graph stays
// equivalent but a traversal will encounter arcs in a different
// order.
//
// If Rand r is nil, the rand package default shared source is used.
//
// There are equivalent labeled and unlabeled versions of this method.
func (g AdjacencyList) ShuffleArcLists(r *rand.Rand) {
ri := rand.Intn
if r != nil {
ri = r.Intn
}
// Knuth-Fisher-Yates
for _, to := range g {
for i := len(to); i > 1; {
j := ri(i)
i--
to[i], to[j] = to[j], to[i]
}
}
}

417
vendor/github.com/soniakeys/graph/adj_cg.go generated vendored Normal file
View file

@ -0,0 +1,417 @@
// Copyright 2014 Sonia Keys
// License MIT: http://opensource.org/licenses/MIT
package graph
// adj_RO.go is code generated from adj_cg.go by directives in graph.go.
// Editing adj_cg.go is okay.
// DO NOT EDIT adj_RO.go. The RO is for Read Only.
import (
"errors"
"fmt"
"math/rand"
"github.com/soniakeys/bits"
)
// ArcDensity returns density for an simple directed graph.
//
// See also ArcDensity function.
//
// There are equivalent labeled and unlabeled versions of this method.
func (g LabeledAdjacencyList) ArcDensity() float64 {
return ArcDensity(len(g), g.ArcSize())
}
// ArcSize returns the number of arcs in g.
//
// Note that for an undirected graph without loops, the number of undirected
// edges -- the traditional meaning of graph size -- will be ArcSize()/2.
// On the other hand, if g is an undirected graph that has or may have loops,
// g.ArcSize()/2 is not a meaningful quantity.
//
// There are equivalent labeled and unlabeled versions of this method.
func (g LabeledAdjacencyList) ArcSize() int {
m := 0
for _, to := range g {
m += len(to)
}
return m
}
// BoundsOk validates that all arcs in g stay within the slice bounds of g.
//
// BoundsOk returns true when no arcs point outside the bounds of g.
// Otherwise it returns false and an example arc that points outside of g.
//
// Most methods of this package assume the BoundsOk condition and may
// panic when they encounter an arc pointing outside of the graph. This
// function can be used to validate a graph when the BoundsOk condition
// is unknown.
//
// There are equivalent labeled and unlabeled versions of this method.
func (g LabeledAdjacencyList) BoundsOk() (ok bool, fr NI, to Half) {
for fr, to := range g {
for _, to := range to {
if to.To < 0 || to.To >= NI(len(g)) {
return false, NI(fr), to
}
}
}
return true, -1, to
}
// BreadthFirst traverses a directed or undirected graph in breadth
// first order.
//
// Traversal starts at node start and visits the nodes reachable from
// start. The function visit is called for each node visited. Nodes
// not reachable from start are not visited.
//
// There are equivalent labeled and unlabeled versions of this method.
//
// See also alt.BreadthFirst, a variant with more options, and
// alt.BreadthFirst2, a direction optimizing variant.
func (g LabeledAdjacencyList) BreadthFirst(start NI, visit func(NI)) {
v := bits.New(len(g))
v.SetBit(int(start), 1)
visit(start)
var next []NI
for frontier := []NI{start}; len(frontier) > 0; {
for _, n := range frontier {
for _, nb := range g[n] {
if v.Bit(int(nb.To)) == 0 {
v.SetBit(int(nb.To), 1)
visit(nb.To)
next = append(next, nb.To)
}
}
}
frontier, next = next, frontier[:0]
}
}
// Copy makes a deep copy of g.
// Copy also computes the arc size ma, the number of arcs.
//
// There are equivalent labeled and unlabeled versions of this method.
func (g LabeledAdjacencyList) Copy() (c LabeledAdjacencyList, ma int) {
c = make(LabeledAdjacencyList, len(g))
for n, to := range g {
c[n] = append([]Half{}, to...)
ma += len(to)
}
return
}
// DepthFirst traverses a directed or undirected graph in depth
// first order.
//
// Traversal starts at node start and visits the nodes reachable from
// start. The function visit is called for each node visited. Nodes
// not reachable from start are not visited.
//
// There are equivalent labeled and unlabeled versions of this method.
//
// See also alt.DepthFirst, a variant with more options.
func (g LabeledAdjacencyList) DepthFirst(start NI, visit func(NI)) {
v := bits.New(len(g))
var f func(NI)
f = func(n NI) {
visit(n)
v.SetBit(int(n), 1)
for _, to := range g[n] {
if v.Bit(int(to.To)) == 0 {
f(to.To)
}
}
}
f(start)
}
// HasArc returns true if g has any arc from node `fr` to node `to`.
//
// Also returned is the index within the slice of arcs from node `fr`.
// If no arc from `fr` to `to` is present, HasArc returns false, -1.
//
// There are equivalent labeled and unlabeled versions of this method.
//
// See also the method ParallelArcs, which finds all parallel arcs from
// `fr` to `to`.
func (g LabeledAdjacencyList) HasArc(fr, to NI) (bool, int) {
for x, h := range g[fr] {
if h.To == to {
return true, x
}
}
return false, -1
}
// AnyLoop identifies if a graph contains a loop, an arc that leads from a
// a node back to the same node.
//
// If g contains a loop, the method returns true and an example of a node
// with a loop. If there are no loops in g, the method returns false, -1.
//
// There are equivalent labeled and unlabeled versions of this method.
func (g LabeledAdjacencyList) AnyLoop() (bool, NI) {
for fr, to := range g {
for _, to := range to {
if NI(fr) == to.To {
return true, to.To
}
}
}
return false, -1
}
// AddNode maps a node in a supergraph to a subgraph node.
//
// Argument p must be an NI in supergraph s.Super. AddNode panics if
// p is not a valid node index of s.Super.
//
// AddNode is idempotent in that it does not add a new node to the subgraph if
// a subgraph node already exists mapped to supergraph node p.
//
// The mapped subgraph NI is returned.
func (s *LabeledSubgraph) AddNode(p NI) (b NI) {
if int(p) < 0 || int(p) >= s.Super.Order() {
panic(fmt.Sprint("AddNode: NI ", p, " not in supergraph"))
}
if b, ok := s.SubNI[p]; ok {
return b
}
a := s.LabeledAdjacencyList
b = NI(len(a))
s.LabeledAdjacencyList = append(a, nil)
s.SuperNI = append(s.SuperNI, p)
s.SubNI[p] = b
return
}
// AddArc adds an arc to a subgraph.
//
// Arguments fr, to must be NIs in supergraph s.Super. As with AddNode,
// AddArc panics if fr and to are not valid node indexes of s.Super.
//
// The arc specfied by fr, to must exist in s.Super. Further, the number of
// parallel arcs in the subgraph cannot exceed the number of corresponding
// parallel arcs in the supergraph. That is, each arc already added to the
// subgraph counts against the arcs available in the supergraph. If a matching
// arc is not available, AddArc returns an error.
//
// If a matching arc is available, subgraph nodes are added as needed, the
// subgraph arc is added, and the method returns nil.
func (s *LabeledSubgraph) AddArc(fr NI, to Half) error {
// verify supergraph NIs first, but without adding subgraph nodes just yet.
if int(fr) < 0 || int(fr) >= s.Super.Order() {
panic(fmt.Sprint("AddArc: NI ", fr, " not in supergraph"))
}
if int(to.To) < 0 || int(to.To) >= s.Super.Order() {
panic(fmt.Sprint("AddArc: NI ", to.To, " not in supergraph"))
}
// count existing matching arcs in subgraph
n := 0
a := s.LabeledAdjacencyList
if bf, ok := s.SubNI[fr]; ok {
if bt, ok := s.SubNI[to.To]; ok {
// both NIs already exist in subgraph, need to count arcs
bTo := to
bTo.To = bt
for _, t := range a[bf] {
if t == bTo {
n++
}
}
}
}
// verify matching arcs are available in supergraph
for _, t := range (*s.Super)[fr] {
if t == to {
if n > 0 {
n-- // match existing arc
continue
}
// no more existing arcs need to be matched. nodes can finally
// be added as needed and then the arc can be added.
bf := s.AddNode(fr)
to.To = s.AddNode(to.To)
s.LabeledAdjacencyList[bf] = append(s.LabeledAdjacencyList[bf], to)
return nil // success
}
}
return errors.New("arc not available in supergraph")
}
func (super LabeledAdjacencyList) induceArcs(sub map[NI]NI, sup []NI) LabeledAdjacencyList {
s := make(LabeledAdjacencyList, len(sup))
for b, p := range sup {
var a []Half
for _, to := range super[p] {
if bt, ok := sub[to.To]; ok {
to.To = bt
a = append(a, to)
}
}
s[b] = a
}
return s
}
// InduceList constructs a node-induced subgraph.
//
// The subgraph is induced on receiver graph g. Argument l must be a list of
// NIs in receiver graph g. Receiver g becomes the supergraph of the induced
// subgraph.
//
// Duplicate NIs are allowed in list l. The duplicates are effectively removed
// and only a single corresponding node is created in the subgraph. Subgraph
// NIs are mapped in the order of list l, execpt for ignoring duplicates.
// NIs in l that are not in g will panic.
//
// Returned is the constructed Subgraph object containing the induced subgraph
// and the mappings to the supergraph.
func (g *LabeledAdjacencyList) InduceList(l []NI) *LabeledSubgraph {
sub, sup := mapList(l)
return &LabeledSubgraph{
Super: g,
SubNI: sub,
SuperNI: sup,
LabeledAdjacencyList: g.induceArcs(sub, sup)}
}
// InduceBits constructs a node-induced subgraph.
//
// The subgraph is induced on receiver graph g. Argument t must be a bitmap
// representing NIs in receiver graph g. Receiver g becomes the supergraph
// of the induced subgraph. NIs in t that are not in g will panic.
//
// Returned is the constructed Subgraph object containing the induced subgraph
// and the mappings to the supergraph.
func (g *LabeledAdjacencyList) InduceBits(t bits.Bits) *LabeledSubgraph {
sub, sup := mapBits(t)
return &LabeledSubgraph{
Super: g,
SubNI: sub,
SuperNI: sup,
LabeledAdjacencyList: g.induceArcs(sub, sup)}
}
// IsSimple checks for loops and parallel arcs.
//
// A graph is "simple" if it has no loops or parallel arcs.
//
// IsSimple returns true, -1 for simple graphs. If a loop or parallel arc is
// found, simple returns false and a node that represents a counterexample
// to the graph being simple.
//
// See also separate methods AnyLoop and AnyParallel.
//
// There are equivalent labeled and unlabeled versions of this method.
func (g LabeledAdjacencyList) IsSimple() (ok bool, n NI) {
if lp, n := g.AnyLoop(); lp {
return false, n
}
if pa, n, _ := g.AnyParallel(); pa {
return false, n
}
return true, -1
}
// IsolatedNodes returns a bitmap of isolated nodes in receiver graph g.
//
// An isolated node is one with no arcs going to or from it.
//
// There are equivalent labeled and unlabeled versions of this method.
func (g LabeledAdjacencyList) IsolatedNodes() (i bits.Bits) {
i = bits.New(len(g))
i.SetAll()
for fr, to := range g {
if len(to) > 0 {
i.SetBit(fr, 0)
for _, to := range to {
i.SetBit(int(to.To), 0)
}
}
}
return
}
// Order is the number of nodes in receiver g.
//
// It is simply a wrapper method for the Go builtin len().
//
// There are equivalent labeled and unlabeled versions of this method.
func (g LabeledAdjacencyList) Order() int {
// Why a wrapper for len()? Mostly for Directed and Undirected.
// u.Order() is a little nicer than len(u.LabeledAdjacencyList).
return len(g)
}
// ParallelArcs identifies all arcs from node `fr` to node `to`.
//
// The returned slice contains an element for each arc from node `fr` to node `to`.
// The element value is the index within the slice of arcs from node `fr`.
//
// There are equivalent labeled and unlabeled versions of this method.
//
// See also the method HasArc, which stops after finding a single arc.
func (g LabeledAdjacencyList) ParallelArcs(fr, to NI) (p []int) {
for x, h := range g[fr] {
if h.To == to {
p = append(p, x)
}
}
return
}
// Permute permutes the node labeling of receiver g.
//
// Argument p must be a permutation of the node numbers of the graph,
// 0 through len(g)-1. A permutation returned by rand.Perm(len(g)) for
// example is acceptable.
//
// The graph is permuted in place. The graph keeps the same underlying
// memory but values of the graph representation are permuted to produce
// an isomorphic graph. The node previously labeled 0 becomes p[0] and so on.
// See example (or the code) for clarification.
//
// There are equivalent labeled and unlabeled versions of this method.
func (g LabeledAdjacencyList) Permute(p []int) {
old := append(LabeledAdjacencyList{}, g...) // shallow copy
for fr, arcs := range old {
for i, to := range arcs {
arcs[i].To = NI(p[to.To])
}
g[p[fr]] = arcs
}
}
// ShuffleArcLists shuffles the arc lists of each node of receiver g.
//
// For example a node with arcs leading to nodes 3 and 7 might have an
// arc list of either [3 7] or [7 3] after calling this method. The
// connectivity of the graph is not changed. The resulting graph stays
// equivalent but a traversal will encounter arcs in a different
// order.
//
// If Rand r is nil, the rand package default shared source is used.
//
// There are equivalent labeled and unlabeled versions of this method.
func (g LabeledAdjacencyList) ShuffleArcLists(r *rand.Rand) {
ri := rand.Intn
if r != nil {
ri = r.Intn
}
// Knuth-Fisher-Yates
for _, to := range g {
for i := len(to); i > 1; {
j := ri(i)
i--
to[i], to[j] = to[j], to[i]
}
}
}

1059
vendor/github.com/soniakeys/graph/dir.go generated vendored Normal file

File diff suppressed because it is too large Load diff

1091
vendor/github.com/soniakeys/graph/dir_RO.go generated vendored Normal file

File diff suppressed because it is too large Load diff

1091
vendor/github.com/soniakeys/graph/dir_cg.go generated vendored Normal file

File diff suppressed because it is too large Load diff

122
vendor/github.com/soniakeys/graph/doc.go generated vendored Normal file
View file

@ -0,0 +1,122 @@
// Copyright 2014 Sonia Keys
// License MIT: http://opensource.org/licenses/MIT
// Graph algorithms: Dijkstra, A*, Bellman Ford, Floyd Warshall;
// Kruskal and Prim minimal spanning tree; topological sort and DAG longest
// and shortest paths; Eulerian cycle and path; degeneracy and k-cores;
// Bron Kerbosch clique finding; connected components; dominance; and others.
//
// This is a graph library of integer indexes. To use it with application
// data, you associate data with integer indexes, perform searches or other
// operations with the library, and then use the integer index results to refer
// back to your application data.
//
// Thus it does not store application data, pointers to application data,
// or require you to implement an interface on your application data.
// The idea is to keep the library methods fast and lean.
//
// Representation overview
//
// The package defines a type for a node index (NI) which is just an integer
// type. It defines types for a number of number graph representations using
// NI. The fundamental graph type is AdjacencyList, which is the
// common "list of lists" graph representation. It is a list as a slice
// with one element for each node of the graph. Each element is a list
// itself, a list of neighbor nodes, implemented as an NI slice. Methods
// on an AdjacencyList generally work on any representable graph, including
// directed or undirected graphs, simple graphs or multigraphs.
//
// The type Undirected embeds an AdjacencyList adding methods specific to
// undirected graphs. Similarly the type Directed adds methods meaningful
// for directed graphs.
//
// Similar to NI, the type LI is a "label index" which labels a
// node-to-neighbor "arc" or edge. Just as an NI can index arbitrary node
// data, an LI can index arbitrary arc or edge data. A number of algorithms
// use a "weight" associated with an arc. This package does not represent
// weighted arcs explicitly, but instead uses the LI as a more general
// mechanism allowing not only weights but arbitrary data to be associated
// with arcs. While AdjacencyList represents an arc with simply an NI,
// the type LabeledAdjacencyList uses a type that pairs an NI with an LI.
// This type is named Half, for half-arc. (A full arc would represent
// both ends.) Types LabeledDirected and LabeledUndirected embed a
// LabeledAdjacencyList.
//
// In contrast to Half, the type Edge represents both ends of an edge (but
// no label.) The type LabeledEdge adds the label. The type WeightedEdgeList
// bundles a list of LabeledEdges with a WeightFunc. (WeightedEdgeList has
// few methods. It exists primarily to support the Kruskal algorithm.)
//
// FromList is a compact rooted tree (or forest) respresentation. Like
// AdjacencyList and LabeledAdjacencyList, it is a list with one element for
// each node of the graph. Each element contains only a single neighbor
// however, its parent in the tree, the "from" node.
//
// Code generation
//
// A number of methods on AdjacencyList, Directed, and Undirected are
// applicable to LabeledAdjacencyList, LabeledDirected, and LabeledUndirected
// simply by ignoring the label. In these cases code generation provides
// methods on both types from a single source implementation. These methods
// are documented with the sentence "There are equivalent labeled and unlabeled
// versions of this method."
//
// Terminology
//
// This package uses the term "node" rather than "vertex." It uses "arc"
// to mean a directed edge, and uses "from" and "to" to refer to the ends
// of an arc. It uses "start" and "end" to refer to endpoints of a search
// or traversal.
//
// The usage of "to" and "from" is perhaps most strange. In common speech
// they are prepositions, but throughout this package they are used as
// adjectives, for example to refer to the "from node" of an arc or the
// "to node". The type "FromList" is named to indicate it stores a list of
// "from" values.
//
// A "half arc" refers to just one end of an arc, either the to or from end.
//
// Two arcs are "reciprocal" if they connect two distinct nodes n1 and n2,
// one arc leading from n1 to n2 and the other arc leading from n2 to n1.
// Undirected graphs are represented with reciprocal arcs.
//
// A node with an arc to itself represents a "loop." Duplicate arcs, where
// a node has multiple arcs to another node, are termed "parallel arcs."
// A graph with no loops or parallel arcs is "simple." A graph that allows
// parallel arcs is a "multigraph"
//
// The "size" of a graph traditionally means the number of undirected edges.
// This package uses "arc size" to mean the number of arcs in a graph. For an
// undirected graph without loops, arc size is 2 * size.
//
// The "order" of a graph is the number of nodes. An "ordering" though means
// an ordered list of nodes.
//
// A number of graph search algorithms use a concept of arc "weights."
// The sum of arc weights along a path is a "distance." In contrast, the
// number of nodes in a path, including start and end nodes, is the path's
// "length." (Yes, mixing weights and lengths would be nonsense physically,
// but the terms used here are just distinct terms for abstract values.
// The actual meaning to an application is likely to be something else
// entirely and is not relevant within this package.)
//
// Finally, this package documentation takes back the word "object" in some
// places to refer to a Go value, especially a value of a type with methods.
//
// Shortest path searches
//
// This package implements a number of shortest path searches. Most work
// with weighted graphs that are directed or undirected, and with graphs
// that may have loops or parallel arcs. For weighted graphs, "shortest"
// is defined as the path distance (sum of arc weights) with path length
// (number of nodes) breaking ties. If multiple paths have the same minimum
// distance with the same minimum length, search methods are free to return
// any of them.
//
// Algorithm Description
// Dijkstra Non-negative arc weights, single or all paths.
// AStar Non-negative arc weights, heuristic guided, single path.
// BellmanFord Negative arc weights allowed, no negative cycles, all paths.
// DAGPath O(n) algorithm for DAGs, arc weights of any sign.
// FloydWarshall all pairs distances, no negative cycles.
package graph

498
vendor/github.com/soniakeys/graph/fromlist.go generated vendored Normal file
View file

@ -0,0 +1,498 @@
// Copyright 2014 Sonia Keys
// License MIT: http://opensource.org/licenses/MIT
package graph
import "github.com/soniakeys/bits"
// FromList represents a rooted tree (or forest) where each node is associated
// with a half arc identifying an arc "from" another node.
//
// Other terms for this data structure include "parent list",
// "predecessor list", "in-tree", "inverse arborescence", and
// "spaghetti stack."
//
// The Paths member represents the tree structure. Leaves and MaxLen are
// not always needed. Where Leaves is used it serves as a bitmap where
// Leaves.Bit(n) == 1 for each leaf n of the tree. Where MaxLen is used it is
// provided primarily as a convenience for functions that might want to
// anticipate the maximum path length that would be encountered traversing
// the tree.
//
// Various graph search methods use a FromList to returns search results.
// For a start node of a search, From will be -1 and Len will be 1. For other
// nodes reached by the search, From represents a half arc in a path back to
// start and Len represents the number of nodes in the path. For nodes not
// reached by the search, From will be -1 and Len will be 0.
//
// A single FromList can also represent a forest. In this case paths from
// all leaves do not return to a single root node, but multiple root nodes.
//
// While a FromList generally encodes a tree or forest, it is technically
// possible to encode a cyclic graph. A number of FromList methods require
// the receiver to be acyclic. Graph methods documented to return a tree or
// forest will never return a cyclic FromList. In other cases however,
// where a FromList is not known to by cyclic, the Cyclic method can be
// useful to validate the acyclic property.
type FromList struct {
Paths []PathEnd // tree representation
Leaves bits.Bits // leaves of tree
MaxLen int // length of longest path, max of all PathEnd.Len values
}
// PathEnd associates a half arc and a path length.
//
// A PathEnd list is an element type of FromList.
type PathEnd struct {
From NI // a "from" half arc, the node the arc comes from
Len int // number of nodes in path from start
}
/* NewFromList could be confusing now with bits also needing allocation.
maybe best to not have this function. Maybe a more useful new would be
one that took a PathEnd slice and intitialized everything including roots
and max len. Maybe its time for a separate []PathEnd type when that's
all that's needed. (and reconsider the name PathEnd)
*/
// NewFromList creates a FromList object of given order.
//
// The Paths member is allocated to the specified order n but other members
// are left as zero values.
func NewFromList(n int) FromList {
return FromList{Paths: make([]PathEnd, n)}
}
// BoundsOk validates the "from" values in the list.
//
// Negative values are allowed as they indicate root nodes.
//
// BoundsOk returns true when all from values are less than len(t).
// Otherwise it returns false and a node with a from value >= len(t).
func (f FromList) BoundsOk() (ok bool, n NI) {
for n, e := range f.Paths {
if int(e.From) >= len(f.Paths) {
return false, NI(n)
}
}
return true, -1
}
// CommonStart returns the common start node of minimal paths to a and b.
//
// It returns -1 if a and b cannot be traced back to a common node.
//
// The method relies on populated PathEnd.Len members. Use RecalcLen if
// the Len members are not known to be present and correct.
func (f FromList) CommonStart(a, b NI) NI {
p := f.Paths
if p[a].Len < p[b].Len {
a, b = b, a
}
for bl := p[b].Len; p[a].Len > bl; {
a = p[a].From
if a < 0 {
return -1
}
}
for a != b {
a = p[a].From
if a < 0 {
return -1
}
b = p[b].From
}
return a
}
// Cyclic determines if f contains a cycle, a non-empty path from a node
// back to itself.
//
// Cyclic returns true if g contains at least one cycle. It also returns
// an example of a node involved in a cycle.
//
// Cyclic returns (false, -1) in the normal case where f is acyclic.
// Note that the bool is not an "ok" return. A cyclic FromList is usually
// not okay.
func (f FromList) Cyclic() (cyclic bool, n NI) {
p := f.Paths
vis := bits.New(len(p))
for i := range p {
path := bits.New(len(p))
for n := i; vis.Bit(n) == 0; {
vis.SetBit(n, 1)
path.SetBit(n, 1)
if n = int(p[n].From); n < 0 {
break
}
if path.Bit(n) == 1 {
return true, NI(n)
}
}
}
return false, -1
}
// IsolatedNodeBits returns a bitmap of isolated nodes in receiver graph f.
//
// An isolated node is one with no arcs going to or from it.
func (f FromList) IsolatedNodes() (iso bits.Bits) {
p := f.Paths
iso = bits.New(len(p))
iso.SetAll()
for n, e := range p {
if e.From >= 0 {
iso.SetBit(n, 0)
iso.SetBit(int(e.From), 0)
}
}
return
}
// PathTo decodes a FromList, recovering a single path.
//
// The path is returned as a list of nodes where the first element will be
// a root node and the last element will be the specified end node.
//
// Only the Paths member of the receiver is used. Other members of the
// FromList do not need to be valid, however the MaxLen member can be useful
// for allocating argument p.
//
// Argument p can provide the result slice. If p has capacity for the result
// it will be used, otherwise a new slice is created for the result.
//
// See also function PathTo.
func (f FromList) PathTo(end NI, p []NI) []NI {
return PathTo(f.Paths, end, p)
}
// PathTo decodes a single path from a PathEnd list.
//
// A PathEnd list is the main data representation in a FromList. See FromList.
//
// PathTo returns a list of nodes where the first element will be
// a root node and the last element will be the specified end node.
//
// Argument p can provide the result slice. If p has capacity for the result
// it will be used, otherwise a new slice is created for the result.
//
// See also method FromList.PathTo.
func PathTo(paths []PathEnd, end NI, p []NI) []NI {
n := paths[end].Len
if n == 0 {
return p[:0]
}
if cap(p) >= n {
p = p[:n]
} else {
p = make([]NI, n)
}
for {
n--
p[n] = end
if n == 0 {
return p
}
end = paths[end].From
}
}
// PathToLabeled decodes a FromList, recovering a single path.
//
// The start of the returned path will be a root node of the FromList.
//
// Only the Paths member of the receiver is used. Other members of the
// FromList do not need to be valid, however the MaxLen member can be useful
// for allocating argument p.
//
// Argument p can provide the result slice. If p has capacity for the result
// it will be used, otherwise a new slice is created for the result.
//
// See also function PathTo.
func (f FromList) PathToLabeled(end NI, labels []LI, p []Half) LabeledPath {
n := f.Paths[end].Len - 1
if n <= 0 {
return LabeledPath{end, p[:0]}
}
if cap(p) >= n {
p = p[:n]
} else {
p = make([]Half, n)
}
for {
n--
p[n] = Half{To: end, Label: labels[end]}
end = f.Paths[end].From
if n == 0 {
return LabeledPath{end, p}
}
}
}
// Preorder traverses a FromList in preorder.
//
// Nodes are visited in order such that for any node n with from node fr,
// fr is visited before n. Where f represents a tree, the visit ordering
// corresponds to a preordering, or depth first traversal of the tree.
// Where f represents a forest, the preorderings of the trees can be
// intermingled.
//
// Leaves must be set correctly first. Use RecalcLeaves if leaves are not
// known to be set correctly. FromList f cannot be cyclic.
//
// Traversal continues while visitor function v returns true. It terminates
// if v returns false. Preorder returns true if it completes without v
// returning false. Preorder returns false if traversal is terminated by v
// returning false.
func (f FromList) Preorder(v func(NI) bool) bool {
p := f.Paths
done := bits.New(len(p))
var df func(NI) bool
df = func(n NI) bool {
done.SetBit(int(n), 1)
if fr := p[n].From; fr >= 0 && done.Bit(int(fr)) == 0 {
df(fr)
}
return v(n)
}
for n := range f.Paths {
p[n].Len = 0
}
return f.Leaves.IterateOnes(func(n int) bool {
return df(NI(n))
})
}
// RecalcLeaves recomputes the Leaves member of f.
func (f *FromList) RecalcLeaves() {
p := f.Paths
lv := &f.Leaves
if lv.Num != len(p) {
*lv = bits.New(len(p))
}
lv.SetAll()
for n := range f.Paths {
if fr := p[n].From; fr >= 0 {
lv.SetBit(int(fr), 0)
}
}
}
// RecalcLen recomputes Len for each path end, and recomputes MaxLen.
//
// RecalcLen relies on the Leaves member being valid. If it is not known
// to be valid, call RecalcLeaves before calling RecalcLen.
//
// RecalcLen will panic if the FromList is cyclic. Use the Cyclic method
// if needed to verify that the FromList is acyclic.
func (f *FromList) RecalcLen() {
p := f.Paths
var setLen func(NI) int
setLen = func(n NI) int {
switch {
case p[n].Len > 0:
return p[n].Len
case p[n].From < 0:
p[n].Len = 1
return 1
}
l := 1 + setLen(p[n].From)
p[n].Len = l
return l
}
for n := range f.Paths {
p[n].Len = 0
}
f.MaxLen = 0
f.Leaves.IterateOnes(func(n int) bool {
if l := setLen(NI(n)); l > f.MaxLen {
f.MaxLen = l
}
return true
})
}
// ReRoot reorients the tree containing n to make n the root node.
//
// It keeps the tree connected by "reversing" the path from n to the old root.
//
// After ReRoot, the Leaves and Len members are invalid.
// Call RecalcLeaves or RecalcLen as needed.
func (f *FromList) ReRoot(n NI) {
p := f.Paths
fr := p[n].From
if fr < 0 {
return
}
p[n].From = -1
for {
ff := p[fr].From
p[fr].From = n
if ff < 0 {
return
}
n = fr
fr = ff
}
}
// Root finds the root of a node in a FromList.
func (f FromList) Root(n NI) NI {
for p := f.Paths; ; {
fr := p[n].From
if fr < 0 {
return n
}
n = fr
}
}
// Transpose constructs the directed graph corresponding to FromList f
// but with arcs in the opposite direction. That is, from roots toward leaves.
//
// If non-nil argrument roots is passed, Transpose populates it as roots of
// the resulting forest and returns nRoots as a count of the roots.
//
// The method relies only on the From member of f.Paths. Other members of
// the FromList are not used.
func (f FromList) Transpose(roots *bits.Bits) (forest Directed, nRoots int) {
p := f.Paths
g := make(AdjacencyList, len(p))
if roots != nil {
nRoots = len(p)
if roots.Num != nRoots {
*roots = bits.New(nRoots)
}
roots.SetAll()
}
for i, e := range p {
if e.From == -1 {
continue
}
g[e.From] = append(g[e.From], NI(i))
if roots != nil && roots.Bit(i) == 1 {
roots.SetBit(i, 0)
nRoots--
}
}
return Directed{g}, nRoots
}
// TransposeLabeled constructs the labeled directed graph corresponding
// to FromList f but with arcs in the opposite direction. That is, from
// roots toward leaves.
//
// The argument labels can be nil. In this case labels are generated matching
// the path indexes. This corresponds to the "to", or child node.
//
// If labels is non-nil, it must be the same length as t.Paths and is used
// to look up label numbers by the path index.
//
// If non-nil argrument roots is passed, Transpose populates it as roots of
// the resulting forest and returns nRoots as a count of the roots.
//
// The method relies only on the From member of f.Paths. Other members of
// the FromList are not used.
func (f FromList) TransposeLabeled(labels []LI, roots *bits.Bits) (forest LabeledDirected, nRoots int) {
p := f.Paths
g := make(LabeledAdjacencyList, len(p))
if roots != nil {
nRoots = len(p)
if roots.Num != nRoots {
*roots = bits.New(nRoots)
}
roots.SetAll()
}
for i, p := range f.Paths {
if p.From == -1 {
continue
}
l := LI(i)
if labels != nil {
l = labels[i]
}
g[p.From] = append(g[p.From], Half{NI(i), l})
if roots != nil && roots.Bit(i) == 1 {
roots.SetBit(i, 0)
nRoots--
}
}
return LabeledDirected{g}, nRoots
}
// Undirected constructs the undirected graph corresponding to FromList f.
//
// The resulting graph will be a tree or forest.
//
// If non-nil argrument roots is passed, Transpose populates it as roots of
// the resulting forest and returns nRoots as a count of the roots.
//
// The method relies only on the From member of f.Paths. Other members of
// the FromList are not used.
func (f FromList) Undirected(roots *bits.Bits) (forest Undirected, nRoots int) {
p := f.Paths
g := make(AdjacencyList, len(p))
if roots != nil {
nRoots = len(p)
if roots.Num != nRoots {
*roots = bits.New(nRoots)
}
roots.SetAll()
}
for i, e := range p {
if e.From == -1 {
continue
}
g[i] = append(g[i], e.From)
g[e.From] = append(g[e.From], NI(i))
if roots != nil && roots.Bit(i) == 1 {
roots.SetBit(i, 0)
nRoots--
}
}
return Undirected{g}, nRoots
}
// LabeledUndirected constructs the labeled undirected graph corresponding
// to FromList f.
//
// The resulting graph will be a tree or forest.
//
// The argument labels can be nil. In this case labels are generated matching
// the path indexes. This corresponds to the "to", or child node.
//
// If labels is non-nil, it must be the same length as t.Paths and is used
// to look up label numbers by the path index.
//
// If non-nil argrument roots is passed, LabeledUndirected populates it as
// roots of the resulting forest and returns nRoots as a count of the roots.
//
// The method relies only on the From member of f.Paths. Other members of
// the FromList are not used.
func (f FromList) LabeledUndirected(labels []LI, roots *bits.Bits) (forest LabeledUndirected, nRoots int) {
p := f.Paths
g := make(LabeledAdjacencyList, len(p))
if roots != nil {
nRoots = len(p)
if roots.Num != nRoots {
*roots = bits.New(nRoots)
}
roots.SetAll()
}
for i, p := range f.Paths {
if p.From == -1 {
continue
}
l := LI(i)
if labels != nil {
l = labels[i]
}
g[i] = append(g[i], Half{p.From, l})
g[p.From] = append(g[p.From], Half{NI(i), l})
if roots != nil && roots.Bit(i) == 1 {
roots.SetBit(i, 0)
nRoots--
}
}
return LabeledUndirected{g}, nRoots
}

3
vendor/github.com/soniakeys/graph/go.mod generated vendored Normal file
View file

@ -0,0 +1,3 @@
module "github.com/soniakeys/graph"
require "github.com/soniakeys/bits" v1.0.0

767
vendor/github.com/soniakeys/graph/graph.go generated vendored Normal file
View file

@ -0,0 +1,767 @@
// Copyright 2014 Sonia Keys
// License MIT: http://opensource.org/licenses/MIT
package graph
import (
"bytes"
"errors"
"fmt"
"math"
"reflect"
"text/template"
"github.com/soniakeys/bits"
)
// graph.go contains type definitions for all graph types and components.
// Also, go generate directives for source transformations.
//
// For readability, the types are defined in a dependency order:
//
// NI
// AdjacencyList
// Directed
// Undirected
// Bipartite
// Subgraph
// DirectedSubgraph
// UndirectedSubgraph
// LI
// Half
// fromHalf
// LabeledAdjacencyList
// LabeledDirected
// LabeledUndirected
// LabeledBipartite
// LabeledSubgraph
// LabeledDirectedSubgraph
// LabeledUndirectedSubgraph
// Edge
// LabeledEdge
// LabeledPath
// WeightFunc
// WeightedEdgeList
// TraverseOption
//go:generate cp adj_cg.go adj_RO.go
//go:generate gofmt -r "LabeledAdjacencyList -> AdjacencyList" -w adj_RO.go
//go:generate gofmt -r "n.To -> n" -w adj_RO.go
//go:generate gofmt -r "Half -> NI" -w adj_RO.go
//go:generate gofmt -r "LabeledSubgraph -> Subgraph" -w adj_RO.go
//go:generate cp dir_cg.go dir_RO.go
//go:generate gofmt -r "LabeledDirected -> Directed" -w dir_RO.go
//go:generate gofmt -r "LabeledDirectedSubgraph -> DirectedSubgraph" -w dir_RO.go
//go:generate gofmt -r "LabeledAdjacencyList -> AdjacencyList" -w dir_RO.go
//go:generate gofmt -r "labEulerian -> eulerian" -w dir_RO.go
//go:generate gofmt -r "newLabEulerian -> newEulerian" -w dir_RO.go
//go:generate gofmt -r "Half{n, -1} -> n" -w dir_RO.go
//go:generate gofmt -r "n.To -> n" -w dir_RO.go
//go:generate gofmt -r "Half -> NI" -w dir_RO.go
//go:generate cp undir_cg.go undir_RO.go
//go:generate gofmt -r "LabeledUndirected -> Undirected" -w undir_RO.go
//go:generate gofmt -r "LabeledBipartite -> Bipartite" -w undir_RO.go
//go:generate gofmt -r "LabeledUndirectedSubgraph -> UndirectedSubgraph" -w undir_RO.go
//go:generate gofmt -r "LabeledAdjacencyList -> AdjacencyList" -w undir_RO.go
//go:generate gofmt -r "newLabEulerian -> newEulerian" -w undir_RO.go
//go:generate gofmt -r "Half{n, -1} -> n" -w undir_RO.go
//go:generate gofmt -r "n.To -> n" -w undir_RO.go
//go:generate gofmt -r "Half -> NI" -w undir_RO.go
// An AdjacencyList represents a graph as a list of neighbors for each node.
// The "node ID" of a node is simply it's slice index in the AdjacencyList.
// For an AdjacencyList g, g[n] represents arcs going from node n to nodes
// g[n].
//
// Adjacency lists are inherently directed but can be used to represent
// directed or undirected graphs. See types Directed and Undirected.
type AdjacencyList [][]NI
// Directed represents a directed graph.
//
// Directed methods generally rely on the graph being directed, specifically
// that arcs do not have reciprocals.
type Directed struct {
AdjacencyList // embedded to include AdjacencyList methods
}
// Undirected represents an undirected graph.
//
// In an undirected graph, for each arc between distinct nodes there is also
// a reciprocal arc, an arc in the opposite direction. Loops do not have
// reciprocals.
//
// Undirected methods generally rely on the graph being undirected,
// specifically that every arc between distinct nodes has a reciprocal.
type Undirected struct {
AdjacencyList // embedded to include AdjacencyList methods
}
// Bipartite represents a bipartite graph.
//
// In a bipartite graph, nodes are partitioned into two sets, or
// "colors," such that every edge in the graph goes from one set to the
// other.
//
// Member Color represents the partition with a bitmap of length the same
// as the number of nodes in the graph. For convenience N0 stores the number
// of zero bits in Color.
//
// To construct a Bipartite object, if you can easily or efficiently use
// available information to construct the Color member, then you should do
// this and construct a Bipartite object with a Go struct literal.
//
// If partition information is not readily available, see the constructor
// Undirected.Bipartite.
//
// Alternatively, in some cases where the graph may have multiple connected
// components, the lower level Undirected.BipartiteComponent can be used to
// control color assignment by component.
type Bipartite struct {
Undirected
Color bits.Bits
N0 int
}
// Subgraph represents a subgraph mapped to a supergraph.
//
// The subgraph is the embedded AdjacencyList and so the Subgraph type inherits
// all methods of Adjacency list.
//
// The embedded subgraph mapped relative to a specific supergraph, member
// Super. A subgraph may have fewer nodes than its supergraph.
// Each node of the subgraph must map to a distinct node of the supergraph.
//
// The mapping giving the supergraph node for a given subgraph node is
// represented by member SuperNI, a slice parallel to the the subgraph.
//
// The mapping in the other direction, giving a subgraph NI for a given
// supergraph NI, is represented with map SubNI.
//
// Multiple Subgraphs can be created relative to a single supergraph.
// The Subgraph type represents a mapping to only a single supergraph however.
//
// See graph methods InduceList and InduceBits for construction of
// node-induced subgraphs.
//
// Alternatively an empty subgraph can be constructed with InduceList(nil).
// Arbitrary subgraphs can then be built up with methods AddNode and AddArc.
type Subgraph struct {
AdjacencyList // the subgraph
Super *AdjacencyList // the supergraph
SubNI map[NI]NI // subgraph NIs, indexed by supergraph NIs
SuperNI []NI // supergraph NIs indexed by subgraph NIs
}
// DirectedSubgraph represents a subgraph mapped to a supergraph.
//
// See additional doc at Subgraph type.
type DirectedSubgraph struct {
Directed
Super *Directed
SubNI map[NI]NI
SuperNI []NI
}
// UndirectedSubgraph represents a subgraph mapped to a supergraph.
//
// See additional doc at Subgraph type.
type UndirectedSubgraph struct {
Undirected
Super *Undirected
SubNI map[NI]NI
SuperNI []NI
}
// LI is a label integer, used for associating labels with arcs.
type LI int32
// Half is a half arc, representing a labeled arc and the "neighbor" node
// that the arc leads to.
//
// Halfs can be composed to form a labeled adjacency list.
type Half struct {
To NI // node ID, usable as a slice index
Label LI // half-arc ID for application data, often a weight
}
// fromHalf is a half arc, representing a labeled arc and the "neighbor" node
// that the arc originates from.
//
// This used internally in a couple of places. It used to be exported but is
// not currently needed anwhere in the API.
type fromHalf struct {
From NI
Label LI
}
// A LabeledAdjacencyList represents a graph as a list of neighbors for each
// node, connected by labeled arcs.
//
// Arc labels are not necessarily unique arc IDs. Different arcs can have
// the same label.
//
// Arc labels are commonly used to assocate a weight with an arc. Arc labels
// are general purpose however and can be used to associate arbitrary
// information with an arc.
//
// Methods implementing weighted graph algorithms will commonly take a
// weight function that turns a label int into a float64 weight.
//
// If only a small amount of information -- such as an integer weight or
// a single printable character -- needs to be associated, it can sometimes
// be possible to encode the information directly into the label int. For
// more generality, some lookup scheme will be needed.
//
// In an undirected labeled graph, reciprocal arcs must have identical labels.
// Note this does not preclude parallel arcs with different labels.
type LabeledAdjacencyList [][]Half
// LabeledDirected represents a directed labeled graph.
//
// This is the labeled version of Directed. See types LabeledAdjacencyList
// and Directed.
type LabeledDirected struct {
LabeledAdjacencyList // embedded to include LabeledAdjacencyList methods
}
// LabeledUndirected represents an undirected labeled graph.
//
// This is the labeled version of Undirected. See types LabeledAdjacencyList
// and Undirected.
type LabeledUndirected struct {
LabeledAdjacencyList // embedded to include LabeledAdjacencyList methods
}
// LabeledBipartite represents a bipartite graph.
//
// In a bipartite graph, nodes are partitioned into two sets, or
// "colors," such that every edge in the graph goes from one set to the
// other.
//
// Member Color represents the partition with a bitmap of length the same
// as the number of nodes in the graph. For convenience N0 stores the number
// of zero bits in Color.
//
// To construct a LabeledBipartite object, if you can easily or efficiently use
// available information to construct the Color member, then you should do
// this and construct a LabeledBipartite object with a Go struct literal.
//
// If partition information is not readily available, see the constructor
// Undirected.LabeledBipartite.
//
// Alternatively, in some cases where the graph may have multiple connected
// components, the lower level LabeledUndirected.BipartiteComponent can be used
// to control color assignment by component.
type LabeledBipartite struct {
LabeledUndirected
Color bits.Bits
N0 int
}
// LabeledSubgraph represents a subgraph mapped to a supergraph.
//
// See additional doc at Subgraph type.
type LabeledSubgraph struct {
LabeledAdjacencyList
Super *LabeledAdjacencyList
SubNI map[NI]NI
SuperNI []NI
}
// LabeledDirectedSubgraph represents a subgraph mapped to a supergraph.
//
// See additional doc at Subgraph type.
type LabeledDirectedSubgraph struct {
LabeledDirected
Super *LabeledDirected
SubNI map[NI]NI
SuperNI []NI
}
// LabeledUndirectedSubgraph represents a subgraph mapped to a supergraph.
//
// See additional doc at Subgraph type.
type LabeledUndirectedSubgraph struct {
LabeledUndirected
Super *LabeledUndirected
SubNI map[NI]NI
SuperNI []NI
}
// Edge is an undirected edge between nodes N1 and N2.
type Edge struct{ N1, N2 NI }
// LabeledEdge is an undirected edge with an associated label.
type LabeledEdge struct {
Edge
LI
}
// LabeledPath is a start node and a path of half arcs leading from start.
type LabeledPath struct {
Start NI
Path []Half
}
// Distance returns total path distance given WeightFunc w.
func (p LabeledPath) Distance(w WeightFunc) float64 {
d := 0.
for _, h := range p.Path {
d += w(h.Label)
}
return d
}
// WeightFunc returns a weight for a given label.
//
// WeightFunc is a parameter type for various search functions. The intent
// is to return a weight corresponding to an arc label. The name "weight"
// is an abstract term. An arc "weight" will typically have some application
// specific meaning other than physical weight.
type WeightFunc func(label LI) (weight float64)
// WeightedEdgeList is a graph representation.
//
// It is a labeled edge list, with an associated weight function to return
// a weight given an edge label.
//
// Also associated is the order, or number of nodes of the graph.
// All nodes occurring in the edge list must be strictly less than Order.
//
// WeigtedEdgeList sorts by weight, obtained by calling the weight function.
// If weight computation is expensive, consider supplying a cached or
// memoized version.
type WeightedEdgeList struct {
Order int
WeightFunc
Edges []LabeledEdge
}
// DistanceMatrix constructs a distance matrix corresponding to the weighted
// edges of l.
//
// An edge n1, n2 with WeightFunc return w is represented by both
// d[n1][n2] == w and d[n2][n1] = w. In case of parallel edges, the lowest
// weight is stored. The distance from any node to itself d[n][n] is 0, unless
// the node has a loop with a negative weight. If g has no edge between n1 and
// distinct n2, +Inf is stored for d[n1][n2] and d[n2][n1].
//
// The returned DistanceMatrix is suitable for DistanceMatrix.FloydWarshall.
func (l WeightedEdgeList) DistanceMatrix() (d DistanceMatrix) {
d = newDM(l.Order)
for _, e := range l.Edges {
n1 := e.Edge.N1
n2 := e.Edge.N2
wt := l.WeightFunc(e.LI)
// < to pick min of parallel arcs (also nicely ignores NaN)
if wt < d[n1][n2] {
d[n1][n2] = wt
d[n2][n1] = wt
}
}
return
}
// A DistanceMatrix is a square matrix representing some distance between
// nodes of a graph. If the graph is directected, d[from][to] represents
// some distance from node 'from' to node 'to'. Depending on context, the
// distance may be an arc weight or path distance. A value of +Inf typically
// means no arc or no path between the nodes.
type DistanceMatrix [][]float64
// little helper function, makes a blank distance matrix for FloydWarshall.
// could be exported?
func newDM(n int) DistanceMatrix {
inf := math.Inf(1)
d := make(DistanceMatrix, n)
for i := range d {
di := make([]float64, n)
for j := range di {
di[j] = inf
}
di[i] = 0
d[i] = di
}
return d
}
// FloydWarshall finds all pairs shortest distances for a weighted graph
// without negative cycles.
//
// It operates on a distance matrix representing arcs of a graph and
// destructively replaces arc weights with shortest path distances.
//
// In receiver d, d[fr][to] will be the shortest distance from node
// 'fr' to node 'to'. An element value of +Inf means no path exists.
// Any diagonal element < 0 indicates a negative cycle exists.
//
// See DistanceMatrix constructor methods of LabeledAdjacencyList and
// WeightedEdgeList for suitable inputs.
func (d DistanceMatrix) FloydWarshall() {
for k, dk := range d {
for _, di := range d {
dik := di[k]
for j := range d {
if d2 := dik + dk[j]; d2 < di[j] {
di[j] = d2
}
}
}
}
}
// PathMatrix is a return type for FloydWarshallPaths.
//
// It encodes all pairs shortest paths.
type PathMatrix [][]NI
// Path returns a shortest path from node start to end.
//
// Argument p is truncated, appended to, and returned as the result.
// Thus the underlying allocation is reused if possible.
// If there is no path from start to end, p is returned truncated to
// zero length.
//
// If receiver m is not a valid populated PathMatrix as returned by
// FloydWarshallPaths, behavior is undefined and a panic is likely.
func (m PathMatrix) Path(start, end NI, p []NI) []NI {
p = p[:0]
for {
p = append(p, start)
if start == end {
return p
}
start = m[start][end]
if start < 0 {
return p[:0]
}
}
}
// FloydWarshallPaths finds all pairs shortest paths for a weighted graph
// without negative cycles.
//
// It operates on a distance matrix representing arcs of a graph and
// destructively replaces arc weights with shortest path distances.
//
// In receiver d, d[fr][to] will be the shortest distance from node
// 'fr' to node 'to'. An element value of +Inf means no path exists.
// Any diagonal element < 0 indicates a negative cycle exists.
//
// The return value encodes the paths. See PathMatrix.Path.
//
// See DistanceMatrix constructor methods of LabeledAdjacencyList and
// WeightedEdgeList for suitable inputs.
//
// See also similar method FloydWarshallFromLists which has a richer
// return value.
func (d DistanceMatrix) FloydWarshallPaths() PathMatrix {
m := make(PathMatrix, len(d))
inf := math.Inf(1)
for i, di := range d {
mi := make([]NI, len(d))
for j, dij := range di {
if dij == inf {
mi[j] = -1
} else {
mi[j] = NI(j)
}
}
m[i] = mi
}
for k, dk := range d {
for i, di := range d {
mi := m[i]
dik := di[k]
for j := range d {
if d2 := dik + dk[j]; d2 < di[j] {
di[j] = d2
mi[j] = mi[k]
}
}
}
}
return m
}
// FloydWarshallFromLists finds all pairs shortest paths for a weighted
// graph without negative cycles.
//
// It operates on a distance matrix representing arcs of a graph and
// destructively replaces arc weights with shortest path distances.
//
// In receiver d, d[fr][to] will be the shortest distance from node
// 'fr' to node 'to'. An element value of +Inf means no path exists.
// Any diagonal element < 0 indicates a negative cycle exists.
//
// The return value encodes the paths. The FromLists are fully populated
// with Leaves and Len values. See for example FromList.PathTo for
// extracting paths. Note though that for i'th FromList of the return
// value, PathTo(j) will return the path from j's root, which will not
// be i in the case that there is no path from i to j. You must check
// the first node of the path to see if it is i. If not, there is no
// path from i to j. See example.
//
// See DistanceMatrix constructor methods of LabeledAdjacencyList and
// WeightedEdgeList for suitable inputs.
//
// See also similar method FloydWarshallPaths, which has a lighter
// weight return value.
func (d DistanceMatrix) FloydWarshallFromLists() []FromList {
l := make([]FromList, len(d))
inf := math.Inf(1)
for i, di := range d {
li := NewFromList(len(d))
p := li.Paths
for j, dij := range di {
if i == j || dij == inf {
p[j] = PathEnd{From: -1}
} else {
p[j] = PathEnd{From: NI(i)}
}
}
l[i] = li
}
for k, dk := range d {
pk := l[k].Paths
for i, di := range d {
dik := di[k]
pi := l[i].Paths
for j := range d {
if d2 := dik + dk[j]; d2 < di[j] {
di[j] = d2
pi[j] = pk[j]
}
}
}
}
for _, li := range l {
li.RecalcLeaves()
li.RecalcLen()
}
return l
}
// AddEdge adds an edge to a subgraph.
//
// For argument e, e.N1 and e.N2 must be NIs in supergraph s.Super. As with
// AddNode, AddEdge panics if e.N1 and e.N2 are not valid node indexes of
// s.Super.
//
// Edge e must exist in s.Super. Further, the number of
// parallel edges in the subgraph cannot exceed the number of corresponding
// parallel edges in the supergraph. That is, each edge already added to the
// subgraph counts against the edges available in the supergraph. If a matching
// edge is not available, AddEdge returns an error.
//
// If a matching edge is available, subgraph nodes are added as needed, the
// subgraph edge is added, and the method returns nil.
func (s *UndirectedSubgraph) AddEdge(n1, n2 NI) error {
// verify supergraph NIs first, but without adding subgraph nodes just yet.
if int(n1) < 0 || int(n1) >= s.Super.Order() {
panic(fmt.Sprint("AddEdge: NI ", n1, " not in supergraph"))
}
if int(n2) < 0 || int(n2) >= s.Super.Order() {
panic(fmt.Sprint("AddEdge: NI ", n2, " not in supergraph"))
}
// count existing matching edges in subgraph
n := 0
a := s.Undirected.AdjacencyList
if b1, ok := s.SubNI[n1]; ok {
if b2, ok := s.SubNI[n2]; ok {
// both NIs already exist in subgraph, need to count edges
for _, t := range a[b1] {
if t == b2 {
n++
}
}
if b1 != b2 {
// verify reciprocal arcs exist
r := 0
for _, t := range a[b2] {
if t == b1 {
r++
}
}
if r < n {
n = r
}
}
}
}
// verify matching edges are available in supergraph
m := 0
for _, t := range (*s.Super).AdjacencyList[n1] {
if t == n2 {
if m == n {
goto r // arc match after all existing arcs matched
}
m++
}
}
return errors.New("edge not available in supergraph")
r:
if n1 != n2 {
// verify reciprocal arcs
m = 0
for _, t := range (*s.Super).AdjacencyList[n2] {
if t == n1 {
if m == n {
goto good
}
m++
}
}
return errors.New("edge not available in supergraph")
}
good:
// matched enough edges. nodes can finally
// be added as needed and then the edge can be added.
b1 := s.AddNode(n1)
b2 := s.AddNode(n2)
s.Undirected.AddEdge(b1, b2)
return nil // success
}
// AddEdge adds an edge to a subgraph.
//
// For argument e, e.N1 and e.N2 must be NIs in supergraph s.Super. As with
// AddNode, AddEdge panics if e.N1 and e.N2 are not valid node indexes of
// s.Super.
//
// Edge e must exist in s.Super with label l. Further, the number of
// parallel edges in the subgraph cannot exceed the number of corresponding
// parallel edges in the supergraph. That is, each edge already added to the
// subgraph counts against the edges available in the supergraph. If a matching
// edge is not available, AddEdge returns an error.
//
// If a matching edge is available, subgraph nodes are added as needed, the
// subgraph edge is added, and the method returns nil.
func (s *LabeledUndirectedSubgraph) AddEdge(e Edge, l LI) error {
// verify supergraph NIs first, but without adding subgraph nodes just yet.
if int(e.N1) < 0 || int(e.N1) >= s.Super.Order() {
panic(fmt.Sprint("AddEdge: NI ", e.N1, " not in supergraph"))
}
if int(e.N2) < 0 || int(e.N2) >= s.Super.Order() {
panic(fmt.Sprint("AddEdge: NI ", e.N2, " not in supergraph"))
}
// count existing matching edges in subgraph
n := 0
a := s.LabeledUndirected.LabeledAdjacencyList
if b1, ok := s.SubNI[e.N1]; ok {
if b2, ok := s.SubNI[e.N2]; ok {
// both NIs already exist in subgraph, need to count edges
h := Half{b2, l}
for _, t := range a[b1] {
if t == h {
n++
}
}
if b1 != b2 {
// verify reciprocal arcs exist
r := 0
h.To = b1
for _, t := range a[b2] {
if t == h {
r++
}
}
if r < n {
n = r
}
}
}
}
// verify matching edges are available in supergraph
m := 0
h := Half{e.N2, l}
for _, t := range (*s.Super).LabeledAdjacencyList[e.N1] {
if t == h {
if m == n {
goto r // arc match after all existing arcs matched
}
m++
}
}
return errors.New("edge not available in supergraph")
r:
if e.N1 != e.N2 {
// verify reciprocal arcs
m = 0
h.To = e.N1
for _, t := range (*s.Super).LabeledAdjacencyList[e.N2] {
if t == h {
if m == n {
goto good
}
m++
}
}
return errors.New("edge not available in supergraph")
}
good:
// matched enough edges. nodes can finally
// be added as needed and then the edge can be added.
n1 := s.AddNode(e.N1)
n2 := s.AddNode(e.N2)
s.LabeledUndirected.AddEdge(Edge{n1, n2}, l)
return nil // success
}
// utility function called from all of the InduceList methods.
func mapList(l []NI) (sub map[NI]NI, sup []NI) {
sub = map[NI]NI{}
// one pass to collect unique NIs
for _, p := range l {
sub[NI(p)] = -1
}
if len(sub) == len(l) { // NIs in l are unique
sup = append([]NI{}, l...) // just copy them
for b, p := range l {
sub[p] = NI(b) // and fill in map
}
} else { // NIs in l not unique
sup = make([]NI, 0, len(sub))
for _, p := range l { // preserve ordering of first occurrences in l
if sub[p] < 0 {
sub[p] = NI(len(sup))
sup = append(sup, p)
}
}
}
return
}
// utility function called from all of the InduceBits methods.
func mapBits(t bits.Bits) (sub map[NI]NI, sup []NI) {
sup = make([]NI, 0, t.OnesCount())
sub = make(map[NI]NI, cap(sup))
t.IterateOnes(func(n int) bool {
sub[NI(n)] = NI(len(sup))
sup = append(sup, NI(n))
return true
})
return
}
// OrderMap formats maps for testable examples.
//
// OrderMap provides simple, no-frills formatting of maps in sorted order,
// convenient in some cases for output of testable examples.
func OrderMap(m interface{}) string {
// in particular exclude slices, which template would happily accept but
// which would probably represent a coding mistake
if reflect.TypeOf(m).Kind() != reflect.Map {
panic("not a map")
}
t := template.Must(template.New("").Parse(
`map[{{range $k, $v := .}}{{$k}}:{{$v}} {{end}}]`))
var b bytes.Buffer
if err := t.Execute(&b, m); err != nil {
panic(err)
}
return b.String()
}

135
vendor/github.com/soniakeys/graph/hacking.adoc generated vendored Normal file
View file

@ -0,0 +1,135 @@
= Hacking
== Get, install
Basic use of the package is just go get, or git clone; go install. There are
no dependencies outside the standard library.
== Build
CI is currently on travis-ci.org.
The build runs go vet with a few exceptions for things I'm not a big fan of.
https://github.com/client9/misspell has been valuable.
Also I wrote https://github.com/soniakeys/vetc to validate that each source
file has copyright/license statement.
Then, its not in the ci script, but I wrote https://github.com/soniakeys/rcv
to put coverage stats in the readme. Maybe it could be commit hook or
something but for now Ill try just running it manually now and then.
Go fmt is not in the ci script, but I have at least one editor set up to run
it on save, so code should stay formatted pretty well.
== Examples with random output
The math/rand generators with constant seeds used to give consistent numbers
across Go versions and so some examples relied on this. Sometime after Go 1.9
though the numbers changed. The technique for now is to go ahead and write
those examples, get them working, then change the `// Output:` line to
`// Random output:`. This keeps them showing in go doc but keeps them from
being run by go test. This works for now. It might be revisited at some
point.
== Plans
The primary to-do list is the issue tracker on Github.
== Direction, focus, features
The project started with no real goal or purpose, just as a place for some code
that might be useful. Here are some elements that characterize the direction.
* The focus has been on algorithms on adjacency lists. That is, adjacency list
is the fundamental representation for most implemented algorithms. There are
many other interesting representations, many reasons to use them, but
adjacency list is common in literature and practice. It has been useful to
focus on this data representation, at first anyway.
* The focus has been on single threaded algorithms. Again, there is much new
and interesting work being done with concurrent, parallel, and distributed
graph algorithms, and Go might be an excellent language to implement some of
these algorithms. But as a preliminary step, more traditional
single-threaded algorithms are implemented.
* The focus has been on static finite graphs. Again there is much interesting
work in online algorithms, dynamic graphs, and infinite graphs, but these
are not generally considered here.
* Algorithms selected for implementation are generally ones commonly appearing
in beginning graph theory discussions and in general purpose graph libraries
in other programming languages. With these as drivers, there's a big risk
developing a library of curiosities and academic exercises rather than a
library of practical utility. But well, it's a start. The hope is that
there are some practical drivers behind graph theory and behind other graph
libraries.
* There is active current research going on in graph algorithm development.
One goal for this library is to implement newer and faster algorithms.
In some cases where it seems not too much work, older/classic/traditional
algorithms may be implemented for comparison. These generally go in the
alt subdirectory.
== General principles
* The API is rather low level.
* Slices instead of maps. Maps are pretty efficient, and the property of
unique keys can be useful, But slices are still faster and more efficient,
and the unique key property is not always needed or wanted. The Adjacency
list implementation of this library is all done in slices. Slices are used
in algorithms where possible, in preference to maps. Maps are still used in
some cases where uniqueness is needed.
* Interfaces not generally used. Algorithms are implemented directly on
concrete data types and not on interfaces describing the capabilities of
the data types. The abstraction of interfaces is a nice match to graph
theory and the convenience of running graph algorithms on any type that
implements an interface is appealing, but the costs seem too high to me.
Slices are rich with capababilites that get hidden behind interfaces and
direct slice manipulation is always faster than going through interfaces.
An impedance for programs using the library is that they will generally
have to implement a mapping from slice indexes to their application data,
often including for example, some other form of node ID. This seems fair
to push this burden outside the graph library; the library cannot know
the needs of this mapping.
* Bitsets are widely used, particularly to store one bit of information per
node of a graph. I used math/big at first but then moved to a dense bitset
of my own. Yes, I considered other third-party bitsets but had my own
feature set I wanted. A slice of bools is another alternative. Bools will
be faster in almost all cases but the bitset will use less memory. I'm
chosing size over speed for now.
* Code generation is used to provide methods that work on both labeled and
unlabeled graphs. Code is written to labeled types, then transformations
generate the unlabled equivalents.
* Methods are named for what they return rather than what they do, where
reasonable anyway.
* Consistency in method signature and behavior across corresponding methods,
for example directed/undirected, labeled/unlabeled, again, as long as it's
reasonable.
* Sometimes in tension with the consistency principle, methods are lazy about
datatypes of parameters and return values. Sometimes a vale might have
different reasonable representations, a set might be a bitset, map, slice
of bools, or slice of set members for example. Methods will take and return
whatever is convenient for them and not convert the form just for consistency
or to try to guess what a caller would prefer.
* Methods return multiple results for whatever the algorithm produces that
might be of interest. Sometimes an algorithm will have a primary result but
then some secondary values that also might be of interest. If they are
already computed as a byproduct of the algorithm, or can be computed at
negligible cost, return them.
* Sometimes in conflict with the multiple result principle, methods will not
speculatively compute secondary results if there is any significant cost
and if the secondary result can be just as easily computed later.
== Code Maintenance
There are tons of cut and paste variants. There's the basic AdjacencyList,
then Directed and Undirected variants, then Labeled variants of each of those.
Code gen helps avoid some cut and paste but there's a bunch that doesn't
code gen very well and so is duplicated with cut and paste. In particular
the testable examples in the _test files don't cg well and so are pretty much
all duplicated by hand. If you change code, think about where there should
be variants and go look to see if the variants need similar changes.

254
vendor/github.com/soniakeys/graph/mst.go generated vendored Normal file
View file

@ -0,0 +1,254 @@
// Copyright 2014 Sonia Keys
// License MIT: http://opensource.org/licenses/MIT
package graph
import (
"container/heap"
"sort"
"github.com/soniakeys/bits"
)
type dsElement struct {
from NI
rank int
}
type disjointSet struct {
set []dsElement
}
func newDisjointSet(n int) disjointSet {
set := make([]dsElement, n)
for i := range set {
set[i].from = -1
}
return disjointSet{set}
}
// return true if disjoint trees were combined.
// false if x and y were already in the same tree.
func (ds disjointSet) union(x, y NI) bool {
xr := ds.find(x)
yr := ds.find(y)
if xr == yr {
return false
}
switch xe, ye := &ds.set[xr], &ds.set[yr]; {
case xe.rank < ye.rank:
xe.from = yr
case xe.rank == ye.rank:
xe.rank++
fallthrough
default:
ye.from = xr
}
return true
}
func (ds disjointSet) find(n NI) NI {
// fast paths for n == root or from root.
// no updates need in these cases.
s := ds.set
fr := s[n].from
if fr < 0 { // n is root
return n
}
n, fr = fr, s[fr].from
if fr < 0 { // n is from root
return n
}
// otherwise updates needed.
// two iterative passes (rather than recursion or stack)
// pass 1: find root
r := fr
for {
f := s[r].from
if f < 0 {
break
}
r = f
}
// pass 2: update froms
for {
s[n].from = r
if fr == r {
return r
}
n = fr
fr = s[n].from
}
}
// Kruskal implements Kruskal's algorithm for constructing a minimum spanning
// forest on an undirected graph.
//
// The forest is returned as an undirected graph.
//
// Also returned is a total distance for the returned forest.
//
// This method is a convenience wrapper for LabeledEdgeList.Kruskal.
// If you have no need for the input graph as a LabeledUndirected, it may be
// more efficient to construct a LabeledEdgeList directly.
func (g LabeledUndirected) Kruskal(w WeightFunc) (spanningForest LabeledUndirected, dist float64) {
return g.WeightedArcsAsEdges(w).Kruskal()
}
// Kruskal implements Kruskal's algorithm for constructing a minimum spanning
// forest on an undirected graph.
//
// The algorithm allows parallel edges, thus it is acceptable to construct
// the receiver with LabeledUndirected.WeightedArcsAsEdges. It may be more
// efficient though, if you can construct the receiver WeightedEdgeList
// directly without parallel edges.
//
// The forest is returned as an undirected graph.
//
// Also returned is a total distance for the returned forest.
//
// The edge list of the receiver is sorted in place as a side effect of this
// method. See KruskalSorted for a version that relies on the edge list being
// already sorted. This method is a wrapper for KruskalSorted. If you can
// generate the input graph sorted as required for KruskalSorted, you can
// call that method directly and avoid the overhead of the sort.
func (l WeightedEdgeList) Kruskal() (g LabeledUndirected, dist float64) {
e := l.Edges
w := l.WeightFunc
sort.Slice(e, func(i, j int) bool { return w(e[i].LI) < w(e[j].LI) })
return l.KruskalSorted()
}
// KruskalSorted implements Kruskal's algorithm for constructing a minimum
// spanning tree on an undirected graph.
//
// When called, the edge list of the receiver must be already sorted by weight.
// See the Kruskal method for a version that accepts an unsorted edge list.
// As with Kruskal, parallel edges are allowed.
//
// The forest is returned as an undirected graph.
//
// Also returned is a total distance for the returned forest.
func (l WeightedEdgeList) KruskalSorted() (g LabeledUndirected, dist float64) {
ds := newDisjointSet(l.Order)
g.LabeledAdjacencyList = make(LabeledAdjacencyList, l.Order)
for _, e := range l.Edges {
if ds.union(e.N1, e.N2) {
g.AddEdge(Edge{e.N1, e.N2}, e.LI)
dist += l.WeightFunc(e.LI)
}
}
return
}
// Prim implements the Jarník-Prim-Dijkstra algorithm for constructing
// a minimum spanning tree on an undirected graph.
//
// Prim computes a minimal spanning tree on the connected component containing
// the given start node. The tree is returned in FromList f. Argument f
// cannot be a nil pointer although it can point to a zero value FromList.
//
// If the passed FromList.Paths has the len of g though, it will be reused.
// In the case of a graph with multiple connected components, this allows a
// spanning forest to be accumulated by calling Prim successively on
// representative nodes of the components. In this case if leaves for
// individual trees are of interest, pass a non-nil zero-value for the argument
// componentLeaves and it will be populated with leaves for the single tree
// spanned by the call.
//
// If argument labels is non-nil, it must have the same length as g and will
// be populated with labels corresponding to the edges of the tree.
//
// Returned are the number of nodes spanned for the single tree (which will be
// the order of the connected component) and the total spanned distance for the
// single tree.
func (g LabeledUndirected) Prim(start NI, w WeightFunc, f *FromList, labels []LI, componentLeaves *bits.Bits) (numSpanned int, dist float64) {
al := g.LabeledAdjacencyList
if len(f.Paths) != len(al) {
*f = NewFromList(len(al))
}
if f.Leaves.Num != len(al) {
f.Leaves = bits.New(len(al))
}
b := make([]prNode, len(al)) // "best"
for n := range b {
b[n].nx = NI(n)
b[n].fx = -1
}
rp := f.Paths
var frontier prHeap
rp[start] = PathEnd{From: -1, Len: 1}
numSpanned = 1
fLeaves := &f.Leaves
fLeaves.SetBit(int(start), 1)
if componentLeaves != nil {
if componentLeaves.Num != len(al) {
*componentLeaves = bits.New(len(al))
}
componentLeaves.SetBit(int(start), 1)
}
for a := start; ; {
for _, nb := range al[a] {
if rp[nb.To].Len > 0 {
continue // already in MST, no action
}
switch bp := &b[nb.To]; {
case bp.fx == -1: // new node for frontier
bp.from = fromHalf{From: a, Label: nb.Label}
bp.wt = w(nb.Label)
heap.Push(&frontier, bp)
case w(nb.Label) < bp.wt: // better arc
bp.from = fromHalf{From: a, Label: nb.Label}
bp.wt = w(nb.Label)
heap.Fix(&frontier, bp.fx)
}
}
if len(frontier) == 0 {
break // done
}
bp := heap.Pop(&frontier).(*prNode)
a = bp.nx
rp[a].Len = rp[bp.from.From].Len + 1
rp[a].From = bp.from.From
if len(labels) != 0 {
labels[a] = bp.from.Label
}
dist += bp.wt
fLeaves.SetBit(int(bp.from.From), 0)
fLeaves.SetBit(int(a), 1)
if componentLeaves != nil {
componentLeaves.SetBit(int(bp.from.From), 0)
componentLeaves.SetBit(int(a), 1)
}
numSpanned++
}
return
}
type prNode struct {
nx NI
from fromHalf
wt float64 // p.Weight(from.Label)
fx int
}
type prHeap []*prNode
func (h prHeap) Len() int { return len(h) }
func (h prHeap) Less(i, j int) bool { return h[i].wt < h[j].wt }
func (h prHeap) Swap(i, j int) {
h[i], h[j] = h[j], h[i]
h[i].fx = i
h[j].fx = j
}
func (p *prHeap) Push(x interface{}) {
nd := x.(*prNode)
nd.fx = len(*p)
*p = append(*p, nd)
}
func (p *prHeap) Pop() interface{} {
r := *p
last := len(r) - 1
*p = r[:last]
return r[last]
}

708
vendor/github.com/soniakeys/graph/random.go generated vendored Normal file
View file

@ -0,0 +1,708 @@
// Copyright 2016 Sonia Keys
// License MIT: https://opensource.org/licenses/MIT
package graph
import (
"errors"
"math"
"math/rand"
"github.com/soniakeys/bits"
)
// ChungLu constructs a random simple undirected graph.
//
// The Chung Lu model is similar to a "configuration model" where each
// node has a specified degree. In the Chung Lu model the degree specified
// for each node is taken as an expected degree, not an exact degree.
//
// Argument w is "weight," the expected degree for each node.
// The values of w must be given in decreasing order.
//
// The constructed graph will have node 0 with expected degree w[0] and so on
// so degree will decrease with node number. To randomize degree across
// node numbers, consider using the Permute method with a rand.Perm.
//
// Also returned is the actual size m of constructed graph g.
//
// If Rand r is nil, the rand package default shared source is used.
func ChungLu(w []float64, rr *rand.Rand) (g Undirected, m int) {
// Ref: "Efficient Generation of Networks with Given Expected Degrees"
// Joel C. Miller and Aric Hagberg
// accessed at http://aric.hagberg.org/papers/miller-2011-efficient.pdf
rf := rand.Float64
if rr != nil {
rf = rr.Float64
}
a := make(AdjacencyList, len(w))
S := 0.
for i := len(w) - 1; i >= 0; i-- {
S += w[i]
}
for u := 0; u < len(w)-1; u++ {
v := u + 1
p := w[u] * w[v] / S
if p > 1 {
p = 1
}
for v < len(w) && p > 0 {
if p != 1 {
v += int(math.Log(rf()) / math.Log(1-p))
}
if v < len(w) {
q := w[u] * w[v] / S
if q > 1 {
q = 1
}
if rf() < q/p {
a[u] = append(a[u], NI(v))
a[v] = append(a[v], NI(u))
m++
}
p = q
v++
}
}
}
return Undirected{a}, m
}
// Euclidean generates a random simple graph on the Euclidean plane.
//
// Nodes are associated with coordinates uniformly distributed on a unit
// square. Arcs are added between random nodes with a bias toward connecting
// nearer nodes.
//
// Unfortunately the function has a few "knobs".
// The returned graph will have order nNodes and arc size nArcs. The affinity
// argument controls the bias toward connecting nearer nodes. The function
// selects random pairs of nodes as a candidate arc then rejects the candidate
// if the nodes fail an affinity test. Also parallel arcs are rejected.
// As more affine or denser graphs are requested, rejections increase,
// increasing run time. The patience argument controls the number of arc
// rejections allowed before the function gives up and returns an error.
// Note that higher affinity will require more patience and that some
// combinations of nNodes and nArcs cannot be achieved with any amount of
// patience given that the returned graph must be simple.
//
// If Rand r is nil, the rand package default shared source is used.
//
// Returned is a directed simple graph and associated positions indexed by
// node number. In the arc list for each node, to-nodes are in random
// order.
//
// See also LabeledEuclidean.
func Euclidean(nNodes, nArcs int, affinity float64, patience int, rr *rand.Rand) (g Directed, pos []struct{ X, Y float64 }, err error) {
a := make(AdjacencyList, nNodes) // graph
ri, rf, re := rand.Intn, rand.Float64, rand.ExpFloat64
if rr != nil {
ri, rf, re = rr.Intn, rr.Float64, rr.ExpFloat64
}
// generate random positions
pos = make([]struct{ X, Y float64 }, nNodes)
for i := range pos {
pos[i].X = rf()
pos[i].Y = rf()
}
// arcs
var tooFar, dup int
arc:
for i := 0; i < nArcs; {
if tooFar == nArcs*patience {
err = errors.New("affinity not found")
return
}
if dup == nArcs*patience {
err = errors.New("overcrowding")
return
}
n1 := NI(ri(nNodes))
var n2 NI
for {
n2 = NI(ri(nNodes))
if n2 != n1 { // no graph loops
break
}
}
c1 := &pos[n1]
c2 := &pos[n2]
dist := math.Hypot(c2.X-c1.X, c2.Y-c1.Y)
if dist*affinity > re() { // favor near nodes
tooFar++
continue
}
for _, nb := range a[n1] {
if nb == n2 { // no parallel arcs
dup++
continue arc
}
}
a[n1] = append(a[n1], n2)
i++
}
g = Directed{a}
return
}
// LabeledEuclidean generates a random simple graph on the Euclidean plane.
//
// Arc label values in the returned graph g are indexes into the return value
// wt. Wt is the Euclidean distance between the from and to nodes of the arc.
//
// Otherwise the function arguments and return values are the same as for
// function Euclidean. See Euclidean.
func LabeledEuclidean(nNodes, nArcs int, affinity float64, patience int, rr *rand.Rand) (g LabeledDirected, pos []struct{ X, Y float64 }, wt []float64, err error) {
a := make(LabeledAdjacencyList, nNodes) // graph
wt = make([]float64, nArcs) // arc weights
ri, rf, re := rand.Intn, rand.Float64, rand.ExpFloat64
if rr != nil {
ri, rf, re = rr.Intn, rr.Float64, rr.ExpFloat64
}
// generate random positions
pos = make([]struct{ X, Y float64 }, nNodes)
for i := range pos {
pos[i].X = rf()
pos[i].Y = rf()
}
// arcs
var tooFar, dup int
arc:
for i := 0; i < nArcs; {
if tooFar == nArcs*patience {
err = errors.New("affinity not found")
return
}
if dup == nArcs*patience {
err = errors.New("overcrowding")
return
}
n1 := NI(ri(nNodes))
var n2 NI
for {
n2 = NI(ri(nNodes))
if n2 != n1 { // no graph loops
break
}
}
c1 := &pos[n1]
c2 := &pos[n2]
dist := math.Hypot(c2.X-c1.X, c2.Y-c1.Y)
if dist*affinity > re() { // favor near nodes
tooFar++
continue
}
for _, nb := range a[n1] {
if nb.To == n2 { // no parallel arcs
dup++
continue arc
}
}
wt[i] = dist
a[n1] = append(a[n1], Half{n2, LI(i)})
i++
}
g = LabeledDirected{a}
return
}
// Geometric generates a random geometric graph (RGG) on the Euclidean plane.
//
// An RGG is an undirected simple graph. Nodes are associated with coordinates
// uniformly distributed on a unit square. Edges are added between all nodes
// falling within a specified distance or radius of each other.
//
// The resulting number of edges is somewhat random but asymptotically
// approaches m = πr²n²/2. The method accumulates and returns the actual
// number of edges constructed. In the arc list for each node, to-nodes are
// ordered. Consider using ShuffleArcLists if random order is important.
//
// If Rand r is nil, the rand package default shared source is used.
//
// See also LabeledGeometric.
func Geometric(nNodes int, radius float64, rr *rand.Rand) (g Undirected, pos []struct{ X, Y float64 }, m int) {
// Expected degree is approximately nπr².
a := make(AdjacencyList, nNodes)
rf := rand.Float64
if rr != nil {
rf = rr.Float64
}
pos = make([]struct{ X, Y float64 }, nNodes)
for i := range pos {
pos[i].X = rf()
pos[i].Y = rf()
}
for u, up := range pos {
for v := u + 1; v < len(pos); v++ {
vp := pos[v]
dx := math.Abs(up.X - vp.X)
if dx >= radius {
continue
}
dy := math.Abs(up.Y - vp.Y)
if dy >= radius {
continue
}
if math.Hypot(dx, dy) < radius {
a[u] = append(a[u], NI(v))
a[v] = append(a[v], NI(u))
m++
}
}
}
g = Undirected{a}
return
}
// LabeledGeometric generates a random geometric graph (RGG) on the Euclidean
// plane.
//
// Edge label values in the returned graph g are indexes into the return value
// wt. Wt is the Euclidean distance between nodes of the edge. The graph
// size m is len(wt).
//
// See Geometric for additional description.
func LabeledGeometric(nNodes int, radius float64, rr *rand.Rand) (g LabeledUndirected, pos []struct{ X, Y float64 }, wt []float64) {
a := make(LabeledAdjacencyList, nNodes)
rf := rand.Float64
if rr != nil {
rf = rr.Float64
}
pos = make([]struct{ X, Y float64 }, nNodes)
for i := range pos {
pos[i].X = rf()
pos[i].Y = rf()
}
for u, up := range pos {
for v := u + 1; v < len(pos); v++ {
vp := pos[v]
if w := math.Hypot(up.X-vp.X, up.Y-vp.Y); w < radius {
a[u] = append(a[u], Half{NI(v), LI(len(wt))})
a[v] = append(a[v], Half{NI(u), LI(len(wt))})
wt = append(wt, w)
}
}
}
g = LabeledUndirected{a}
return
}
// GnmUndirected constructs a random simple undirected graph.
//
// Construction is by the ErdősRényi model where the specified number of
// distinct edges is selected from all possible edges with equal probability.
//
// Argument n is number of nodes, m is number of edges and must be <= n(n-1)/2.
//
// If Rand r is nil, the rand package default shared source is used.
//
// In the generated arc list for each node, to-nodes are ordered.
// Consider using ShuffleArcLists if random order is important.
//
// See also Gnm3Undirected, a method producing a statistically equivalent
// result, but by an algorithm with somewhat different performance properties.
// Performance of the two methods is expected to be similar in most cases but
// it may be worth trying both with your data to see if one has a clear
// advantage.
func GnmUndirected(n, m int, rr *rand.Rand) Undirected {
// based on Alg. 2 from "Efficient Generation of Large Random Networks",
// Vladimir Batagelj and Ulrik Brandes.
// accessed at http://algo.uni-konstanz.de/publications/bb-eglrn-05.pdf
ri := rand.Intn
if rr != nil {
ri = rr.Intn
}
re := n * (n - 1) / 2
ml := m
if m*2 > re {
ml = re - m
}
e := map[int]struct{}{}
for len(e) < ml {
e[ri(re)] = struct{}{}
}
a := make(AdjacencyList, n)
if m*2 > re {
i := 0
for v := 1; v < n; v++ {
for w := 0; w < v; w++ {
if _, ok := e[i]; !ok {
a[v] = append(a[v], NI(w))
a[w] = append(a[w], NI(v))
}
i++
}
}
} else {
for i := range e {
v := 1 + int(math.Sqrt(.25+float64(2*i))-.5)
w := i - (v * (v - 1) / 2)
a[v] = append(a[v], NI(w))
a[w] = append(a[w], NI(v))
}
}
return Undirected{a}
}
// GnmDirected constructs a random simple directed graph.
//
// Construction is by the ErdősRényi model where the specified number of
// distinct arcs is selected from all possible arcs with equal probability.
//
// Argument n is number of nodes, ma is number of arcs and must be <= n(n-1).
//
// If Rand r is nil, the rand package default shared source is used.
//
// In the generated arc list for each node, to-nodes are ordered.
// Consider using ShuffleArcLists if random order is important.
//
// See also Gnm3Directed, a method producing a statistically equivalent
// result, but by
// an algorithm with somewhat different performance properties. Performance
// of the two methods is expected to be similar in most cases but it may be
// worth trying both with your data to see if one has a clear advantage.
func GnmDirected(n, ma int, rr *rand.Rand) Directed {
// based on Alg. 2 from "Efficient Generation of Large Random Networks",
// Vladimir Batagelj and Ulrik Brandes.
// accessed at http://algo.uni-konstanz.de/publications/bb-eglrn-05.pdf
ri := rand.Intn
if rr != nil {
ri = rr.Intn
}
re := n * (n - 1)
ml := ma
if ma*2 > re {
ml = re - ma
}
e := map[int]struct{}{}
for len(e) < ml {
e[ri(re)] = struct{}{}
}
a := make(AdjacencyList, n)
if ma*2 > re {
i := 0
for v := 0; v < n; v++ {
for w := 0; w < n; w++ {
if w == v {
continue
}
if _, ok := e[i]; !ok {
a[v] = append(a[v], NI(w))
}
i++
}
}
} else {
for i := range e {
v := i / (n - 1)
w := i % (n - 1)
if w >= v {
w++
}
a[v] = append(a[v], NI(w))
}
}
return Directed{a}
}
// Gnm3Undirected constructs a random simple undirected graph.
//
// Construction is by the ErdősRényi model where the specified number of
// distinct edges is selected from all possible edges with equal probability.
//
// Argument n is number of nodes, m is number of edges and must be <= n(n-1)/2.
//
// If Rand r is nil, the rand package default shared source is used.
//
// In the generated arc list for each node, to-nodes are ordered.
// Consider using ShuffleArcLists if random order is important.
//
// See also GnmUndirected, a method producing a statistically equivalent
// result, but by an algorithm with somewhat different performance properties.
// Performance of the two methods is expected to be similar in most cases but
// it may be worth trying both with your data to see if one has a clear
// advantage.
func Gnm3Undirected(n, m int, rr *rand.Rand) Undirected {
// based on Alg. 3 from "Efficient Generation of Large Random Networks",
// Vladimir Batagelj and Ulrik Brandes.
// accessed at http://algo.uni-konstanz.de/publications/bb-eglrn-05.pdf
//
// I like this algorithm for its elegance. Pitty it tends to run a
// a little slower than the retry algorithm of Gnm.
ri := rand.Intn
if rr != nil {
ri = rr.Intn
}
a := make(AdjacencyList, n)
re := n * (n - 1) / 2
rm := map[int]int{}
for i := 0; i < m; i++ {
er := i + ri(re-i)
eNew := er
if rp, ok := rm[er]; ok {
eNew = rp
}
if rp, ok := rm[i]; !ok {
rm[er] = i
} else {
rm[er] = rp
}
v := 1 + int(math.Sqrt(.25+float64(2*eNew))-.5)
w := eNew - (v * (v - 1) / 2)
a[v] = append(a[v], NI(w))
a[w] = append(a[w], NI(v))
}
return Undirected{a}
}
// Gnm3Directed constructs a random simple directed graph.
//
// Construction is by the ErdősRényi model where the specified number of
// distinct arcs is selected from all possible arcs with equal probability.
//
// Argument n is number of nodes, ma is number of arcs and must be <= n(n-1).
//
// If Rand r is nil, the rand package default shared source is used.
//
// In the generated arc list for each node, to-nodes are ordered.
// Consider using ShuffleArcLists if random order is important.
//
// See also GnmDirected, a method producing a statistically equivalent result,
// but by an algorithm with somewhat different performance properties.
// Performance of the two methods is expected to be similar in most cases
// but it may be worth trying both with your data to see if one has a clear
// advantage.
func Gnm3Directed(n, ma int, rr *rand.Rand) Directed {
// based on Alg. 3 from "Efficient Generation of Large Random Networks",
// Vladimir Batagelj and Ulrik Brandes.
// accessed at http://algo.uni-konstanz.de/publications/bb-eglrn-05.pdf
ri := rand.Intn
if rr != nil {
ri = rr.Intn
}
a := make(AdjacencyList, n)
re := n * (n - 1)
rm := map[int]int{}
for i := 0; i < ma; i++ {
er := i + ri(re-i)
eNew := er
if rp, ok := rm[er]; ok {
eNew = rp
}
if rp, ok := rm[i]; !ok {
rm[er] = i
} else {
rm[er] = rp
}
v := eNew / (n - 1)
w := eNew % (n - 1)
if w >= v {
w++
}
a[v] = append(a[v], NI(w))
}
return Directed{a}
}
// GnpUndirected constructs a random simple undirected graph.
//
// Construction is by the Gilbert model, an ErdősRényi like model where
// distinct edges are independently selected from all possible edges with
// the specified probability.
//
// Argument n is number of nodes, p is probability for selecting an edge.
//
// If Rand r is nil, the rand package default shared source is used.
//
// In the generated arc list for each node, to-nodes are ordered.
// Consider using ShuffleArcLists if random order is important.
//
// Also returned is the actual size m of constructed graph g.
func GnpUndirected(n int, p float64, rr *rand.Rand) (g Undirected, m int) {
a := make(AdjacencyList, n)
if n < 2 {
return Undirected{a}, 0
}
rf := rand.Float64
if rr != nil {
rf = rr.Float64
}
// based on Alg. 1 from "Efficient Generation of Large Random Networks",
// Vladimir Batagelj and Ulrik Brandes.
// accessed at http://algo.uni-konstanz.de/publications/bb-eglrn-05.pdf
var v, w NI = 1, -1
g:
for c := 1 / math.Log(1-p); ; {
w += 1 + NI(c*math.Log(1-rf()))
for {
if w < v {
a[v] = append(a[v], w)
a[w] = append(a[w], v)
m++
continue g
}
w -= v
v++
if v == NI(n) {
break g
}
}
}
return Undirected{a}, m
}
// GnpDirected constructs a random simple directed graph.
//
// Construction is by the Gilbert model, an ErdősRényi like model where
// distinct arcs are independently selected from all possible arcs with
// the specified probability.
//
// Argument n is number of nodes, p is probability for selecting an arc.
//
// If Rand r is nil, the rand package default shared source is used.
//
// In the generated arc list for each node, to-nodes are ordered.
// Consider using ShuffleArcLists if random order is important.
//
// Also returned is the actual arc size m of constructed graph g.
func GnpDirected(n int, p float64, rr *rand.Rand) (g Directed, ma int) {
a := make(AdjacencyList, n)
if n < 2 {
return Directed{a}, 0
}
rf := rand.Float64
if rr != nil {
rf = rr.Float64
}
// based on Alg. 1 from "Efficient Generation of Large Random Networks",
// Vladimir Batagelj and Ulrik Brandes.
// accessed at http://algo.uni-konstanz.de/publications/bb-eglrn-05.pdf
var v, w NI = 0, -1
g:
for c := 1 / math.Log(1-p); ; {
w += 1 + NI(c*math.Log(1-rf()))
for ; ; w -= NI(n) {
if w == v {
w++
}
if w < NI(n) {
a[v] = append(a[v], w)
ma++
continue g
}
v++
if v == NI(n) {
break g
}
}
}
return Directed{a}, ma
}
// KroneckerDirected generates a Kronecker-like random directed graph.
//
// The returned graph g is simple and has no isolated nodes but is not
// necessarily fully connected. The number of of nodes will be <= 2^scale,
// and will be near 2^scale for typical values of arcFactor, >= 2.
// ArcFactor * 2^scale arcs are generated, although loops and duplicate arcs
// are rejected. In the arc list for each node, to-nodes are in random
// order.
//
// If Rand r is nil, the rand package default shared source is used.
//
// Return value ma is the number of arcs retained in the result graph.
func KroneckerDirected(scale uint, arcFactor float64, rr *rand.Rand) (g Directed, ma int) {
a, m := kronecker(scale, arcFactor, true, rr)
return Directed{a}, m
}
// KroneckerUndirected generates a Kronecker-like random undirected graph.
//
// The returned graph g is simple and has no isolated nodes but is not
// necessarily fully connected. The number of of nodes will be <= 2^scale,
// and will be near 2^scale for typical values of edgeFactor, >= 2.
// EdgeFactor * 2^scale edges are generated, although loops and duplicate edges
// are rejected. In the arc list for each node, to-nodes are in random
// order.
//
// If Rand r is nil, the rand package default shared source is used.
//
// Return value m is the true number of edges--not arcs--retained in the result
// graph.
func KroneckerUndirected(scale uint, edgeFactor float64, rr *rand.Rand) (g Undirected, m int) {
al, s := kronecker(scale, edgeFactor, false, rr)
return Undirected{al}, s
}
// Styled after the Graph500 example code. Not well tested currently.
// Graph500 example generates undirected only. No idea if the directed variant
// here is meaningful or not.
//
// note mma returns arc size ma for dir=true, but returns size m for dir=false
func kronecker(scale uint, edgeFactor float64, dir bool, rr *rand.Rand) (g AdjacencyList, mma int) {
rf, ri, rp := rand.Float64, rand.Intn, rand.Perm
if rr != nil {
rf, ri, rp = rr.Float64, rr.Intn, rr.Perm
}
N := 1 << scale // node extent
M := int(edgeFactor*float64(N) + .5) // number of arcs/edges to generate
a, b, c := 0.57, 0.19, 0.19 // initiator probabilities
ab := a + b
cNorm := c / (1 - ab)
aNorm := a / ab
ij := make([][2]NI, M)
bm := bits.New(N)
var nNodes int
for k := range ij {
var i, j int
for b := 1; b < N; b <<= 1 {
if rf() > ab {
i |= b
if rf() > cNorm {
j |= b
}
} else if rf() > aNorm {
j |= b
}
}
if bm.Bit(i) == 0 {
bm.SetBit(i, 1)
nNodes++
}
if bm.Bit(j) == 0 {
bm.SetBit(j, 1)
nNodes++
}
r := ri(k + 1) // shuffle edges as they are generated
ij[k] = ij[r]
ij[r] = [2]NI{NI(i), NI(j)}
}
p := rp(nNodes) // mapping to shuffle IDs of non-isolated nodes
px := 0
rn := make([]NI, N)
for i := range rn {
if bm.Bit(i) == 1 {
rn[i] = NI(p[px]) // fill lookup table
px++
}
}
g = make(AdjacencyList, nNodes)
ij:
for _, e := range ij {
if e[0] == e[1] {
continue // skip loops
}
ri, rj := rn[e[0]], rn[e[1]]
for _, nb := range g[ri] {
if nb == rj {
continue ij // skip parallel edges
}
}
g[ri] = append(g[ri], rj)
mma++
if !dir {
g[rj] = append(g[rj], ri)
}
}
return
}

50
vendor/github.com/soniakeys/graph/readme.adoc generated vendored Normal file
View file

@ -0,0 +1,50 @@
= Graph
A graph library with goals of speed and simplicity, Graph implements
graph algorithms on graphs of zero-based integer node IDs.
image:https://godoc.org/github.com/soniakeys/graph?status.svg[link=https://godoc.org/github.com/soniakeys/graph]
image:http://gowalker.org/api/v1/badge[link=https://gowalker.org/github.com/soniakeys/graph]
image:http://go-search.org/badge?id=github.com%2Fsoniakeys%2Fgraph[link=http://go-search.org/view?id=github.com%2Fsoniakeys%2Fgraph]
image:https://travis-ci.org/soniakeys/graph.svg?branch=master[link=https://travis-ci.org/soniakeys/graph]
The library provides efficient graph representations and many methods on
graph types. It can be imported and used directly in many applications that
require or can benefit from graph algorithms.
The library should also be considered as library of source code that can serve
as starting material for coding variant or more complex algorithms.
== Ancillary material of interest
The directory link:tutorials[tutorials] is a work in progress - there are only
a few tutorials there yet - but the concept is to provide some topical
walk-throughs to supplement godoc. The source-based godoc documentation
remains the primary documentation.
The directory link:anecdote[anecdote] contains a stand-alone program that
performs single runs of a number of methods, collecting one-off or "anecdotal"
timings. It currently runs only a small fraction of the library methods but
may still be of interest for giving a general idea of how fast some methods
run.
The directory link:bench[bench] is another work in progress. The concept is
to present some plots showing benchmark performance approaching some
theoretical asymptote.
link:hacking.adoc[hacking.adoc] has some information about how the library is
developed, built, and tested. It might be of interest if for example you
plan to fork or contribute to the the repository.
== Test coverage
1 Jul 2017
....
graph 93.7%
graph/alt 88.0%
graph/dot 77.7%
graph/treevis 79.4%
....
== License
All files in the repository are licensed with the MIT License,
https://opensource.org/licenses/MIT.

761
vendor/github.com/soniakeys/graph/sssp.go generated vendored Normal file
View file

@ -0,0 +1,761 @@
// Copyright 2013 Sonia Keys
// License MIT: http://opensource.org/licenses/MIT
package graph
import (
"container/heap"
"fmt"
"math"
"github.com/soniakeys/bits"
)
// rNode holds data for a "reached" node
type rNode struct {
nx NI
state int8 // state constants defined below
f float64 // "g+h", path dist + heuristic estimate
fx int // heap.Fix index
}
// for rNode.state
const (
unreached = 0
reached = 1
open = 1
closed = 2
)
type openHeap []*rNode
// A Heuristic is defined on a specific end node. The function
// returns an estimate of the path distance from node argument
// "from" to the end node. Two subclasses of heuristics are "admissible"
// and "monotonic."
//
// Admissible means the value returned is guaranteed to be less than or
// equal to the actual shortest path distance from the node to end.
//
// An admissible estimate may further be monotonic.
// Monotonic means that for any neighboring nodes A and B with half arc aB
// leading from A to B, and for heuristic h defined on some end node, then
// h(A) <= aB.ArcWeight + h(B).
//
// See AStarA for additional notes on implementing heuristic functions for
// AStar search methods.
type Heuristic func(from NI) float64
// Admissible returns true if heuristic h is admissible on graph g relative to
// the given end node.
//
// If h is inadmissible, the string result describes a counter example.
func (h Heuristic) Admissible(g LabeledAdjacencyList, w WeightFunc, end NI) (bool, string) {
// invert graph
inv := make(LabeledAdjacencyList, len(g))
for from, nbs := range g {
for _, nb := range nbs {
inv[nb.To] = append(inv[nb.To],
Half{To: NI(from), Label: nb.Label})
}
}
// run dijkstra
// Dijkstra.AllPaths takes a start node but after inverting the graph
// argument end now represents the start node of the inverted graph.
f, _, dist, _ := inv.Dijkstra(end, -1, w)
// compare h to found shortest paths
for n := range inv {
if f.Paths[n].Len == 0 {
continue // no path, any heuristic estimate is fine.
}
if !(h(NI(n)) <= dist[n]) {
return false, fmt.Sprintf("h(%d) = %g, "+
"required to be <= found shortest path (%g)",
n, h(NI(n)), dist[n])
}
}
return true, ""
}
// Monotonic returns true if heuristic h is monotonic on weighted graph g.
//
// If h is non-monotonic, the string result describes a counter example.
func (h Heuristic) Monotonic(g LabeledAdjacencyList, w WeightFunc) (bool, string) {
// precompute
hv := make([]float64, len(g))
for n := range g {
hv[n] = h(NI(n))
}
// iterate over all edges
for from, nbs := range g {
for _, nb := range nbs {
arcWeight := w(nb.Label)
if !(hv[from] <= arcWeight+hv[nb.To]) {
return false, fmt.Sprintf("h(%d) = %g, "+
"required to be <= arc weight + h(%d) (= %g + %g = %g)",
from, hv[from],
nb.To, arcWeight, hv[nb.To], arcWeight+hv[nb.To])
}
}
}
return true, ""
}
// AStarA finds a path between two nodes.
//
// AStarA implements both algorithm A and algorithm A*. The difference in the
// two algorithms is strictly in the heuristic estimate returned by argument h.
// If h is an "admissible" heuristic estimate, then the algorithm is termed A*,
// otherwise it is algorithm A.
//
// Like Dijkstra's algorithm, AStarA with an admissible heuristic finds the
// shortest path between start and end. AStarA generally runs faster than
// Dijkstra though, by using the heuristic distance estimate.
//
// AStarA with an inadmissible heuristic becomes algorithm A. Algorithm A
// will find a path, but it is not guaranteed to be the shortest path.
// The heuristic still guides the search however, so a nearly admissible
// heuristic is likely to find a very good path, if not the best. Quality
// of the path returned degrades gracefully with the quality of the heuristic.
//
// The heuristic function h should ideally be fairly inexpensive. AStarA
// may call it more than once for the same node, especially as graph density
// increases. In some cases it may be worth the effort to memoize or
// precompute values.
//
// Argument g is the graph to be searched, with arc weights returned by w.
// As usual for AStar, arc weights must be non-negative.
// Graphs may be directed or undirected.
//
// If AStarA finds a path it returns a FromList encoding the path, the arc
// labels for path nodes, the total path distance, and ok = true.
// Otherwise it returns ok = false.
func (g LabeledAdjacencyList) AStarA(w WeightFunc, start, end NI, h Heuristic) (f FromList, labels []LI, dist float64, ok bool) {
// NOTE: AStarM is largely duplicate code.
f = NewFromList(len(g))
labels = make([]LI, len(g))
d := make([]float64, len(g))
r := make([]rNode, len(g))
for i := range r {
r[i].nx = NI(i)
}
// start node is reached initially
cr := &r[start]
cr.state = reached
cr.f = h(start) // total path estimate is estimate from start
rp := f.Paths
rp[start] = PathEnd{Len: 1, From: -1} // path length at start is 1 node
// oh is a heap of nodes "open" for exploration. nodes go on the heap
// when they get an initial or new "g" path distance, and therefore a
// new "f" which serves as priority for exploration.
oh := openHeap{cr}
for len(oh) > 0 {
bestPath := heap.Pop(&oh).(*rNode)
bestNode := bestPath.nx
if bestNode == end {
return f, labels, d[end], true
}
bp := &rp[bestNode]
nextLen := bp.Len + 1
for _, nb := range g[bestNode] {
alt := &r[nb.To]
ap := &rp[alt.nx]
// "g" path distance from start
g := d[bestNode] + w(nb.Label)
if alt.state == reached {
if g > d[nb.To] {
// candidate path to nb is longer than some alternate path
continue
}
if g == d[nb.To] && nextLen >= ap.Len {
// candidate path has identical length of some alternate
// path but it takes no fewer hops.
continue
}
// cool, we found a better way to get to this node.
// record new path data for this node and
// update alt with new data and make sure it's on the heap.
*ap = PathEnd{From: bestNode, Len: nextLen}
labels[nb.To] = nb.Label
d[nb.To] = g
alt.f = g + h(nb.To)
if alt.fx < 0 {
heap.Push(&oh, alt)
} else {
heap.Fix(&oh, alt.fx)
}
} else {
// bestNode being reached for the first time.
*ap = PathEnd{From: bestNode, Len: nextLen}
labels[nb.To] = nb.Label
d[nb.To] = g
alt.f = g + h(nb.To)
alt.state = reached
heap.Push(&oh, alt) // and it's now open for exploration
}
}
}
return // no path
}
// AStarAPath finds a shortest path using the AStarA algorithm.
//
// This is a convenience method with a simpler result than the AStarA method.
// See documentation on the AStarA method.
//
// If a path is found, the non-nil node path is returned with the total path
// distance. Otherwise the returned path will be nil.
func (g LabeledAdjacencyList) AStarAPath(start, end NI, h Heuristic, w WeightFunc) (LabeledPath, float64) {
f, labels, d, _ := g.AStarA(w, start, end, h)
return f.PathToLabeled(end, labels, nil), d
}
// AStarM is AStarA optimized for monotonic heuristic estimates.
//
// Note that this function requires a monotonic heuristic. Results will
// not be meaningful if argument h is non-monotonic.
//
// See AStarA for general usage. See Heuristic for notes on monotonicity.
func (g LabeledAdjacencyList) AStarM(w WeightFunc, start, end NI, h Heuristic) (f FromList, labels []LI, dist float64, ok bool) {
// NOTE: AStarM is largely code duplicated from AStarA.
// Differences are noted in comments in this method.
f = NewFromList(len(g))
labels = make([]LI, len(g))
d := make([]float64, len(g))
r := make([]rNode, len(g))
for i := range r {
r[i].nx = NI(i)
}
cr := &r[start]
// difference from AStarA:
// instead of a bit to mark a reached node, there are two states,
// open and closed. open marks nodes "open" for exploration.
// nodes are marked open as they are reached, then marked
// closed as they are found to be on the best path.
cr.state = open
cr.f = h(start)
rp := f.Paths
rp[start] = PathEnd{Len: 1, From: -1}
oh := openHeap{cr}
for len(oh) > 0 {
bestPath := heap.Pop(&oh).(*rNode)
bestNode := bestPath.nx
if bestNode == end {
return f, labels, d[end], true
}
// difference from AStarA:
// move nodes to closed list as they are found to be best so far.
bestPath.state = closed
bp := &rp[bestNode]
nextLen := bp.Len + 1
for _, nb := range g[bestNode] {
alt := &r[nb.To]
// difference from AStarA:
// Monotonicity means that f cannot be improved.
if alt.state == closed {
continue
}
ap := &rp[alt.nx]
g := d[bestNode] + w(nb.Label)
// difference from AStarA:
// test for open state, not just reached
if alt.state == open {
if g > d[nb.To] {
continue
}
if g == d[nb.To] && nextLen >= ap.Len {
continue
}
*ap = PathEnd{From: bestNode, Len: nextLen}
labels[nb.To] = nb.Label
d[nb.To] = g
alt.f = g + h(nb.To)
// difference from AStarA:
// we know alt was on the heap because we found it marked open
heap.Fix(&oh, alt.fx)
} else {
*ap = PathEnd{From: bestNode, Len: nextLen}
labels[nb.To] = nb.Label
d[nb.To] = g
alt.f = g + h(nb.To)
// difference from AStarA:
// nodes are opened when first reached
alt.state = open
heap.Push(&oh, alt)
}
}
}
return
}
// AStarMPath finds a shortest path using the AStarM algorithm.
//
// This is a convenience method with a simpler result than the AStarM method.
// See documentation on the AStarM and AStarA methods.
//
// If a path is found, the non-nil node path is returned with the total path
// distance. Otherwise the returned path will be nil.
func (g LabeledAdjacencyList) AStarMPath(start, end NI, h Heuristic, w WeightFunc) (LabeledPath, float64) {
f, labels, d, _ := g.AStarM(w, start, end, h)
return f.PathToLabeled(end, labels, nil), d
}
// implement container/heap
func (h openHeap) Len() int { return len(h) }
func (h openHeap) Less(i, j int) bool { return h[i].f < h[j].f }
func (h openHeap) Swap(i, j int) {
h[i], h[j] = h[j], h[i]
h[i].fx = i
h[j].fx = j
}
func (p *openHeap) Push(x interface{}) {
h := *p
fx := len(h)
h = append(h, x.(*rNode))
h[fx].fx = fx
*p = h
}
func (p *openHeap) Pop() interface{} {
h := *p
last := len(h) - 1
*p = h[:last]
h[last].fx = -1
return h[last]
}
// BellmanFord finds shortest paths from a start node in a weighted directed
// graph using the Bellman-Ford-Moore algorithm.
//
// WeightFunc w must translate arc labels to arc weights.
// Negative arc weights are allowed but not negative cycles.
// Loops and parallel arcs are allowed.
//
// If the algorithm completes without encountering a negative cycle the method
// returns shortest paths encoded in a FromList, labels and path distances
// indexed by node, and return value end = -1.
//
// If it encounters a negative cycle reachable from start it returns end >= 0.
// In this case the cycle can be obtained by calling f.BellmanFordCycle(end).
//
// Negative cycles are only detected when reachable from start. A negative
// cycle not reachable from start will not prevent the algorithm from finding
// shortest paths from start.
//
// See also NegativeCycle to find a cycle anywhere in the graph, see
// NegativeCycles for enumerating all negative cycles, and see
// HasNegativeCycle for lighter-weight negative cycle detection,
func (g LabeledDirected) BellmanFord(w WeightFunc, start NI) (f FromList, labels []LI, dist []float64, end NI) {
a := g.LabeledAdjacencyList
f = NewFromList(len(a))
labels = make([]LI, len(a))
dist = make([]float64, len(a))
inf := math.Inf(1)
for i := range dist {
dist[i] = inf
}
rp := f.Paths
rp[start] = PathEnd{Len: 1, From: -1}
dist[start] = 0
for _ = range a[1:] {
imp := false
for from, nbs := range a {
fp := &rp[from]
d1 := dist[from]
for _, nb := range nbs {
d2 := d1 + w(nb.Label)
to := &rp[nb.To]
// TODO improve to break ties
if fp.Len > 0 && d2 < dist[nb.To] {
*to = PathEnd{From: NI(from), Len: fp.Len + 1}
labels[nb.To] = nb.Label
dist[nb.To] = d2
imp = true
}
}
}
if !imp {
break
}
}
for from, nbs := range a {
d1 := dist[from]
for _, nb := range nbs {
if d1+w(nb.Label) < dist[nb.To] {
// return nb as end of a path with negative cycle at root
return f, labels, dist, NI(from)
}
}
}
return f, labels, dist, -1
}
// BellmanFordCycle decodes a negative cycle detected by BellmanFord.
//
// Receiver f and argument end must be results returned from BellmanFord.
func (f FromList) BellmanFordCycle(end NI) (c []NI) {
p := f.Paths
b := bits.New(len(p))
for b.Bit(int(end)) == 0 {
b.SetBit(int(end), 1)
end = p[end].From
}
for b.Bit(int(end)) == 1 {
c = append(c, end)
b.SetBit(int(end), 0)
end = p[end].From
}
for i, j := 0, len(c)-1; i < j; i, j = i+1, j-1 {
c[i], c[j] = c[j], c[i]
}
return
}
// HasNegativeCycle returns true if the graph contains any negative cycle.
//
// HasNegativeCycle uses a Bellman-Ford-like algorithm, but finds negative
// cycles anywhere in the graph. Also path information is not computed,
// reducing memory use somewhat compared to BellmanFord.
//
// See also NegativeCycle to obtain the cycle, see NegativeCycles for
// enumerating all negative cycles, and see BellmanFord for single source
// shortest path searches with negative cycle detection.
func (g LabeledDirected) HasNegativeCycle(w WeightFunc) bool {
a := g.LabeledAdjacencyList
dist := make([]float64, len(a))
for _ = range a[1:] {
imp := false
for from, nbs := range a {
d1 := dist[from]
for _, nb := range nbs {
d2 := d1 + w(nb.Label)
if d2 < dist[nb.To] {
dist[nb.To] = d2
imp = true
}
}
}
if !imp {
break
}
}
for from, nbs := range a {
d1 := dist[from]
for _, nb := range nbs {
if d1+w(nb.Label) < dist[nb.To] {
return true // negative cycle
}
}
}
return false
}
// NegativeCycle finds a negative cycle if one exists.
//
// NegativeCycle uses a Bellman-Ford-like algorithm, but finds negative
// cycles anywhere in the graph. If a negative cycle exists, one will be
// returned. The result is nil if no negative cycle exists.
//
// See also NegativeCycles for enumerating all negative cycles, see
// HasNegativeCycle for lighter-weight cycle detection, and see
// BellmanFord for single source shortest paths, also with negative cycle
// detection.
func (g LabeledDirected) NegativeCycle(w WeightFunc) (c []Half) {
a := g.LabeledAdjacencyList
f := NewFromList(len(a))
p := f.Paths
for n := range p {
p[n] = PathEnd{From: -1, Len: 1}
}
labels := make([]LI, len(a))
dist := make([]float64, len(a))
for _ = range a {
imp := false
for from, nbs := range a {
fp := &p[from]
d1 := dist[from]
for _, nb := range nbs {
d2 := d1 + w(nb.Label)
to := &p[nb.To]
if fp.Len > 0 && d2 < dist[nb.To] {
*to = PathEnd{From: NI(from), Len: fp.Len + 1}
labels[nb.To] = nb.Label
dist[nb.To] = d2
imp = true
}
}
}
if !imp {
return nil
}
}
vis := bits.New(len(a))
a:
for n := range a {
end := n
b := bits.New(len(a))
for b.Bit(end) == 0 {
if vis.Bit(end) == 1 {
continue a
}
vis.SetBit(end, 1)
b.SetBit(end, 1)
end = int(p[end].From)
if end < 0 {
continue a
}
}
for b.Bit(end) == 1 {
c = append(c, Half{NI(end), labels[end]})
b.SetBit(end, 0)
end = int(p[end].From)
}
for i, j := 0, len(c)-1; i < j; i, j = i+1, j-1 {
c[i], c[j] = c[j], c[i]
}
return c
}
return nil // no negative cycle
}
// DAGMinDistPath finds a single shortest path.
//
// Shortest means minimum sum of arc weights.
//
// Returned is the path and distance as returned by FromList.PathTo.
//
// This is a convenience method. See DAGOptimalPaths for more options.
func (g LabeledDirected) DAGMinDistPath(start, end NI, w WeightFunc) (LabeledPath, float64, error) {
return g.dagPath(start, end, w, false)
}
// DAGMaxDistPath finds a single longest path.
//
// Longest means maximum sum of arc weights.
//
// Returned is the path and distance as returned by FromList.PathTo.
//
// This is a convenience method. See DAGOptimalPaths for more options.
func (g LabeledDirected) DAGMaxDistPath(start, end NI, w WeightFunc) (LabeledPath, float64, error) {
return g.dagPath(start, end, w, true)
}
func (g LabeledDirected) dagPath(start, end NI, w WeightFunc, longest bool) (LabeledPath, float64, error) {
o, _ := g.Topological()
if o == nil {
return LabeledPath{}, 0, fmt.Errorf("not a DAG")
}
f, labels, dist, _ := g.DAGOptimalPaths(start, end, o, w, longest)
if f.Paths[end].Len == 0 {
return LabeledPath{}, 0, fmt.Errorf("no path from %d to %d", start, end)
}
return f.PathToLabeled(end, labels, nil), dist[end], nil
}
// DAGOptimalPaths finds either longest or shortest distance paths in a
// directed acyclic graph.
//
// Path distance is the sum of arc weights on the path.
// Negative arc weights are allowed.
// Where multiple paths exist with the same distance, the path length
// (number of nodes) is used as a tie breaker.
//
// Receiver g must be a directed acyclic graph. Argument o must be either nil
// or a topological ordering of g. If nil, a topologcal ordering is
// computed internally. If longest is true, an optimal path is a longest
// distance path. Otherwise it is a shortest distance path.
//
// Argument start is the start node for paths, end is the end node. If end
// is a valid node number, the method returns as soon as the optimal path
// to end is found. If end is -1, all optimal paths from start are found.
//
// Paths and path distances are encoded in the returned FromList, labels,
// and dist slices. The number of nodes reached is returned as nReached.
func (g LabeledDirected) DAGOptimalPaths(start, end NI, ordering []NI, w WeightFunc, longest bool) (f FromList, labels []LI, dist []float64, nReached int) {
a := g.LabeledAdjacencyList
f = NewFromList(len(a))
f.Leaves = bits.New(len(a))
labels = make([]LI, len(a))
dist = make([]float64, len(a))
if ordering == nil {
ordering, _ = g.Topological()
}
// search ordering for start
o := 0
for ordering[o] != start {
o++
}
var fBetter func(cand, ext float64) bool
var iBetter func(cand, ext int) bool
if longest {
fBetter = func(cand, ext float64) bool { return cand > ext }
iBetter = func(cand, ext int) bool { return cand > ext }
} else {
fBetter = func(cand, ext float64) bool { return cand < ext }
iBetter = func(cand, ext int) bool { return cand < ext }
}
p := f.Paths
p[start] = PathEnd{From: -1, Len: 1}
f.MaxLen = 1
leaves := &f.Leaves
leaves.SetBit(int(start), 1)
nReached = 1
for n := start; n != end; n = ordering[o] {
if p[n].Len > 0 && len(a[n]) > 0 {
nDist := dist[n]
candLen := p[n].Len + 1 // len for any candidate arc followed from n
for _, to := range a[n] {
leaves.SetBit(int(to.To), 1)
candDist := nDist + w(to.Label)
switch {
case p[to.To].Len == 0: // first path to node to.To
nReached++
case fBetter(candDist, dist[to.To]): // better distance
case candDist == dist[to.To] && iBetter(candLen, p[to.To].Len): // same distance but better path length
default:
continue
}
dist[to.To] = candDist
p[to.To] = PathEnd{From: n, Len: candLen}
labels[to.To] = to.Label
if candLen > f.MaxLen {
f.MaxLen = candLen
}
}
leaves.SetBit(int(n), 0)
}
o++
if o == len(ordering) {
break
}
}
return
}
// Dijkstra finds shortest paths by Dijkstra's algorithm.
//
// Shortest means shortest distance where distance is the
// sum of arc weights. Where multiple paths exist with the same distance,
// a path with the minimum number of nodes is returned.
//
// As usual for Dijkstra's algorithm, arc weights must be non-negative.
// Graphs may be directed or undirected. Loops and parallel arcs are
// allowed.
//
// Paths and path distances are encoded in the returned FromList and dist
// slice. Returned labels are the labels of arcs followed to each node.
// The number of nodes reached is returned as nReached.
func (g LabeledAdjacencyList) Dijkstra(start, end NI, w WeightFunc) (f FromList, labels []LI, dist []float64, nReached int) {
r := make([]tentResult, len(g))
for i := range r {
r[i].nx = NI(i)
}
f = NewFromList(len(g))
labels = make([]LI, len(g))
dist = make([]float64, len(g))
current := start
rp := f.Paths
rp[current] = PathEnd{Len: 1, From: -1} // path length at start is 1 node
cr := &r[current]
cr.dist = 0 // distance at start is 0.
cr.done = true // mark start done. it skips the heap.
nDone := 1 // accumulated for a return value
var t tent
for current != end {
nextLen := rp[current].Len + 1
for _, nb := range g[current] {
// d.arcVis++
hr := &r[nb.To]
if hr.done {
continue // skip nodes already done
}
dist := cr.dist + w(nb.Label)
vl := rp[nb.To].Len
visited := vl > 0
if visited {
if dist > hr.dist {
continue // distance is worse
}
// tie breaker is a nice touch and doesn't seem to
// impact performance much.
if dist == hr.dist && nextLen >= vl {
continue // distance same, but number of nodes is no better
}
}
// the path through current to this node is shortest so far.
// record new path data for this node and update tentative set.
hr.dist = dist
rp[nb.To].Len = nextLen
rp[nb.To].From = current
labels[nb.To] = nb.Label
if visited {
heap.Fix(&t, hr.fx)
} else {
heap.Push(&t, hr)
}
}
//d.ndVis++
if len(t) == 0 {
// no more reachable nodes. AllPaths normal return
return f, labels, dist, nDone
}
// new current is node with smallest tentative distance
cr = heap.Pop(&t).(*tentResult)
cr.done = true
nDone++
current = cr.nx
dist[current] = cr.dist // store final distance
}
// normal return for single shortest path search
return f, labels, dist, -1
}
// DijkstraPath finds a single shortest path.
//
// Returned is the path as returned by FromList.LabeledPathTo and the total
// path distance.
func (g LabeledAdjacencyList) DijkstraPath(start, end NI, w WeightFunc) (LabeledPath, float64) {
f, labels, dist, _ := g.Dijkstra(start, end, w)
return f.PathToLabeled(end, labels, nil), dist[end]
}
// tent implements container/heap
func (t tent) Len() int { return len(t) }
func (t tent) Less(i, j int) bool { return t[i].dist < t[j].dist }
func (t tent) Swap(i, j int) {
t[i], t[j] = t[j], t[i]
t[i].fx = i
t[j].fx = j
}
func (s *tent) Push(x interface{}) {
nd := x.(*tentResult)
nd.fx = len(*s)
*s = append(*s, nd)
}
func (s *tent) Pop() interface{} {
t := *s
last := len(t) - 1
*s = t[:last]
return t[last]
}
type tentResult struct {
dist float64 // tentative distance, sum of arc weights
nx NI // slice index, "node id"
fx int // heap.Fix index
done bool
}
type tent []*tentResult

817
vendor/github.com/soniakeys/graph/undir.go generated vendored Normal file
View file

@ -0,0 +1,817 @@
// Copyright 2014 Sonia Keys
// License MIT: http://opensource.org/licenses/MIT
package graph
// undir.go has methods specific to undirected graphs, Undirected and
// LabeledUndirected.
import (
"fmt"
"github.com/soniakeys/bits"
)
// AddEdge adds an edge to a graph.
//
// It can be useful for constructing undirected graphs.
//
// When n1 and n2 are distinct, it adds the arc n1->n2 and the reciprocal
// n2->n1. When n1 and n2 are the same, it adds a single arc loop.
//
// The pointer receiver allows the method to expand the graph as needed
// to include the values n1 and n2. If n1 or n2 happen to be greater than
// len(*p) the method does not panic, but simply expands the graph.
//
// If you know or can compute the final graph order however, consider
// preallocating to avoid any overhead of expanding the graph.
// See second example, "More".
func (p *Undirected) AddEdge(n1, n2 NI) {
// Similar code in LabeledAdjacencyList.AddEdge.
// determine max of the two end points
max := n1
if n2 > max {
max = n2
}
// expand graph if needed, to include both
g := p.AdjacencyList
if int(max) >= len(g) {
p.AdjacencyList = make(AdjacencyList, max+1)
copy(p.AdjacencyList, g)
g = p.AdjacencyList
}
// create one half-arc,
g[n1] = append(g[n1], n2)
// and except for loops, create the reciprocal
if n1 != n2 {
g[n2] = append(g[n2], n1)
}
}
// RemoveEdge removes a single edge between nodes n1 and n2.
//
// It removes reciprocal arcs in the case of distinct n1 and n2 or removes
// a single arc loop in the case of n1 == n2.
//
// Returns true if the specified edge is found and successfully removed,
// false if the edge does not exist.
func (g Undirected) RemoveEdge(n1, n2 NI) (ok bool) {
ok, x1, x2 := g.HasEdge(n1, n2)
if !ok {
return
}
a := g.AdjacencyList
to := a[n1]
last := len(to) - 1
to[x1] = to[last]
a[n1] = to[:last]
if n1 == n2 {
return
}
to = a[n2]
last = len(to) - 1
to[x2] = to[last]
a[n2] = to[:last]
return
}
// ArcDensity returns density for a simple directed graph.
//
// Parameter n is order, or number of nodes of a simple directed graph.
// Parameter a is the arc size, or number of directed arcs.
//
// Returned density is the fraction `a` over the total possible number of arcs
// or a / (n * (n-1)).
//
// See also Density for density of a simple undirected graph.
//
// See also the corresponding methods AdjacencyList.ArcDensity and
// LabeledAdjacencyList.ArcDensity.
func ArcDensity(n, a int) float64 {
return float64(a) / (float64(n) * float64(n-1))
}
// Density returns density for a simple undirected graph.
//
// Parameter n is order, or number of nodes of a simple undirected graph.
// Parameter m is the size, or number of undirected edges.
//
// Returned density is the fraction m over the total possible number of edges
// or m / ((n * (n-1))/2).
//
// See also ArcDensity for simple directed graphs.
//
// See also the corresponding methods AdjacencyList.Density and
// LabeledAdjacencyList.Density.
func Density(n, m int) float64 {
return float64(m) * 2 / (float64(n) * float64(n-1))
}
// An EdgeVisitor is an argument to some traversal methods.
//
// Traversal methods call the visitor function for each edge visited.
// Argument e is the edge being visited.
type EdgeVisitor func(e Edge)
// Edges iterates over the edges of an undirected graph.
//
// Edge visitor v is called for each edge of the graph. That is, it is called
// once for each reciprocal arc pair and once for each loop.
//
// See also LabeledUndirected.Edges for a labeled version.
// See also Undirected.SimpleEdges for a version that emits only the simple
// subgraph.
func (g Undirected) Edges(v EdgeVisitor) {
a := g.AdjacencyList
unpaired := make(AdjacencyList, len(a))
for fr, to := range a {
arc: // for each arc in a
for _, to := range to {
if to == NI(fr) {
v(Edge{NI(fr), to}) // output loop
continue
}
// search unpaired arcs
ut := unpaired[to]
for i, u := range ut {
if u == NI(fr) { // found reciprocal
v(Edge{u, to}) // output edge
last := len(ut) - 1
ut[i] = ut[last]
unpaired[to] = ut[:last]
continue arc
}
}
// reciprocal not found
unpaired[fr] = append(unpaired[fr], to)
}
}
// undefined behavior is that unpaired arcs are silently ignored.
}
// FromList builds a forest with a tree spanning each connected component.
//
// For each component a root is chosen and spanning is done with the method
// Undirected.SpanTree, and so is breadth-first. Returned is a FromList with
// all spanned trees, a list of roots chosen, and a bool indicating if the
// receiver graph g was found to be a simple graph connected as a forest.
// Any cycles, loops, or parallel edges in any component will cause
// simpleForest to be false, but FromList f will still be populated with
// a valid and complete spanning forest.
func (g Undirected) FromList() (f FromList, roots []NI, simpleForest bool) {
p := make([]PathEnd, g.Order())
for i := range p {
p[i].From = -1
}
f.Paths = p
simpleForest = true
ts := 0
for n := range g.AdjacencyList {
if p[n].From >= 0 {
continue
}
roots = append(roots, NI(n))
ns, st := g.SpanTree(NI(n), &f)
if !st {
simpleForest = false
}
ts += ns
if ts == len(p) {
break
}
}
return
}
// HasEdge returns true if g has any edge between nodes n1 and n2.
//
// Also returned are indexes x1 and x2 such that g[n1][x1] == n2
// and g[n2][x2] == n1. If no edge between n1 and n2 is present HasArc
// returns `has` == false.
//
// See also HasArc. If you are interested only in the boolean result and
// g is a well formed (passes IsUndirected) then HasArc is an adequate test.
func (g Undirected) HasEdge(n1, n2 NI) (has bool, x1, x2 int) {
if has, x1 = g.HasArc(n1, n2); !has {
return has, x1, x1
}
has, x2 = g.HasArc(n2, n1)
return
}
// SimpleEdges iterates over the edges of the simple subgraph of an undirected
// graph.
//
// Edge visitor v is called for each pair of distinct nodes that is connected
// with an edge. That is, loops are ignored and parallel edges are reduced to
// a single edge.
//
// See also Undirected.Edges for a version that emits all edges.
func (g Undirected) SimpleEdges(v EdgeVisitor) {
for fr, to := range g.AdjacencyList {
e := bits.New(len(g.AdjacencyList))
for _, to := range to {
if to > NI(fr) && e.Bit(int(to)) == 0 {
e.SetBit(int(to), 1)
v(Edge{NI(fr), to})
}
}
}
// undefined behavior is that unpaired arcs may or may not be emitted.
}
// SpanTree builds a tree spanning a connected component.
//
// The component is spanned by breadth-first search from the given root.
// The resulting spanning tree in stored a FromList.
//
// If FromList.Paths is not the same length as g, it is allocated and
// initialized. This allows a zero value FromList to be passed as f.
// If FromList.Paths is the same length as g, it is used as is and is not
// reinitialized. This allows multiple trees to be spanned in the same
// FromList with successive calls.
//
// For nodes spanned, the Path member of the returned FromList is populated
// with both From and Len values. The MaxLen member will be updated but
// not Leaves.
//
// Returned is the number of nodes spanned, which will be the number of nodes
// in the component, and a bool indicating if the component was found to be a
// simply connected unrooted tree in the receiver graph g. Any cycles, loops,
// or parallel edges in the component will cause simpleTree to be false, but
// FromList f will still be populated with a valid and complete spanning tree.
func (g Undirected) SpanTree(root NI, f *FromList) (nSpanned int, simpleTree bool) {
a := g.AdjacencyList
p := f.Paths
if len(p) != len(a) {
p = make([]PathEnd, len(a))
for i := range p {
p[i].From = -1
}
f.Paths = p
}
simpleTree = true
p[root] = PathEnd{From: -1, Len: 1}
type arc struct {
from NI
half NI
}
var next []arc
frontier := []arc{{-1, root}}
for len(frontier) > 0 {
for _, fa := range frontier { // fa frontier arc
nSpanned++
l := p[fa.half].Len + 1
for _, to := range a[fa.half] {
if to == fa.from {
continue
}
if p[to].Len > 0 {
simpleTree = false
continue
}
p[to] = PathEnd{From: fa.half, Len: l}
if l > f.MaxLen {
f.MaxLen = l
}
next = append(next, arc{fa.half, to})
}
}
frontier, next = next, frontier[:0]
}
return
}
// TarjanBiconnectedComponents decomposes a graph into maximal biconnected
// components, components for which if any node were removed the component
// would remain connected.
//
// The receiver g must be a simple graph. The method calls the emit argument
// for each component identified, as long as emit returns true. If emit
// returns false, TarjanBiconnectedComponents returns immediately.
//
// See also the eqivalent labeled TarjanBiconnectedComponents.
func (g Undirected) TarjanBiconnectedComponents(emit func([]Edge) bool) {
// Implemented closely to pseudocode in "Depth-first search and linear
// graph algorithms", Robert Tarjan, SIAM J. Comput. Vol. 1, No. 2,
// June 1972.
//
// Note Tarjan's "adjacency structure" is graph.AdjacencyList,
// His "adjacency list" is an element of a graph.AdjacencyList, also
// termed a "to-list", "neighbor list", or "child list."
a := g.AdjacencyList
number := make([]int, len(a))
lowpt := make([]int, len(a))
var stack []Edge
var i int
var biconnect func(NI, NI) bool
biconnect = func(v, u NI) bool {
i++
number[v] = i
lowpt[v] = i
for _, w := range a[v] {
if number[w] == 0 {
stack = append(stack, Edge{v, w})
if !biconnect(w, v) {
return false
}
if lowpt[w] < lowpt[v] {
lowpt[v] = lowpt[w]
}
if lowpt[w] >= number[v] {
var bcc []Edge
top := len(stack) - 1
for number[stack[top].N1] >= number[w] {
bcc = append(bcc, stack[top])
stack = stack[:top]
top--
}
bcc = append(bcc, stack[top])
stack = stack[:top]
top--
if !emit(bcc) {
return false
}
}
} else if number[w] < number[v] && w != u {
stack = append(stack, Edge{v, w})
if number[w] < lowpt[v] {
lowpt[v] = number[w]
}
}
}
return true
}
for w := range a {
if number[w] == 0 && !biconnect(NI(w), -1) {
return
}
}
}
func (g Undirected) BlockCut(block func([]Edge) bool, cut func(NI) bool, isolated func(NI) bool) {
a := g.AdjacencyList
number := make([]int, len(a))
lowpt := make([]int, len(a))
var stack []Edge
var i, rc int
var biconnect func(NI, NI) bool
biconnect = func(v, u NI) bool {
i++
number[v] = i
lowpt[v] = i
for _, w := range a[v] {
if number[w] == 0 {
if u < 0 {
rc++
}
stack = append(stack, Edge{v, w})
if !biconnect(w, v) {
return false
}
if lowpt[w] < lowpt[v] {
lowpt[v] = lowpt[w]
}
if lowpt[w] >= number[v] {
if u >= 0 && !cut(v) {
return false
}
var bcc []Edge
top := len(stack) - 1
for number[stack[top].N1] >= number[w] {
bcc = append(bcc, stack[top])
stack = stack[:top]
top--
}
bcc = append(bcc, stack[top])
stack = stack[:top]
top--
if !block(bcc) {
return false
}
}
} else if number[w] < number[v] && w != u {
stack = append(stack, Edge{v, w})
if number[w] < lowpt[v] {
lowpt[v] = number[w]
}
}
}
if u < 0 && rc > 1 {
return cut(v)
}
return true
}
for w := range a {
if number[w] > 0 {
continue
}
if len(a[w]) == 0 {
if !isolated(NI(w)) {
return
}
continue
}
rc = 0
if !biconnect(NI(w), -1) {
return
}
}
}
// AddEdge adds an edge to a labeled graph.
//
// It can be useful for constructing undirected graphs.
//
// When n1 and n2 are distinct, it adds the arc n1->n2 and the reciprocal
// n2->n1. When n1 and n2 are the same, it adds a single arc loop.
//
// If the edge already exists in *p, a parallel edge is added.
//
// The pointer receiver allows the method to expand the graph as needed
// to include the values n1 and n2. If n1 or n2 happen to be greater than
// len(*p) the method does not panic, but simply expands the graph.
func (p *LabeledUndirected) AddEdge(e Edge, l LI) {
// Similar code in AdjacencyList.AddEdge.
// determine max of the two end points
max := e.N1
if e.N2 > max {
max = e.N2
}
// expand graph if needed, to include both
g := p.LabeledAdjacencyList
if max >= NI(len(g)) {
p.LabeledAdjacencyList = make(LabeledAdjacencyList, max+1)
copy(p.LabeledAdjacencyList, g)
g = p.LabeledAdjacencyList
}
// create one half-arc,
g[e.N1] = append(g[e.N1], Half{To: e.N2, Label: l})
// and except for loops, create the reciprocal
if e.N1 != e.N2 {
g[e.N2] = append(g[e.N2], Half{To: e.N1, Label: l})
}
}
// A LabeledEdgeVisitor is an argument to some traversal methods.
//
// Traversal methods call the visitor function for each edge visited.
// Argument e is the edge being visited.
type LabeledEdgeVisitor func(e LabeledEdge)
// Edges iterates over the edges of a labeled undirected graph.
//
// Edge visitor v is called for each edge of the graph. That is, it is called
// once for each reciprocal arc pair and once for each loop.
//
// See also Undirected.Edges for an unlabeled version.
// See also the more simplistic LabeledAdjacencyList.ArcsAsEdges.
func (g LabeledUndirected) Edges(v LabeledEdgeVisitor) {
// similar code in LabeledAdjacencyList.InUndirected
a := g.LabeledAdjacencyList
unpaired := make(LabeledAdjacencyList, len(a))
for fr, to := range a {
arc: // for each arc in a
for _, to := range to {
if to.To == NI(fr) {
v(LabeledEdge{Edge{NI(fr), to.To}, to.Label}) // output loop
continue
}
// search unpaired arcs
ut := unpaired[to.To]
for i, u := range ut {
if u.To == NI(fr) && u.Label == to.Label { // found reciprocal
v(LabeledEdge{Edge{NI(fr), to.To}, to.Label}) // output edge
last := len(ut) - 1
ut[i] = ut[last]
unpaired[to.To] = ut[:last]
continue arc
}
}
// reciprocal not found
unpaired[fr] = append(unpaired[fr], to)
}
}
}
// FromList builds a forest with a tree spanning each connected component in g.
//
// A root is chosen and spanning is done with the LabeledUndirected.SpanTree
// method, and so is breadth-first. Returned is a FromList with all spanned
// trees, labels corresponding to arcs in f,
// a list of roots chosen, and a bool indicating if the receiver graph g was
// found to be a simple graph connected as a forest. Any cycles, loops, or
// parallel edges in any component will cause simpleForest to be false, but
// FromList f will still be populated with a valid and complete spanning forest.
// FromList builds a forest with a tree spanning each connected component.
//
// For each component a root is chosen and spanning is done with the method
// Undirected.SpanTree, and so is breadth-first. Returned is a FromList with
// all spanned trees, labels corresponding to arcs in f, a list of roots
// chosen, and a bool indicating if the receiver graph g was found to be a
// simple graph connected as a forest. Any cycles, loops, or parallel edges
// in any component will cause simpleForest to be false, but FromList f will
// still be populated with a valid and complete spanning forest.
func (g LabeledUndirected) FromList() (f FromList, labels []LI, roots []NI, simpleForest bool) {
p := make([]PathEnd, g.Order())
for i := range p {
p[i].From = -1
}
f.Paths = p
labels = make([]LI, len(p))
simpleForest = true
ts := 0
for n := range g.LabeledAdjacencyList {
if p[n].From >= 0 {
continue
}
roots = append(roots, NI(n))
ns, st := g.SpanTree(NI(n), &f, labels)
if !st {
simpleForest = false
}
ts += ns
if ts == len(p) {
break
}
}
return
}
// SpanTree builds a tree spanning a connected component.
//
// The component is spanned by breadth-first search from the given root.
// The resulting spanning tree in stored a FromList, and arc labels optionally
// stored in a slice.
//
// If FromList.Paths is not the same length as g, it is allocated and
// initialized. This allows a zero value FromList to be passed as f.
// If FromList.Paths is the same length as g, it is used as is and is not
// reinitialized. This allows multiple trees to be spanned in the same
// FromList with successive calls.
//
// For nodes spanned, the Path member of returned FromList f is populated
// populated with both From and Len values. The MaxLen member will be
// updated but not Leaves.
//
// The labels slice will be populated only if it is same length as g.
// Nil can be passed for example if labels are not needed.
//
// Returned is the number of nodes spanned, which will be the number of nodes
// in the component, and a bool indicating if the component was found to be a
// simply connected unrooted tree in the receiver graph g. Any cycles, loops,
// or parallel edges in the component will cause simpleTree to be false, but
// FromList f will still be populated with a valid and complete spanning tree.
func (g LabeledUndirected) SpanTree(root NI, f *FromList, labels []LI) (nSpanned int, simple bool) {
a := g.LabeledAdjacencyList
p := f.Paths
if len(p) != len(a) {
p = make([]PathEnd, len(a))
for i := range p {
p[i].From = -1
}
f.Paths = p
}
simple = true
p[root].Len = 1
type arc struct {
from NI
half Half
}
var next []arc
frontier := []arc{{-1, Half{root, -1}}}
for len(frontier) > 0 {
for _, fa := range frontier { // fa frontier arc
nSpanned++
l := p[fa.half.To].Len + 1
for _, to := range a[fa.half.To] {
if to.To == fa.from && to.Label == fa.half.Label {
continue
}
if p[to.To].Len > 0 {
simple = false
continue
}
p[to.To] = PathEnd{From: fa.half.To, Len: l}
if len(labels) == len(p) {
labels[to.To] = to.Label
}
if l > f.MaxLen {
f.MaxLen = l
}
next = append(next, arc{fa.half.To, to})
}
}
frontier, next = next, frontier[:0]
}
return
}
// HasEdge returns true if g has any edge between nodes n1 and n2.
//
// Also returned are indexes x1 and x2 such that g[n1][x1] == Half{n2, l}
// and g[n2][x2] == {n1, l} for some label l. If no edge between n1 and n2
// exists, HasArc returns `has` == false.
//
// See also HasArc. If you are only interested in the boolean result then
// HasArc is an adequate test.
func (g LabeledUndirected) HasEdge(n1, n2 NI) (has bool, x1, x2 int) {
if has, x1 = g.HasArc(n1, n2); !has {
return has, x1, x1
}
has, x2 = g.HasArcLabel(n2, n1, g.LabeledAdjacencyList[n1][x1].Label)
return
}
// HasEdgeLabel returns true if g has any edge between nodes n1 and n2 with
// label l.
//
// Also returned are indexes x1 and x2 such that g[n1][x1] == Half{n2, l}
// and g[n2][x2] == Half{n1, l}. If no edge between n1 and n2 with label l
// is present HasArc returns `has` == false.
func (g LabeledUndirected) HasEdgeLabel(n1, n2 NI, l LI) (has bool, x1, x2 int) {
if has, x1 = g.HasArcLabel(n1, n2, l); !has {
return has, x1, x1
}
has, x2 = g.HasArcLabel(n2, n1, l)
return
}
// RemoveEdge removes a single edge between nodes n1 and n2.
//
// It removes reciprocal arcs in the case of distinct n1 and n2 or removes
// a single arc loop in the case of n1 == n2.
//
// If the specified edge is found and successfully removed, RemoveEdge returns
// true and the label of the edge removed. If no edge exists between n1 and n2,
// RemoveEdge returns false, 0.
func (g LabeledUndirected) RemoveEdge(n1, n2 NI) (ok bool, label LI) {
ok, x1, x2 := g.HasEdge(n1, n2)
if !ok {
return
}
a := g.LabeledAdjacencyList
to := a[n1]
label = to[x1].Label // return value
last := len(to) - 1
to[x1] = to[last]
a[n1] = to[:last]
if n1 == n2 {
return
}
to = a[n2]
last = len(to) - 1
to[x2] = to[last]
a[n2] = to[:last]
return
}
// RemoveEdgeLabel removes a single edge between nodes n1 and n2 with label l.
//
// It removes reciprocal arcs in the case of distinct n1 and n2 or removes
// a single arc loop in the case of n1 == n2.
//
// Returns true if the specified edge is found and successfully removed,
// false if the edge does not exist.
func (g LabeledUndirected) RemoveEdgeLabel(n1, n2 NI, l LI) (ok bool) {
ok, x1, x2 := g.HasEdgeLabel(n1, n2, l)
if !ok {
return
}
a := g.LabeledAdjacencyList
to := a[n1]
last := len(to) - 1
to[x1] = to[last]
a[n1] = to[:last]
if n1 == n2 {
return
}
to = a[n2]
last = len(to) - 1
to[x2] = to[last]
a[n2] = to[:last]
return
}
// TarjanBiconnectedComponents decomposes a graph into maximal biconnected
// components, components for which if any node were removed the component
// would remain connected.
//
// The receiver g must be a simple graph. The method calls the emit argument
// for each component identified, as long as emit returns true. If emit
// returns false, TarjanBiconnectedComponents returns immediately.
//
// See also the eqivalent unlabeled TarjanBiconnectedComponents.
func (g LabeledUndirected) TarjanBiconnectedComponents(emit func([]LabeledEdge) bool) {
// Code nearly identical to unlabled version.
number := make([]int, g.Order())
lowpt := make([]int, g.Order())
var stack []LabeledEdge
var i int
var biconnect func(NI, NI) bool
biconnect = func(v, u NI) bool {
i++
number[v] = i
lowpt[v] = i
for _, w := range g.LabeledAdjacencyList[v] {
if number[w.To] == 0 {
stack = append(stack, LabeledEdge{Edge{v, w.To}, w.Label})
if !biconnect(w.To, v) {
return false
}
if lowpt[w.To] < lowpt[v] {
lowpt[v] = lowpt[w.To]
}
if lowpt[w.To] >= number[v] {
var bcc []LabeledEdge
top := len(stack) - 1
for number[stack[top].N1] >= number[w.To] {
bcc = append(bcc, stack[top])
stack = stack[:top]
top--
}
bcc = append(bcc, stack[top])
stack = stack[:top]
top--
if !emit(bcc) {
return false
}
}
} else if number[w.To] < number[v] && w.To != u {
stack = append(stack, LabeledEdge{Edge{v, w.To}, w.Label})
if number[w.To] < lowpt[v] {
lowpt[v] = number[w.To]
}
}
}
return true
}
for w := range g.LabeledAdjacencyList {
if number[w] == 0 && !biconnect(NI(w), -1) {
return
}
}
}
func (e *eulerian) pushUndir() error {
for u := e.top(); ; {
e.uv.SetBit(int(u), 0)
arcs := e.g[u]
if len(arcs) == 0 {
return nil
}
w := arcs[0]
e.s++
e.p[e.s] = w
e.g[u] = arcs[1:] // consume arc
// difference from directed counterpart in dir.go:
// as long as it's not a loop, consume reciprocal arc as well
if w != u {
a2 := e.g[w]
for x, rx := range a2 {
if rx == u { // here it is
last := len(a2) - 1
a2[x] = a2[last] // someone else gets the seat
e.g[w] = a2[:last] // and it's gone.
goto l
}
}
return fmt.Errorf("graph not undirected. %d -> %d reciprocal not found", u, w)
}
l:
u = w
}
}
func (e *labEulerian) pushUndir() error {
for u := e.top(); ; {
e.uv.SetBit(int(u.To), 0)
arcs := e.g[u.To]
if len(arcs) == 0 {
return nil
}
w := arcs[0]
e.s++
e.p[e.s] = w
e.g[u.To] = arcs[1:] // consume arc
// difference from directed counterpart in dir.go:
// as long as it's not a loop, consume reciprocal arc as well
if w.To != u.To {
a2 := e.g[w.To]
for x, rx := range a2 {
if rx.To == u.To && rx.Label == w.Label { // here it is
last := len(a2) - 1
a2[x] = a2[last] // someone else can have the seat
e.g[w.To] = a2[:last] // and it's gone.
goto l
}
}
return fmt.Errorf("graph not undirected. %d -> %v reciprocal not found", u.To, w)
}
l:
u = w
}
}

1138
vendor/github.com/soniakeys/graph/undir_RO.go generated vendored Normal file

File diff suppressed because it is too large Load diff

1138
vendor/github.com/soniakeys/graph/undir_cg.go generated vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -350,8 +350,8 @@ func (db *hostKeyDB) check(address string, remote net.Addr, remoteKey ssh.Public
return db.checkAddr(hostToCheck, remoteKey)
}
// checkAddrs checks if we can find the given public key for any of
// the given addresses. If we only find an entry for the IP address,
// checkAddr checks if we can find the given public key for the
// given address. If we only find an entry for the IP address,
// or only the hostname, then this still succeeds.
func (db *hostKeyDB) checkAddr(a addr, remoteKey ssh.PublicKey) error {
// TODO(hanwen): are these the right semantics? What if there

View file

@ -159,6 +159,10 @@ func bytesToKey(b []byte, pasteActive bool) (rune, []byte) {
return keyClearScreen, b[1:]
case 23: // ^W
return keyDeleteWord, b[1:]
case 14: // ^N
return keyDown, b[1:]
case 16: // ^P
return keyUp, b[1:]
}
}

View file

@ -79,8 +79,13 @@ func FromURL(u *url.URL, forward Dialer) (Dialer, error) {
}
switch u.Scheme {
case "socks5":
return SOCKS5("tcp", u.Host, auth, forward)
case "socks5", "socks5h":
addr := u.Hostname()
port := u.Port()
if port == "" {
port = "1080"
}
return SOCKS5("tcp", net.JoinHostPort(addr, port), auth, forward)
}
// If the scheme doesn't match any of the built-in schemes, see if it

29
vendor/golang.org/x/sys/unix/asm_netbsd_arm64.s generated vendored Normal file
View file

@ -0,0 +1,29 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !gccgo
#include "textflag.h"
//
// System call support for ARM64, NetBSD
//
// Just jump to package syscall's implementation for all these functions.
// The runtime may know about them.
TEXT ·Syscall(SB),NOSPLIT,$0-56
B syscall·Syscall(SB)
TEXT ·Syscall6(SB),NOSPLIT,$0-80
B syscall·Syscall6(SB)
TEXT ·Syscall9(SB),NOSPLIT,$0-104
B syscall·Syscall9(SB)
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
B syscall·RawSyscall(SB)
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
B syscall·RawSyscall6(SB)

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd linux netbsd openbsd
// +build dragonfly freebsd linux netbsd openbsd
package unix

18
vendor/golang.org/x/sys/unix/fcntl_darwin.go generated vendored Normal file
View file

@ -0,0 +1,18 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package unix
import "unsafe"
// FcntlInt performs a fcntl syscall on fd with the provided command and argument.
func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
return fcntl(int(fd), cmd, arg)
}
// FcntlFlock performs a fcntl syscall for the F_GETLK, F_SETLK or F_SETLKW command.
func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error {
_, err := fcntl(int(fd), cmd, int(uintptr(unsafe.Pointer(lk))))
return err
}

View file

@ -62,12 +62,12 @@ _* | *_ | _)
;;
aix_ppc)
mkerrors="$mkerrors -maix32"
mksyscall="./mksyscall_aix_ppc.pl -aix"
mksyscall="go run mksyscall_aix_ppc.go -aix"
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
aix_ppc64)
mkerrors="$mkerrors -maix64"
mksyscall="./mksyscall_aix_ppc64.pl -aix"
mksyscall="go run mksyscall_aix_ppc64.go -aix"
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
darwin_386)
@ -99,31 +99,31 @@ darwin_arm64)
dragonfly_amd64)
mkerrors="$mkerrors -m64"
mksyscall="go run mksyscall.go -dragonfly"
mksysnum="go run mksysnum.go 'http://gitweb.dragonflybsd.org/dragonfly.git/blob_plain/HEAD:/sys/kern/syscalls.master'"
mksysnum="go run mksysnum.go 'https://gitweb.dragonflybsd.org/dragonfly.git/blob_plain/HEAD:/sys/kern/syscalls.master'"
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
freebsd_386)
mkerrors="$mkerrors -m32"
mksyscall="go run mksyscall.go -l32"
mksysnum="go run mksysnum.go 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master'"
mksysnum="go run mksysnum.go 'https://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master'"
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
freebsd_amd64)
mkerrors="$mkerrors -m64"
mksysnum="go run mksysnum.go 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master'"
mksysnum="go run mksysnum.go 'https://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master'"
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
freebsd_arm)
mkerrors="$mkerrors"
mksyscall="go run mksyscall.go -l32 -arm"
mksysnum="go run mksysnum.go 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master'"
mksysnum="go run mksysnum.go 'https://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master'"
# Let the type of C char be signed for making the bare syscall
# API consistent across platforms.
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
;;
freebsd_arm64)
mkerrors="$mkerrors -m64"
mksysnum="go run mksysnum.go 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master'"
mksysnum="go run mksysnum.go 'https://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master'"
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
netbsd_386)
@ -150,21 +150,21 @@ openbsd_386)
mkerrors="$mkerrors -m32"
mksyscall="go run mksyscall.go -l32 -openbsd"
mksysctl="./mksysctl_openbsd.pl"
mksysnum="go run mksysnum.go 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'"
mksysnum="go run mksysnum.go 'https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'"
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
openbsd_amd64)
mkerrors="$mkerrors -m64"
mksyscall="go run mksyscall.go -openbsd"
mksysctl="./mksysctl_openbsd.pl"
mksysnum="go run mksysnum.go 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'"
mksysnum="go run mksysnum.go 'https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'"
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
openbsd_arm)
mkerrors="$mkerrors"
mksyscall="go run mksyscall.go -l32 -openbsd -arm"
mksysctl="./mksysctl_openbsd.pl"
mksysnum="go run mksysnum.go 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'"
mksysnum="go run mksysnum.go 'https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'"
# Let the type of C char be signed for making the bare syscall
# API consistent across platforms.
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"

View file

@ -179,8 +179,10 @@ struct ltchars {
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/signalfd.h>
#include <sys/socket.h>
#include <sys/xattr.h>
#include <linux/errqueue.h>
#include <linux/if.h>
#include <linux/if_alg.h>
#include <linux/if_arp.h>
@ -453,7 +455,7 @@ ccflags="$@"
$2 !~ "MNT_BITS" &&
$2 ~ /^(MS|MNT|UMOUNT)_/ ||
$2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ ||
$2 ~ /^(O|F|E?FD|NAME|S|PTRACE|PT)_/ ||
$2 ~ /^(O|F|[ES]?FD|NAME|S|PTRACE|PT)_/ ||
$2 ~ /^KEXEC_/ ||
$2 ~ /^LINUX_REBOOT_CMD_/ ||
$2 ~ /^LINUX_REBOOT_MAGIC[12]$/ ||
@ -474,12 +476,13 @@ ccflags="$@"
$2 ~ /^CLONE_[A-Z_]+/ ||
$2 !~ /^(BPF_TIMEVAL)$/ &&
$2 ~ /^(BPF|DLT)_/ ||
$2 ~ /^CLOCK_/ ||
$2 ~ /^(CLOCK|TIMER)_/ ||
$2 ~ /^CAN_/ ||
$2 ~ /^CAP_/ ||
$2 ~ /^ALG_/ ||
$2 ~ /^FS_(POLICY_FLAGS|KEY_DESC|ENCRYPTION_MODE|[A-Z0-9_]+_KEY_SIZE|IOC_(GET|SET)_ENCRYPTION)/ ||
$2 ~ /^GRND_/ ||
$2 ~ /^RND/ ||
$2 ~ /^KEY_(SPEC|REQKEY_DEFL)_/ ||
$2 ~ /^KEYCTL_/ ||
$2 ~ /^PERF_EVENT_IOC_/ ||

404
vendor/golang.org/x/sys/unix/mksyscall_aix_ppc.go generated vendored Normal file
View file

@ -0,0 +1,404 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ignore
/*
This program reads a file containing function prototypes
(like syscall_aix.go) and generates system call bodies.
The prototypes are marked by lines beginning with "//sys"
and read like func declarations if //sys is replaced by func, but:
* The parameter lists must give a name for each argument.
This includes return parameters.
* The parameter lists must give a type for each argument:
the (x, y, z int) shorthand is not allowed.
* If the return parameter is an error number, it must be named err.
* If go func name needs to be different than its libc name,
* or the function is not in libc, name could be specified
* at the end, after "=" sign, like
//sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) = libsocket.getsockopt
*/
package main
import (
"bufio"
"flag"
"fmt"
"os"
"regexp"
"strings"
)
var (
b32 = flag.Bool("b32", false, "32bit big-endian")
l32 = flag.Bool("l32", false, "32bit little-endian")
aix = flag.Bool("aix", false, "aix")
tags = flag.String("tags", "", "build tags")
)
// cmdLine returns this programs's commandline arguments
func cmdLine() string {
return "go run mksyscall_aix_ppc.go " + strings.Join(os.Args[1:], " ")
}
// buildTags returns build tags
func buildTags() string {
return *tags
}
// Param is function parameter
type Param struct {
Name string
Type string
}
// usage prints the program usage
func usage() {
fmt.Fprintf(os.Stderr, "usage: go run mksyscall_aix_ppc.go [-b32 | -l32] [-tags x,y] [file ...]\n")
os.Exit(1)
}
// parseParamList parses parameter list and returns a slice of parameters
func parseParamList(list string) []string {
list = strings.TrimSpace(list)
if list == "" {
return []string{}
}
return regexp.MustCompile(`\s*,\s*`).Split(list, -1)
}
// parseParam splits a parameter into name and type
func parseParam(p string) Param {
ps := regexp.MustCompile(`^(\S*) (\S*)$`).FindStringSubmatch(p)
if ps == nil {
fmt.Fprintf(os.Stderr, "malformed parameter: %s\n", p)
os.Exit(1)
}
return Param{ps[1], ps[2]}
}
func main() {
flag.Usage = usage
flag.Parse()
if len(flag.Args()) <= 0 {
fmt.Fprintf(os.Stderr, "no files to parse provided\n")
usage()
}
endianness := ""
if *b32 {
endianness = "big-endian"
} else if *l32 {
endianness = "little-endian"
}
pack := ""
text := ""
cExtern := "/*\n#include <stdint.h>\n#include <stddef.h>\n"
for _, path := range flag.Args() {
file, err := os.Open(path)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
s := bufio.NewScanner(file)
for s.Scan() {
t := s.Text()
t = strings.TrimSpace(t)
t = regexp.MustCompile(`\s+`).ReplaceAllString(t, ` `)
if p := regexp.MustCompile(`^package (\S+)$`).FindStringSubmatch(t); p != nil && pack == "" {
pack = p[1]
}
nonblock := regexp.MustCompile(`^\/\/sysnb `).FindStringSubmatch(t)
if regexp.MustCompile(`^\/\/sys `).FindStringSubmatch(t) == nil && nonblock == nil {
continue
}
// Line must be of the form
// func Open(path string, mode int, perm int) (fd int, err error)
// Split into name, in params, out params.
f := regexp.MustCompile(`^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$`).FindStringSubmatch(t)
if f == nil {
fmt.Fprintf(os.Stderr, "%s:%s\nmalformed //sys declaration\n", path, t)
os.Exit(1)
}
funct, inps, outps, modname, sysname := f[2], f[3], f[4], f[5], f[6]
// Split argument lists on comma.
in := parseParamList(inps)
out := parseParamList(outps)
inps = strings.Join(in, ", ")
outps = strings.Join(out, ", ")
// Try in vain to keep people from editing this file.
// The theory is that they jump into the middle of the file
// without reading the header.
text += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"
// Check if value return, err return available
errvar := ""
retvar := ""
rettype := ""
for _, param := range out {
p := parseParam(param)
if p.Type == "error" {
errvar = p.Name
} else {
retvar = p.Name
rettype = p.Type
}
}
// System call name.
if sysname == "" {
sysname = funct
}
sysname = regexp.MustCompile(`([a-z])([A-Z])`).ReplaceAllString(sysname, `${1}_$2`)
sysname = strings.ToLower(sysname) // All libc functions are lowercase.
cRettype := ""
if rettype == "unsafe.Pointer" {
cRettype = "uintptr_t"
} else if rettype == "uintptr" {
cRettype = "uintptr_t"
} else if regexp.MustCompile(`^_`).FindStringSubmatch(rettype) != nil {
cRettype = "uintptr_t"
} else if rettype == "int" {
cRettype = "int"
} else if rettype == "int32" {
cRettype = "int"
} else if rettype == "int64" {
cRettype = "long long"
} else if rettype == "uint32" {
cRettype = "unsigned int"
} else if rettype == "uint64" {
cRettype = "unsigned long long"
} else {
cRettype = "int"
}
if sysname == "exit" {
cRettype = "void"
}
// Change p.Types to c
var cIn []string
for _, param := range in {
p := parseParam(param)
if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil {
cIn = append(cIn, "uintptr_t")
} else if p.Type == "string" {
cIn = append(cIn, "uintptr_t")
} else if regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type) != nil {
cIn = append(cIn, "uintptr_t", "size_t")
} else if p.Type == "unsafe.Pointer" {
cIn = append(cIn, "uintptr_t")
} else if p.Type == "uintptr" {
cIn = append(cIn, "uintptr_t")
} else if regexp.MustCompile(`^_`).FindStringSubmatch(p.Type) != nil {
cIn = append(cIn, "uintptr_t")
} else if p.Type == "int" {
cIn = append(cIn, "int")
} else if p.Type == "int32" {
cIn = append(cIn, "int")
} else if p.Type == "int64" {
cIn = append(cIn, "long long")
} else if p.Type == "uint32" {
cIn = append(cIn, "unsigned int")
} else if p.Type == "uint64" {
cIn = append(cIn, "unsigned long long")
} else {
cIn = append(cIn, "int")
}
}
if funct != "fcntl" && funct != "FcntlInt" && funct != "readlen" && funct != "writelen" {
// Imports of system calls from libc
cExtern += fmt.Sprintf("%s %s", cRettype, sysname)
cIn := strings.Join(cIn, ", ")
cExtern += fmt.Sprintf("(%s);\n", cIn)
}
// So file name.
if *aix {
if modname == "" {
modname = "libc.a/shr_64.o"
} else {
fmt.Fprintf(os.Stderr, "%s: only syscall using libc are available\n", funct)
os.Exit(1)
}
}
strconvfunc := "C.CString"
// Go function header.
if outps != "" {
outps = fmt.Sprintf(" (%s)", outps)
}
if text != "" {
text += "\n"
}
text += fmt.Sprintf("func %s(%s)%s {\n", funct, strings.Join(in, ", "), outps)
// Prepare arguments to Syscall.
var args []string
n := 0
argN := 0
for _, param := range in {
p := parseParam(param)
if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil {
args = append(args, "C.uintptr_t(uintptr(unsafe.Pointer("+p.Name+")))")
} else if p.Type == "string" && errvar != "" {
text += fmt.Sprintf("\t_p%d := uintptr(unsafe.Pointer(%s(%s)))\n", n, strconvfunc, p.Name)
args = append(args, fmt.Sprintf("C.uintptr_t(_p%d)", n))
n++
} else if p.Type == "string" {
fmt.Fprintf(os.Stderr, path+":"+funct+" uses string arguments, but has no error return\n")
text += fmt.Sprintf("\t_p%d := uintptr(unsafe.Pointer(%s(%s)))\n", n, strconvfunc, p.Name)
args = append(args, fmt.Sprintf("C.uintptr_t(_p%d)", n))
n++
} else if m := regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type); m != nil {
// Convert slice into pointer, length.
// Have to be careful not to take address of &a[0] if len == 0:
// pass nil in that case.
text += fmt.Sprintf("\tvar _p%d *%s\n", n, m[1])
text += fmt.Sprintf("\tif len(%s) > 0 {\n\t\t_p%d = &%s[0]\n\t}\n", p.Name, n, p.Name)
args = append(args, fmt.Sprintf("C.uintptr_t(uintptr(unsafe.Pointer(_p%d)))", n))
n++
text += fmt.Sprintf("\tvar _p%d int\n", n)
text += fmt.Sprintf("\t_p%d = len(%s)\n", n, p.Name)
args = append(args, fmt.Sprintf("C.size_t(_p%d)", n))
n++
} else if p.Type == "int64" && endianness != "" {
if endianness == "big-endian" {
args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name))
} else {
args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name))
}
n++
} else if p.Type == "bool" {
text += fmt.Sprintf("\tvar _p%d uint32\n", n)
text += fmt.Sprintf("\tif %s {\n\t\t_p%d = 1\n\t} else {\n\t\t_p%d = 0\n\t}\n", p.Name, n, n)
args = append(args, fmt.Sprintf("_p%d", n))
} else if regexp.MustCompile(`^_`).FindStringSubmatch(p.Type) != nil {
args = append(args, fmt.Sprintf("C.uintptr_t(uintptr(%s))", p.Name))
} else if p.Type == "unsafe.Pointer" {
args = append(args, fmt.Sprintf("C.uintptr_t(uintptr(%s))", p.Name))
} else if p.Type == "int" {
if (argN == 2) && ((funct == "readlen") || (funct == "writelen")) {
args = append(args, fmt.Sprintf("C.size_t(%s)", p.Name))
} else if argN == 0 && funct == "fcntl" {
args = append(args, fmt.Sprintf("C.uintptr_t(%s)", p.Name))
} else if (argN == 2) && ((funct == "fcntl") || (funct == "FcntlInt")) {
args = append(args, fmt.Sprintf("C.uintptr_t(%s)", p.Name))
} else {
args = append(args, fmt.Sprintf("C.int(%s)", p.Name))
}
} else if p.Type == "int32" {
args = append(args, fmt.Sprintf("C.int(%s)", p.Name))
} else if p.Type == "int64" {
args = append(args, fmt.Sprintf("C.longlong(%s)", p.Name))
} else if p.Type == "uint32" {
args = append(args, fmt.Sprintf("C.uint(%s)", p.Name))
} else if p.Type == "uint64" {
args = append(args, fmt.Sprintf("C.ulonglong(%s)", p.Name))
} else if p.Type == "uintptr" {
args = append(args, fmt.Sprintf("C.uintptr_t(%s)", p.Name))
} else {
args = append(args, fmt.Sprintf("C.int(%s)", p.Name))
}
argN++
}
// Actual call.
arglist := strings.Join(args, ", ")
call := ""
if sysname == "exit" {
if errvar != "" {
call += "er :="
} else {
call += ""
}
} else if errvar != "" {
call += "r0,er :="
} else if retvar != "" {
call += "r0,_ :="
} else {
call += ""
}
call += fmt.Sprintf("C.%s(%s)", sysname, arglist)
// Assign return values.
body := ""
for i := 0; i < len(out); i++ {
p := parseParam(out[i])
reg := ""
if p.Name == "err" {
reg = "e1"
} else {
reg = "r0"
}
if reg != "e1" {
body += fmt.Sprintf("\t%s = %s(%s)\n", p.Name, p.Type, reg)
}
}
// verify return
if sysname != "exit" && errvar != "" {
if regexp.MustCompile(`^uintptr`).FindStringSubmatch(cRettype) != nil {
body += "\tif (uintptr(r0) ==^uintptr(0) && er != nil) {\n"
body += fmt.Sprintf("\t\t%s = er\n", errvar)
body += "\t}\n"
} else {
body += "\tif (r0 ==-1 && er != nil) {\n"
body += fmt.Sprintf("\t\t%s = er\n", errvar)
body += "\t}\n"
}
} else if errvar != "" {
body += "\tif (er != nil) {\n"
body += fmt.Sprintf("\t\t%s = er\n", errvar)
body += "\t}\n"
}
text += fmt.Sprintf("\t%s\n", call)
text += body
text += "\treturn\n"
text += "}\n"
}
if err := s.Err(); err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
file.Close()
}
imp := ""
if pack != "unix" {
imp = "import \"golang.org/x/sys/unix\"\n"
}
fmt.Printf(srcTemplate, cmdLine(), buildTags(), pack, cExtern, imp, text)
}
const srcTemplate = `// %s
// Code generated by the command above; see README.md. DO NOT EDIT.
// +build %s
package %s
%s
*/
import "C"
import (
"unsafe"
)
%s
%s
`

View file

@ -1,384 +0,0 @@
#!/usr/bin/env perl
# Copyright 2018 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
# This program reads a file containing function prototypes
# (like syscall_aix.go) and generates system call bodies.
# The prototypes are marked by lines beginning with "//sys"
# and read like func declarations if //sys is replaced by func, but:
# * The parameter lists must give a name for each argument.
# This includes return parameters.
# * The parameter lists must give a type for each argument:
# the (x, y, z int) shorthand is not allowed.
# * If the return parameter is an error number, it must be named err.
# * If go func name needs to be different than its libc name,
# * or the function is not in libc, name could be specified
# * at the end, after "=" sign, like
# //sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) = libsocket.getsockopt
use strict;
my $cmdline = "mksyscall_aix_ppc.pl " . join(' ', @ARGV);
my $errors = 0;
my $_32bit = "";
my $tags = ""; # build tags
my $aix = 0;
my $solaris = 0;
binmode STDOUT;
if($ARGV[0] eq "-b32") {
$_32bit = "big-endian";
shift;
} elsif($ARGV[0] eq "-l32") {
$_32bit = "little-endian";
shift;
}
if($ARGV[0] eq "-aix") {
$aix = 1;
shift;
}
if($ARGV[0] eq "-tags") {
shift;
$tags = $ARGV[0];
shift;
}
if($ARGV[0] =~ /^-/) {
print STDERR "usage: mksyscall_aix.pl [-b32 | -l32] [-tags x,y] [file ...]\n";
exit 1;
}
sub parseparamlist($) {
my ($list) = @_;
$list =~ s/^\s*//;
$list =~ s/\s*$//;
if($list eq "") {
return ();
}
return split(/\s*,\s*/, $list);
}
sub parseparam($) {
my ($p) = @_;
if($p !~ /^(\S*) (\S*)$/) {
print STDERR "$ARGV:$.: malformed parameter: $p\n";
$errors = 1;
return ("xx", "int");
}
return ($1, $2);
}
my $package = "";
my $text = "";
my $c_extern = "/*\n#include <stdint.h>\n#include <stddef.h>\n";
my @vars = ();
while(<>) {
chomp;
s/\s+/ /g;
s/^\s+//;
s/\s+$//;
$package = $1 if !$package && /^package (\S+)$/;
my $nonblock = /^\/\/sysnb /;
next if !/^\/\/sys / && !$nonblock;
# Line must be of the form
# func Open(path string, mode int, perm int) (fd int, err error)
# Split into name, in params, out params.
if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$/) {
print STDERR "$ARGV:$.: malformed //sys declaration\n";
$errors = 1;
next;
}
my ($nb, $func, $in, $out, $modname, $sysname) = ($1, $2, $3, $4, $5, $6);
# Split argument lists on comma.
my @in = parseparamlist($in);
my @out = parseparamlist($out);
$in = join(', ', @in);
$out = join(', ', @out);
# Try in vain to keep people from editing this file.
# The theory is that they jump into the middle of the file
# without reading the header.
$text .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n";
# Check if value return, err return available
my $errvar = "";
my $retvar = "";
my $rettype = "";
foreach my $p (@out) {
my ($name, $type) = parseparam($p);
if($type eq "error") {
$errvar = $name;
} else {
$retvar = $name;
$rettype = $type;
}
}
# System call name.
#if($func ne "fcntl") {
if($sysname eq "") {
$sysname = "$func";
}
$sysname =~ s/([a-z])([A-Z])/${1}_$2/g;
$sysname =~ y/A-Z/a-z/; # All libc functions are lowercase.
my $C_rettype = "";
if($rettype eq "unsafe.Pointer") {
$C_rettype = "uintptr_t";
} elsif($rettype eq "uintptr") {
$C_rettype = "uintptr_t";
} elsif($rettype =~ /^_/) {
$C_rettype = "uintptr_t";
} elsif($rettype eq "int") {
$C_rettype = "int";
} elsif($rettype eq "int32") {
$C_rettype = "int";
} elsif($rettype eq "int64") {
$C_rettype = "long long";
} elsif($rettype eq "uint32") {
$C_rettype = "unsigned int";
} elsif($rettype eq "uint64") {
$C_rettype = "unsigned long long";
} else {
$C_rettype = "int";
}
if($sysname eq "exit") {
$C_rettype = "void";
}
# Change types to c
my @c_in = ();
foreach my $p (@in) {
my ($name, $type) = parseparam($p);
if($type =~ /^\*/) {
push @c_in, "uintptr_t";
} elsif($type eq "string") {
push @c_in, "uintptr_t";
} elsif($type =~ /^\[\](.*)/) {
push @c_in, "uintptr_t", "size_t";
} elsif($type eq "unsafe.Pointer") {
push @c_in, "uintptr_t";
} elsif($type eq "uintptr") {
push @c_in, "uintptr_t";
} elsif($type =~ /^_/) {
push @c_in, "uintptr_t";
} elsif($type eq "int") {
push @c_in, "int";
} elsif($type eq "int32") {
push @c_in, "int";
} elsif($type eq "int64") {
push @c_in, "long long";
} elsif($type eq "uint32") {
push @c_in, "unsigned int";
} elsif($type eq "uint64") {
push @c_in, "unsigned long long";
} else {
push @c_in, "int";
}
}
if ($func ne "fcntl" && $func ne "FcntlInt" && $func ne "readlen" && $func ne "writelen") {
# Imports of system calls from libc
$c_extern .= "$C_rettype $sysname";
my $c_in = join(', ', @c_in);
$c_extern .= "($c_in);\n";
}
# So file name.
if($aix) {
if($modname eq "") {
$modname = "libc.a/shr_64.o";
} else {
print STDERR "$func: only syscall using libc are available\n";
$errors = 1;
next;
}
}
my $strconvfunc = "C.CString";
my $strconvtype = "*byte";
# Go function header.
if($out ne "") {
$out = " ($out)";
}
if($text ne "") {
$text .= "\n"
}
$text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out ;
# Prepare arguments to call.
my @args = ();
my $n = 0;
my $arg_n = 0;
foreach my $p (@in) {
my ($name, $type) = parseparam($p);
if($type =~ /^\*/) {
push @args, "C.uintptr_t(uintptr(unsafe.Pointer($name)))";
} elsif($type eq "string" && $errvar ne "") {
$text .= "\t_p$n := uintptr(unsafe.Pointer($strconvfunc($name)))\n";
push @args, "C.uintptr_t(_p$n)";
$n++;
} elsif($type eq "string") {
print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n";
$text .= "\t_p$n := uintptr(unsafe.Pointer($strconvfunc($name)))\n";
push @args, "C.uintptr_t(_p$n)";
$n++;
} elsif($type =~ /^\[\](.*)/) {
# Convert slice into pointer, length.
# Have to be careful not to take address of &a[0] if len == 0:
# pass nil in that case.
$text .= "\tvar _p$n *$1\n";
$text .= "\tif len($name) > 0 {\n\t\t_p$n = \&$name\[0]\n\t}\n";
push @args, "C.uintptr_t(uintptr(unsafe.Pointer(_p$n)))";
$n++;
$text .= "\tvar _p$n int\n";
$text .= "\t_p$n = len($name)\n";
push @args, "C.size_t(_p$n)";
$n++;
} elsif($type eq "int64" && $_32bit ne "") {
if($_32bit eq "big-endian") {
push @args, "uintptr($name >> 32)", "uintptr($name)";
} else {
push @args, "uintptr($name)", "uintptr($name >> 32)";
}
$n++;
} elsif($type eq "bool") {
$text .= "\tvar _p$n uint32\n";
$text .= "\tif $name {\n\t\t_p$n = 1\n\t} else {\n\t\t_p$n = 0\n\t}\n";
push @args, "_p$n";
$n++;
} elsif($type =~ /^_/) {
push @args, "C.uintptr_t(uintptr($name))";
} elsif($type eq "unsafe.Pointer") {
push @args, "C.uintptr_t(uintptr($name))";
} elsif($type eq "int") {
if (($arg_n == 2) && (($func eq "readlen") || ($func eq "writelen"))) {
push @args, "C.size_t($name)";
} elsif ($arg_n == 0 && $func eq "fcntl") {
push @args, "C.uintptr_t($name)";
} elsif (($arg_n == 2) && (($func eq "fcntl") || ($func eq "FcntlInt"))) {
push @args, "C.uintptr_t($name)";
} else {
push @args, "C.int($name)";
}
} elsif($type eq "int32") {
push @args, "C.int($name)";
} elsif($type eq "int64") {
push @args, "C.longlong($name)";
} elsif($type eq "uint32") {
push @args, "C.uint($name)";
} elsif($type eq "uint64") {
push @args, "C.ulonglong($name)";
} elsif($type eq "uintptr") {
push @args, "C.uintptr_t($name)";
} else {
push @args, "C.int($name)";
}
$arg_n++;
}
my $nargs = @args;
# Determine which form to use; pad args with zeros.
if ($nonblock) {
}
my $args = join(', ', @args);
my $call = "";
if ($sysname eq "exit") {
if ($errvar ne "") {
$call .= "er :=";
} else {
$call .= "";
}
} elsif ($errvar ne "") {
$call .= "r0,er :=";
} elsif ($retvar ne "") {
$call .= "r0,_ :=";
} else {
$call .= ""
}
$call .= "C.$sysname($args)";
# Assign return values.
my $body = "";
my $failexpr = "";
for(my $i=0; $i<@out; $i++) {
my $p = $out[$i];
my ($name, $type) = parseparam($p);
my $reg = "";
if($name eq "err") {
$reg = "e1";
} else {
$reg = "r0";
}
if($reg ne "e1" ) {
$body .= "\t$name = $type($reg)\n";
}
}
# verify return
if ($sysname ne "exit" && $errvar ne "") {
if ($C_rettype =~ /^uintptr/) {
$body .= "\tif \(uintptr\(r0\) ==\^uintptr\(0\) && er != nil\) {\n";
$body .= "\t\t$errvar = er\n";
$body .= "\t}\n";
} else {
$body .= "\tif \(r0 ==-1 && er != nil\) {\n";
$body .= "\t\t$errvar = er\n";
$body .= "\t}\n";
}
} elsif ($errvar ne "") {
$body .= "\tif \(er != nil\) {\n";
$body .= "\t\t$errvar = er\n";
$body .= "\t}\n";
}
$text .= "\t$call\n";
$text .= $body;
$text .= "\treturn\n";
$text .= "}\n";
}
if($errors) {
exit 1;
}
print <<EOF;
// $cmdline
// Code generated by the command above; see README.md. DO NOT EDIT.
// +build $tags
package $package
$c_extern
*/
import "C"
import (
"unsafe"
)
EOF
print "import \"golang.org/x/sys/unix\"\n" if $package ne "unix";
chomp($_=<<EOF);
$text
EOF
print $_;
exit 0;

602
vendor/golang.org/x/sys/unix/mksyscall_aix_ppc64.go generated vendored Normal file
View file

@ -0,0 +1,602 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ignore
/*
This program reads a file containing function prototypes
(like syscall_aix.go) and generates system call bodies.
The prototypes are marked by lines beginning with "//sys"
and read like func declarations if //sys is replaced by func, but:
* The parameter lists must give a name for each argument.
This includes return parameters.
* The parameter lists must give a type for each argument:
the (x, y, z int) shorthand is not allowed.
* If the return parameter is an error number, it must be named err.
* If go func name needs to be different than its libc name,
* or the function is not in libc, name could be specified
* at the end, after "=" sign, like
//sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) = libsocket.getsockopt
This program will generate three files and handle both gc and gccgo implementation:
- zsyscall_aix_ppc64.go: the common part of each implementation (error handler, pointer creation)
- zsyscall_aix_ppc64_gc.go: gc part with //go_cgo_import_dynamic and a call to syscall6
- zsyscall_aix_ppc64_gccgo.go: gccgo part with C function and conversion to C type.
The generated code looks like this
zsyscall_aix_ppc64.go
func asyscall(...) (n int, err error) {
// Pointer Creation
r1, e1 := callasyscall(...)
// Type Conversion
// Error Handler
return
}
zsyscall_aix_ppc64_gc.go
//go:cgo_import_dynamic libc_asyscall asyscall "libc.a/shr_64.o"
//go:linkname libc_asyscall libc_asyscall
var asyscall syscallFunc
func callasyscall(...) (r1 uintptr, e1 Errno) {
r1, _, e1 = syscall6(uintptr(unsafe.Pointer(&libc_asyscall)), "nb_args", ... )
return
}
zsyscall_aix_ppc64_ggcgo.go
// int asyscall(...)
import "C"
func callasyscall(...) (r1 uintptr, e1 Errno) {
r1 = uintptr(C.asyscall(...))
e1 = syscall.GetErrno()
return
}
*/
package main
import (
"bufio"
"flag"
"fmt"
"io/ioutil"
"os"
"regexp"
"strings"
)
var (
b32 = flag.Bool("b32", false, "32bit big-endian")
l32 = flag.Bool("l32", false, "32bit little-endian")
aix = flag.Bool("aix", false, "aix")
tags = flag.String("tags", "", "build tags")
)
// cmdLine returns this programs's commandline arguments
func cmdLine() string {
return "go run mksyscall_aix_ppc64.go " + strings.Join(os.Args[1:], " ")
}
// buildTags returns build tags
func buildTags() string {
return *tags
}
// Param is function parameter
type Param struct {
Name string
Type string
}
// usage prints the program usage
func usage() {
fmt.Fprintf(os.Stderr, "usage: go run mksyscall_aix_ppc64.go [-b32 | -l32] [-tags x,y] [file ...]\n")
os.Exit(1)
}
// parseParamList parses parameter list and returns a slice of parameters
func parseParamList(list string) []string {
list = strings.TrimSpace(list)
if list == "" {
return []string{}
}
return regexp.MustCompile(`\s*,\s*`).Split(list, -1)
}
// parseParam splits a parameter into name and type
func parseParam(p string) Param {
ps := regexp.MustCompile(`^(\S*) (\S*)$`).FindStringSubmatch(p)
if ps == nil {
fmt.Fprintf(os.Stderr, "malformed parameter: %s\n", p)
os.Exit(1)
}
return Param{ps[1], ps[2]}
}
func main() {
flag.Usage = usage
flag.Parse()
if len(flag.Args()) <= 0 {
fmt.Fprintf(os.Stderr, "no files to parse provided\n")
usage()
}
endianness := ""
if *b32 {
endianness = "big-endian"
} else if *l32 {
endianness = "little-endian"
}
pack := ""
// GCCGO
textgccgo := ""
cExtern := "/*\n#include <stdint.h>\n"
// GC
textgc := ""
dynimports := ""
linknames := ""
var vars []string
// COMMON
textcommon := ""
for _, path := range flag.Args() {
file, err := os.Open(path)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
s := bufio.NewScanner(file)
for s.Scan() {
t := s.Text()
t = strings.TrimSpace(t)
t = regexp.MustCompile(`\s+`).ReplaceAllString(t, ` `)
if p := regexp.MustCompile(`^package (\S+)$`).FindStringSubmatch(t); p != nil && pack == "" {
pack = p[1]
}
nonblock := regexp.MustCompile(`^\/\/sysnb `).FindStringSubmatch(t)
if regexp.MustCompile(`^\/\/sys `).FindStringSubmatch(t) == nil && nonblock == nil {
continue
}
// Line must be of the form
// func Open(path string, mode int, perm int) (fd int, err error)
// Split into name, in params, out params.
f := regexp.MustCompile(`^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$`).FindStringSubmatch(t)
if f == nil {
fmt.Fprintf(os.Stderr, "%s:%s\nmalformed //sys declaration\n", path, t)
os.Exit(1)
}
funct, inps, outps, modname, sysname := f[2], f[3], f[4], f[5], f[6]
// Split argument lists on comma.
in := parseParamList(inps)
out := parseParamList(outps)
inps = strings.Join(in, ", ")
outps = strings.Join(out, ", ")
if sysname == "" {
sysname = funct
}
onlyCommon := false
if funct == "readlen" || funct == "writelen" || funct == "FcntlInt" || funct == "FcntlFlock" {
// This function call another syscall which is already implemented.
// Therefore, the gc and gccgo part must not be generated.
onlyCommon = true
}
// Try in vain to keep people from editing this file.
// The theory is that they jump into the middle of the file
// without reading the header.
textcommon += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"
if !onlyCommon {
textgccgo += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"
textgc += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"
}
// Check if value return, err return available
errvar := ""
rettype := ""
for _, param := range out {
p := parseParam(param)
if p.Type == "error" {
errvar = p.Name
} else {
rettype = p.Type
}
}
sysname = regexp.MustCompile(`([a-z])([A-Z])`).ReplaceAllString(sysname, `${1}_$2`)
sysname = strings.ToLower(sysname) // All libc functions are lowercase.
// GCCGO Prototype return type
cRettype := ""
if rettype == "unsafe.Pointer" {
cRettype = "uintptr_t"
} else if rettype == "uintptr" {
cRettype = "uintptr_t"
} else if regexp.MustCompile(`^_`).FindStringSubmatch(rettype) != nil {
cRettype = "uintptr_t"
} else if rettype == "int" {
cRettype = "int"
} else if rettype == "int32" {
cRettype = "int"
} else if rettype == "int64" {
cRettype = "long long"
} else if rettype == "uint32" {
cRettype = "unsigned int"
} else if rettype == "uint64" {
cRettype = "unsigned long long"
} else {
cRettype = "int"
}
if sysname == "exit" {
cRettype = "void"
}
// GCCGO Prototype arguments type
var cIn []string
for i, param := range in {
p := parseParam(param)
if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil {
cIn = append(cIn, "uintptr_t")
} else if p.Type == "string" {
cIn = append(cIn, "uintptr_t")
} else if regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type) != nil {
cIn = append(cIn, "uintptr_t", "size_t")
} else if p.Type == "unsafe.Pointer" {
cIn = append(cIn, "uintptr_t")
} else if p.Type == "uintptr" {
cIn = append(cIn, "uintptr_t")
} else if regexp.MustCompile(`^_`).FindStringSubmatch(p.Type) != nil {
cIn = append(cIn, "uintptr_t")
} else if p.Type == "int" {
if (i == 0 || i == 2) && funct == "fcntl" {
// These fcntl arguments needs to be uintptr to be able to call FcntlInt and FcntlFlock
cIn = append(cIn, "uintptr_t")
} else {
cIn = append(cIn, "int")
}
} else if p.Type == "int32" {
cIn = append(cIn, "int")
} else if p.Type == "int64" {
cIn = append(cIn, "long long")
} else if p.Type == "uint32" {
cIn = append(cIn, "unsigned int")
} else if p.Type == "uint64" {
cIn = append(cIn, "unsigned long long")
} else {
cIn = append(cIn, "int")
}
}
if !onlyCommon {
// GCCGO Prototype Generation
// Imports of system calls from libc
cExtern += fmt.Sprintf("%s %s", cRettype, sysname)
cIn := strings.Join(cIn, ", ")
cExtern += fmt.Sprintf("(%s);\n", cIn)
}
// GC Library name
if modname == "" {
modname = "libc.a/shr_64.o"
} else {
fmt.Fprintf(os.Stderr, "%s: only syscall using libc are available\n", funct)
os.Exit(1)
}
sysvarname := fmt.Sprintf("libc_%s", sysname)
if !onlyCommon {
// GC Runtime import of function to allow cross-platform builds.
dynimports += fmt.Sprintf("//go:cgo_import_dynamic %s %s \"%s\"\n", sysvarname, sysname, modname)
// GC Link symbol to proc address variable.
linknames += fmt.Sprintf("//go:linkname %s %s\n", sysvarname, sysvarname)
// GC Library proc address variable.
vars = append(vars, sysvarname)
}
strconvfunc := "BytePtrFromString"
strconvtype := "*byte"
// Go function header.
if outps != "" {
outps = fmt.Sprintf(" (%s)", outps)
}
if textcommon != "" {
textcommon += "\n"
}
textcommon += fmt.Sprintf("func %s(%s)%s {\n", funct, strings.Join(in, ", "), outps)
// Prepare arguments tocall.
var argscommon []string // Arguments in the common part
var argscall []string // Arguments for call prototype
var argsgc []string // Arguments for gc call (with syscall6)
var argsgccgo []string // Arguments for gccgo call (with C.name_of_syscall)
n := 0
argN := 0
for _, param := range in {
p := parseParam(param)
if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil {
argscommon = append(argscommon, fmt.Sprintf("uintptr(unsafe.Pointer(%s))", p.Name))
argscall = append(argscall, fmt.Sprintf("%s uintptr", p.Name))
argsgc = append(argsgc, p.Name)
argsgccgo = append(argsgccgo, fmt.Sprintf("C.uintptr_t(%s)", p.Name))
} else if p.Type == "string" && errvar != "" {
textcommon += fmt.Sprintf("\tvar _p%d %s\n", n, strconvtype)
textcommon += fmt.Sprintf("\t_p%d, %s = %s(%s)\n", n, errvar, strconvfunc, p.Name)
textcommon += fmt.Sprintf("\tif %s != nil {\n\t\treturn\n\t}\n", errvar)
argscommon = append(argscommon, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n))
argscall = append(argscall, fmt.Sprintf("_p%d uintptr ", n))
argsgc = append(argsgc, fmt.Sprintf("_p%d", n))
argsgccgo = append(argsgccgo, fmt.Sprintf("C.uintptr_t(_p%d)", n))
n++
} else if p.Type == "string" {
fmt.Fprintf(os.Stderr, path+":"+funct+" uses string arguments, but has no error return\n")
textcommon += fmt.Sprintf("\tvar _p%d %s\n", n, strconvtype)
textcommon += fmt.Sprintf("\t_p%d, %s = %s(%s)\n", n, errvar, strconvfunc, p.Name)
textcommon += fmt.Sprintf("\tif %s != nil {\n\t\treturn\n\t}\n", errvar)
argscommon = append(argscommon, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n))
argscall = append(argscall, fmt.Sprintf("_p%d uintptr", n))
argsgc = append(argsgc, fmt.Sprintf("_p%d", n))
argsgccgo = append(argsgccgo, fmt.Sprintf("C.uintptr_t(_p%d)", n))
n++
} else if m := regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type); m != nil {
// Convert slice into pointer, length.
// Have to be careful not to take address of &a[0] if len == 0:
// pass nil in that case.
textcommon += fmt.Sprintf("\tvar _p%d *%s\n", n, m[1])
textcommon += fmt.Sprintf("\tif len(%s) > 0 {\n\t\t_p%d = &%s[0]\n\t}\n", p.Name, n, p.Name)
argscommon = append(argscommon, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n), fmt.Sprintf("len(%s)", p.Name))
argscall = append(argscall, fmt.Sprintf("_p%d uintptr", n), fmt.Sprintf("_lenp%d int", n))
argsgc = append(argsgc, fmt.Sprintf("_p%d", n), fmt.Sprintf("uintptr(_lenp%d)", n))
argsgccgo = append(argsgccgo, fmt.Sprintf("C.uintptr_t(_p%d)", n), fmt.Sprintf("C.size_t(_lenp%d)", n))
n++
} else if p.Type == "int64" && endianness != "" {
fmt.Fprintf(os.Stderr, path+":"+funct+" uses int64 with 32 bits mode. Case not yet implemented\n")
} else if p.Type == "bool" {
fmt.Fprintf(os.Stderr, path+":"+funct+" uses bool. Case not yet implemented\n")
} else if regexp.MustCompile(`^_`).FindStringSubmatch(p.Type) != nil || p.Type == "unsafe.Pointer" {
argscommon = append(argscommon, fmt.Sprintf("uintptr(%s)", p.Name))
argscall = append(argscall, fmt.Sprintf("%s uintptr", p.Name))
argsgc = append(argsgc, p.Name)
argsgccgo = append(argsgccgo, fmt.Sprintf("C.uintptr_t(%s)", p.Name))
} else if p.Type == "int" {
if (argN == 0 || argN == 2) && ((funct == "fcntl") || (funct == "FcntlInt") || (funct == "FcntlFlock")) {
// These fcntl arguments need to be uintptr to be able to call FcntlInt and FcntlFlock
argscommon = append(argscommon, fmt.Sprintf("uintptr(%s)", p.Name))
argscall = append(argscall, fmt.Sprintf("%s uintptr", p.Name))
argsgc = append(argsgc, p.Name)
argsgccgo = append(argsgccgo, fmt.Sprintf("C.uintptr_t(%s)", p.Name))
} else {
argscommon = append(argscommon, p.Name)
argscall = append(argscall, fmt.Sprintf("%s int", p.Name))
argsgc = append(argsgc, fmt.Sprintf("uintptr(%s)", p.Name))
argsgccgo = append(argsgccgo, fmt.Sprintf("C.int(%s)", p.Name))
}
} else if p.Type == "int32" {
argscommon = append(argscommon, p.Name)
argscall = append(argscall, fmt.Sprintf("%s int32", p.Name))
argsgc = append(argsgc, fmt.Sprintf("uintptr(%s)", p.Name))
argsgccgo = append(argsgccgo, fmt.Sprintf("C.int(%s)", p.Name))
} else if p.Type == "int64" {
argscommon = append(argscommon, p.Name)
argscall = append(argscall, fmt.Sprintf("%s int64", p.Name))
argsgc = append(argsgc, fmt.Sprintf("uintptr(%s)", p.Name))
argsgccgo = append(argsgccgo, fmt.Sprintf("C.longlong(%s)", p.Name))
} else if p.Type == "uint32" {
argscommon = append(argscommon, p.Name)
argscall = append(argscall, fmt.Sprintf("%s uint32", p.Name))
argsgc = append(argsgc, fmt.Sprintf("uintptr(%s)", p.Name))
argsgccgo = append(argsgccgo, fmt.Sprintf("C.uint(%s)", p.Name))
} else if p.Type == "uint64" {
argscommon = append(argscommon, p.Name)
argscall = append(argscall, fmt.Sprintf("%s uint64", p.Name))
argsgc = append(argsgc, fmt.Sprintf("uintptr(%s)", p.Name))
argsgccgo = append(argsgccgo, fmt.Sprintf("C.ulonglong(%s)", p.Name))
} else if p.Type == "uintptr" {
argscommon = append(argscommon, p.Name)
argscall = append(argscall, fmt.Sprintf("%s uintptr", p.Name))
argsgc = append(argsgc, p.Name)
argsgccgo = append(argsgccgo, fmt.Sprintf("C.uintptr_t(%s)", p.Name))
} else {
argscommon = append(argscommon, fmt.Sprintf("int(%s)", p.Name))
argscall = append(argscall, fmt.Sprintf("%s int", p.Name))
argsgc = append(argsgc, fmt.Sprintf("uintptr(%s)", p.Name))
argsgccgo = append(argsgccgo, fmt.Sprintf("C.int(%s)", p.Name))
}
argN++
}
nargs := len(argsgc)
// COMMON function generation
argscommonlist := strings.Join(argscommon, ", ")
callcommon := fmt.Sprintf("call%s(%s)", sysname, argscommonlist)
ret := []string{"_", "_"}
body := ""
doErrno := false
for i := 0; i < len(out); i++ {
p := parseParam(out[i])
reg := ""
if p.Name == "err" {
reg = "e1"
ret[1] = reg
doErrno = true
} else {
reg = "r0"
ret[0] = reg
}
if p.Type == "bool" {
reg = fmt.Sprintf("%s != 0", reg)
}
if reg != "e1" {
body += fmt.Sprintf("\t%s = %s(%s)\n", p.Name, p.Type, reg)
}
}
if ret[0] == "_" && ret[1] == "_" {
textcommon += fmt.Sprintf("\t%s\n", callcommon)
} else {
textcommon += fmt.Sprintf("\t%s, %s := %s\n", ret[0], ret[1], callcommon)
}
textcommon += body
if doErrno {
textcommon += "\tif e1 != 0 {\n"
textcommon += "\t\terr = errnoErr(e1)\n"
textcommon += "\t}\n"
}
textcommon += "\treturn\n"
textcommon += "}\n"
if onlyCommon {
continue
}
// CALL Prototype
callProto := fmt.Sprintf("func call%s(%s) (r1 uintptr, e1 Errno) {\n", sysname, strings.Join(argscall, ", "))
// GC function generation
asm := "syscall6"
if nonblock != nil {
asm = "rawSyscall6"
}
if len(argsgc) <= 6 {
for len(argsgc) < 6 {
argsgc = append(argsgc, "0")
}
} else {
fmt.Fprintf(os.Stderr, "%s: too many arguments to system call", funct)
os.Exit(1)
}
argsgclist := strings.Join(argsgc, ", ")
callgc := fmt.Sprintf("%s(uintptr(unsafe.Pointer(&%s)), %d, %s)", asm, sysvarname, nargs, argsgclist)
textgc += callProto
textgc += fmt.Sprintf("\tr1, _, e1 = %s\n", callgc)
textgc += "\treturn\n}\n"
// GCCGO function generation
argsgccgolist := strings.Join(argsgccgo, ", ")
callgccgo := fmt.Sprintf("C.%s(%s)", sysname, argsgccgolist)
textgccgo += callProto
textgccgo += fmt.Sprintf("\tr1 = uintptr(%s)\n", callgccgo)
textgccgo += "\te1 = syscall.GetErrno()\n"
textgccgo += "\treturn\n}\n"
}
if err := s.Err(); err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
file.Close()
}
imp := ""
if pack != "unix" {
imp = "import \"golang.org/x/sys/unix\"\n"
}
// Print zsyscall_aix_ppc64.go
err := ioutil.WriteFile("zsyscall_aix_ppc64.go",
[]byte(fmt.Sprintf(srcTemplate1, cmdLine(), buildTags(), pack, imp, textcommon)),
0644)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
// Print zsyscall_aix_ppc64_gc.go
vardecls := "\t" + strings.Join(vars, ",\n\t")
vardecls += " syscallFunc"
err = ioutil.WriteFile("zsyscall_aix_ppc64_gc.go",
[]byte(fmt.Sprintf(srcTemplate2, cmdLine(), buildTags(), pack, imp, dynimports, linknames, vardecls, textgc)),
0644)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
// Print zsyscall_aix_ppc64_gccgo.go
err = ioutil.WriteFile("zsyscall_aix_ppc64_gccgo.go",
[]byte(fmt.Sprintf(srcTemplate3, cmdLine(), buildTags(), pack, cExtern, imp, textgccgo)),
0644)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
}
const srcTemplate1 = `// %s
// Code generated by the command above; see README.md. DO NOT EDIT.
// +build %s
package %s
import (
"unsafe"
)
%s
%s
`
const srcTemplate2 = `// %s
// Code generated by the command above; see README.md. DO NOT EDIT.
// +build %s
// +build !gccgo
package %s
import (
"unsafe"
)
%s
%s
%s
type syscallFunc uintptr
var (
%s
)
// Implemented in runtime/syscall_aix.go.
func rawSyscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
func syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
%s
`
const srcTemplate3 = `// %s
// Code generated by the command above; see README.md. DO NOT EDIT.
// +build %s
// +build gccgo
package %s
%s
*/
import "C"
import (
"syscall"
)
%s
%s
`

View file

@ -1,579 +0,0 @@
#!/usr/bin/env perl
# Copyright 2018 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
# This program reads a file containing function prototypes
# (like syscall_aix.go) and generates system call bodies.
# The prototypes are marked by lines beginning with "//sys"
# and read like func declarations if //sys is replaced by func, but:
# * The parameter lists must give a name for each argument.
# This includes return parameters.
# * The parameter lists must give a type for each argument:
# the (x, y, z int) shorthand is not allowed.
# * If the return parameter is an error number, it must be named err.
# * If go func name needs to be different than its libc name,
# * or the function is not in libc, name could be specified
# * at the end, after "=" sign, like
# //sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) = libsocket.getsockopt
# This program will generate three files and handle both gc and gccgo implementation:
# - zsyscall_aix_ppc64.go: the common part of each implementation (error handler, pointer creation)
# - zsyscall_aix_ppc64_gc.go: gc part with //go_cgo_import_dynamic and a call to syscall6
# - zsyscall_aix_ppc64_gccgo.go: gccgo part with C function and conversion to C type.
# The generated code looks like this
#
# zsyscall_aix_ppc64.go
# func asyscall(...) (n int, err error) {
# // Pointer Creation
# r1, e1 := callasyscall(...)
# // Type Conversion
# // Error Handler
# return
# }
#
# zsyscall_aix_ppc64_gc.go
# //go:cgo_import_dynamic libc_asyscall asyscall "libc.a/shr_64.o"
# //go:linkname libc_asyscall libc_asyscall
# var asyscall syscallFunc
#
# func callasyscall(...) (r1 uintptr, e1 Errno) {
# r1, _, e1 = syscall6(uintptr(unsafe.Pointer(&libc_asyscall)), "nb_args", ... )
# return
# }
#
# zsyscall_aix_ppc64_ggcgo.go
# /*
# int asyscall(...)
#
# */
# import "C"
#
# func callasyscall(...) (r1 uintptr, e1 Errno) {
# r1 = uintptr(C.asyscall(...))
# e1 = syscall.GetErrno()
# return
# }
use strict;
my $cmdline = "mksyscall_aix_ppc64.pl " . join(' ', @ARGV);
my $errors = 0;
my $_32bit = "";
my $tags = ""; # build tags
my $aix = 0;
my $solaris = 0;
binmode STDOUT;
if($ARGV[0] eq "-b32") {
$_32bit = "big-endian";
shift;
} elsif($ARGV[0] eq "-l32") {
$_32bit = "little-endian";
shift;
}
if($ARGV[0] eq "-aix") {
$aix = 1;
shift;
}
if($ARGV[0] eq "-tags") {
shift;
$tags = $ARGV[0];
shift;
}
if($ARGV[0] =~ /^-/) {
print STDERR "usage: mksyscall_aix.pl [-b32 | -l32] [-tags x,y] [file ...]\n";
exit 1;
}
sub parseparamlist($) {
my ($list) = @_;
$list =~ s/^\s*//;
$list =~ s/\s*$//;
if($list eq "") {
return ();
}
return split(/\s*,\s*/, $list);
}
sub parseparam($) {
my ($p) = @_;
if($p !~ /^(\S*) (\S*)$/) {
print STDERR "$ARGV:$.: malformed parameter: $p\n";
$errors = 1;
return ("xx", "int");
}
return ($1, $2);
}
my $package = "";
# GCCGO
my $textgccgo = "";
my $c_extern = "/*\n#include <stdint.h>\n";
# GC
my $textgc = "";
my $dynimports = "";
my $linknames = "";
my @vars = ();
# COMMUN
my $textcommon = "";
while(<>) {
chomp;
s/\s+/ /g;
s/^\s+//;
s/\s+$//;
$package = $1 if !$package && /^package (\S+)$/;
my $nonblock = /^\/\/sysnb /;
next if !/^\/\/sys / && !$nonblock;
# Line must be of the form
# func Open(path string, mode int, perm int) (fd int, err error)
# Split into name, in params, out params.
if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$/) {
print STDERR "$ARGV:$.: malformed //sys declaration\n";
$errors = 1;
next;
}
my ($nb, $func, $in, $out, $modname, $sysname) = ($1, $2, $3, $4, $5, $6);
# Split argument lists on comma.
my @in = parseparamlist($in);
my @out = parseparamlist($out);
$in = join(', ', @in);
$out = join(', ', @out);
if($sysname eq "") {
$sysname = "$func";
}
my $onlyCommon = 0;
if ($func eq "readlen" || $func eq "writelen" || $func eq "FcntlInt" || $func eq "FcntlFlock") {
# This function call another syscall which is already implemented.
# Therefore, the gc and gccgo part must not be generated.
$onlyCommon = 1
}
# Try in vain to keep people from editing this file.
# The theory is that they jump into the middle of the file
# without reading the header.
$textcommon .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n";
if (!$onlyCommon) {
$textgccgo .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n";
$textgc .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n";
}
# Check if value return, err return available
my $errvar = "";
my $retvar = "";
my $rettype = "";
foreach my $p (@out) {
my ($name, $type) = parseparam($p);
if($type eq "error") {
$errvar = $name;
} else {
$retvar = $name;
$rettype = $type;
}
}
$sysname =~ s/([a-z])([A-Z])/${1}_$2/g;
$sysname =~ y/A-Z/a-z/; # All libc functions are lowercase.
# GCCGO Prototype return type
my $C_rettype = "";
if($rettype eq "unsafe.Pointer") {
$C_rettype = "uintptr_t";
} elsif($rettype eq "uintptr") {
$C_rettype = "uintptr_t";
} elsif($rettype =~ /^_/) {
$C_rettype = "uintptr_t";
} elsif($rettype eq "int") {
$C_rettype = "int";
} elsif($rettype eq "int32") {
$C_rettype = "int";
} elsif($rettype eq "int64") {
$C_rettype = "long long";
} elsif($rettype eq "uint32") {
$C_rettype = "unsigned int";
} elsif($rettype eq "uint64") {
$C_rettype = "unsigned long long";
} else {
$C_rettype = "int";
}
if($sysname eq "exit") {
$C_rettype = "void";
}
# GCCGO Prototype arguments type
my @c_in = ();
foreach my $i (0 .. $#in) {
my ($name, $type) = parseparam($in[$i]);
if($type =~ /^\*/) {
push @c_in, "uintptr_t";
} elsif($type eq "string") {
push @c_in, "uintptr_t";
} elsif($type =~ /^\[\](.*)/) {
push @c_in, "uintptr_t", "size_t";
} elsif($type eq "unsafe.Pointer") {
push @c_in, "uintptr_t";
} elsif($type eq "uintptr") {
push @c_in, "uintptr_t";
} elsif($type =~ /^_/) {
push @c_in, "uintptr_t";
} elsif($type eq "int") {
if (($i == 0 || $i == 2) && $func eq "fcntl"){
# These fcntl arguments needs to be uintptr to be able to call FcntlInt and FcntlFlock
push @c_in, "uintptr_t";
} else {
push @c_in, "int";
}
} elsif($type eq "int32") {
push @c_in, "int";
} elsif($type eq "int64") {
push @c_in, "long long";
} elsif($type eq "uint32") {
push @c_in, "unsigned int";
} elsif($type eq "uint64") {
push @c_in, "unsigned long long";
} else {
push @c_in, "int";
}
}
if (!$onlyCommon){
# GCCGO Prototype Generation
# Imports of system calls from libc
$c_extern .= "$C_rettype $sysname";
my $c_in = join(', ', @c_in);
$c_extern .= "($c_in);\n";
}
# GC Library name
if($modname eq "") {
$modname = "libc.a/shr_64.o";
} else {
print STDERR "$func: only syscall using libc are available\n";
$errors = 1;
next;
}
my $sysvarname = "libc_${sysname}";
if (!$onlyCommon){
# GC Runtime import of function to allow cross-platform builds.
$dynimports .= "//go:cgo_import_dynamic ${sysvarname} ${sysname} \"$modname\"\n";
# GC Link symbol to proc address variable.
$linknames .= "//go:linkname ${sysvarname} ${sysvarname}\n";
# GC Library proc address variable.
push @vars, $sysvarname;
}
my $strconvfunc ="BytePtrFromString";
my $strconvtype = "*byte";
# Go function header.
if($out ne "") {
$out = " ($out)";
}
if($textcommon ne "") {
$textcommon .= "\n"
}
$textcommon .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out ;
# Prepare arguments to call.
my @argscommun = (); # Arguments in the commun part
my @argscall = (); # Arguments for call prototype
my @argsgc = (); # Arguments for gc call (with syscall6)
my @argsgccgo = (); # Arguments for gccgo call (with C.name_of_syscall)
my $n = 0;
my $arg_n = 0;
foreach my $p (@in) {
my ($name, $type) = parseparam($p);
if($type =~ /^\*/) {
push @argscommun, "uintptr(unsafe.Pointer($name))";
push @argscall, "$name uintptr";
push @argsgc, "$name";
push @argsgccgo, "C.uintptr_t($name)";
} elsif($type eq "string" && $errvar ne "") {
$textcommon .= "\tvar _p$n $strconvtype\n";
$textcommon .= "\t_p$n, $errvar = $strconvfunc($name)\n";
$textcommon .= "\tif $errvar != nil {\n\t\treturn\n\t}\n";
push @argscommun, "uintptr(unsafe.Pointer(_p$n))";
push @argscall, "_p$n uintptr ";
push @argsgc, "_p$n";
push @argsgccgo, "C.uintptr_t(_p$n)";
$n++;
} elsif($type eq "string") {
print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n";
$textcommon .= "\tvar _p$n $strconvtype\n";
$textcommon .= "\t_p$n, $errvar = $strconvfunc($name)\n";
$textcommon .= "\tif $errvar != nil {\n\t\treturn\n\t}\n";
push @argscommun, "uintptr(unsafe.Pointer(_p$n))";
push @argscall, "_p$n uintptr";
push @argsgc, "_p$n";
push @argsgccgo, "C.uintptr_t(_p$n)";
$n++;
} elsif($type =~ /^\[\](.*)/) {
# Convert slice into pointer, length.
# Have to be careful not to take address of &a[0] if len == 0:
# pass nil in that case.
$textcommon .= "\tvar _p$n *$1\n";
$textcommon .= "\tif len($name) > 0 {\n\t\t_p$n = \&$name\[0]\n\t}\n";
push @argscommun, "uintptr(unsafe.Pointer(_p$n))", "len($name)";
push @argscall, "_p$n uintptr", "_lenp$n int";
push @argsgc, "_p$n", "uintptr(_lenp$n)";
push @argsgccgo, "C.uintptr_t(_p$n)", "C.size_t(_lenp$n)";
$n++;
} elsif($type eq "int64" && $_32bit ne "") {
print STDERR "$ARGV:$.: $func uses int64 with 32 bits mode. Case not yet implemented\n";
# if($_32bit eq "big-endian") {
# push @args, "uintptr($name >> 32)", "uintptr($name)";
# } else {
# push @args, "uintptr($name)", "uintptr($name >> 32)";
# }
# $n++;
} elsif($type eq "bool") {
print STDERR "$ARGV:$.: $func uses bool. Case not yet implemented\n";
# $text .= "\tvar _p$n uint32\n";
# $text .= "\tif $name {\n\t\t_p$n = 1\n\t} else {\n\t\t_p$n = 0\n\t}\n";
# push @args, "_p$n";
# $n++;
} elsif($type =~ /^_/ ||$type eq "unsafe.Pointer") {
push @argscommun, "uintptr($name)";
push @argscall, "$name uintptr";
push @argsgc, "$name";
push @argsgccgo, "C.uintptr_t($name)";
} elsif($type eq "int") {
if (($arg_n == 0 || $arg_n == 2) && ($func eq "fcntl" || $func eq "FcntlInt" || $func eq "FcntlFlock")) {
# These fcntl arguments need to be uintptr to be able to call FcntlInt and FcntlFlock
push @argscommun, "uintptr($name)";
push @argscall, "$name uintptr";
push @argsgc, "$name";
push @argsgccgo, "C.uintptr_t($name)";
} else {
push @argscommun, "$name";
push @argscall, "$name int";
push @argsgc, "uintptr($name)";
push @argsgccgo, "C.int($name)";
}
} elsif($type eq "int32") {
push @argscommun, "$name";
push @argscall, "$name int32";
push @argsgc, "uintptr($name)";
push @argsgccgo, "C.int($name)";
} elsif($type eq "int64") {
push @argscommun, "$name";
push @argscall, "$name int64";
push @argsgc, "uintptr($name)";
push @argsgccgo, "C.longlong($name)";
} elsif($type eq "uint32") {
push @argscommun, "$name";
push @argscall, "$name uint32";
push @argsgc, "uintptr($name)";
push @argsgccgo, "C.uint($name)";
} elsif($type eq "uint64") {
push @argscommun, "$name";
push @argscall, "$name uint64";
push @argsgc, "uintptr($name)";
push @argsgccgo, "C.ulonglong($name)";
} elsif($type eq "uintptr") {
push @argscommun, "$name";
push @argscall, "$name uintptr";
push @argsgc, "$name";
push @argsgccgo, "C.uintptr_t($name)";
} else {
push @argscommun, "int($name)";
push @argscall, "$name int";
push @argsgc, "uintptr($name)";
push @argsgccgo, "C.int($name)";
}
$arg_n++;
}
my $nargs = @argsgc;
# COMMUN function generation
my $argscommun = join(', ', @argscommun);
my $callcommun = "call$sysname($argscommun)";
my @ret = ("_", "_");
my $body = "";
my $do_errno = 0;
for(my $i=0; $i<@out; $i++) {
my $p = $out[$i];
my ($name, $type) = parseparam($p);
my $reg = "";
if($name eq "err") {
$reg = "e1";
$ret[1] = $reg;
$do_errno = 1;
} else {
$reg = "r0";
$ret[0] = $reg;
}
if($type eq "bool") {
$reg = "$reg != 0";
}
if($reg ne "e1") {
$body .= "\t$name = $type($reg)\n";
}
}
if ($ret[0] eq "_" && $ret[1] eq "_") {
$textcommon .= "\t$callcommun\n";
} else {
$textcommon .= "\t$ret[0], $ret[1] := $callcommun\n";
}
$textcommon .= $body;
if ($do_errno) {
$textcommon .= "\tif e1 != 0 {\n";
$textcommon .= "\t\terr = errnoErr(e1)\n";
$textcommon .= "\t}\n";
}
$textcommon .= "\treturn\n";
$textcommon .= "}\n";
if ($onlyCommon){
next
}
# CALL Prototype
my $callProto = sprintf "func call%s(%s) (r1 uintptr, e1 Errno) {\n", $sysname, join(', ', @argscall);
# GC function generation
my $asm = "syscall6";
if ($nonblock) {
$asm = "rawSyscall6";
}
if(@argsgc <= 6) {
while(@argsgc < 6) {
push @argsgc, "0";
}
} else {
print STDERR "$ARGV:$.: too many arguments to system call\n";
}
my $argsgc = join(', ', @argsgc);
my $callgc = "$asm(uintptr(unsafe.Pointer(&$sysvarname)), $nargs, $argsgc)";
$textgc .= $callProto;
$textgc .= "\tr1, _, e1 = $callgc\n";
$textgc .= "\treturn\n}\n";
# GCCGO function generation
my $argsgccgo = join(', ', @argsgccgo);
my $callgccgo = "C.$sysname($argsgccgo)";
$textgccgo .= $callProto;
$textgccgo .= "\tr1 = uintptr($callgccgo)\n";
$textgccgo .= "\te1 = syscall.GetErrno()\n";
$textgccgo .= "\treturn\n}\n";
}
if($errors) {
exit 1;
}
# Print zsyscall_aix_ppc64.go
open(my $fcommun, '>', 'zsyscall_aix_ppc64.go');
my $tofcommun = <<EOF;
// $cmdline
// Code generated by the command above; see README.md. DO NOT EDIT.
// +build $tags
package $package
import (
"unsafe"
)
EOF
$tofcommun .= "import \"golang.org/x/sys/unix\"\n" if $package ne "unix";
$tofcommun .=<<EOF;
$textcommon
EOF
print $fcommun $tofcommun;
# Print zsyscall_aix_ppc64_gc.go
open(my $fgc, '>', 'zsyscall_aix_ppc64_gc.go');
my $tofgc = <<EOF;
// $cmdline
// Code generated by the command above; see README.md. DO NOT EDIT.
// +build $tags
// +build !gccgo
package $package
import (
"unsafe"
)
EOF
$tofgc .= "import \"golang.org/x/sys/unix\"\n" if $package ne "unix";
my $vardecls = "\t" . join(",\n\t", @vars);
$vardecls .= " syscallFunc";
$tofgc .=<<EOF;
$dynimports
$linknames
type syscallFunc uintptr
var (
$vardecls
)
// Implemented in runtime/syscall_aix.go.
func rawSyscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
func syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
$textgc
EOF
print $fgc $tofgc;
# Print zsyscall_aix_ppc64_gc.go
open(my $fgccgo, '>', 'zsyscall_aix_ppc64_gccgo.go');
my $tofgccgo = <<EOF;
// $cmdline
// Code generated by the command above; see README.md. DO NOT EDIT.
// +build $tags
// +build gccgo
package $package
$c_extern
*/
import "C"
import (
"syscall"
)
EOF
$tofgccgo .= "import \"golang.org/x/sys/unix\"\n" if $package ne "unix";
$tofgccgo .=<<EOF;
$textgccgo
EOF
print $fgccgo $tofgccgo;
exit 0;

View file

@ -106,7 +106,7 @@ func main() {
file := strings.TrimSpace(os.Args[1])
var syscalls io.Reader
if strings.HasPrefix(file, "http://") {
if strings.HasPrefix(file, "https://") || strings.HasPrefix(file, "http://") {
// Download syscalls.master file
syscalls = fetchFile(file)
} else {

View file

@ -227,7 +227,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
// Some versions of AIX have a bug in getsockname (see IV78655).
// We can't rely on sa.Len being set correctly.
n := SizeofSockaddrUnix - 3 // substract leading Family, Len, terminating NUL.
n := SizeofSockaddrUnix - 3 // subtract leading Family, Len, terminating NUL.
for i := 0; i < n; i++ {
if pp.Path[i] == 0 {
n = i

View file

@ -304,6 +304,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
//sys read(fd int, p []byte) (n int, err error)
//sys Readlink(path string, buf []byte) (n int, err error)
//sys Rename(from string, to string) (err error)
//sys Renameat(fromfd int, from string, tofd int, to string) (err error)
//sys Revoke(path string) (err error)
//sys Rmdir(path string) (err error)
//sys Seek(fd int, offset int64, whence int) (newoffset int64, err error) = SYS_LSEEK

View file

@ -14,6 +14,7 @@ package unix
import (
"encoding/binary"
"net"
"runtime"
"syscall"
"unsafe"
)
@ -80,6 +81,12 @@ func ioctlSetTermios(fd int, req uint, value *Termios) error {
return ioctl(fd, req, uintptr(unsafe.Pointer(value)))
}
func IoctlSetRTCTime(fd int, value *RTCTime) error {
err := ioctl(fd, RTC_SET_TIME, uintptr(unsafe.Pointer(value)))
runtime.KeepAlive(value)
return err
}
// IoctlGetInt performs an ioctl operation which gets an integer value
// from fd, using the specified request number.
func IoctlGetInt(fd int, req uint) (int, error) {
@ -100,6 +107,12 @@ func IoctlGetTermios(fd int, req uint) (*Termios, error) {
return &value, err
}
func IoctlGetRTCTime(fd int) (*RTCTime, error) {
var value RTCTime
err := ioctl(fd, RTC_RD_TIME, uintptr(unsafe.Pointer(&value)))
return &value, err
}
//sys Linkat(olddirfd int, oldpath string, newdirfd int, newpath string, flags int) (err error)
func Link(oldpath string, newpath string) (err error) {
@ -1381,6 +1394,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
//sys Chroot(path string) (err error)
//sys ClockGetres(clockid int32, res *Timespec) (err error)
//sys ClockGettime(clockid int32, time *Timespec) (err error)
//sys ClockNanosleep(clockid int32, flags int, request *Timespec, remain *Timespec) (err error)
//sys Close(fd int) (err error)
//sys CopyFileRange(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int, err error)
//sys DeleteModule(name string, flags int) (err error)
@ -1441,7 +1455,6 @@ func Getpgrp() (pid int) {
//sys Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) = SYS_PSELECT6
//sys read(fd int, p []byte) (n int, err error)
//sys Removexattr(path string, attr string) (err error)
//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error)
//sys Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) (err error)
//sys RequestKey(keyType string, description string, callback string, destRingid int) (id int, err error)
//sys Setdomainname(p []byte) (err error)
@ -1466,6 +1479,7 @@ func Setgid(uid int) (err error) {
//sys Setpriority(which int, who int, prio int) (err error)
//sys Setxattr(path string, attr string, data []byte, flags int) (err error)
//sys Signalfd(fd int, mask *Sigset_t, flags int) = SYS_SIGNALFD4
//sys Statx(dirfd int, path string, flags int, mask int, stat *Statx_t) (err error)
//sys Sync()
//sys Syncfs(fd int) (err error)
@ -1682,7 +1696,6 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
// Shmdt
// Shmget
// Sigaltstack
// Signalfd
// Swapoff
// Swapon
// Sysfs

View file

@ -68,6 +68,7 @@ func Pipe2(p []int, flags int) (err error) {
//sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64
//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64
//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error)
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64
//sys Setfsgid(gid int) (err error) = SYS_SETFSGID32
//sys Setfsuid(uid int) (err error) = SYS_SETFSUID32

View file

@ -43,6 +43,7 @@ func Lstat(path string, stat *Stat_t) (err error) {
//sys Pause() (err error)
//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64
//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error)
//sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK
func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) {

View file

@ -89,6 +89,7 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
//sys Listen(s int, n int) (err error)
//sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64
//sys Pause() (err error)
//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error)
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64
//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) = SYS__NEWSELECT
//sys Setfsgid(gid int) (err error) = SYS_SETFSGID32

View file

@ -30,6 +30,7 @@ func EpollCreate(size int) (fd int, err error) {
//sys Listen(s int, n int) (err error)
//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64
//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error)
//sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK
func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) {

View file

@ -24,6 +24,7 @@ package unix
//sys Pause() (err error)
//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64
//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error)
//sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK
func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) {

View file

@ -28,6 +28,7 @@ func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr,
//sys Listen(s int, n int) (err error)
//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64
//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error)
//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) = SYS__NEWSELECT
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64
//sys Setfsgid(gid int) (err error)

View file

@ -30,6 +30,7 @@ package unix
//sys Pause() (err error)
//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64
//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error)
//sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK
//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) = SYS__NEWSELECT
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)

View file

@ -207,3 +207,7 @@ func Poll(fds []PollFd, timeout int) (n int, err error) {
}
return ppoll(&fds[0], len(fds), ts, nil)
}
func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) {
return Renameat2(olddirfd, oldpath, newdirfd, newpath, 0)
}

View file

@ -30,6 +30,7 @@ import (
//sys Pause() (err error)
//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64
//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error)
//sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK
//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error)
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)

View file

@ -26,6 +26,7 @@ package unix
//sys Pause() (err error)
//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64
//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error)
//sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK
//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error)
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)

33
vendor/golang.org/x/sys/unix/syscall_netbsd_arm64.go generated vendored Normal file
View file

@ -0,0 +1,33 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build arm64,netbsd
package unix
func setTimespec(sec, nsec int64) Timespec {
return Timespec{Sec: sec, Nsec: nsec}
}
func setTimeval(sec, usec int64) Timeval {
return Timeval{Sec: sec, Usec: int32(usec)}
}
func SetKevent(k *Kevent_t, fd, mode, flags int) {
k.Ident = uint64(fd)
k.Filter = uint32(mode)
k.Flags = uint32(flags)
}
func (iov *Iovec) SetLen(length int) {
iov.Len = uint64(length)
}
func (msghdr *Msghdr) SetControllen(length int) {
msghdr.Controllen = uint32(length)
}
func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint32(length)
}

Some files were not shown because too many files have changed in this diff Show more