<commit_before>// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gitindex

import (
	"fmt"
	"log"
	"net/url"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/google/zoekt"
	"github.com/google/zoekt/build"

	git "github.com/libgit2/git2go"
)

// RepoModTime returns the time of last fetch of a git repository.
func RepoModTime(dir string) (time.Time, error) {
	var last time.Time
	refDir := filepath.Join(dir, "refs")
	if _, err := os.Lstat(refDir); err == nil {
		if err := filepath.Walk(refDir,
			func(name string, fi os.FileInfo, err error) error {
				if !fi.IsDir() && last.Before(fi.ModTime()) {
					last = fi.ModTime()
				}
				return nil
			}); err != nil {
			return last, err
		}
	}

	// git gc compresses refs into the following file:
	for _, fn := range []string{"info/refs", "packed-refs"} {
		if fi, err := os.Lstat(filepath.Join(dir, fn)); err == nil && !fi.IsDir() && last.Before(fi.ModTime()) {
			last = fi.ModTime()
		}
	}

	return last, nil
}

// FindGitRepos finds git repositories and returns repodir => name map.
func FindGitRepos(arg string) (map[string]string, error) {
	arg, err := filepath.Abs(arg)
	if err != nil {
		return nil, err
	}
	var dirs []string
	gitDirs := map[string]string{}
	if err := filepath.Walk(arg, func(name string, fi os.FileInfo, err error) error {
		if fi, err := os.Lstat(filepath.Join(name, ".git")); err == nil && fi.IsDir() {
			dirs = append(dirs, filepath.Join(name, ".git"))
			return filepath.SkipDir
		}

		if !strings.HasSuffix(name, ".git") || !fi.IsDir() {
			return nil
		}

		fi, err = os.Lstat(filepath.Join(name, "objects"))
		if err != nil || !fi.IsDir() {
			return nil
		}

		dirs = append(dirs, name)
		return filepath.SkipDir
	}); err != nil {
		return nil, err
	}

	for _, dir := range dirs {
		name := strings.TrimSuffix(dir, ".git")
		name = strings.TrimSuffix(name, "/")
		name = strings.TrimPrefix(name, arg)
		name = strings.TrimPrefix(name, "/")
		gitDirs[dir] = name
	}

	return gitDirs, nil
}

type templates struct {
	repo, commit, file, line string
}

// guessRepoURL guesses the URL template for a repo mirrored from a
// well-known git hosting site.
func guessRepoURL(repoDir string) (*templates, error) {
	base, err := git.NewConfig()
	if err != nil {
		return nil, err
	}
	cfg, err := git.OpenOndisk(base, filepath.Join(repoDir, "config"))
	if err != nil {
		return nil, err
	}

	remoteURL, err := cfg.LookupString("remote.origin.url")
	if err != nil {
		return nil, err
	}

	parsed, err := url.Parse(remoteURL)
	if err != nil {
		return nil, err
	}

	if strings.HasSuffix(parsed.Host, "googlesource.com") {
		/// eg. https://gerrit.googlesource.com/gitiles/+/master/tools/run_dev.sh#20
		return &templates{
			repo:   remoteURL,
			commit: remoteURL + "/+/{{.Version}}",
			file:   remoteURL + "/+/{{.Branch}}/{{.Path}}",
			line:   "{{.LineNumber}}",
		}, nil
	} else if parsed.Host == "github.com" {
		// CloneURL from the JSON API has .git
		parsed.Path = strings.TrimSuffix(parsed.Path, ".git")

		// eg. https://github.com/hanwen/go-fuse/blob/notify/genversion.sh#L10
		return &templates{
			repo:   parsed.String(),
			commit: parsed.String() + "/commit/{{.Version}}",
			file:   parsed.String() + "/blob/{{.Branch}}/{{.Path}}",
			line:   "L{{.LineNumber}}",
		}, nil
	}

	return nil, fmt.Errorf("scheme unknown for URL %s", remoteURL)
}

// getCommit returns a tree object for the given reference.
func getCommit(repo *git.Repository, ref string) (*git.Commit, error) {
	obj, err := repo.RevparseSingle(ref)
	if err != nil {
		return nil, err
	}
	defer obj.Free()

	commitObj, err := obj.Peel(git.ObjectCommit)
	if err != nil {
		return nil, err
	}
	return commitObj.AsCommit()
}

// IndexGitRepo indexes the git repository as specified by the options and arguments.
func IndexGitRepo(opts build.Options, branchPrefix string, branches []string, submodules bool) error {
	repo, err := git.OpenRepository(opts.RepoDir)
	if err != nil {
		return err
	}

	if tpl, err := guessRepoURL(opts.RepoDir); err != nil {
		log.Printf("guessRepoURL(%s): %s", opts.RepoDir, err)
	} else {
		opts.RepoURL = tpl.repo
		opts.FileURLTemplate = tpl.file
		opts.CommitURLTemplate = tpl.commit
		opts.LineFragmentTemplate = tpl.line
	}

	// name => branch
	allfiles := map[string][]string{}

	var names []string

	// branch => name => sha1
	data := map[string]map[string]git.Oid{}
	repos := map[git.Oid]*git.Repository{}
	for _, b := range branches {
		commit, err := getCommit(repo, filepath.Join(branchPrefix, b))
		if err != nil {
			return err
		}
		opts.Branches = append(opts.Branches, build.Branch{
			Name:    b,
			Version: commit.Id().String(),
		})

		tree, err := commit.Tree()
		if err != nil {
			return err
		}

		fs, subRepos, err := treeToFiles(repo, tree, submodules)
		if err != nil {
			return err
		}
		for k, v := range subRepos {
			repos[k] = v
		}

		for f := range fs {
			allfiles[f] = append(allfiles[f], b)
		}
		data[b] = fs
	}

	builder, err := build.NewBuilder(opts)
	if err != nil {
		return err
	}

	for n := range allfiles {
		names = append(names, n)
	}
	sort.Strings(names)

	for _, n := range names {
		shas := map[git.Oid][]string{}
		for _, b := range allfiles[n] {
			shas[data[b][n]] = append(shas[data[b][n]], b)
		}

		for sha, branches := range shas {
			r := repos[sha]
			if r == nil {
				return fmt.Errorf("no repo found for %s (%s)", n, branches)
			}
			blob, err := r.LookupBlob(&sha)
			if err != nil {
				return err
			}

			if blob.Size() > int64(opts.SizeMax) {
				continue
			}

			builder.Add(zoekt.Document{
				Name:     n,
				Content:  blob.Contents(),
				Branches: branches,
			})
		}
	}
	builder.Finish()

	return nil
}
<commit_msg>git: plug configuration mem leaks.<commit_after>// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gitindex

import (
	"fmt"
	"log"
	"net/url"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/google/zoekt"
	"github.com/google/zoekt/build"

	git "github.com/libgit2/git2go"
)

// RepoModTime returns the time of last fetch of a git repository.
func RepoModTime(dir string) (time.Time, error) {
	var last time.Time
	refDir := filepath.Join(dir, "refs")
	if _, err := os.Lstat(refDir); err == nil {
		if err := filepath.Walk(refDir,
			func(name string, fi os.FileInfo, err error) error {
				if !fi.IsDir() && last.Before(fi.ModTime()) {
					last = fi.ModTime()
				}
				return nil
			}); err != nil {
			return last, err
		}
	}

	// git gc compresses refs into the following file:
	for _, fn := range []string{"info/refs", "packed-refs"} {
		if fi, err := os.Lstat(filepath.Join(dir, fn)); err == nil && !fi.IsDir() && last.Before(fi.ModTime()) {
			last = fi.ModTime()
		}
	}

	return last, nil
}

// FindGitRepos finds git repositories and returns repodir => name map.
func FindGitRepos(arg string) (map[string]string, error) {
	arg, err := filepath.Abs(arg)
	if err != nil {
		return nil, err
	}
	var dirs []string
	gitDirs := map[string]string{}
	if err := filepath.Walk(arg, func(name string, fi os.FileInfo, err error) error {
		if fi, err := os.Lstat(filepath.Join(name, ".git")); err == nil && fi.IsDir() {
			dirs = append(dirs, filepath.Join(name, ".git"))
			return filepath.SkipDir
		}

		if !strings.HasSuffix(name, ".git") || !fi.IsDir() {
			return nil
		}

		fi, err = os.Lstat(filepath.Join(name, "objects"))
		if err != nil || !fi.IsDir() {
			return nil
		}

		dirs = append(dirs, name)
		return filepath.SkipDir
	}); err != nil {
		return nil, err
	}

	for _, dir := range dirs {
		name := strings.TrimSuffix(dir, ".git")
		name = strings.TrimSuffix(name, "/")
		name = strings.TrimPrefix(name, arg)
		name = strings.TrimPrefix(name, "/")
		gitDirs[dir] = name
	}

	return gitDirs, nil
}

type templates struct {
	repo, commit, file, line string
}

// guessRepoURL guesses the URL template for a repo mirrored from a
// well-known git hosting site.
func guessRepoURL(repoDir string) (*templates, error) {
	base, err := git.NewConfig()
	if err != nil {
		return nil, err
	}
	defer base.Free()
	cfg, err := git.OpenOndisk(base, filepath.Join(repoDir, "config"))
	if err != nil {
		return nil, err
	}
	defer cfg.Free()

	remoteURL, err := cfg.LookupString("remote.origin.url")
	if err != nil {
		return nil, err
	}

	parsed, err := url.Parse(remoteURL)
	if err != nil {
		return nil, err
	}

	if strings.HasSuffix(parsed.Host, "googlesource.com") {
		/// eg. https://gerrit.googlesource.com/gitiles/+/master/tools/run_dev.sh#20
		return &templates{
			repo:   remoteURL,
			commit: remoteURL + "/+/{{.Version}}",
			file:   remoteURL + "/+/{{.Branch}}/{{.Path}}",
			line:   "{{.LineNumber}}",
		}, nil
	} else if parsed.Host == "github.com" {
		// CloneURL from the JSON API has .git
		parsed.Path = strings.TrimSuffix(parsed.Path, ".git")

		// eg. https://github.com/hanwen/go-fuse/blob/notify/genversion.sh#L10
		return &templates{
			repo:   parsed.String(),
			commit: parsed.String() + "/commit/{{.Version}}",
			file:   parsed.String() + "/blob/{{.Branch}}/{{.Path}}",
			line:   "L{{.LineNumber}}",
		}, nil
	}

	return nil, fmt.Errorf("scheme unknown for URL %s", remoteURL)
}

// getCommit returns a tree object for the given reference.
func getCommit(repo *git.Repository, ref string) (*git.Commit, error) {
	obj, err := repo.RevparseSingle(ref)
	if err != nil {
		return nil, err
	}
	defer obj.Free()

	commitObj, err := obj.Peel(git.ObjectCommit)
	if err != nil {
		return nil, err
	}
	return commitObj.AsCommit()
}

// IndexGitRepo indexes the git repository as specified by the options and arguments.
func IndexGitRepo(opts build.Options, branchPrefix string, branches []string, submodules bool) error {
	repo, err := git.OpenRepository(opts.RepoDir)
	if err != nil {
		return err
	}

	if tpl, err := guessRepoURL(opts.RepoDir); err != nil {
		log.Printf("guessRepoURL(%s): %s", opts.RepoDir, err)
	} else {
		opts.RepoURL = tpl.repo
		opts.FileURLTemplate = tpl.file
		opts.CommitURLTemplate = tpl.commit
		opts.LineFragmentTemplate = tpl.line
	}

	// name => branch
	allfiles := map[string][]string{}

	var names []string

	// branch => name => sha1
	data := map[string]map[string]git.Oid{}
	repos := map[git.Oid]*git.Repository{}
	for _, b := range branches {
		commit, err := getCommit(repo, filepath.Join(branchPrefix, b))
		if err != nil {
			return err
		}
		opts.Branches = append(opts.Branches, build.Branch{
			Name:    b,
			Version: commit.Id().String(),
		})

		tree, err := commit.Tree()
		if err != nil {
			return err
		}

		fs, subRepos, err := treeToFiles(repo, tree, submodules)
		if err != nil {
			return err
		}
		for k, v := range subRepos {
			repos[k] = v
		}

		for f := range fs {
			allfiles[f] = append(allfiles[f], b)
		}
		data[b] = fs
	}

	builder, err := build.NewBuilder(opts)
	if err != nil {
		return err
	}

	for n := range allfiles {
		names = append(names, n)
	}
	sort.Strings(names)

	for _, n := range names {
		shas := map[git.Oid][]string{}
		for _, b := range allfiles[n] {
			shas[data[b][n]] = append(shas[data[b][n]], b)
		}

		for sha, branches := range shas {
			r := repos[sha]
			if r == nil {
				return fmt.Errorf("no repo found for %s (%s)", n, branches)
			}
			blob, err := r.LookupBlob(&sha)
			if err != nil {
				return err
			}

			if blob.Size() > int64(opts.SizeMax) {
				continue
			}

			builder.Add(zoekt.Document{
				Name:     n,
				Content:  blob.Contents(),
				Branches: branches,
			})
		}
	}
	builder.Finish()

	return nil
}
<|endoftext|>


<commit_before>/*
 * Copyright 2017 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cli

import (
	"context"
	"flag"
	"strings"

	xpb "kythe.io/kythe/proto/xref_proto"
)

type docsCommand struct {
	nodeFilters     string
	includeChildren bool
}

func (docsCommand) Name() string     { return "docs" }
func (docsCommand) Synopsis() string { return "display documentation for a node" }
func (docsCommand) Usage() string    { return "" }
func (c *docsCommand) SetFlags(flag *flag.FlagSet) {
	flag.StringVar(&c.nodeFilters, "filters", "", "Comma-separated list of node fact filters (default returns all)")
	flag.BoolVar(&c.includeChildren, "include_children", false, "Include documentation for children of the given node")
}
func (c docsCommand) Run(ctx context.Context, flag *flag.FlagSet, api API) error {
	req := &xpb.DocumentationRequest{
		Ticket:          flag.Args(),
		IncludeChildren: c.includeChildren,
	}
	if c.nodeFilters != "" {
		req.Filter = strings.Split(c.nodeFilters, ",")
	}
	LogRequest(req)
	reply, err := api.XRefService.Documentation(ctx, req)
	if err != nil {
		return err
	}
	return c.displayDocumentation(reply)
}

func (c docsCommand) displayDocumentation(reply *xpb.DocumentationReply) error {
	// TODO(zarko): Emit formatted data for -json=false.
	return PrintJSONMessage(reply)
}
<commit_msg>kythe cli: simple textual documentation rendering<commit_after>/*
 * Copyright 2017 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cli

import (
	"context"
	"flag"
	"fmt"
	"log"
	"strings"

	xpb "kythe.io/kythe/proto/xref_proto"
)

type docsCommand struct {
	nodeFilters     string
	includeChildren bool
}

func (docsCommand) Name() string     { return "docs" }
func (docsCommand) Synopsis() string { return "display documentation for a node" }
func (docsCommand) Usage() string    { return "" }
func (c *docsCommand) SetFlags(flag *flag.FlagSet) {
	flag.StringVar(&c.nodeFilters, "filters", "", "Comma-separated list of node fact filters (default returns all)")
	flag.BoolVar(&c.includeChildren, "include_children", false, "Include documentation for children of the given node")
}
func (c docsCommand) Run(ctx context.Context, flag *flag.FlagSet, api API) error {
	req := &xpb.DocumentationRequest{
		Ticket:          flag.Args(),
		IncludeChildren: c.includeChildren,
	}
	if c.nodeFilters != "" {
		req.Filter = strings.Split(c.nodeFilters, ",")
	}
	LogRequest(req)
	reply, err := api.XRefService.Documentation(ctx, req)
	if err != nil {
		return err
	}
	return c.displayDocumentation(reply)
}

func findLinkText(rawText string) []string {
	var linkText []string
	var current []int
	var invalid bool
	for i := 0; i < len(rawText); i++ {
		c := rawText[i]
		switch c {
		case '[':
			current = append(current, len(linkText))
			linkText = append(linkText, "")
		case ']':
			if len(current) == 0 {
				invalid = true
				continue
			}
			current = current[:len(current)-1]
		default:
			if c == '\\' {
				if i+1 >= len(rawText) {
					invalid = true
					continue
				}
				i++
				c = rawText[i]
			}
			for _, l := range current {
				linkText[l] += string(c)
			}
		}
	}
	if invalid {
		log.Printf("WARNING: invalid document raw text: %q", rawText)
	}
	return linkText
}

func displayDoc(indent string, doc *xpb.DocumentationReply_Document) {
	fmt.Println(indent + showSignature(doc.MarkedSource))
	if len(doc.Text.GetRawText()) > 0 {
		fmt.Println(indent + doc.Text.RawText)
		linkText := findLinkText(doc.Text.RawText)
		for i, link := range doc.Text.Link {
			if i >= len(linkText) {
				log.Printf("WARNING: mismatch between raw text and number of links: %v", doc)
				break
			}
			if len(link.Definition) > 0 {
				fmt.Printf("%s    %s: %s\n", indent, linkText[i], strings.Join(link.Definition, "\t"))
			}
		}
	}

	for _, child := range doc.Children {
		fmt.Println()
		displayDoc(indent+"  ", child)
	}
}

func (c docsCommand) displayDocumentation(reply *xpb.DocumentationReply) error {
	if DisplayJSON {
		return PrintJSONMessage(reply)
	} else if len(reply.Document) == 0 {
		return nil
	}

	displayDoc("", reply.Document[0])
	for _, doc := range reply.Document[1:] {
		fmt.Println()
		displayDoc("", doc)
	}
	return nil
}
<|endoftext|>


<commit_before>// Package errutil provides common error handling tools
// NOTE: Subject to change, do not rely on this package from outside git-lfs source
package errutil

// The LFS error system provides a simple wrapper around Go errors and the
// ability to inspect errors. It is strongly influenced by Dave Cheney's post
// at http://dave.cheney.net/2014/12/24/inspecting-errors.
//
// When passing errors out of lfs package functions, the return type should
// always be `error`. The wrappedError details are not exported. If an error is
// the kind of error a caller should need to investigate, an IsXError()
// function is provided that tells the caller if the error is of that type.
// There should only be a handfull of cases where a simple `error` is
// insufficient.
//
// The error behaviors can be nested when created. For example, the not
// implemented error can also be marked as a fatal error:
//
//	func LfsFunction() error {
//		err := functionCall()
//		if err != nil {
//			return newFatalError(newNotImplementedError(err))
//		}
//		return nil
//	}
//
// Then in the caller:
//
//	err := lfs.LfsFunction()
//	if lfs.IsNotImplementedError(err) {
//		log.Print("feature not implemented")
//	}
//	if lfs.IsFatalError(err) {
//		os.Exit(1)
//	}
//
// Wrapped errors contain a context, which is a map[string]string. These
// contexts can be accessed through the Error*Context functions. Calling these
// functions on a regular Go error will have no effect.
//
// Example:
//
//	err := lfs.SomeFunction()
//	errutil.ErrorSetContext(err, "foo", "bar")
//	errutil.ErrorGetContext(err, "foo") // => "bar"
//	errutil.ErrorDelContext(err, "foo")
//
// Wrapped errors also contain the stack from the point at which they are
// called. The stack is accessed via ErrorStack(). Calling ErrorStack() on a
// regular Go error will return an empty byte slice.

import (
	"fmt"
	"reflect"
	"runtime"

	"github.com/pkg/errors"
)

type errorWithCause interface {
	Error() string
	Cause() error
}

func parentOf(err error) error {
	if c, ok := err.(errorWithCause); ok {
		return c.Cause()
	}

	return nil
}

// IsFatalError indicates that the error is fatal and the process should exit
// immediately after handling the error.
func IsFatalError(err error) bool {
	fmt.Println(err, reflect.TypeOf(err))
	if e, ok := err.(interface {
		Fatal() bool
	}); ok {
		return e.Fatal()
	}
	if parent := parentOf(err); parent != nil {
		return IsFatalError(parent)
	}
	return false
}

// IsNotImplementedError indicates the client attempted to use a feature the
// server has not implemented (e.g. the batch endpoint).
func IsNotImplementedError(err error) bool {
	if e, ok := err.(interface {
		NotImplemented() bool
	}); ok {
		return e.NotImplemented()
	}
	if parent := parentOf(err); parent != nil {
		return IsNotImplementedError(parent)
	}
	return false
}

// IsAuthError indicates the client provided a request with invalid or no
// authentication credentials when credentials are required (e.g. HTTP 401).
func IsAuthError(err error) bool {
	if e, ok := err.(interface {
		AuthError() bool
	}); ok {
		return e.AuthError()
	}
	if parent := parentOf(err); parent != nil {
		return IsAuthError(parent)
	}
	return false
}

// IsInvalidPointerError indicates an attempt to parse data that was not a
// valid pointer.
func IsInvalidPointerError(err error) bool {
	if e, ok := err.(interface {
		InvalidPointer() bool
	}); ok {
		return e.InvalidPointer()
	}
	if parent := parentOf(err); parent != nil {
		return IsInvalidPointerError(parent)
	}
	return false
}

// IsInvalidRepoError indicates an operation was attempted from outside a git
// repository.
func IsInvalidRepoError(err error) bool {
	if e, ok := err.(interface {
		InvalidRepo() bool
	}); ok {
		return e.InvalidRepo()
	}
	if parent := parentOf(err); parent != nil {
		return IsInvalidRepoError(parent)
	}
	return false
}

// IsSmudgeError indicates an error while smudging a files.
func IsSmudgeError(err error) bool {
	if e, ok := err.(interface {
		SmudgeError() bool
	}); ok {
		return e.SmudgeError()
	}
	if parent := parentOf(err); parent != nil {
		return IsSmudgeError(parent)
	}
	return false
}

// IsCleanPointerError indicates an error while cleaning a file.
func IsCleanPointerError(err error) bool {
	if e, ok := err.(interface {
		CleanPointerError() bool
	}); ok {
		return e.CleanPointerError()
	}
	if parent := parentOf(err); parent != nil {
		return IsCleanPointerError(parent)
	}
	return false
}

// IsNotAPointerError indicates the parsed data is not an LFS pointer.
func IsNotAPointerError(err error) bool {
	if e, ok := err.(interface {
		NotAPointerError() bool
	}); ok {
		return e.NotAPointerError()
	}
	if parent := parentOf(err); parent != nil {
		return IsNotAPointerError(parent)
	}
	return false
}

// IsBadPointerKeyError indicates that the parsed data has an invalid key.
func IsBadPointerKeyError(err error) bool {
	if e, ok := err.(interface {
		BadPointerKeyError() bool
	}); ok {
		return e.BadPointerKeyError()
	}

	if parent := parentOf(err); parent != nil {
		return IsBadPointerKeyError(parent)
	}
	return false
}

// If an error is abad pointer error of any type, returns NotAPointerError
func StandardizeBadPointerError(err error) error {
	if IsBadPointerKeyError(err) {
		badErr := err.(badPointerKeyError)
		if badErr.Expected == "version" {
			return NewNotAPointerError(err)
		}
	}
	return err
}

// IsDownloadDeclinedError indicates that the smudge operation should not download.
// TODO: I don't really like using errors to control that flow, it should be refactored.
func IsDownloadDeclinedError(err error) bool {
	if e, ok := err.(interface {
		DownloadDeclinedError() bool
	}); ok {
		return e.DownloadDeclinedError()
	}
	if parent := parentOf(err); parent != nil {
		return IsDownloadDeclinedError(parent)
	}
	return false
}

// IsRetriableError indicates the low level transfer had an error but the
// caller may retry the operation.
func IsRetriableError(err error) bool {
	if e, ok := err.(interface {
		RetriableError() bool
	}); ok {
		return e.RetriableError()
	}
	if parent := parentOf(err); parent != nil {
		return IsRetriableError(parent)
	}
	return false
}

func GetInnerError(err error) error {
	if parent := parentOf(err); parent != nil {
		return parent
	}

	return nil
}

// Error wraps an error with an empty message.
func Error(err error) error {
	return Errorf(err, "")
}

// Errorf wraps an error with an additional formatted message.
func Errorf(err error, format string, args ...interface{}) error {
	if err == nil {
		err = errors.New("")
	}

	message := ""
	if len(format) > 0 {
		message = fmt.Sprintf(format, args...)
	}

	return newWrappedError(err, message)
}

// ErrorSetContext sets a value in the error's context. If the error has not
// been wrapped, it does nothing.
func ErrorSetContext(err error, key string, value interface{}) {
	if e, ok := err.(errorWrapper); ok {
		e.Set(key, value)
	}
}

// ErrorGetContext gets a value from the error's context. If the error has not
// been wrapped, it returns an empty string.
func ErrorGetContext(err error, key string) interface{} {
	if e, ok := err.(errorWrapper); ok {
		return e.Get(key)
	}
	return ""
}

// ErrorDelContext removes a value from the error's context. If the error has
// not been wrapped, it does nothing.
func ErrorDelContext(err error, key string) {
	if e, ok := err.(errorWrapper); ok {
		e.Del(key)
	}
}

// ErrorContext returns the context map for an error if it is a wrappedError.
// If it is not a wrappedError it will return an empty map.
func ErrorContext(err error) map[string]interface{} {
	if e, ok := err.(errorWrapper); ok {
		return e.Context()
	}
	return nil
}

type errorWrapper interface {
	errorWithCause

	Set(string, interface{})
	Get(string) interface{}
	Del(string)
	Context() map[string]interface{}
}

// wrappedError is the base error wrapper. It provides a Message string, a
// stack, and a context map around a regular Go error.
type wrappedError struct {
	errorWithCause
	context map[string]interface{}
}

// newWrappedError creates a wrappedError.
func newWrappedError(err error, message string) errorWrapper {
	if err == nil {
		err = errors.New("Error")
	}

	var errWithCause errorWithCause

	if len(message) > 0 {
		errWithCause = errors.Wrap(err, message).(errorWithCause)
	} else if ewc, ok := err.(errorWithCause); ok {
		errWithCause = ewc
	} else {
		errWithCause = errors.Wrap(err, "LFS").(errorWithCause)
	}

	return &wrappedError{
		context:        make(map[string]interface{}),
		errorWithCause: errWithCause,
	}
}

// Set sets the value for the key in the context.
func (e wrappedError) Set(key string, val interface{}) {
	e.context[key] = val
}

// Get gets the value for a key in the context.
func (e wrappedError) Get(key string) interface{} {
	return e.context[key]
}

// Del removes a key from the context.
func (e wrappedError) Del(key string) {
	delete(e.context, key)
}

// Context returns the underlying context.
func (e wrappedError) Context() map[string]interface{} {
	return e.context
}

// Definitions for IsFatalError()

type fatalError struct {
	errorWrapper
}

func (e fatalError) Fatal() bool {
	return true
}

func NewFatalError(err error) error {
	return fatalError{newWrappedError(err, "Fatal error")}
}

// Definitions for IsNotImplementedError()

type notImplementedError struct {
	errorWrapper
}

func (e notImplementedError) NotImplemented() bool {
	return true
}

func NewNotImplementedError(err error) error {
	return notImplementedError{newWrappedError(err, "Not implemented")}
}

// Definitions for IsAuthError()

type authError struct {
	errorWrapper
}

func (e authError) AuthError() bool {
	return true
}

func NewAuthError(err error) error {
	return authError{newWrappedError(err, "Authentication required")}
}

// Definitions for IsInvalidPointerError()

type invalidPointerError struct {
	errorWrapper
}

func (e invalidPointerError) InvalidPointer() bool {
	return true
}

func NewInvalidPointerError(err error) error {
	return invalidPointerError{newWrappedError(err, "Invalid pointer")}
}

// Definitions for IsInvalidRepoError()

type invalidRepoError struct {
	errorWrapper
}

func (e invalidRepoError) InvalidRepo() bool {
	return true
}

func NewInvalidRepoError(err error) error {
	return invalidRepoError{newWrappedError(err, "Not in a git repository")}
}

// Definitions for IsSmudgeError()

type smudgeError struct {
	errorWrapper
}

func (e smudgeError) SmudgeError() bool {
	return true
}

func NewSmudgeError(err error, oid, filename string) error {
	e := smudgeError{newWrappedError(err, "Smudge error")}
	ErrorSetContext(e, "OID", oid)
	ErrorSetContext(e, "FileName", filename)
	return e
}

// Definitions for IsCleanPointerError()

type cleanPointerError struct {
	errorWrapper
}

func (e cleanPointerError) CleanPointerError() bool {
	return true
}

func NewCleanPointerError(err error, pointer interface{}, bytes []byte) error {
	e := cleanPointerError{newWrappedError(err, "Clean pointer error")}
	ErrorSetContext(e, "pointer", pointer)
	ErrorSetContext(e, "bytes", bytes)
	return e
}

// Definitions for IsNotAPointerError()

type notAPointerError struct {
	errorWrapper
}

func (e notAPointerError) NotAPointerError() bool {
	return true
}

func NewNotAPointerError(err error) error {
	return notAPointerError{newWrappedError(err, "Not a valid Git LFS pointer file.")}
}

type badPointerKeyError struct {
	Expected string
	Actual   string

	errorWrapper
}

func (e badPointerKeyError) BadPointerKeyError() bool {
	return true
}

func NewBadPointerKeyError(expected, actual string) error {
	err := fmt.Errorf("Error parsing LFS Pointer. Expected key %s, got %s", expected, actual)
	return badPointerKeyError{expected, actual, newWrappedError(err, "")}
}

// Definitions for IsDownloadDeclinedError()

type downloadDeclinedError struct {
	errorWrapper
}

func (e downloadDeclinedError) DownloadDeclinedError() bool {
	return true
}

func NewDownloadDeclinedError(err error) error {
	return downloadDeclinedError{newWrappedError(err, "File missing and download is not allowed")}
}

// Definitions for IsRetriableError()

type retriableError struct {
	errorWrapper
}

func (e retriableError) RetriableError() bool {
	return true
}

func NewRetriableError(err error) error {
	return retriableError{newWrappedError(err, "")}
}

// Stack returns a byte slice containing the runtime.Stack()
func Stack() []byte {
	stackBuf := make([]byte, 1024*1024)
	written := runtime.Stack(stackBuf, false)
	return stackBuf[:written]
}
<commit_msg>remove debug msg<commit_after>// Package errutil provides common error handling tools
// NOTE: Subject to change, do not rely on this package from outside git-lfs source
package errutil

// The LFS error system provides a simple wrapper around Go errors and the
// ability to inspect errors. It is strongly influenced by Dave Cheney's post
// at http://dave.cheney.net/2014/12/24/inspecting-errors.
//
// When passing errors out of lfs package functions, the return type should
// always be `error`. The wrappedError details are not exported. If an error is
// the kind of error a caller should need to investigate, an IsXError()
// function is provided that tells the caller if the error is of that type.
// There should only be a handfull of cases where a simple `error` is
// insufficient.
//
// The error behaviors can be nested when created. For example, the not
// implemented error can also be marked as a fatal error:
//
//	func LfsFunction() error {
//		err := functionCall()
//		if err != nil {
//			return newFatalError(newNotImplementedError(err))
//		}
//		return nil
//	}
//
// Then in the caller:
//
//	err := lfs.LfsFunction()
//	if lfs.IsNotImplementedError(err) {
//		log.Print("feature not implemented")
//	}
//	if lfs.IsFatalError(err) {
//		os.Exit(1)
//	}
//
// Wrapped errors contain a context, which is a map[string]string. These
// contexts can be accessed through the Error*Context functions. Calling these
// functions on a regular Go error will have no effect.
//
// Example:
//
//	err := lfs.SomeFunction()
//	errutil.ErrorSetContext(err, "foo", "bar")
//	errutil.ErrorGetContext(err, "foo") // => "bar"
//	errutil.ErrorDelContext(err, "foo")
//
// Wrapped errors also contain the stack from the point at which they are
// called. The stack is accessed via ErrorStack(). Calling ErrorStack() on a
// regular Go error will return an empty byte slice.

import (
	"fmt"
	"runtime"

	"github.com/pkg/errors"
)

type errorWithCause interface {
	Error() string
	Cause() error
}

func parentOf(err error) error {
	if c, ok := err.(errorWithCause); ok {
		return c.Cause()
	}

	return nil
}

// IsFatalError indicates that the error is fatal and the process should exit
// immediately after handling the error.
func IsFatalError(err error) bool {
	if e, ok := err.(interface {
		Fatal() bool
	}); ok {
		return e.Fatal()
	}
	if parent := parentOf(err); parent != nil {
		return IsFatalError(parent)
	}
	return false
}

// IsNotImplementedError indicates the client attempted to use a feature the
// server has not implemented (e.g. the batch endpoint).
func IsNotImplementedError(err error) bool {
	if e, ok := err.(interface {
		NotImplemented() bool
	}); ok {
		return e.NotImplemented()
	}
	if parent := parentOf(err); parent != nil {
		return IsNotImplementedError(parent)
	}
	return false
}

// IsAuthError indicates the client provided a request with invalid or no
// authentication credentials when credentials are required (e.g. HTTP 401).
func IsAuthError(err error) bool {
	if e, ok := err.(interface {
		AuthError() bool
	}); ok {
		return e.AuthError()
	}
	if parent := parentOf(err); parent != nil {
		return IsAuthError(parent)
	}
	return false
}

// IsInvalidPointerError indicates an attempt to parse data that was not a
// valid pointer.
func IsInvalidPointerError(err error) bool {
	if e, ok := err.(interface {
		InvalidPointer() bool
	}); ok {
		return e.InvalidPointer()
	}
	if parent := parentOf(err); parent != nil {
		return IsInvalidPointerError(parent)
	}
	return false
}

// IsInvalidRepoError indicates an operation was attempted from outside a git
// repository.
func IsInvalidRepoError(err error) bool {
	if e, ok := err.(interface {
		InvalidRepo() bool
	}); ok {
		return e.InvalidRepo()
	}
	if parent := parentOf(err); parent != nil {
		return IsInvalidRepoError(parent)
	}
	return false
}

// IsSmudgeError indicates an error while smudging a files.
func IsSmudgeError(err error) bool {
	if e, ok := err.(interface {
		SmudgeError() bool
	}); ok {
		return e.SmudgeError()
	}
	if parent := parentOf(err); parent != nil {
		return IsSmudgeError(parent)
	}
	return false
}

// IsCleanPointerError indicates an error while cleaning a file.
func IsCleanPointerError(err error) bool {
	if e, ok := err.(interface {
		CleanPointerError() bool
	}); ok {
		return e.CleanPointerError()
	}
	if parent := parentOf(err); parent != nil {
		return IsCleanPointerError(parent)
	}
	return false
}

// IsNotAPointerError indicates the parsed data is not an LFS pointer.
func IsNotAPointerError(err error) bool {
	if e, ok := err.(interface {
		NotAPointerError() bool
	}); ok {
		return e.NotAPointerError()
	}
	if parent := parentOf(err); parent != nil {
		return IsNotAPointerError(parent)
	}
	return false
}

// IsBadPointerKeyError indicates that the parsed data has an invalid key.
func IsBadPointerKeyError(err error) bool {
	if e, ok := err.(interface {
		BadPointerKeyError() bool
	}); ok {
		return e.BadPointerKeyError()
	}

	if parent := parentOf(err); parent != nil {
		return IsBadPointerKeyError(parent)
	}
	return false
}

// If an error is abad pointer error of any type, returns NotAPointerError
func StandardizeBadPointerError(err error) error {
	if IsBadPointerKeyError(err) {
		badErr := err.(badPointerKeyError)
		if badErr.Expected == "version" {
			return NewNotAPointerError(err)
		}
	}
	return err
}

// IsDownloadDeclinedError indicates that the smudge operation should not download.
// TODO: I don't really like using errors to control that flow, it should be refactored.
func IsDownloadDeclinedError(err error) bool {
	if e, ok := err.(interface {
		DownloadDeclinedError() bool
	}); ok {
		return e.DownloadDeclinedError()
	}
	if parent := parentOf(err); parent != nil {
		return IsDownloadDeclinedError(parent)
	}
	return false
}

// IsRetriableError indicates the low level transfer had an error but the
// caller may retry the operation.
func IsRetriableError(err error) bool {
	if e, ok := err.(interface {
		RetriableError() bool
	}); ok {
		return e.RetriableError()
	}
	if parent := parentOf(err); parent != nil {
		return IsRetriableError(parent)
	}
	return false
}

func GetInnerError(err error) error {
	if parent := parentOf(err); parent != nil {
		return parent
	}

	return nil
}

// Error wraps an error with an empty message.
func Error(err error) error {
	return Errorf(err, "")
}

// Errorf wraps an error with an additional formatted message.
func Errorf(err error, format string, args ...interface{}) error {
	if err == nil {
		err = errors.New("")
	}

	message := ""
	if len(format) > 0 {
		message = fmt.Sprintf(format, args...)
	}

	return newWrappedError(err, message)
}

// ErrorSetContext sets a value in the error's context. If the error has not
// been wrapped, it does nothing.
func ErrorSetContext(err error, key string, value interface{}) {
	if e, ok := err.(errorWrapper); ok {
		e.Set(key, value)
	}
}

// ErrorGetContext gets a value from the error's context. If the error has not
// been wrapped, it returns an empty string.
func ErrorGetContext(err error, key string) interface{} {
	if e, ok := err.(errorWrapper); ok {
		return e.Get(key)
	}
	return ""
}

// ErrorDelContext removes a value from the error's context. If the error has
// not been wrapped, it does nothing.
func ErrorDelContext(err error, key string) {
	if e, ok := err.(errorWrapper); ok {
		e.Del(key)
	}
}

// ErrorContext returns the context map for an error if it is a wrappedError.
// If it is not a wrappedError it will return an empty map.
func ErrorContext(err error) map[string]interface{} {
	if e, ok := err.(errorWrapper); ok {
		return e.Context()
	}
	return nil
}

type errorWrapper interface {
	errorWithCause

	Set(string, interface{})
	Get(string) interface{}
	Del(string)
	Context() map[string]interface{}
}

// wrappedError is the base error wrapper. It provides a Message string, a
// stack, and a context map around a regular Go error.
type wrappedError struct {
	errorWithCause
	context map[string]interface{}
}

// newWrappedError creates a wrappedError.
func newWrappedError(err error, message string) errorWrapper {
	if err == nil {
		err = errors.New("Error")
	}

	var errWithCause errorWithCause

	if len(message) > 0 {
		errWithCause = errors.Wrap(err, message).(errorWithCause)
	} else if ewc, ok := err.(errorWithCause); ok {
		errWithCause = ewc
	} else {
		errWithCause = errors.Wrap(err, "LFS").(errorWithCause)
	}

	return &wrappedError{
		context:        make(map[string]interface{}),
		errorWithCause: errWithCause,
	}
}

// Set sets the value for the key in the context.
func (e wrappedError) Set(key string, val interface{}) {
	e.context[key] = val
}

// Get gets the value for a key in the context.
func (e wrappedError) Get(key string) interface{} {
	return e.context[key]
}

// Del removes a key from the context.
func (e wrappedError) Del(key string) {
	delete(e.context, key)
}

// Context returns the underlying context.
func (e wrappedError) Context() map[string]interface{} {
	return e.context
}

// Definitions for IsFatalError()

type fatalError struct {
	errorWrapper
}

func (e fatalError) Fatal() bool {
	return true
}

func NewFatalError(err error) error {
	return fatalError{newWrappedError(err, "Fatal error")}
}

// Definitions for IsNotImplementedError()

type notImplementedError struct {
	errorWrapper
}

func (e notImplementedError) NotImplemented() bool {
	return true
}

func NewNotImplementedError(err error) error {
	return notImplementedError{newWrappedError(err, "Not implemented")}
}

// Definitions for IsAuthError()

type authError struct {
	errorWrapper
}

func (e authError) AuthError() bool {
	return true
}

func NewAuthError(err error) error {
	return authError{newWrappedError(err, "Authentication required")}
}

// Definitions for IsInvalidPointerError()

type invalidPointerError struct {
	errorWrapper
}

func (e invalidPointerError) InvalidPointer() bool {
	return true
}

func NewInvalidPointerError(err error) error {
	return invalidPointerError{newWrappedError(err, "Invalid pointer")}
}

// Definitions for IsInvalidRepoError()

type invalidRepoError struct {
	errorWrapper
}

func (e invalidRepoError) InvalidRepo() bool {
	return true
}

func NewInvalidRepoError(err error) error {
	return invalidRepoError{newWrappedError(err, "Not in a git repository")}
}

// Definitions for IsSmudgeError()

type smudgeError struct {
	errorWrapper
}

func (e smudgeError) SmudgeError() bool {
	return true
}

func NewSmudgeError(err error, oid, filename string) error {
	e := smudgeError{newWrappedError(err, "Smudge error")}
	ErrorSetContext(e, "OID", oid)
	ErrorSetContext(e, "FileName", filename)
	return e
}

// Definitions for IsCleanPointerError()

type cleanPointerError struct {
	errorWrapper
}

func (e cleanPointerError) CleanPointerError() bool {
	return true
}

func NewCleanPointerError(err error, pointer interface{}, bytes []byte) error {
	e := cleanPointerError{newWrappedError(err, "Clean pointer error")}
	ErrorSetContext(e, "pointer", pointer)
	ErrorSetContext(e, "bytes", bytes)
	return e
}

// Definitions for IsNotAPointerError()

type notAPointerError struct {
	errorWrapper
}

func (e notAPointerError) NotAPointerError() bool {
	return true
}

func NewNotAPointerError(err error) error {
	return notAPointerError{newWrappedError(err, "Not a valid Git LFS pointer file.")}
}

type badPointerKeyError struct {
	Expected string
	Actual   string

	errorWrapper
}

func (e badPointerKeyError) BadPointerKeyError() bool {
	return true
}

func NewBadPointerKeyError(expected, actual string) error {
	err := fmt.Errorf("Error parsing LFS Pointer. Expected key %s, got %s", expected, actual)
	return badPointerKeyError{expected, actual, newWrappedError(err, "")}
}

// Definitions for IsDownloadDeclinedError()

type downloadDeclinedError struct {
	errorWrapper
}

func (e downloadDeclinedError) DownloadDeclinedError() bool {
	return true
}

func NewDownloadDeclinedError(err error) error {
	return downloadDeclinedError{newWrappedError(err, "File missing and download is not allowed")}
}

// Definitions for IsRetriableError()

type retriableError struct {
	errorWrapper
}

func (e retriableError) RetriableError() bool {
	return true
}

func NewRetriableError(err error) error {
	return retriableError{newWrappedError(err, "")}
}

// Stack returns a byte slice containing the runtime.Stack()
func Stack() []byte {
	stackBuf := make([]byte, 1024*1024)
	written := runtime.Stack(stackBuf, false)
	return stackBuf[:written]
}
<|endoftext|>


<commit_before>package wkb

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"

	"github.com/twpayne/gogeom2/geom"
)

const (
	wkbXDR = 0
	wkbNDR = 1
)

type ByteOrder binary.ByteOrder

var (
	XDR = ByteOrder(binary.BigEndian)
	NDR = ByteOrder(binary.LittleEndian)
)

type ErrUnknownByteOrder byte

func (e ErrUnknownByteOrder) Error() string {
	return fmt.Sprintf("wkb: unknown byte order: %b", byte(e))
}

type Type uint32

type ErrUnknownType Type

func (e ErrUnknownType) Error() string {
	return fmt.Sprintf("wkb: unknown type: %d", uint(e))
}

type ErrUnsupportedType Type

func (e ErrUnsupportedType) Error() string {
	return fmt.Sprintf("wkb: unsupported type: %d", uint(e))
}

const (
	wkbPoint              = 1
	wkbLineString         = 2
	wkbPolygon            = 3
	wkbMultiPoint         = 4
	wkbMultiLineString    = 5
	wkbMultiPolygon       = 6
	wkbGeometryCollection = 7
	wkbPolyhedralSurface  = 15
	wkbTIN                = 16
	wkbTriangle           = 17
)

const (
	wkbXY   = 0
	wkbXYZ  = 1000
	wkbXYM  = 2000
	wkbXYZM = 3000
)

const (
	Point              = Type(wkbPoint + wkbXY)
	LineString         = Type(wkbLineString + wkbXY)
	Polygon            = Type(wkbPolygon + wkbXY)
	MultiPoint         = Type(wkbMultiPoint + wkbXY)
	MultiLineString    = Type(wkbMultiLineString + wkbXY)
	MultiPolygon       = Type(wkbMultiPolygon + wkbXY)
	GeometryCollection = Type(wkbGeometryCollection + wkbXY)
	PolyhedralSurface  = Type(wkbPolyhedralSurface + wkbXY)
	TIN                = Type(wkbTIN + wkbXY)
	Triangle           = Type(wkbTriangle + wkbXY)

	PointZ              = Type(wkbPoint + wkbXYZ)
	LineStringZ         = Type(wkbLineString + wkbXYZ)
	PolygonZ            = Type(wkbPolygon + wkbXYZ)
	MultiPointZ         = Type(wkbMultiPoint + wkbXYZ)
	MultiLineStringZ    = Type(wkbMultiLineString + wkbXYZ)
	MultiPolygonZ       = Type(wkbMultiPolygon + wkbXYZ)
	GeometryCollectionZ = Type(wkbGeometryCollection + wkbXYZ)
	PolyhedralSurfaceZ  = Type(wkbPolyhedralSurface + wkbXYZ)
	TINZ                = Type(wkbTIN + wkbXYZ)
	TriangleZ           = Type(wkbTriangle + wkbXYZ)

	PointM              = Type(wkbPoint + wkbXYM)
	LineStringM         = Type(wkbLineString + wkbXYM)
	PolygonM            = Type(wkbPolygon + wkbXYM)
	MultiPointM         = Type(wkbMultiPoint + wkbXYM)
	MultiLineStringM    = Type(wkbMultiLineString + wkbXYM)
	MultiPolygonM       = Type(wkbMultiPolygon + wkbXYM)
	GeometryCollectionM = Type(wkbGeometryCollection + wkbXYM)
	PolyhedralSurfaceM  = Type(wkbPolyhedralSurface + wkbXYM)
	TINM                = Type(wkbTIN + wkbXYM)
	TriangleM           = Type(wkbTriangle + wkbXYM)

	PointZM              = Type(wkbPoint + wkbXYZM)
	LineStringZM         = Type(wkbLineString + wkbXYZM)
	PolygonZM            = Type(wkbPolygon + wkbXYZM)
	MultiPointZM         = Type(wkbMultiPoint + wkbXYZM)
	MultiLineStringZM    = Type(wkbMultiLineString + wkbXYZM)
	MultiPolygonZM       = Type(wkbMultiPolygon + wkbXYZM)
	GeometryCollectionZM = Type(wkbGeometryCollection + wkbXYZM)
	PolyhedralSurfaceZM  = Type(wkbPolyhedralSurface + wkbXYZM)
	TINZM                = Type(wkbTIN + wkbXYZM)
	TriangleZM           = Type(wkbTriangle + wkbXYZM)
)

func (t Type) layout() (geom.Layout, error) {
	switch t {
	case Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, GeometryCollection, PolyhedralSurface, TIN, Triangle:
		return geom.XY, nil
	case PointZ, LineStringZ, PolygonZ, MultiPointZ, MultiLineStringZ, MultiPolygonZ, GeometryCollectionZ, PolyhedralSurfaceZ, TINZ, TriangleZ:
		return geom.XYZ, nil
	case PointM, LineStringM, PolygonM, MultiPointM, MultiLineStringM, MultiPolygonM, GeometryCollectionM, PolyhedralSurfaceM, TINM, TriangleM:
		return geom.XYM, nil
	case PointZM, LineStringZM, PolygonZM, MultiPointZM, MultiLineStringZM, MultiPolygonZM, GeometryCollectionZM, PolyhedralSurfaceZM, TINZM, TriangleZM:
		return geom.XYZM, nil
	default:
		return geom.Layout(0), ErrUnknownType(t)
	}
}

func readFlatCoords0(r io.Reader, byteOrder ByteOrder, stride int) ([]float64, error) {
	coord := make([]float64, stride)
	if err := binary.Read(r, byteOrder, &coord); err != nil {
		return nil, err
	}
	return coord, nil
}

func readFlatCoords1(r io.Reader, byteOrder ByteOrder, stride int) ([]float64, error) {
	var n uint32
	if err := binary.Read(r, byteOrder, &n); err != nil {
		return nil, err
	}
	flatCoords := make([]float64, int(n)*stride)
	if err := binary.Read(r, byteOrder, &flatCoords); err != nil {
		return nil, err
	}
	return flatCoords, nil
}

func readFlatCoords2(r io.Reader, byteOrder ByteOrder, stride int) ([]float64, []int, error) {
	var n uint32
	if err := binary.Read(r, byteOrder, &n); err != nil {
		return nil, nil, err
	}
	var flatCoordss []float64
	var ends []int
	for i := 0; i < int(n); i++ {
		flatCoords, err := readFlatCoords1(r, byteOrder, stride)
		if err != nil {
			return nil, nil, err
		}
		flatCoordss = append(flatCoordss, flatCoords...)
		ends = append(ends, len(flatCoords))
	}
	return flatCoordss, ends, nil
}

func read(r io.Reader) (geom.T, error) {

	var wkbByteOrder byte
	if err := binary.Read(r, binary.LittleEndian, &wkbByteOrder); err != nil {
		return nil, err
	}
	var byteOrder ByteOrder
	switch wkbByteOrder {
	case wkbXDR:
		byteOrder = XDR
	case wkbNDR:
		byteOrder = NDR
	default:
		return nil, ErrUnknownByteOrder(wkbByteOrder)
	}

	var wkbGeometryType uint32
	if err := binary.Read(r, byteOrder, &wkbGeometryType); err != nil {
		return nil, err
	}

	t := Type(wkbGeometryType)
	layout, err := t.layout()
	if err != nil {
		return nil, err
	}

	switch t {
	case Point, PointZ, PointM, PointZM:
		flatCoords, err := readFlatCoords0(r, byteOrder, layout.Stride())
		if err != nil {
			return nil, err
		}
		return geom.NewPointFlat(layout, flatCoords), nil
	case LineString, LineStringZ, LineStringM, LineStringZM:
		flatCoords, err := readFlatCoords1(r, byteOrder, layout.Stride())
		if err != nil {
			return nil, err
		}
		return geom.NewLineStringFlat(layout, flatCoords), nil
	case Polygon, PolygonZ, PolygonM, PolygonZM:
		flatCoords, ends, err := readFlatCoords2(r, byteOrder, layout.Stride())
		if err != nil {
			return nil, err
		}
		return geom.NewPolygonFlat(layout, flatCoords, ends), nil
	case MultiPoint, MultiPointZ, MultiPointM, MultiPointZM:
		var n uint32
		if err := binary.Read(r, byteOrder, &n); err != nil {
			return nil, err
		}
		mp := geom.NewMultiPoint(layout)
		for i := uint32(0); i < n; i++ {
			g, err := read(r)
			if err != nil {
				return nil, err
			}
			p, ok := g.(*geom.Point)
			if !ok {
				return nil, fmt.Errorf("wkb: got a %T, want *geom.Point", g)
			}
			if err = mp.Push(p); err != nil {
				return nil, err
			}
		}
		return mp, nil
	default:
		return nil, ErrUnsupportedType(wkbGeometryType)
	}

}

func Read(data []byte) (geom.T, error) {
	return read(bytes.NewBuffer(data))
}

func writeFlatCoords0(w io.Writer, byteOrder ByteOrder, coord []float64) error {
	return binary.Write(w, byteOrder, coord)
}

func writeFlatCoords1(w io.Writer, byteOrder ByteOrder, coords []float64, stride int) error {
	if err := binary.Write(w, byteOrder, uint32(len(coords)/stride)); err != nil {
		return err
	}
	return binary.Write(w, byteOrder, coords)
}

func writeFlatCoords2(w io.Writer, byteOrder ByteOrder, flatCoords []float64, ends []int, stride int) error {
	if err := binary.Write(w, byteOrder, uint32(len(ends))); err != nil {
		return err
	}
	offset := 0
	for _, end := range ends {
		if err := writeFlatCoords1(w, byteOrder, flatCoords[offset:end], stride); err != nil {
			return err
		}
		offset = end
	}
	return nil
}

func write(w io.Writer, byteOrder ByteOrder, g geom.T) error {

	var wkbByteOrder byte
	switch byteOrder {
	case XDR:
		wkbByteOrder = wkbXDR
	case NDR:
		wkbByteOrder = wkbNDR
	default:
		panic("wkb: invalid byte order") // FIXME
	}
	if err := binary.Write(w, byteOrder, wkbByteOrder); err != nil {
		return err
	}

	var wkbGeometryType uint32
	switch g.(type) {
	case *geom.Point:
		wkbGeometryType = wkbPoint
	case *geom.LineString:
		wkbGeometryType = wkbLineString
	case *geom.Polygon:
		wkbGeometryType = wkbPolygon
	case *geom.MultiPoint:
		wkbGeometryType = wkbMultiPoint
	case *geom.MultiLineString:
		wkbGeometryType = wkbMultiLineString
	case *geom.MultiPolygon:
		wkbGeometryType = wkbPolygon
	default:
		panic("wkb: unsupported type") // FIXME
	}
	switch g.Layout() {
	case geom.XY:
		wkbGeometryType += wkbXY
	case geom.XYZ:
		wkbGeometryType += wkbXYZ
	case geom.XYM:
		wkbGeometryType += wkbXYM
	case geom.XYZM:
		wkbGeometryType += wkbXYZM
	default:
		panic("wkb: unsupported layout") // FIXME
	}
	if err := binary.Write(w, byteOrder, wkbGeometryType); err != nil {
		return err
	}

	switch g.(type) {
	case *geom.Point:
		return writeFlatCoords0(w, byteOrder, g.FlatCoords())
	case *geom.LineString:
		return writeFlatCoords1(w, byteOrder, g.FlatCoords(), g.Stride())
	case *geom.Polygon:
		return writeFlatCoords2(w, byteOrder, g.FlatCoords(), g.Ends(), g.Stride())
	case *geom.MultiPoint:
		mp := g.(*geom.MultiPoint)
		n := mp.NumPoints()
		if err := binary.Write(w, byteOrder, uint32(n)); err != nil {
			return err
		}
		for i := 0; i < n; i++ {
			if err := write(w, byteOrder, mp.Point(i)); err != nil {
				return err
			}
		}
		return nil
	case *geom.MultiLineString:
		mls := g.(*geom.MultiLineString)
		n := mls.NumLineStrings()
		if err := binary.Write(w, byteOrder, uint32(n)); err != nil {
			return err
		}
		for i := 0; i < n; i++ {
			if err := write(w, byteOrder, mls.LineString(i)); err != nil {
				return err
			}
		}
		return nil
	case *geom.MultiPolygon:
		mp := g.(*geom.MultiPolygon)
		n := mp.NumPolygons()
		if err := binary.Write(w, byteOrder, uint32(n)); err != nil {
			return err
		}
		for i := 0; i < n; i++ {
			if err := write(w, byteOrder, mp.Polygon(i)); err != nil {
				return err
			}
		}
	}

	return fmt.Errorf("wkb: unsupported or unknown geometry type: %T", g)

}

func Write(g geom.T, byteOrder ByteOrder) ([]byte, error) {
	w := bytes.NewBuffer(nil)
	if err := write(w, byteOrder, g); err != nil {
		return nil, err
	}
	return w.Bytes(), nil
}
<commit_msg>Add MultiLineString and MultiPolygon reading<commit_after>package wkb

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"

	"github.com/twpayne/gogeom2/geom"
)

const (
	wkbXDR = 0
	wkbNDR = 1
)

type ByteOrder binary.ByteOrder

var (
	XDR = ByteOrder(binary.BigEndian)
	NDR = ByteOrder(binary.LittleEndian)
)

type ErrUnknownByteOrder byte

func (e ErrUnknownByteOrder) Error() string {
	return fmt.Sprintf("wkb: unknown byte order: %b", byte(e))
}

type Type uint32

type ErrUnknownType Type

func (e ErrUnknownType) Error() string {
	return fmt.Sprintf("wkb: unknown type: %d", uint(e))
}

type ErrUnsupportedType Type

func (e ErrUnsupportedType) Error() string {
	return fmt.Sprintf("wkb: unsupported type: %d", uint(e))
}

const (
	wkbPoint              = 1
	wkbLineString         = 2
	wkbPolygon            = 3
	wkbMultiPoint         = 4
	wkbMultiLineString    = 5
	wkbMultiPolygon       = 6
	wkbGeometryCollection = 7
	wkbPolyhedralSurface  = 15
	wkbTIN                = 16
	wkbTriangle           = 17
)

const (
	wkbXY   = 0
	wkbXYZ  = 1000
	wkbXYM  = 2000
	wkbXYZM = 3000
)

const (
	Point              = Type(wkbPoint + wkbXY)
	LineString         = Type(wkbLineString + wkbXY)
	Polygon            = Type(wkbPolygon + wkbXY)
	MultiPoint         = Type(wkbMultiPoint + wkbXY)
	MultiLineString    = Type(wkbMultiLineString + wkbXY)
	MultiPolygon       = Type(wkbMultiPolygon + wkbXY)
	GeometryCollection = Type(wkbGeometryCollection + wkbXY)
	PolyhedralSurface  = Type(wkbPolyhedralSurface + wkbXY)
	TIN                = Type(wkbTIN + wkbXY)
	Triangle           = Type(wkbTriangle + wkbXY)

	PointZ              = Type(wkbPoint + wkbXYZ)
	LineStringZ         = Type(wkbLineString + wkbXYZ)
	PolygonZ            = Type(wkbPolygon + wkbXYZ)
	MultiPointZ         = Type(wkbMultiPoint + wkbXYZ)
	MultiLineStringZ    = Type(wkbMultiLineString + wkbXYZ)
	MultiPolygonZ       = Type(wkbMultiPolygon + wkbXYZ)
	GeometryCollectionZ = Type(wkbGeometryCollection + wkbXYZ)
	PolyhedralSurfaceZ  = Type(wkbPolyhedralSurface + wkbXYZ)
	TINZ                = Type(wkbTIN + wkbXYZ)
	TriangleZ           = Type(wkbTriangle + wkbXYZ)

	PointM              = Type(wkbPoint + wkbXYM)
	LineStringM         = Type(wkbLineString + wkbXYM)
	PolygonM            = Type(wkbPolygon + wkbXYM)
	MultiPointM         = Type(wkbMultiPoint + wkbXYM)
	MultiLineStringM    = Type(wkbMultiLineString + wkbXYM)
	MultiPolygonM       = Type(wkbMultiPolygon + wkbXYM)
	GeometryCollectionM = Type(wkbGeometryCollection + wkbXYM)
	PolyhedralSurfaceM  = Type(wkbPolyhedralSurface + wkbXYM)
	TINM                = Type(wkbTIN + wkbXYM)
	TriangleM           = Type(wkbTriangle + wkbXYM)

	PointZM              = Type(wkbPoint + wkbXYZM)
	LineStringZM         = Type(wkbLineString + wkbXYZM)
	PolygonZM            = Type(wkbPolygon + wkbXYZM)
	MultiPointZM         = Type(wkbMultiPoint + wkbXYZM)
	MultiLineStringZM    = Type(wkbMultiLineString + wkbXYZM)
	MultiPolygonZM       = Type(wkbMultiPolygon + wkbXYZM)
	GeometryCollectionZM = Type(wkbGeometryCollection + wkbXYZM)
	PolyhedralSurfaceZM  = Type(wkbPolyhedralSurface + wkbXYZM)
	TINZM                = Type(wkbTIN + wkbXYZM)
	TriangleZM           = Type(wkbTriangle + wkbXYZM)
)

func (t Type) layout() (geom.Layout, error) {
	switch t {
	case Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, GeometryCollection, PolyhedralSurface, TIN, Triangle:
		return geom.XY, nil
	case PointZ, LineStringZ, PolygonZ, MultiPointZ, MultiLineStringZ, MultiPolygonZ, GeometryCollectionZ, PolyhedralSurfaceZ, TINZ, TriangleZ:
		return geom.XYZ, nil
	case PointM, LineStringM, PolygonM, MultiPointM, MultiLineStringM, MultiPolygonM, GeometryCollectionM, PolyhedralSurfaceM, TINM, TriangleM:
		return geom.XYM, nil
	case PointZM, LineStringZM, PolygonZM, MultiPointZM, MultiLineStringZM, MultiPolygonZM, GeometryCollectionZM, PolyhedralSurfaceZM, TINZM, TriangleZM:
		return geom.XYZM, nil
	default:
		return geom.Layout(0), ErrUnknownType(t)
	}
}

func readFlatCoords0(r io.Reader, byteOrder ByteOrder, stride int) ([]float64, error) {
	coord := make([]float64, stride)
	if err := binary.Read(r, byteOrder, &coord); err != nil {
		return nil, err
	}
	return coord, nil
}

func readFlatCoords1(r io.Reader, byteOrder ByteOrder, stride int) ([]float64, error) {
	var n uint32
	if err := binary.Read(r, byteOrder, &n); err != nil {
		return nil, err
	}
	flatCoords := make([]float64, int(n)*stride)
	if err := binary.Read(r, byteOrder, &flatCoords); err != nil {
		return nil, err
	}
	return flatCoords, nil
}

func readFlatCoords2(r io.Reader, byteOrder ByteOrder, stride int) ([]float64, []int, error) {
	var n uint32
	if err := binary.Read(r, byteOrder, &n); err != nil {
		return nil, nil, err
	}
	var flatCoordss []float64
	var ends []int
	for i := 0; i < int(n); i++ {
		flatCoords, err := readFlatCoords1(r, byteOrder, stride)
		if err != nil {
			return nil, nil, err
		}
		flatCoordss = append(flatCoordss, flatCoords...)
		ends = append(ends, len(flatCoords))
	}
	return flatCoordss, ends, nil
}

func read(r io.Reader) (geom.T, error) {

	var wkbByteOrder byte
	if err := binary.Read(r, binary.LittleEndian, &wkbByteOrder); err != nil {
		return nil, err
	}
	var byteOrder ByteOrder
	switch wkbByteOrder {
	case wkbXDR:
		byteOrder = XDR
	case wkbNDR:
		byteOrder = NDR
	default:
		return nil, ErrUnknownByteOrder(wkbByteOrder)
	}

	var wkbGeometryType uint32
	if err := binary.Read(r, byteOrder, &wkbGeometryType); err != nil {
		return nil, err
	}

	t := Type(wkbGeometryType)
	layout, err := t.layout()
	if err != nil {
		return nil, err
	}

	switch t {
	case Point, PointZ, PointM, PointZM:
		flatCoords, err := readFlatCoords0(r, byteOrder, layout.Stride())
		if err != nil {
			return nil, err
		}
		return geom.NewPointFlat(layout, flatCoords), nil
	case LineString, LineStringZ, LineStringM, LineStringZM:
		flatCoords, err := readFlatCoords1(r, byteOrder, layout.Stride())
		if err != nil {
			return nil, err
		}
		return geom.NewLineStringFlat(layout, flatCoords), nil
	case Polygon, PolygonZ, PolygonM, PolygonZM:
		flatCoords, ends, err := readFlatCoords2(r, byteOrder, layout.Stride())
		if err != nil {
			return nil, err
		}
		return geom.NewPolygonFlat(layout, flatCoords, ends), nil
	case MultiPoint, MultiPointZ, MultiPointM, MultiPointZM:
		var n uint32
		if err := binary.Read(r, byteOrder, &n); err != nil {
			return nil, err
		}
		mp := geom.NewMultiPoint(layout)
		for i := uint32(0); i < n; i++ {
			g, err := read(r)
			if err != nil {
				return nil, err
			}
			p, ok := g.(*geom.Point)
			if !ok {
				return nil, fmt.Errorf("wkb: got a %T, want *geom.Point", g)
			}
			if err = mp.Push(p); err != nil {
				return nil, err
			}
		}
		return mp, nil
	case MultiLineString, MultiLineStringZ, MultiLineStringM, MultiLineStringZM:
		var n uint32
		if err := binary.Read(r, byteOrder, &n); err != nil {
			return nil, err
		}
		mls := geom.NewMultiLineString(layout)
		for i := uint32(0); i < n; i++ {
			g, err := read(r)
			if err != nil {
				return nil, err
			}
			p, ok := g.(*geom.LineString)
			if !ok {
				return nil, fmt.Errorf("wkb: got a %T, want *geom.LineString", g)
			}
			if err = mls.Push(p); err != nil {
				return nil, err
			}
		}
		return mls, nil
	case MultiPolygon, MultiPolygonZ, MultiPolygonM, MultiPolygonZM:
		var n uint32
		if err := binary.Read(r, byteOrder, &n); err != nil {
			return nil, err
		}
		mp := geom.NewMultiPolygon(layout)
		for i := uint32(0); i < n; i++ {
			g, err := read(r)
			if err != nil {
				return nil, err
			}
			p, ok := g.(*geom.Polygon)
			if !ok {
				return nil, fmt.Errorf("wkb: got a %T, want *geom.Polygon", g)
			}
			if err = mp.Push(p); err != nil {
				return nil, err
			}
		}
		return mp, nil
	default:
		return nil, ErrUnsupportedType(wkbGeometryType)
	}

}

func Read(data []byte) (geom.T, error) {
	return read(bytes.NewBuffer(data))
}

func writeFlatCoords0(w io.Writer, byteOrder ByteOrder, coord []float64) error {
	return binary.Write(w, byteOrder, coord)
}

func writeFlatCoords1(w io.Writer, byteOrder ByteOrder, coords []float64, stride int) error {
	if err := binary.Write(w, byteOrder, uint32(len(coords)/stride)); err != nil {
		return err
	}
	return binary.Write(w, byteOrder, coords)
}

func writeFlatCoords2(w io.Writer, byteOrder ByteOrder, flatCoords []float64, ends []int, stride int) error {
	if err := binary.Write(w, byteOrder, uint32(len(ends))); err != nil {
		return err
	}
	offset := 0
	for _, end := range ends {
		if err := writeFlatCoords1(w, byteOrder, flatCoords[offset:end], stride); err != nil {
			return err
		}
		offset = end
	}
	return nil
}

func write(w io.Writer, byteOrder ByteOrder, g geom.T) error {

	var wkbByteOrder byte
	switch byteOrder {
	case XDR:
		wkbByteOrder = wkbXDR
	case NDR:
		wkbByteOrder = wkbNDR
	default:
		panic("wkb: invalid byte order") // FIXME
	}
	if err := binary.Write(w, byteOrder, wkbByteOrder); err != nil {
		return err
	}

	var wkbGeometryType uint32
	switch g.(type) {
	case *geom.Point:
		wkbGeometryType = wkbPoint
	case *geom.LineString:
		wkbGeometryType = wkbLineString
	case *geom.Polygon:
		wkbGeometryType = wkbPolygon
	case *geom.MultiPoint:
		wkbGeometryType = wkbMultiPoint
	case *geom.MultiLineString:
		wkbGeometryType = wkbMultiLineString
	case *geom.MultiPolygon:
		wkbGeometryType = wkbPolygon
	default:
		panic("wkb: unsupported type") // FIXME
	}
	switch g.Layout() {
	case geom.XY:
		wkbGeometryType += wkbXY
	case geom.XYZ:
		wkbGeometryType += wkbXYZ
	case geom.XYM:
		wkbGeometryType += wkbXYM
	case geom.XYZM:
		wkbGeometryType += wkbXYZM
	default:
		panic("wkb: unsupported layout") // FIXME
	}
	if err := binary.Write(w, byteOrder, wkbGeometryType); err != nil {
		return err
	}

	switch g.(type) {
	case *geom.Point:
		return writeFlatCoords0(w, byteOrder, g.FlatCoords())
	case *geom.LineString:
		return writeFlatCoords1(w, byteOrder, g.FlatCoords(), g.Stride())
	case *geom.Polygon:
		return writeFlatCoords2(w, byteOrder, g.FlatCoords(), g.Ends(), g.Stride())
	case *geom.MultiPoint:
		mp := g.(*geom.MultiPoint)
		n := mp.NumPoints()
		if err := binary.Write(w, byteOrder, uint32(n)); err != nil {
			return err
		}
		for i := 0; i < n; i++ {
			if err := write(w, byteOrder, mp.Point(i)); err != nil {
				return err
			}
		}
		return nil
	case *geom.MultiLineString:
		mls := g.(*geom.MultiLineString)
		n := mls.NumLineStrings()
		if err := binary.Write(w, byteOrder, uint32(n)); err != nil {
			return err
		}
		for i := 0; i < n; i++ {
			if err := write(w, byteOrder, mls.LineString(i)); err != nil {
				return err
			}
		}
		return nil
	case *geom.MultiPolygon:
		mp := g.(*geom.MultiPolygon)
		n := mp.NumPolygons()
		if err := binary.Write(w, byteOrder, uint32(n)); err != nil {
			return err
		}
		for i := 0; i < n; i++ {
			if err := write(w, byteOrder, mp.Polygon(i)); err != nil {
				return err
			}
		}
	}

	return fmt.Errorf("wkb: unsupported or unknown geometry type: %T", g)

}

func Write(g geom.T, byteOrder ByteOrder) ([]byte, error) {
	w := bytes.NewBuffer(nil)
	if err := write(w, byteOrder, g); err != nil {
		return nil, err
	}
	return w.Bytes(), nil
}
<|endoftext|>


<commit_before>package gordian

import (
	"code.google.com/p/go.net/websocket"

	"fmt"
	"io"
)

const (
	CONNECT = iota
	DISCONNECT
)

// ClientId identifies a websocket client.  It must be usable as a map key
type ClientId interface{}

// MessageData is the unmarshalled payload of an websocket message
type MessageData interface{}

// Message is used to transfer messages between the application and clients.
type Message struct {
	// From identifies the client that sent the message.
	From ClientId
	// To identifies the client that receives the message.
	To ClientId
	// Data contains the message payload.
	Data MessageData
}

type ClientInfo struct {
	Id       ClientId
	CtrlType int
}

// clientData conveys information about a client's state to clientManager.
type clientData struct {
	// toClient is used to deliver messages to the client.
	toClient chan *Message

	ClientInfo
}

// The Gordian class is responsible for managing client connections and
// reading and distributing client messages.
type Gordian struct {
	Connect  chan *websocket.Conn
	Control  chan *ClientInfo
	Messages chan *Message

	// clientCtrl sends client connection events and messages to
	// clientManager.
	clientCtrl chan *clientData
	// clients maps an id to a client's information.
	clients map[ClientId]clientData
}

// New constructs a Gordian object.
func New() *Gordian {
	g := &Gordian{
		Messages: make(chan *Message),
		Control:  make(chan *ClientInfo),

		clientCtrl: make(chan *clientData),
		clients:    make(map[ClientId]clientData),
	}
	return g
}

// Run starts the goroutine that manages connections and message distribution.
func (g *Gordian) Run() {
	go g.manageClients()
}

// WSHandler returns a function to be called by http.ListenAndServe to handle
// a new websocket connection.
func (g *Gordian) WSHandler() func(ws *websocket.Conn) {
	return func(ws *websocket.Conn) {
		g.Connect <- ws
		ci := <-g.Control
		if ci.Id == nil || ci.CtrlType != CONNECT {
			return
		}
		toClient := make(chan *Message)
		cd := &clientData{toClient, *ci}
		g.clientCtrl <- cd
		go g.writeToWS(ws, cd)
		g.readFromWS(ws, cd)
		ci.CtrlType = DISCONNECT
		g.Control <- ci
		cd.CtrlType = DISCONNECT
		g.clientCtrl <- cd
	}
}

// manageClients waits for connection or message events and updates the
// internal state or delivers the message, respectively.
func (g *Gordian) manageClients() {
	for {
		select {
		case msg := <-g.Messages:
			if cd, ok := g.clients[msg.To]; ok {
				cd.toClient <- msg
			}
		case cd := <-g.clientCtrl:
			if cd.CtrlType == CONNECT {
				g.clients[cd.Id] = *cd
			} else {
				close(cd.toClient)
				delete(g.clients, cd.Id)
			}
		}
	}
}

// readFromWS reads a message from the client and passes it to clientManager.
func (g *Gordian) readFromWS(ws *websocket.Conn, cd *clientData) {
	for {
		var data MessageData
		err := websocket.JSON.Receive(ws, &data)
		switch err {
		case nil:
			g.Messages <- &Message{cd.Id, nil, data}
		case io.EOF:
			return
		default:
			fmt.Println(err)
		}
	}
}

// writeToWS gets messages from clientManager and sends them to the client.
func (g *Gordian) writeToWS(ws *websocket.Conn, cd *clientData) {
	for {
		msg, ok := <-cd.toClient
		if !ok {
			return
		}
		if err := websocket.JSON.Send(ws, msg.Data); err != nil {
			fmt.Println(err)
		}
	}
}
<commit_msg>Made some names more descriptive.  Combined ClientInfo and clientData.<commit_after>package gordian

import (
	"code.google.com/p/go.net/websocket"

	"fmt"
	"io"
)

const (
	CONNECT = iota
	DISCONNECT
)

// ClientId identifies a websocket client.  It must be usable as a map key
type ClientId interface{}

// MessageData is the unmarshalled payload of an websocket message
type MessageData interface{}

// Message is used to transfer messages between the application and clients.
type Message struct {
	// From identifies the client that sent the message.
	From ClientId
	// To identifies the client that receives the message.
	To ClientId
	// Data contains the message payload.
	Data MessageData
}

// ClientData holds information about a client's state
type ClientData struct {
	Id       ClientId
	CtrlType int
	toClient chan *Message
}

// The Gordian class is responsible for managing client connections and reading
// and distributing client messages.
type Gordian struct {
	Connect    chan *websocket.Conn
	Control    chan *ClientData
	Messages   chan *Message
	clientCtrl chan *ClientData
	clients    map[ClientId]ClientData
}

// New constructs a Gordian object.
func New() *Gordian {
	g := &Gordian{
		Connect:    make(chan *websocket.Conn),
		Control:    make(chan *ClientData),
		Messages:   make(chan *Message),
		clientCtrl: make(chan *ClientData),
		clients:    make(map[ClientId]ClientData),
	}
	return g
}

// Run starts the goroutine that manages connections and message distribution.
func (g *Gordian) Run() {
	go g.manageClients()
}

// WSHandler returns a function to be called by http.ListenAndServe to handle a
// new websocket connection.
func (g *Gordian) WSHandler() func(conn *websocket.Conn) {
	return func(conn *websocket.Conn) {
		g.Connect <- conn
		clientData := <-g.Control
		if clientData.Id == nil || clientData.CtrlType != CONNECT {
			return
		}
		clientData.toClient = make(chan *Message)
		g.clientCtrl <- clientData
		go g.writeToWS(conn, clientData)
		g.readFromWS(conn, clientData)
		clientData.CtrlType = DISCONNECT
		g.Control <- clientData
		clientData.CtrlType = DISCONNECT
		g.clientCtrl <- clientData
	}
}

// manageClients waits for connection or message events and updates the
// internal state or delivers the message, respectively.
func (g *Gordian) manageClients() {
	for {
		select {
		case msg := <-g.Messages:
			if clientData, ok := g.clients[msg.To]; ok {
				clientData.toClient <- msg
			}
		case clientData := <-g.clientCtrl:
			if clientData.CtrlType == CONNECT {
				g.clients[clientData.Id] = *clientData
			} else {
				close(clientData.toClient)
				delete(g.clients, clientData.Id)
			}
		}
	}
}

// readFromWS reads a message from the client and passes it to clientManager.
func (g *Gordian) readFromWS(conn *websocket.Conn, clientData *ClientData) {
	for {
		var data MessageData
		err := websocket.JSON.Receive(conn, &data)
		switch err {
		case nil:
			g.Messages <- &Message{clientData.Id, nil, data}
		case io.EOF:
			return
		default:
			fmt.Println(err)
		}
	}
}

// writeToWS gets messages from clientManager and sends them to the client.
func (g *Gordian) writeToWS(conn *websocket.Conn, clientData *ClientData) {
	for {
		msg, ok := <-clientData.toClient
		if !ok {
			return
		}
		if err := websocket.JSON.Send(conn, msg.Data); err != nil {
			fmt.Println(err)
		}
	}
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2015 Fredy Wijaya
//
// 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.

package main

import (
	"fmt"
	"github.com/nsf/termbox-go"
	"math/rand"
	"os"
	"time"
)

const (
	author      string        = "Fredy Wijaya"
	leftX       int           = 1
	leftY       int           = 1
	rightX      int           = 60
	rightY      int           = 20
	snakeX      int           = rightX / 2
	snakeY      int           = rightY / 2
	idle        int           = -1
	left        int           = 0
	right       int           = 1
	up          int           = 2
	down        int           = 3
	speed       time.Duration = 300
	xStep       int           = 2
	yStep       int           = 1
	numFood     int           = 15
	scoreWeight int           = 10
)

func drawTopLine() {
	colorDefault := termbox.ColorDefault
	for i := leftX; i <= rightX; i++ {
		var c rune
		if i == leftX {
			c = '\u250c'
		} else if i == rightX {
			c = '\u2510'
		} else {
			c = '\u2500'
		}
		termbox.SetCell(i, leftY, c, colorDefault, colorDefault)
	}
}

func drawLeftLine() {
	colorDefault := termbox.ColorDefault
	for i := leftY + 1; i <= rightY; i++ {
		c := '\u2502'
		termbox.SetCell(leftX, i, c, colorDefault, colorDefault)
	}
}

func drawRightLine() {
	colorDefault := termbox.ColorDefault
	for i := leftX; i <= rightX; i++ {
		var c rune
		if i == leftX {
			c = '\u2514'
		} else if i == rightX {
			c = '\u2518'
		} else {
			c = '\u2500'
		}
		termbox.SetCell(i, rightY+1, c, colorDefault, colorDefault)
	}
}

func drawBottomLine() {
	colorDefault := termbox.ColorDefault
	for i := leftY + 1; i <= rightY; i++ {
		c := '\u2502'
		termbox.SetCell(rightX, i, c, colorDefault, colorDefault)
	}
}

func drawBox() {
	drawTopLine()
	drawLeftLine()
	drawRightLine()
	drawBottomLine()
}

func drawSnake(snake *snake) {
	colorDefault := termbox.ColorDefault
	for idx, coordinate := range snake.coordinates {
		var ch rune
		if idx == 0 {
			ch = '@'
		} else {
			ch = '*'
		}
		termbox.SetCell(coordinate.x, coordinate.y, ch, colorDefault, colorDefault)
	}
}

func drawLevel(level int) {
	x := leftX + 1
	y := leftY - 1
	text := fmt.Sprintf("Level: %d", level)
	drawText(x, y, text)
}

func drawScore(score int) {
	x := rightX - 11
	y := leftY - 1
	text := fmt.Sprintf("Score: %d", score)
	drawText(x, y, text)
}

func drawText(x, y int, text string) {
	colorDefault := termbox.ColorDefault
	for _, ch := range text {
		termbox.SetCell(x, y, ch, colorDefault, colorDefault)
		x++
	}
}

func drawFood(food *food) {
	colorDefault := termbox.ColorDefault
	for _, coordinate := range food.coordinates {
		termbox.SetCell(coordinate.x, coordinate.y, '\u2665', colorDefault, colorDefault)
	}
}

func drawAuthor() {
	x := leftX + 1
	y := rightY + 2
	text := fmt.Sprintf("Author: %s", author)
	drawText(x, y, text)
}

func redrawAll(game *game) {
	colorDefault := termbox.ColorDefault
	termbox.Clear(colorDefault, colorDefault)

	drawLevel(game.level)
	drawScore(game.score)
	drawBox()
	drawSnake(&game.snake)
	drawFood(&game.food)
	drawAuthor()

	termbox.Flush()
}

type game struct {
	score int
	level int
	snake snake
	food  food
}

type food struct {
	coordinates []coordinate
}

type coordinate struct {
	x int
	y int
}

type snake struct {
	coordinates []coordinate
	direction   int
}

func (s *snake) update(moveHead func(idx int)) {
	// move the head
	idx := 0
	prev := s.coordinates[idx]
	moveHead(idx)
	// update the tail
	for i := idx + 1; i < len(s.coordinates); i++ {
		tmp := s.coordinates[i]
		s.coordinates[i].x = prev.x
		s.coordinates[i].y = prev.y
		prev = tmp
	}
}

func (s *snake) moveUpIdx(idx int) {
	s.coordinates[idx].y -= yStep
	if s.coordinates[idx].y <= leftY {
		s.coordinates[idx].y = rightY
	}
	s.direction = up
}

func (s *snake) moveUp() {
	s.update(s.moveUpIdx)
}

func (s *snake) moveDownIdx(idx int) {
	s.coordinates[idx].y += yStep
	if s.coordinates[idx].y >= rightY {
		s.coordinates[idx].y = leftY + yStep
	}
	s.direction = down
}

func (s *snake) moveDown() {
	s.update(s.moveDownIdx)
}

func (s *snake) moveLeftIdx(idx int) {
	s.coordinates[idx].x -= xStep
	if s.coordinates[idx].x <= leftX+1 {
		s.coordinates[idx].x = rightX - xStep
	}
	s.direction = left
}

func (s *snake) moveLeft() {
	s.update(s.moveLeftIdx)
}

func (s *snake) moveRightIdx(idx int) {
	s.coordinates[idx].x += xStep
	if s.coordinates[idx].x >= rightX-1 {
		s.coordinates[idx].x = leftX + xStep
	}
	s.direction = right
}

func (s *snake) moveRight() {
	s.update(s.moveRightIdx)
}

func (s *snake) setDirection(direction int) {
	if s.direction == idle {
		if direction == left || direction == right || direction == up || direction == down {
			s.direction = direction
		}
	} else {
		// the snake can't go backward
		if direction == left && !(s.direction == left || s.direction == right) {
			s.direction = left
		} else if direction == right && !(s.direction == left || s.direction == right) {
			s.direction = right
		} else if direction == up && !(s.direction == up || s.direction == down) {
			s.direction = up
		} else if direction == down && !(s.direction == up || s.direction == down) {
			s.direction = down
		}
	}
}

func (s *snake) move() {
	if s.direction == left {
		s.moveLeft()
	} else if s.direction == right {
		s.moveRight()
	} else if s.direction == up {
		s.moveUp()
	} else if s.direction == down {
		s.moveDown()
	}
}

func (g *game) isFoodEaten(snake *snake, food *food) bool {
	head := snake.coordinates[0]
	var newFood []coordinate
	eaten := false
	for _, foodCoord := range food.coordinates {
		if head.x == foodCoord.x && head.y == foodCoord.y {
			snake.coordinates = append([]coordinate{{x: head.x, y: head.y}},
				snake.coordinates...)
			eaten = true
		} else {
			newFood = append(newFood, foodCoord)
		}
	}
	food.coordinates = newFood
	return eaten
}

func (g *game) run() {
	g.snake.move()
	if g.isFoodEaten(&g.snake, &g.food) {
		g.score += scoreWeight
	}
}

func initSnake() *snake {
	// TODO: fix the initial snake position
	snake := &snake{
		coordinates: []coordinate{
			coordinate{x: snakeX, y: snakeY},
			coordinate{x: snakeX - xStep, y: snakeY},
			coordinate{x: snakeX - (xStep * 2), y: snakeY},
		},
		direction: idle,
	}
	return snake
}

func initFood(snake *snake) *food {
	var foodCoordinates []coordinate
	rand.Seed(time.Now().UTC().UnixNano())
	nFood := 0
	for nFood < numFood {
		x := rand.Intn((rightX-leftX)+1) + leftX
		y := rand.Intn((rightY-leftY)+1) + leftY
		if x%2 != 0 || x <= leftX+1 || x >= rightX {
			continue
		}
		if y <= leftY || y >= rightY {
			continue
		}
		good := true
		for _, snakeCoordinate := range snake.coordinates {
			if snakeCoordinate.x == x && snakeCoordinate.y == y {
				good = false
				break
			}
		}
		if good {
			foodCoordinates = append(foodCoordinates,
				coordinate{x: x, y: y})
			nFood++
		}
	}
	food := &food{coordinates: foodCoordinates}
	return food
}

func runGame() {
	err := termbox.Init()
	if err != nil {
		errorAndExit(err)
	}
	defer termbox.Close()
	snake := initSnake()
	food := initFood(snake)
	game := &game{
		score: 0,
		level: 1,
		snake: *snake,
		food:  *food,
	}
	redrawAll(game)

	ticker := time.NewTicker(speed * time.Millisecond)

	eventQueue := make(chan termbox.Event)
	go func() {
		for {
			eventQueue <- termbox.PollEvent()
		}
	}()
loop:
	for {
		select {
		case ev := <-eventQueue:
			switch ev.Key {
			case termbox.KeyEsc:
				break loop
			case termbox.KeyArrowUp:
				game.snake.setDirection(up)
			case termbox.KeyArrowDown:
				game.snake.setDirection(down)
			case termbox.KeyArrowLeft:
				game.snake.setDirection(left)
			case termbox.KeyArrowRight:
				game.snake.setDirection(right)
			}
		case <-ticker.C:
			game.run()
		}
		redrawAll(game)
	}
}

func errorAndExit(message interface{}) {
	fmt.Println(message)
	os.Exit(1)
}

func main() {
	runGame()
}
<commit_msg>Add next level<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2015 Fredy Wijaya
//
// 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.

package main

import (
	"fmt"
	"github.com/nsf/termbox-go"
	"math/rand"
	"os"
	"time"
)

const (
	author       string        = "Fredy Wijaya"
	leftX        int           = 1
	leftY        int           = 1
	rightX       int           = 60
	rightY       int           = 20
	snakeX       int           = rightX / 2
	snakeY       int           = rightY / 2
	idle         int           = -1
	left         int           = 0
	right        int           = 1
	up           int           = 2
	down         int           = 3
	initialSpeed time.Duration = 250
	xStep        int           = 2
	yStep        int           = 1
	numFood      int           = 15
	scoreWeight  int           = 10
	maxLevel     int           = 10
)

func drawTopLine() {
	colorDefault := termbox.ColorDefault
	for i := leftX; i <= rightX; i++ {
		var c rune
		if i == leftX {
			c = '\u250c'
		} else if i == rightX {
			c = '\u2510'
		} else {
			c = '\u2500'
		}
		termbox.SetCell(i, leftY, c, colorDefault, colorDefault)
	}
}

func drawLeftLine() {
	colorDefault := termbox.ColorDefault
	for i := leftY + 1; i <= rightY; i++ {
		c := '\u2502'
		termbox.SetCell(leftX, i, c, colorDefault, colorDefault)
	}
}

func drawRightLine() {
	colorDefault := termbox.ColorDefault
	for i := leftX; i <= rightX; i++ {
		var c rune
		if i == leftX {
			c = '\u2514'
		} else if i == rightX {
			c = '\u2518'
		} else {
			c = '\u2500'
		}
		termbox.SetCell(i, rightY+1, c, colorDefault, colorDefault)
	}
}

func drawBottomLine() {
	colorDefault := termbox.ColorDefault
	for i := leftY + 1; i <= rightY; i++ {
		c := '\u2502'
		termbox.SetCell(rightX, i, c, colorDefault, colorDefault)
	}
}

func drawBox() {
	drawTopLine()
	drawLeftLine()
	drawRightLine()
	drawBottomLine()
}

func drawSnake(snake *snake) {
	colorDefault := termbox.ColorDefault
	for idx, coordinate := range snake.coordinates {
		var ch rune
		if idx == 0 {
			ch = '@'
		} else {
			ch = '*'
		}
		termbox.SetCell(coordinate.x, coordinate.y, ch, colorDefault, colorDefault)
	}
}

func drawLevel(level int) {
	x := leftX + 1
	y := leftY - 1
	text := fmt.Sprintf("Level: %d", level)
	drawText(x, y, text)
}

func drawScore(score int) {
	x := rightX - 11
	y := leftY - 1
	text := fmt.Sprintf("Score: %d", score)
	drawText(x, y, text)
}

func drawText(x, y int, text string) {
	colorDefault := termbox.ColorDefault
	for _, ch := range text {
		termbox.SetCell(x, y, ch, colorDefault, colorDefault)
		x++
	}
}

func drawFood(food *food) {
	colorDefault := termbox.ColorDefault
	for _, coordinate := range food.coordinates {
		termbox.SetCell(coordinate.x, coordinate.y, '\u2665', colorDefault, colorDefault)
	}
}

func drawAuthor() {
	x := leftX + 1
	y := rightY + 2
	text := fmt.Sprintf("Created By: %s", author)
	drawText(x, y, text)
}

func redrawAll(game *game) {
	colorDefault := termbox.ColorDefault
	termbox.Clear(colorDefault, colorDefault)

	drawLevel(game.level)
	drawScore(game.score)
	drawBox()
	drawSnake(&game.snake)
	drawFood(&game.food)
	drawAuthor()

	termbox.Flush()
}

type game struct {
	score int
	level int
	speed time.Duration
	snake snake
	food  food
}

type food struct {
	coordinates []coordinate
}

type coordinate struct {
	x int
	y int
}

type snake struct {
	coordinates []coordinate
	direction   int
}

func (s *snake) update(moveHead func(idx int)) {
	// move the head
	idx := 0
	prev := s.coordinates[idx]
	moveHead(idx)
	// update the tail
	for i := idx + 1; i < len(s.coordinates); i++ {
		tmp := s.coordinates[i]
		s.coordinates[i].x = prev.x
		s.coordinates[i].y = prev.y
		prev = tmp
	}
}

func (s *snake) moveUpIdx(idx int) {
	s.coordinates[idx].y -= yStep
	if s.coordinates[idx].y <= leftY {
		s.coordinates[idx].y = rightY
	}
	s.direction = up
}

func (s *snake) moveUp() {
	s.update(s.moveUpIdx)
}

func (s *snake) moveDownIdx(idx int) {
	s.coordinates[idx].y += yStep
	if s.coordinates[idx].y >= rightY {
		s.coordinates[idx].y = leftY + yStep
	}
	s.direction = down
}

func (s *snake) moveDown() {
	s.update(s.moveDownIdx)
}

func (s *snake) moveLeftIdx(idx int) {
	s.coordinates[idx].x -= xStep
	if s.coordinates[idx].x <= leftX+1 {
		s.coordinates[idx].x = rightX - xStep
	}
	s.direction = left
}

func (s *snake) moveLeft() {
	s.update(s.moveLeftIdx)
}

func (s *snake) moveRightIdx(idx int) {
	s.coordinates[idx].x += xStep
	if s.coordinates[idx].x >= rightX-1 {
		s.coordinates[idx].x = leftX + xStep
	}
	s.direction = right
}

func (s *snake) moveRight() {
	s.update(s.moveRightIdx)
}

func (s *snake) setDirection(direction int) {
	if s.direction == idle {
		if direction == left || direction == right || direction == up || direction == down {
			s.direction = direction
		}
	} else {
		// the snake can't go backward
		if direction == left && !(s.direction == left || s.direction == right) {
			s.direction = left
		} else if direction == right && !(s.direction == left || s.direction == right) {
			s.direction = right
		} else if direction == up && !(s.direction == up || s.direction == down) {
			s.direction = up
		} else if direction == down && !(s.direction == up || s.direction == down) {
			s.direction = down
		}
	}
}

func (s *snake) move() {
	if s.direction == left {
		s.moveLeft()
	} else if s.direction == right {
		s.moveRight()
	} else if s.direction == up {
		s.moveUp()
	} else if s.direction == down {
		s.moveDown()
	}
}

func (g *game) isFoodEaten(snake *snake, food *food) bool {
	head := snake.coordinates[0]
	var newFood []coordinate
	eaten := false
	for _, foodCoord := range food.coordinates {
		if head.x == foodCoord.x && head.y == foodCoord.y {
			snake.coordinates = append([]coordinate{{x: head.x, y: head.y}},
				snake.coordinates...)
			eaten = true
		} else {
			newFood = append(newFood, foodCoord)
		}
	}
	food.coordinates = newFood
	return eaten
}

func (g *game) run() bool {
	g.snake.move()
	if g.isFoodEaten(&g.snake, &g.food) {
		g.score += scoreWeight
	}
	if len(g.food.coordinates) == 0 {
		return true
	}
	// TODO: snake will die if it eats its own body
	return false
}

func initSnake() *snake {
	// TODO: fix the initial snake position
	snake := &snake{
		coordinates: []coordinate{
			coordinate{x: snakeX, y: snakeY},
			coordinate{x: snakeX - xStep, y: snakeY},
			coordinate{x: snakeX - (xStep * 2), y: snakeY},
		},
		direction: idle,
	}
	return snake
}

func initFood(snake *snake) *food {
	var foodCoordinates []coordinate
	rand.Seed(time.Now().UTC().UnixNano())
	nFood := 0
	for nFood < numFood {
		x := rand.Intn((rightX-leftX)+1) + leftX
		y := rand.Intn((rightY-leftY)+1) + leftY
		if x%2 != 0 || x <= leftX+1 || x >= rightX {
			continue
		}
		if y <= leftY || y >= rightY {
			continue
		}
		good := true
		for _, snakeCoordinate := range snake.coordinates {
			if snakeCoordinate.x == x && snakeCoordinate.y == y {
				good = false
				break
			}
		}
		if good {
			foodCoordinates = append(foodCoordinates,
				coordinate{x: x, y: y})
			nFood++
		}
	}
	food := &food{coordinates: foodCoordinates}
	return food
}

func runGame() {
	err := termbox.Init()
	if err != nil {
		errorAndExit(err)
	}
	defer termbox.Close()

	eventQueue := make(chan termbox.Event)
	go func() {
		for {
			eventQueue <- termbox.PollEvent()
		}
	}()

	game := &game{
		score: 0,
		level: 0,
		speed: initialSpeed,
	}
exitGame:
	for {
		snake := initSnake()
		food := initFood(snake)
		game.snake = *snake
		game.food = *food
		game.speed -= time.Duration(20)
		game.level++
		ticker := time.NewTicker(game.speed * time.Millisecond)
		redrawAll(game)
	nextLevel:
		for {
			select {
			case ev := <-eventQueue:
				switch ev.Key {
				case termbox.KeyEsc:
					break exitGame
				case termbox.KeyArrowUp:
					game.snake.setDirection(up)
				case termbox.KeyArrowDown:
					game.snake.setDirection(down)
				case termbox.KeyArrowLeft:
					game.snake.setDirection(left)
				case termbox.KeyArrowRight:
					game.snake.setDirection(right)
				}
			case <-ticker.C:
				if game.run() {
					break nextLevel
				}
			}
			redrawAll(game)
		}
	}
}

func errorAndExit(message interface{}) {
	fmt.Println(message)
	os.Exit(1)
}

func main() {
	runGame()
}
<|endoftext|>