<commit_before>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 save

import (
	"fmt"
	"io"
	"os"
	"regexp"

	"golang.org/x/net/context"

	"github.com/jacobsa/comeback/internal/graph"
	"github.com/jacobsa/syncutil"
)

// Given a base directory and a set of exclusions, list the files and
// directories that would be saved by a backup job with the same info in a
// human-readable format. Write the output to the supplied writer.
func List(
	ctx context.Context,
	w io.Writer,
	basePath string,
	exclusions []*regexp.Regexp) (err error) {
	b := syncutil.NewBundle(context.Background())

	// Explore the file system graph, writing all non-excluded nodes into a
	// channel.
	graphNodes := make(chan graph.Node, 100)
	b.Add(func(ctx context.Context) (err error) {
		// Set up a root node.
		rootNode := &fsNode{
			RelPath: "",
			Parent:  nil,
		}

		rootNode.Info, err = os.Lstat(basePath)
		if err != nil {
			err = fmt.Errorf("os.Lstat: %v", err)
			return
		}

		if !rootNode.Info.IsDir() {
			err = fmt.Errorf("Not a directory: %q", basePath)
			return
		}

		// Explore the graph.
		defer close(graphNodes)
		sf := newSuccessorFinder(basePath, exclusions)

		const parallelism = 8
		err = graph.ExploreDirectedGraph(
			ctx,
			sf,
			[]graph.Node{rootNode},
			graphNodes,
			parallelism)

		if err != nil {
			err = fmt.Errorf("ExploreDirectedGraph: %v", err)
			return
		}

		return
	})

	// Print out info about each node.
	b.Add(func(ctx context.Context) (err error) {
		for graphNode := range graphNodes {
			n, ok := graphNode.(*fsNode)
			if !ok {
				err = fmt.Errorf("Unexpected node type: %T", n)
				return
			}

			// Skip the root node.
			_, err = fmt.Fprintf(w, "%q %d\n", n.RelPath, n.Info.Size())
			if err != nil {
				err = fmt.Errorf("Fprintf: %v", err)
				return
			}
		}

		return
	})

	err = b.Join()
	return
}
<commit_msg>Declared listNodes.<commit_after>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 save

import (
	"errors"
	"fmt"
	"io"
	"os"
	"regexp"

	"golang.org/x/net/context"

	"github.com/jacobsa/comeback/internal/graph"
	"github.com/jacobsa/syncutil"
)

// Given a base directory and a set of exclusions, list all file system nodes
// involved, filling in only the RelPath and Parent fields.
func listNodes(
	ctx context.Context,
	basePath string,
	exclusions []*regexp.Regexp,
	nodes chan<- *fsNode) (err error) {
	err = errors.New("TODO")
	return
}

// Given a base directory and a set of exclusions, list the files and
// directories that would be saved by a backup job with the same info in a
// human-readable format. Write the output to the supplied writer.
func List(
	ctx context.Context,
	w io.Writer,
	basePath string,
	exclusions []*regexp.Regexp) (err error) {
	// TODO(jacobsa): Make this function use listNodes.

	b := syncutil.NewBundle(context.Background())

	// Explore the file system graph, writing all non-excluded nodes into a
	// channel.
	graphNodes := make(chan graph.Node, 100)
	b.Add(func(ctx context.Context) (err error) {
		// Set up a root node.
		rootNode := &fsNode{
			RelPath: "",
			Parent:  nil,
		}

		rootNode.Info, err = os.Lstat(basePath)
		if err != nil {
			err = fmt.Errorf("os.Lstat: %v", err)
			return
		}

		if !rootNode.Info.IsDir() {
			err = fmt.Errorf("Not a directory: %q", basePath)
			return
		}

		// Explore the graph.
		defer close(graphNodes)
		sf := newSuccessorFinder(basePath, exclusions)

		const parallelism = 8
		err = graph.ExploreDirectedGraph(
			ctx,
			sf,
			[]graph.Node{rootNode},
			graphNodes,
			parallelism)

		if err != nil {
			err = fmt.Errorf("ExploreDirectedGraph: %v", err)
			return
		}

		return
	})

	// Print out info about each node.
	b.Add(func(ctx context.Context) (err error) {
		for graphNode := range graphNodes {
			n, ok := graphNode.(*fsNode)
			if !ok {
				err = fmt.Errorf("Unexpected node type: %T", n)
				return
			}

			// Skip the root node.
			_, err = fmt.Fprintf(w, "%q %d\n", n.RelPath, n.Info.Size())
			if err != nil {
				err = fmt.Errorf("Fprintf: %v", err)
				return
			}
		}

		return
	})

	err = b.Join()
	return
}
<|endoftext|>


<commit_before>// Package page is used to render the HTML to the end user
package page

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"html/template"
	"log"
	"net/http"
	"strings"

	"github.com/hacdias/caddy-filemanager/assets"
	"github.com/hacdias/caddy-filemanager/config"
	"github.com/hacdias/caddy-filemanager/utils/variables"
)

// Page contains the informations and functions needed to show the Page
type Page struct {
	*Info
	Minimal bool
}

// Info contains the information of a Page
type Info struct {
	Name    string
	Path    string
	IsDir   bool
	User    *config.User
	Config  *config.Config
	Data    interface{}
	Editor  bool
	Display string
	Token   string
}

// Commit returns the current build commit
func (i *Info) Commit() string {
	return GIT_COMMIT_HASH
}

// BreadcrumbMapItem ...
type BreadcrumbMapItem struct {
	Name string
	URL  string
}

// BreadcrumbMap returns p.Path where every element is a map
// of URLs and path segment names.
func (i Info) BreadcrumbMap() []BreadcrumbMapItem {
	result := []BreadcrumbMapItem{}

	if len(i.Path) == 0 {
		return result
	}

	// skip trailing slash
	lpath := i.Path
	if lpath[len(lpath)-1] == '/' {
		lpath = lpath[:len(lpath)-1]
	}

	parts := strings.Split(lpath, "/")
	for i, part := range parts {
		if i == len(parts)-1 {
			continue
		}

		if i == 0 && part == "" {
			result = append([]BreadcrumbMapItem{{
				Name: "/",
				URL:  "/",
			}}, result...)
			continue
		}

		result = append([]BreadcrumbMapItem{{
			Name: part,
			URL:  strings.Join(parts[:i+1], "/"),
		}}, result...)
	}

	return result
}

// PreviousLink returns the path of the previous folder
func (i Info) PreviousLink() string {
	path := strings.TrimSuffix(i.Path, "/")
	path = strings.TrimPrefix(path, "/")
	path = i.Config.AbsoluteURL() + "/" + path
	path = path[0 : len(path)-len(i.Name)]

	if len(path) < len(i.Config.AbsoluteURL()+"/") {
		return ""
	}

	return path
}

// PrintAsHTML formats the page in HTML and executes the template
func (p Page) PrintAsHTML(w http.ResponseWriter, templates ...string) (int, error) {
	// Create the functions map, then the template, check for erros and
	// execute the template if there aren't errors
	functions := template.FuncMap{
		"Defined": variables.Defined,
		"CSS": func(s string) template.CSS {
			return template.CSS(s)
		},
		"Marshal": func(v interface{}) template.JS {
			a, _ := json.Marshal(v)
			return template.JS(a)
		},
		"EncodeBase64": func(s string) string {
			return base64.StdEncoding.EncodeToString([]byte(s))
		},
	}

	if p.Minimal {
		templates = append(templates, "minimal")
	} else {
		templates = append(templates, "base")
	}

	var tpl *template.Template

	// For each template, add it to the the tpl variable
	for i, t := range templates {
		// Get the template from the assets
		Page, err := assets.Asset("templates/" + t + ".tmpl")

		// Check if there is some error. If so, the template doesn't exist
		if err != nil {
			log.Print(err)
			return http.StatusInternalServerError, err
		}

		// If it's the first iteration, creates a new template and add the
		// functions map
		if i == 0 {
			tpl, err = template.New(t).Funcs(functions).Parse(string(Page))
		} else {
			tpl, err = tpl.Parse(string(Page))
		}

		if err != nil {
			log.Print(err)
			return http.StatusInternalServerError, err
		}
	}

	buf := &bytes.Buffer{}
	err := tpl.Execute(buf, p.Info)

	if err != nil {
		return http.StatusInternalServerError, err
	}

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	_, err = buf.WriteTo(w)
	return http.StatusOK, err
}

// PrintAsJSON prints the current Page information in JSON
func (p Page) PrintAsJSON(w http.ResponseWriter) (int, error) {
	marsh, err := json.MarshalIndent(p.Info.Data, "", "    ")
	if err != nil {
		return http.StatusInternalServerError, err
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	if _, err := w.Write(marsh); err != nil {
		return http.StatusInternalServerError, err
	}

	return http.StatusOK, nil
}
<commit_msg>close #74<commit_after>// Package page is used to render the HTML to the end user
package page

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"html/template"
	"log"
	"net/http"
	"strings"

	"github.com/hacdias/caddy-filemanager/assets"
	"github.com/hacdias/caddy-filemanager/config"
	"github.com/hacdias/caddy-filemanager/utils/variables"
)

// Page contains the informations and functions needed to show the Page
type Page struct {
	*Info
	Minimal bool
}

// Info contains the information of a Page
type Info struct {
	Name    string
	Path    string
	IsDir   bool
	User    *config.User
	Config  *config.Config
	Data    interface{}
	Editor  bool
	Display string
	Token   string
}

// Commit returns the current build commit
func (i *Info) Commit() string {
	return GIT_COMMIT_HASH
}

// BreadcrumbMapItem ...
type BreadcrumbMapItem struct {
	Name string
	URL  string
}

// BreadcrumbMap returns p.Path where every element is a map
// of URLs and path segment names.
func (i Info) BreadcrumbMap() []BreadcrumbMapItem {
	result := []BreadcrumbMapItem{}

	if len(i.Path) == 0 {
		return result
	}

	// skip trailing slash
	lpath := i.Path
	if lpath[len(lpath)-1] == '/' {
		lpath = lpath[:len(lpath)-1]
	}

	parts := strings.Split(lpath, "/")
	for i, part := range parts {
		if i == len(parts)-1 {
			continue
		}

		if i == 0 && part == "" {
			result = append([]BreadcrumbMapItem{{
				Name: "/",
				URL:  "/",
			}}, result...)
			continue
		}

		result = append([]BreadcrumbMapItem{{
			Name: part,
			URL:  strings.Join(parts[:i+1], "/") + "/",
		}}, result...)
	}

	return result
}

// PreviousLink returns the path of the previous folder
func (i Info) PreviousLink() string {
	path := strings.TrimSuffix(i.Path, "/")
	path = strings.TrimPrefix(path, "/")
	path = i.Config.AbsoluteURL() + "/" + path
	path = path[0 : len(path)-len(i.Name)]

	if len(path) < len(i.Config.AbsoluteURL()+"/") {
		return ""
	}

	return path
}

// PrintAsHTML formats the page in HTML and executes the template
func (p Page) PrintAsHTML(w http.ResponseWriter, templates ...string) (int, error) {
	// Create the functions map, then the template, check for erros and
	// execute the template if there aren't errors
	functions := template.FuncMap{
		"Defined": variables.Defined,
		"CSS": func(s string) template.CSS {
			return template.CSS(s)
		},
		"Marshal": func(v interface{}) template.JS {
			a, _ := json.Marshal(v)
			return template.JS(a)
		},
		"EncodeBase64": func(s string) string {
			return base64.StdEncoding.EncodeToString([]byte(s))
		},
	}

	if p.Minimal {
		templates = append(templates, "minimal")
	} else {
		templates = append(templates, "base")
	}

	var tpl *template.Template

	// For each template, add it to the the tpl variable
	for i, t := range templates {
		// Get the template from the assets
		Page, err := assets.Asset("templates/" + t + ".tmpl")

		// Check if there is some error. If so, the template doesn't exist
		if err != nil {
			log.Print(err)
			return http.StatusInternalServerError, err
		}

		// If it's the first iteration, creates a new template and add the
		// functions map
		if i == 0 {
			tpl, err = template.New(t).Funcs(functions).Parse(string(Page))
		} else {
			tpl, err = tpl.Parse(string(Page))
		}

		if err != nil {
			log.Print(err)
			return http.StatusInternalServerError, err
		}
	}

	buf := &bytes.Buffer{}
	err := tpl.Execute(buf, p.Info)

	if err != nil {
		return http.StatusInternalServerError, err
	}

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	_, err = buf.WriteTo(w)
	return http.StatusOK, err
}

// PrintAsJSON prints the current Page information in JSON
func (p Page) PrintAsJSON(w http.ResponseWriter) (int, error) {
	marsh, err := json.MarshalIndent(p.Info.Data, "", "    ")
	if err != nil {
		return http.StatusInternalServerError, err
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	if _, err := w.Write(marsh); err != nil {
		return http.StatusInternalServerError, err
	}

	return http.StatusOK, nil
}
<|endoftext|>


<commit_before>// Copyright 2017 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package tree

import (
	"bytes"
	"errors"
	"fmt"
	"net/http"
	"regexp"
	"regexp/syntax"
	"sort"
	"strings"
	"sync"

	"github.com/issue9/mux/internal/tree/handlers"
	ts "github.com/issue9/mux/internal/tree/syntax"
)

var nodesPool = &sync.Pool{
	New: func() interface{} {
		return make([]*Node, 0, 10)
	},
}

// Node 表示路由中的节点。
type Node struct {
	parent   *Node
	nodeType ts.Type
	children []*Node
	pattern  string
	handlers *handlers.Handlers

	// 用于表示当前节点是否为终点，仅对 nodeType 为 TypeRegexp 和 TypeNamed 有用。
	// 此值为 true，该节点的优先级会比同类型的节点低，以便优先对比其它非最终节点，
	// 且该节点之后不会有子节点。
	endpoint bool

	// 命名参数特有的参数
	name   string // 缓存着名称
	suffix string // 保存着命名之后的字符串内容

	// 正则特有的参数
	expr       *regexp.Regexp
	syntaxExpr *syntax.Regexp
}

// 当前节点的优先级，根据节点类型来判断，
// 若类型相同时，则有子节点的优先级低一些，但不会超过其它不同类型的节点。
func (n *Node) priority() int {
	p := int(n.nodeType)

	if len(n.children) > 0 {
		p++
	}
	if n.endpoint {
		p++
	}

	return p
}

// 添加一条路由，当 methods 为空时，表示仅添加节点，而不添加任何处理函数。
func (n *Node) add(segments []*ts.Segment, h http.Handler, methods ...string) error {
	child, err := n.addSegment(segments[0])
	if err != nil {
		return err
	}

	if len(segments) == 1 { // 最后一个节点
		if child.handlers == nil {
			child.handlers = handlers.New()
		}
		return child.handlers.Add(h, methods...)
	}
	return child.add(segments[1:], h, methods...)
}

// 将 ts.Segment 添加到当前节点，并返回新节点
func (n *Node) addSegment(s *ts.Segment) (*Node, error) {
	var child *Node // 找到的最匹配节点
	var l int       // 最大的匹配字符数量

	for _, c := range n.children { // 提取两者的共同前缀
		if c.endpoint != s.Endpoint {
			continue
		}

		if c.endpoint == s.Endpoint && // 有完全相同的节点
			c.pattern == s.Value &&
			c.nodeType == s.Type {
			return c, nil
		}

		if l1 := ts.PrefixLen(c.pattern, s.Value); l1 > l {
			l = l1
			child = c
		}
	}

	if l <= 0 { // 没有共同前缀，声明一个新的加入到当前节点
		return n.newChild(s)
	}

	parent, err := splitNode(child, l)
	if err != nil {
		return nil, err
	}

	if len(s.Value) == l {
		return parent, nil
	}
	return parent.addSegment(ts.NewSegment(s.Value[l:]))
}

// 根据 seg 内容为当前节点产生一个子节点，并返回该新节点。
func (n *Node) newChild(seg *ts.Segment) (*Node, error) {
	child := &Node{
		parent:   n,
		pattern:  seg.Value,
		nodeType: seg.Type,
	}

	switch seg.Type {
	case ts.TypeNamed:
		endIndex := strings.IndexByte(seg.Value, ts.NameEnd)
		if endIndex == -1 { // TODO 由 ts.Segment 保证语法是有效的，是否更佳？
			return nil, fmt.Errorf("无效的路由语法：%s", seg.Value)
		}
		child.suffix = seg.Value[endIndex+1:]
		child.name = seg.Value[1:endIndex]
		child.endpoint = seg.Endpoint
	case ts.TypeRegexp:
		reg := ts.Regexp(seg.Value)
		expr, err := regexp.Compile(reg)
		if err != nil {
			return nil, err
		}

		syntaxExpr, err := syntax.Parse(reg, syntax.Perl)
		if err != nil {
			return nil, err
		}
		child.expr = expr
		child.syntaxExpr = syntaxExpr
		child.endpoint = seg.Endpoint
	} // end switch

	n.children = append(n.children, child)
	sort.SliceStable(n.children, func(i, j int) bool {
		return n.children[i].priority() < n.children[j].priority()
	})

	return child, nil
}

// 查找路由项，不存在返回 nil
func (n *Node) find(pattern string) *Node {
	for _, child := range n.children {
		if len(child.pattern) < len(pattern) {
			if !strings.HasPrefix(pattern, child.pattern) {
				continue
			}

			nn := child.find(pattern[len(child.pattern):])
			if nn != nil {
				return nn
			}
		}

		if child.pattern == pattern {
			return child
		}
	}

	return nil
}

// Clean 清除路由项
func (n *Node) Clean(prefix string) {
	if len(prefix) == 0 {
		n.children = n.children[:0]
		return
	}

	dels := make([]string, 0, len(n.children))
	for _, child := range n.children {
		if len(child.pattern) < len(prefix) {
			if strings.HasPrefix(prefix, child.pattern) {
				child.Clean(prefix[len(child.pattern):])
			}
		}

		if strings.HasPrefix(child.pattern, prefix) {
			dels = append(dels, child.pattern)
		}
	}

	for _, del := range dels {
		n.children = removeNodes(n.children, del)
	}
}

// Remove 移除路由项
func (n *Node) Remove(pattern string, methods ...string) error {
	child := n.find(pattern)

	if child == nil {
		return fmt.Errorf("不存在的节点 %v", pattern)
	}

	if child.handlers == nil {
		return nil
	}

	if child.handlers.Remove(methods...) {
		child.parent.children = removeNodes(child.parent.children, child.pattern)
	}
	return nil
}

// Match 从子节点中查找与当前路径匹配的节点，若找不到，则返回 nil
func (n *Node) Match(path string) *Node {
	if len(n.children) == 0 && len(path) == 0 {
		return n
	}

	for _, node := range n.children {
		matched := false
		newPath := path

		switch node.nodeType {
		case ts.TypeBasic:
			matched = strings.HasPrefix(path, node.pattern)
			if matched {
				newPath = path[len(node.pattern):]
			}
		case ts.TypeNamed:
			if node.endpoint {
				matched = true
				newPath = path[:0]
			} else {
				index := strings.Index(path, node.suffix)
				if index > 0 { // 为零说明前面没有命名参数，肯定不正确
					matched = true
					newPath = path[index+len(node.suffix):]
				}
			}
		case ts.TypeRegexp:
			loc := node.expr.FindStringIndex(path)
			if loc != nil && loc[0] == 0 {
				matched = true
				if loc[1] == len(path) {
					newPath = path[:0]
				} else {
					newPath = path[loc[1]+1:]
				}
			}
		default:
			// nodeType 错误，肯定是代码级别的错误，直接 panic
			panic("无效的 nodeType 值")
		}

		if matched {
			// 即使 newPath 为空，也有可能子节点正好可以匹配空的内容。
			// 比如 /posts/{path:\\w*} 后面的 path 即为空节点。
			if nn := node.Match(newPath); nn != nil {
				return nn
			}
			// 已经到达最终点
			if len(newPath) == 0 && node.handlers != nil && node.handlers.Len() > 0 {
				return node
			}
		}
	} // end for

	return nil
}

// Params 由调用方确保能正常匹配 path
//
// 调用方需确保 path 与 n.Match 中传递的是相同的值，此函数中，不再作验证。
func (n *Node) Params(path string) map[string]string {
	nodes := n.getParents()
	defer nodesPool.Put(nodes)

	params := make(map[string]string, 10)

LOOP:
	for i := len(nodes) - 1; i >= 0; i-- {
		node := nodes[i]
		switch node.nodeType {
		case ts.TypeBasic:
			path = path[len(node.pattern):]
		case ts.TypeNamed:
			if node.endpoint {
				params[node.name] = path
				break LOOP
			}

			index := strings.Index(path, node.suffix)
			params[node.name] = path[:index]
			path = path[index+len(node.suffix):]
		case ts.TypeRegexp:
			subexps := node.expr.SubexpNames()
			args := node.expr.FindStringSubmatch(path)
			for index, name := range subexps {
				if len(name) > 0 && index < len(args) {
					params[name] = args[index]
				}
			}

			path = path[len(args[0]):]
		}
	}

	return params
}

// URL 根据参数生成地址
func (n *Node) URL(params map[string]string) (string, error) {
	nodes := n.getParents()
	defer nodesPool.Put(nodes)

	buf := new(bytes.Buffer)

	for i := len(nodes) - 1; i >= 0; i-- {
		node := nodes[i]
		switch node.nodeType {
		case ts.TypeBasic:
			buf.WriteString(node.pattern)
		case ts.TypeNamed:
			param, exists := params[node.name]
			if !exists {
				return "", fmt.Errorf("未找到参数 %s 的值", node.name)
			}
			buf.WriteString(param)
			buf.WriteString(node.suffix) // 如果是 endpoint suffix 肯定为空
		case ts.TypeRegexp:
			url := node.syntaxExpr.String()
			subs := append(node.syntaxExpr.Sub, node.syntaxExpr)
			for _, sub := range subs {
				if len(sub.Name) == 0 {
					continue
				}

				param, exists := params[sub.Name]
				if !exists {
					return "", fmt.Errorf("未找到参数 %v 的值", sub.Name)
				}
				url = strings.Replace(url, sub.String(), param, -1)
			}

			buf.WriteString(url)
		}
	}

	return buf.String(), nil
}

// 逐级向上获取父节点，包含当前节点。
// NOTE: 记得将 []*Node 放回对象池中。
func (n *Node) getParents() []*Node {
	nodes := nodesPool.Get().([]*Node)[:0]

	for curr := n; curr != nil; curr = curr.parent { // 从尾部向上开始获取节点
		nodes = append(nodes, curr)
	}

	return nodes
}

// SetAllow 设置当前节点的 allow 报头
func (n *Node) SetAllow(allow string) {
	if n.handlers == nil {
		n.handlers = handlers.New()
	}

	n.handlers.SetAllow(allow)
}

// Handler 获取该节点下与参数相对应的处理函数
func (n *Node) Handler(method string) http.Handler {
	if n.handlers == nil {
		return nil
	}

	return n.handlers.Handler(method)
}

// 向客户端打印节点的树状结构
func (n *Node) print(deep int) {
	fmt.Println(strings.Repeat(" ", deep*4), n.pattern)

	for _, child := range n.children {
		child.print(deep + 1)
	}
}

// 获取路由数量
func (n *Node) len() int {
	var cnt int
	for _, child := range n.children {
		cnt += child.len()
	}

	if n.handlers != nil && n.handlers.Len() > 0 {
		cnt++
	}

	return cnt
}

func removeNodes(nodes []*Node, pattern string) []*Node {
	lastIndex := len(nodes) - 1
	for index, n := range nodes {
		if n.pattern != pattern {
			continue
		}

		switch {
		case len(nodes) == 1: // 只有一个元素
			return nodes[:0]
		case index == lastIndex: // 最后一个元素
			return nodes[:lastIndex]
		default:
			return append(nodes[:index], nodes[index+1:]...)
		}
	} // end for

	return nodes
}

// 将节点 n 从 pos 位置进行拆分。后一段作为当前段的子节点，并返回当前节点。
// 若 pos 大于或等于 n.pattern 的长度，则直接返回 n 不会拆分。
//
// NOTE: 调用者需确保 pos 位置是可拆分的。
func splitNode(n *Node, pos int) (*Node, error) {
	if len(n.pattern) <= pos { // 不需要拆分
		return n, nil
	}

	p := n.parent
	if p == nil {
		return nil, errors.New("split:节点必须要有一个有效的父节点，才能进行拆分")
	}

	// 先从父节点中删除老的 n
	p.children = removeNodes(p.children, n.pattern)

	ret, err := p.newChild(ts.NewSegment(n.pattern[:pos]))
	if err != nil {
		return nil, err
	}

	c, err := ret.newChild(ts.NewSegment(n.pattern[pos:]))
	if err != nil {
		return nil, err
	}
	c.handlers = n.handlers
	c.children = n.children
	for _, item := range c.children {
		item.parent = c
	}

	return ret, nil
}
<commit_msg>[internal/tree] 拆分 Node.Match<commit_after>// Copyright 2017 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package tree

import (
	"bytes"
	"errors"
	"fmt"
	"net/http"
	"regexp"
	"regexp/syntax"
	"sort"
	"strings"
	"sync"

	"github.com/issue9/mux/internal/tree/handlers"
	ts "github.com/issue9/mux/internal/tree/syntax"
)

var nodesPool = &sync.Pool{
	New: func() interface{} {
		return make([]*Node, 0, 10)
	},
}

// Node 表示路由中的节点。
type Node struct {
	parent   *Node
	nodeType ts.Type
	children []*Node
	pattern  string
	handlers *handlers.Handlers

	// 用于表示当前节点是否为终点，仅对 nodeType 为 TypeRegexp 和 TypeNamed 有用。
	// 此值为 true，该节点的优先级会比同类型的节点低，以便优先对比其它非最终节点，
	// 且该节点之后不会有子节点。
	endpoint bool

	// 命名参数特有的参数
	name   string // 缓存着名称
	suffix string // 保存着命名之后的字符串内容

	// 正则特有的参数
	expr       *regexp.Regexp
	syntaxExpr *syntax.Regexp
}

// 当前节点的优先级，根据节点类型来判断，
// 若类型相同时，则有子节点的优先级低一些，但不会超过其它不同类型的节点。
func (n *Node) priority() int {
	p := int(n.nodeType)

	if len(n.children) > 0 {
		p++
	}
	if n.endpoint {
		p++
	}

	return p
}

// 添加一条路由，当 methods 为空时，表示仅添加节点，而不添加任何处理函数。
func (n *Node) add(segments []*ts.Segment, h http.Handler, methods ...string) error {
	child, err := n.addSegment(segments[0])
	if err != nil {
		return err
	}

	if len(segments) == 1 { // 最后一个节点
		if child.handlers == nil {
			child.handlers = handlers.New()
		}
		return child.handlers.Add(h, methods...)
	}
	return child.add(segments[1:], h, methods...)
}

// 将 ts.Segment 添加到当前节点，并返回新节点
func (n *Node) addSegment(s *ts.Segment) (*Node, error) {
	var child *Node // 找到的最匹配节点
	var l int       // 最大的匹配字符数量

	for _, c := range n.children { // 提取两者的共同前缀
		if c.endpoint != s.Endpoint {
			continue
		}

		if c.endpoint == s.Endpoint && // 有完全相同的节点
			c.pattern == s.Value &&
			c.nodeType == s.Type {
			return c, nil
		}

		if l1 := ts.PrefixLen(c.pattern, s.Value); l1 > l {
			l = l1
			child = c
		}
	}

	if l <= 0 { // 没有共同前缀，声明一个新的加入到当前节点
		return n.newChild(s)
	}

	parent, err := splitNode(child, l)
	if err != nil {
		return nil, err
	}

	if len(s.Value) == l {
		return parent, nil
	}
	return parent.addSegment(ts.NewSegment(s.Value[l:]))
}

// 根据 seg 内容为当前节点产生一个子节点，并返回该新节点。
func (n *Node) newChild(seg *ts.Segment) (*Node, error) {
	child := &Node{
		parent:   n,
		pattern:  seg.Value,
		nodeType: seg.Type,
	}

	switch seg.Type {
	case ts.TypeNamed:
		endIndex := strings.IndexByte(seg.Value, ts.NameEnd)
		if endIndex == -1 { // TODO 由 ts.Segment 保证语法是有效的，是否更佳？
			return nil, fmt.Errorf("无效的路由语法：%s", seg.Value)
		}
		child.suffix = seg.Value[endIndex+1:]
		child.name = seg.Value[1:endIndex]
		child.endpoint = seg.Endpoint
	case ts.TypeRegexp:
		reg := ts.Regexp(seg.Value)
		expr, err := regexp.Compile(reg)
		if err != nil {
			return nil, err
		}

		syntaxExpr, err := syntax.Parse(reg, syntax.Perl)
		if err != nil {
			return nil, err
		}
		child.expr = expr
		child.syntaxExpr = syntaxExpr
		child.endpoint = seg.Endpoint
	} // end switch

	n.children = append(n.children, child)
	sort.SliceStable(n.children, func(i, j int) bool {
		return n.children[i].priority() < n.children[j].priority()
	})

	return child, nil
}

// 查找路由项，不存在返回 nil
func (n *Node) find(pattern string) *Node {
	for _, child := range n.children {
		if len(child.pattern) < len(pattern) {
			if !strings.HasPrefix(pattern, child.pattern) {
				continue
			}

			nn := child.find(pattern[len(child.pattern):])
			if nn != nil {
				return nn
			}
		}

		if child.pattern == pattern {
			return child
		}
	}

	return nil
}

// Clean 清除路由项
func (n *Node) Clean(prefix string) {
	if len(prefix) == 0 {
		n.children = n.children[:0]
		return
	}

	dels := make([]string, 0, len(n.children))
	for _, child := range n.children {
		if len(child.pattern) < len(prefix) {
			if strings.HasPrefix(prefix, child.pattern) {
				child.Clean(prefix[len(child.pattern):])
			}
		}

		if strings.HasPrefix(child.pattern, prefix) {
			dels = append(dels, child.pattern)
		}
	}

	for _, del := range dels {
		n.children = removeNodes(n.children, del)
	}
}

// Remove 移除路由项
func (n *Node) Remove(pattern string, methods ...string) error {
	child := n.find(pattern)

	if child == nil {
		return fmt.Errorf("不存在的节点 %v", pattern)
	}

	if child.handlers == nil {
		return nil
	}

	if child.handlers.Remove(methods...) {
		child.parent.children = removeNodes(child.parent.children, child.pattern)
	}
	return nil
}

// Match 从子节点中查找与当前路径匹配的节点，若找不到，则返回 nil
func (n *Node) Match(path string) *Node {
	if len(n.children) == 0 && len(path) == 0 {
		return n
	}

	for _, node := range n.children {
		matched := false
		newPath := path

		switch node.nodeType {
		case ts.TypeBasic:
			matched = strings.HasPrefix(path, node.pattern)
			if matched {
				newPath = path[len(node.pattern):]
			}
		case ts.TypeNamed:
			matched, newPath = node.matchNamed(path)
		case ts.TypeRegexp:
			matched, newPath = node.matchRegexp(path)
		default: // nodeType 错误，肯定是代码级别的错误，直接 panic
			panic("无效的 nodeType 值")
		}

		if !matched {
			continue
		}

		// 即使 newPath 为空，也有可能子节点正好可以匹配空的内容。
		// 比如 /posts/{path:\\w*} 后面的 path 即为空节点。
		if nn := node.Match(newPath); nn != nil {
			return nn
		}

		if len(newPath) == 0 && // 已经到达最终点
			node.handlers != nil &&
			node.handlers.Len() > 0 {
			return node
		}
	} // end for

	return nil
}

func (n *Node) matchRegexp(path string) (bool, string) {
	loc := n.expr.FindStringIndex(path)
	if loc != nil && loc[0] == 0 {
		if loc[1] == len(path) {
			return true, path[:0]
		}
		return true, path[loc[1]+1:]
	}

	return false, path
}

func (n *Node) matchNamed(path string) (bool, string) {
	if n.endpoint {
		return true, path[:0]
	}

	index := strings.Index(path, n.suffix)
	if index > 0 { // 为零说明前面没有命名参数，肯定不正确
		return true, path[index+len(n.suffix):]
	}

	return false, path
}

// Params 由调用方确保能正常匹配 path
//
// 调用方需确保 path 与 n.Match 中传递的是相同的值，此函数中，不再作验证。
func (n *Node) Params(path string) map[string]string {
	nodes := n.getParents()
	defer nodesPool.Put(nodes)

	params := make(map[string]string, 10)

LOOP:
	for i := len(nodes) - 1; i >= 0; i-- {
		node := nodes[i]
		switch node.nodeType {
		case ts.TypeBasic:
			path = path[len(node.pattern):]
		case ts.TypeNamed:
			if node.endpoint {
				params[node.name] = path
				break LOOP
			}

			index := strings.Index(path, node.suffix)
			params[node.name] = path[:index]
			path = path[index+len(node.suffix):]
		case ts.TypeRegexp:
			subexps := node.expr.SubexpNames()
			args := node.expr.FindStringSubmatch(path)
			for index, name := range subexps {
				if len(name) > 0 && index < len(args) {
					params[name] = args[index]
				}
			}

			path = path[len(args[0]):]
		}
	}

	return params
}

// URL 根据参数生成地址
func (n *Node) URL(params map[string]string) (string, error) {
	nodes := n.getParents()
	defer nodesPool.Put(nodes)

	buf := new(bytes.Buffer)

	for i := len(nodes) - 1; i >= 0; i-- {
		node := nodes[i]
		switch node.nodeType {
		case ts.TypeBasic:
			buf.WriteString(node.pattern)
		case ts.TypeNamed:
			param, exists := params[node.name]
			if !exists {
				return "", fmt.Errorf("未找到参数 %s 的值", node.name)
			}
			buf.WriteString(param)
			buf.WriteString(node.suffix) // 如果是 endpoint suffix 肯定为空
		case ts.TypeRegexp:
			url := node.syntaxExpr.String()
			subs := append(node.syntaxExpr.Sub, node.syntaxExpr)
			for _, sub := range subs {
				if len(sub.Name) == 0 {
					continue
				}

				param, exists := params[sub.Name]
				if !exists {
					return "", fmt.Errorf("未找到参数 %v 的值", sub.Name)
				}
				url = strings.Replace(url, sub.String(), param, -1)
			}

			buf.WriteString(url)
		}
	}

	return buf.String(), nil
}

// 逐级向上获取父节点，包含当前节点。
// NOTE: 记得将 []*Node 放回对象池中。
func (n *Node) getParents() []*Node {
	nodes := nodesPool.Get().([]*Node)[:0]

	for curr := n; curr != nil; curr = curr.parent { // 从尾部向上开始获取节点
		nodes = append(nodes, curr)
	}

	return nodes
}

// SetAllow 设置当前节点的 allow 报头
func (n *Node) SetAllow(allow string) {
	if n.handlers == nil {
		n.handlers = handlers.New()
	}

	n.handlers.SetAllow(allow)
}

// Handler 获取该节点下与参数相对应的处理函数
func (n *Node) Handler(method string) http.Handler {
	if n.handlers == nil {
		return nil
	}

	return n.handlers.Handler(method)
}

// 向客户端打印节点的树状结构
func (n *Node) print(deep int) {
	fmt.Println(strings.Repeat(" ", deep*4), n.pattern)

	for _, child := range n.children {
		child.print(deep + 1)
	}
}

// 获取路由数量
func (n *Node) len() int {
	var cnt int
	for _, child := range n.children {
		cnt += child.len()
	}

	if n.handlers != nil && n.handlers.Len() > 0 {
		cnt++
	}

	return cnt
}

func removeNodes(nodes []*Node, pattern string) []*Node {
	lastIndex := len(nodes) - 1
	for index, n := range nodes {
		if n.pattern != pattern {
			continue
		}

		switch {
		case len(nodes) == 1: // 只有一个元素
			return nodes[:0]
		case index == lastIndex: // 最后一个元素
			return nodes[:lastIndex]
		default:
			return append(nodes[:index], nodes[index+1:]...)
		}
	} // end for

	return nodes
}

// 将节点 n 从 pos 位置进行拆分。后一段作为当前段的子节点，并返回当前节点。
// 若 pos 大于或等于 n.pattern 的长度，则直接返回 n 不会拆分。
//
// NOTE: 调用者需确保 pos 位置是可拆分的。
func splitNode(n *Node, pos int) (*Node, error) {
	if len(n.pattern) <= pos { // 不需要拆分
		return n, nil
	}

	p := n.parent
	if p == nil {
		return nil, errors.New("split:节点必须要有一个有效的父节点，才能进行拆分")
	}

	// 先从父节点中删除老的 n
	p.children = removeNodes(p.children, n.pattern)

	ret, err := p.newChild(ts.NewSegment(n.pattern[:pos]))
	if err != nil {
		return nil, err
	}

	c, err := ret.newChild(ts.NewSegment(n.pattern[pos:]))
	if err != nil {
		return nil, err
	}
	c.handlers = n.handlers
	c.children = n.children
	for _, item := range c.children {
		item.parent = c
	}

	return ret, nil
}
<|endoftext|>


<commit_before>// Copyright 2012, Bryan Matsuo. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*  Filename:    webapi.go
 *  Author:      Bryan Matsuo <bryan.matsuo [at] gmail.com>
 *  Created:     2012-08-12 17:11:47.70504 -0700 PDT
 *  Description: Main source file in webapi
 */

package webapi

import (
	"code.google.com/p/gorilla/context"

	"net/http"
)

// See http://gorilla-web.appspot.com/pkg/context
var Context = context.DefaultContext

type handler struct{ Interface }

// An http.Handler sets Params(r) before the api's handler is called.
func Handler(api Interface) http.Handler { return handler{api} }
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	p, err := h.Params(r)
	if err != nil {
		if h := err.Header(); h != nil {
			for k, v := range h {
				w.Header().Set(k, v)
			}
		}
		http.Error(w, string(err.Body()), err.Code())
		return
	}
	setParams(r, p)
	defer deleteParams(r)
	h.Interface.ServeHTTP(w, r)
}

// An interface for http API endpoints.
type Interface interface {
	http.Handler
	ParamsParser
}

// Create an object that implements Interface.
func New(pfn ParamsFunc, h http.Handler) Interface {
	return &webAPI{pfn, h}
}

type webAPI struct {
	pfunc ParamsFunc // couldn't resist
	h     http.Handler
}

func (api *webAPI) Params(r *http.Request) (P, Error)                { return api.pfunc(r) }
func (api *webAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) { api.h.ServeHTTP(w, r) }
<commit_msg>docs: add godoc summary<commit_after>// Copyright 2012, Bryan Matsuo. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*  Filename:    webapi.go
 *  Author:      Bryan Matsuo <bryan.matsuo [at] gmail.com>
 *  Created:     2012-08-12 17:11:47.70504 -0700 PDT
 *  Description: Main source file in webapi
 */

// Package webapi is a tooling for building web server APIs
package webapi

import (
	"code.google.com/p/gorilla/context"

	"net/http"
)

// See http://gorilla-web.appspot.com/pkg/context
var Context = context.DefaultContext

type handler struct{ Interface }

// An http.Handler sets Params(r) before the api's handler is called.
func Handler(api Interface) http.Handler { return handler{api} }
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	p, err := h.Params(r)
	if err != nil {
		if h := err.Header(); h != nil {
			for k, v := range h {
				w.Header().Set(k, v)
			}
		}
		http.Error(w, string(err.Body()), err.Code())
		return
	}
	setParams(r, p)
	defer deleteParams(r)
	h.Interface.ServeHTTP(w, r)
}

// An interface for http API endpoints.
type Interface interface {
	http.Handler
	ParamsParser
}

// Create an object that implements Interface.
func New(pfn ParamsFunc, h http.Handler) Interface {
	return &webAPI{pfn, h}
}

type webAPI struct {
	pfunc ParamsFunc // couldn't resist
	h     http.Handler
}

func (api *webAPI) Params(r *http.Request) (P, Error)                { return api.pfunc(r) }
func (api *webAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) { api.h.ServeHTTP(w, r) }
<|endoftext|>


<commit_before>// Copyright 2013 tsuru 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 io

import (
	"net/http"
	"sync"
)

var writeMutex sync.Mutex

// FlushingWriter is a custom writer that flushes after writing, if the
// underlying ResponseWriter is also an http.Flusher.
type FlushingWriter struct {
	http.ResponseWriter
	wrote bool
}

func (w *FlushingWriter) WriteHeader(code int) {
	w.wrote = true
	w.ResponseWriter.WriteHeader(code)
}

// Write writes and flushes the data.
func (w *FlushingWriter) Write(data []byte) (int, error) {
	writeMutex.Lock()
	defer writeMutex.Unlock()
	w.wrote = true
	n, err := w.ResponseWriter.Write(data)
	if f, ok := w.ResponseWriter.(http.Flusher); ok {
		f.Flush()
	}
	return n, err
}

// Wrote returns whether the method WriteHeader has been called or not.
func (w *FlushingWriter) Wrote() bool {
	return w.wrote
}
<commit_msg>updated license year.<commit_after>// Copyright 2014 tsuru 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 io

import (
	"net/http"
	"sync"
)

var writeMutex sync.Mutex

// FlushingWriter is a custom writer that flushes after writing, if the
// underlying ResponseWriter is also an http.Flusher.
type FlushingWriter struct {
	http.ResponseWriter
	wrote bool
}

func (w *FlushingWriter) WriteHeader(code int) {
	w.wrote = true
	w.ResponseWriter.WriteHeader(code)
}

// Write writes and flushes the data.
func (w *FlushingWriter) Write(data []byte) (int, error) {
	writeMutex.Lock()
	defer writeMutex.Unlock()
	w.wrote = true
	n, err := w.ResponseWriter.Write(data)
	if f, ok := w.ResponseWriter.(http.Flusher); ok {
		f.Flush()
	}
	return n, err
}

// Wrote returns whether the method WriteHeader has been called or not.
func (w *FlushingWriter) Wrote() bool {
	return w.wrote
}
<|endoftext|>


<commit_before>package ui

import (
	"fmt"
	glfw "github.com/go-gl/glfw3"
	gl "github.com/vizstra/opengl/gl43"
	"github.com/vizstra/ui/color"
	"github.com/vizstra/vg"
	"log"
	"runtime"
	"time"
)

var debug bool = false

var RR gl.Float = 0.0

type Window struct {
	Rectangle
	name     string
	endchan  chan bool
	window   *glfw.Window
	renderer vg.Renderer
	child    Drawer
	KeyDispatch
	CharDispatch
	MouseDispatch
	ScrollDispatch
}

// NewWindow returns a new Window. The returned Window
// will init opengl when its Start function is called.
func NewWindow(name, title string, x, y, w, h int) *Window {
	glfw.SetErrorCallback(errorCallback)

	// TODO Monitor support needs to be added
	window, err := glfw.CreateWindow(w, h, title, nil, nil)
	if err != nil {
		panic(err)
	}

	win := &Window{
		NewRectangle(float64(x), float64(y), float64(w), float64(h)),
		name,
		make(chan bool),
		window,
		nil,
		nil,
		NewKeyDispatch(),
		NewCharDispatch(),
		NewMouseDispatch(),
		NewScrollDispatch(),
	}

	window.SetPosition(x, y)
	window.MakeContextCurrent()

	var lastScroll time.Time
	var yoffTicks, xoffTicks float64
	var maxScrollTick = 25.0
	window.SetScrollCallback(func(w *glfw.Window, xoff float64, yoff float64) {
		if yoff != 0 && time.Since(lastScroll) < 50*time.Millisecond {
			if yoff > -maxScrollTick || yoff < maxScrollTick {
				yoffTicks = yoffTicks + yoff
			}
		} else {
			yoffTicks = yoff
		}

		if xoff != 0 && time.Since(lastScroll) < 100*time.Millisecond {
			if xoff > -maxScrollTick || xoff < maxScrollTick {
				xoffTicks = xoffTicks + xoff
			}
		} else {
			xoffTicks = xoff
		}

		lastScroll = time.Now()

		for h, _ := range win.ScrollHandlers {
			h.HandleScroll(xoffTicks, yoffTicks)
		}
	})

	window.SetKeyCallback(func(w *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
		ke := KeyEvent{Key(key), scancode, Action(action), ModifierKey(mods)}
		win.KeyDispatch.Dispatch(ke)
	})

	window.SetCharacterCallback(func(w *glfw.Window, char uint) {
		win.CharDispatch.Dispatch(char)
	})

	window.SetMouseButtonCallback(func(w *glfw.Window, button glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) {
		for h, _ := range win.MouseClickHandlers {
			h.HandleMouseClick(MouseButtonState{MouseButton(button), Action(action), ModifierKey(mods)})
		}
	})

	window.SetCursorPositionCallback(func(w *glfw.Window, xpos float64, ypos float64) {
		for h, _ := range win.MousePositionHandlers {
			h.HandleMousePosition(xpos, ypos)
		}
	})

	window.SetCursorEnterCallback(func(w *glfw.Window, entered bool) {
		for h, _ := range win.MouseEnterHandlers {
			h.HandleMouseEnter(entered)
		}
	})

	win.AddMouseEnterHandler(win)
	return win
}

func (self *Window) HandleMouseEnter(b bool) {
	if b {
		RR = 1
	} else {
		RR = 0
	}
}

func (self *Window) Draw(ctx vg.Context) {
	_, _, w, h := self.Bounds()
	fbw, fbh := self.FramebufferSize()

	// Calculate pixel ration for hi-dpi devices.
	bg := color.Gray13

	gl.ClearColor(gl.Float(bg.R), gl.Float(bg.G), gl.Float(bg.B), gl.Float(bg.A))
	gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT)

	//Do OpenGL stuff
	// Iterate over scene, rendering what can be rendered
	if self.renderer != nil {
		self.renderer.Render()
	}

	////////////////////////////
	// gl.Viewport(0, 0, 50, 50)
	// gl.Begin(gl.TRIANGLES)
	// gl.Color3f(RR, 0.2, 0.3)
	// gl.Vertex3f(0, 0, 0)
	// gl.Vertex3f(1, 0, 0)
	// gl.Vertex3f(0, 1, 0)
	// gl.End()
	// gl.Rotatef(1, 0, 0, 1)
	///////////////////////////

	now := time.Now()
	// 2D Display
	// r := float64(fbw) / float64(w)
	gl.Viewport(0, 0, gl.Sizei(fbw), gl.Sizei(fbh))

	if self.child != nil {
		ctx.BeginFrame(int(w), int(h), 1.0)
		self.child.SetBounds(0.0, 0.0, float64(w), float64(h))
		self.child.Draw(ctx)
		ctx.EndFrame()
	}
	if debug {
		fmt.Println(time.Since(now))
	}
}

func (self *Window) Start() chan bool {
	e := gl.Init()
	if e != nil {
		panic(e)
	}

	now := time.Now()
	// This is the ever important draw goroutine
	go func() {

		// super important
		runtime.LockOSThread()
		ctx := vg.NewContext()
		for !self.window.ShouldClose() {
			// gl.Enable(gl.BLEND)
			// gl.Enable(gl.LINE_SMOOTH)
			// gl.Hint(gl.LINE_SMOOTH_HINT, gl.NICEST)
			// gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)

			w, h := self.Size()
			self.SetSize(Size{float64(w), float64(h)})
			self.child.SetBounds(0.0, 0.0, float64(w), float64(h))

			if time.Since(now) > 2*time.Second {
				debug = true
				self.Draw(ctx)
				debug = false
				now = time.Now()
			} else {
				self.Draw(ctx)
			}
			self.window.SwapBuffers()
			glfw.PollEvents()

			// var total gl.Int
			// gl.GetIntegerv(0x9048, &total)

			// var available gl.Int
			// gl.GetIntegerv(0x9049, &available)

			// fmt.Println(total, available)
		}
		glfw.Terminate()
		self.endchan <- true
	}()
	return self.endchan
}

func (self *Window) SetChild(child Drawer) {
	self.child = child
	self.AddDrawerKeyHandler(child)
	self.AddDrawerCharHandler(child)
	self.AddDrawerMouseClickHandler(child)
	self.AddDrawerMousePositionHandler(child)
	self.AddDrawerMouseEnterHandler(child)
}

func (self *Window) Child() Drawer {
	return self.child
}

func (self *Window) SetRenderer(renderer vg.Renderer) {
	self.renderer = renderer
}

func (self *Window) Renderer() vg.Renderer {
	return self.renderer
}

func (self *Window) SetTitle(title string) {
	self.window.SetTitle(title)
}

func (self *Window) SetSize(s Size) {
	self.window.SetSize(int(s.W), int(s.H))
}

func (self *Window) Size() (w, h int) {
	return self.window.GetSize()
}

func (self *Window) FramebufferSize() (w, h int) {
	return self.window.GetFramebufferSize()
}

func (self *Window) SetPosition(p Position) {
	self.window.SetPosition(int(p.X), int(p.Y))
}

func (self *Window) Position() (x, y int) {
	return self.window.GetPosition()
}

func (self *Window) Hide() {
	self.window.Hide()
}

func (self *Window) Iconify() {
	self.window.Iconify()
}

func (self *Window) Show() {
	self.window.Show()
}

func (self *Window) Restore() {
	self.window.Restore()
}

func errorCallback(err glfw.ErrorCode, desc string) {
	log.Printf("%v: %v\n", err, desc)
}

func init() {
	if !glfw.Init() {
		panic("Cannot initialize windowing library.")
	}
}
<commit_msg>window.go<commit_after>package ui

import (
	"fmt"
	glfw "github.com/go-gl/glfw3"
	gl "github.com/vizstra/opengl/gl43"
	"github.com/vizstra/ui/color"
	"github.com/vizstra/vg"
	"log"
	"runtime"
	"time"
)

var debug bool = false

var RR gl.Float = 0.0

type Window struct {
	Rectangle
	name     string
	endchan  chan bool
	window   *glfw.Window
	renderer vg.Renderer
	child    Drawer
	KeyDispatch
	CharDispatch
	MouseDispatch
	ScrollDispatch
}

// NewWindow returns a new Window. The returned Window
// will init opengl when its Start function is called.
func NewWindow(name, title string, x, y, w, h int) *Window {
	glfw.SetErrorCallback(errorCallback)

	// TODO Monitor support needs to be added
	window, err := glfw.CreateWindow(w, h, title, nil, nil)
	if err != nil {
		panic(err)
	}

	win := &Window{
		NewRectangle(float64(x), float64(y), float64(w), float64(h)),
		name,
		make(chan bool),
		window,
		nil,
		nil,
		NewKeyDispatch(),
		NewCharDispatch(),
		NewMouseDispatch(),
		NewScrollDispatch(),
	}

	window.SetPosition(x, y)
	window.MakeContextCurrent()

	var lastScroll time.Time
	var yoffTicks, xoffTicks float64
	var maxScrollTick = 25.0
	window.SetScrollCallback(func(w *glfw.Window, xoff float64, yoff float64) {
		if yoff != 0 && time.Since(lastScroll) < 50*time.Millisecond {
			if yoff > -maxScrollTick || yoff < maxScrollTick {
				yoffTicks = yoffTicks + yoff
			}
		} else {
			yoffTicks = yoff
		}

		if xoff != 0 && time.Since(lastScroll) < 100*time.Millisecond {
			if xoff > -maxScrollTick || xoff < maxScrollTick {
				xoffTicks = xoffTicks + xoff
			}
		} else {
			xoffTicks = xoff
		}

		lastScroll = time.Now()

		for h, _ := range win.ScrollHandlers {
			h.HandleScroll(xoffTicks, yoffTicks)
		}
	})

	window.SetKeyCallback(func(w *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
		ke := KeyEvent{Key(key), scancode, Action(action), ModifierKey(mods)}
		win.KeyDispatch.Dispatch(ke)
	})

	window.SetCharacterCallback(func(w *glfw.Window, char uint) {
		win.CharDispatch.Dispatch(char)
	})

	window.SetMouseButtonCallback(func(w *glfw.Window, button glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) {
		for h, _ := range win.MouseClickHandlers {
			h.HandleMouseClick(MouseButtonState{MouseButton(button), Action(action), ModifierKey(mods)})
		}
	})

	window.SetCursorPositionCallback(func(w *glfw.Window, xpos float64, ypos float64) {
		for h, _ := range win.MousePositionHandlers {
			h.HandleMousePosition(xpos, ypos)
		}
	})

	window.SetCursorEnterCallback(func(w *glfw.Window, entered bool) {
		for h, _ := range win.MouseEnterHandlers {
			h.HandleMouseEnter(entered)
		}
	})

	win.AddMouseEnterHandler(win)
	return win
}

func (self *Window) HandleMouseEnter(b bool) {
	if b {
		RR = 1
	} else {
		RR = 0
	}
}

func (self *Window) Draw(ctx vg.Context) {
	_, _, w, h := self.Bounds()
	fbw, fbh := self.FramebufferSize()

	// Calculate pixel ration for hi-dpi devices.
	bg := color.Gray13

	gl.ClearColor(gl.Float(bg.R), gl.Float(bg.G), gl.Float(bg.B), gl.Float(bg.A))
	gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT)

	//Do OpenGL stuff
	// Iterate over scene, rendering what can be rendered
	if self.renderer != nil {
		self.renderer.Render()
	}

	////////////////////////////
	// gl.Viewport(0, 0, 50, 50)
	// gl.Begin(gl.TRIANGLES)
	// gl.Color3f(RR, 0.2, 0.3)
	// gl.Vertex3f(0, 0, 0)
	// gl.Vertex3f(1, 0, 0)
	// gl.Vertex3f(0, 1, 0)
	// gl.End()
	// gl.Rotatef(1, 0, 0, 1)
	///////////////////////////

	now := time.Now()
	// 2D Display
	// r := float64(fbw) / float64(w)
	gl.Viewport(0, 0, gl.Sizei(fbw), gl.Sizei(fbh))

	if self.child != nil {
		ctx.BeginFrame(int(w), int(h), 1.0)
		self.child.SetBounds(0.0, 0.0, float64(w), float64(h))
		self.child.Draw(ctx)
		ctx.EndFrame()
	}
	if debug {
		fmt.Println(time.Since(now))
	}
}

func (self *Window) Start() chan bool {
	e := gl.Init()
	if e != nil {
		panic(e)
	}

	now := time.Now()
	// This is the ever important draw goroutine
	go func() {

		// super important
		runtime.LockOSThread()
		ctx := vg.NewContext()
		for !self.window.ShouldClose() {
			// gl.Enable(gl.BLEND)
			// gl.Enable(gl.LINE_SMOOTH)
			// gl.Hint(gl.LINE_SMOOTH_HINT, gl.NICEST)
			// gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)

			w, h := self.Size()
			self.SetBounds(0, 0, float64(w), float64(h))

			if time.Since(now) > 2*time.Second {
				debug = true

				self.Draw(ctx)
				debug = false
				now = time.Now()
			} else {
				self.Draw(ctx)
			}
			self.window.SwapBuffers()
			glfw.PollEvents()

			// var total gl.Int
			// gl.GetIntegerv(0x9048, &total)

			// var available gl.Int
			// gl.GetIntegerv(0x9049, &available)

			// fmt.Println(total, available)
		}
		glfw.Terminate()
		self.endchan <- true
	}()
	return self.endchan
}

func (self *Window) SetChild(child Drawer) {
	self.child = child
	self.AddDrawerKeyHandler(child)
	self.AddDrawerCharHandler(child)
	self.AddDrawerMouseClickHandler(child)
	self.AddDrawerMousePositionHandler(child)
	self.AddDrawerMouseEnterHandler(child)
}

func (self *Window) Child() Drawer {
	return self.child
}

func (self *Window) SetRenderer(renderer vg.Renderer) {
	self.renderer = renderer
}

func (self *Window) Renderer() vg.Renderer {
	return self.renderer
}

func (self *Window) SetTitle(title string) {
	self.window.SetTitle(title)
}

func (self *Window) SetSize(size Size) {
	self.window.SetSize(int(size.W), int(size.H))
}

func (self *Window) Size() (w, h int) {
	return self.window.GetSize()
}

func (self *Window) FramebufferSize() (w, h int) {
	return self.window.GetFramebufferSize()
}

func (self *Window) SetPosition(pos Position) {
	self.window.SetPosition(int(pos.X), int(pos.Y))
}

func (self *Window) Position() (x, y int) {
	return self.window.GetPosition()
}

func (self *Window) Hide() {
	self.window.Hide()
}

func (self *Window) Iconify() {
	self.window.Iconify()
}

func (self *Window) Show() {
	self.window.Show()
}

func (self *Window) Restore() {
	self.window.Restore()
}

func errorCallback(err glfw.ErrorCode, desc string) {
	log.Printf("%v: %v\n", err, desc)
}

func init() {
	if !glfw.Init() {
		panic("Cannot initialize windowing library.")
	}
}
<|endoftext|>


<commit_before>// go-rst - A reStructuredText parser for Go
// 2014 (c) The go-rst Authors
// MIT Licensed. See LICENSE for details.

package parse

import (
	"code.google.com/p/go.text/unicode/norm"
	"github.com/demizer/go-elog"
	"strings"
	"unicode"
	"unicode/utf8"
)

type Id int

func (i Id) IdNumber() Id { return i }

// The line number of an item in the input string
type Line int

func (l Line) LineNumber() Line { return l }

// The begining location of an item in the input.
type StartPosition int

func (s StartPosition) Position() StartPosition { return s }

// itemElement are the types that are emitted by the lexer.
type itemElement int

const (
	itemEOF itemElement = iota
	itemError
	itemTitle
	itemSectionAdornment
	itemParagraph
	itemBlockquote
	itemLiteralBlock
	itemSystemMessage
	itemSpace
	itemBlankLine
)

var elements = [...]string{
	"itemEOF",
	"itemError",
	"itemTitle",
	"itemSectionAdornment",
	"itemParagraph",
	"itemBlockquote",
	"itemLiteralBlock",
	"itemSystemMessage",
	"itemSpace",
	"itemBlankLine",
}

// String implements the Stringer interface for printing itemElement types.
func (t itemElement) String() string { return elements[t] }

func (t *itemElement) UnmarshalJSON(data []byte) error {
	for num, elm := range elements {
		if elm == string(data[1:len(data)-1]) {
			*t = itemElement(num)
		}
	}
	return nil
}

// Valid section adornment runes
var sectionAdornments = []rune{'!', '"', '#', '$', '\'', '%', '&', '(', ')', '*',
	'+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\',
	']', '^', '_', '`', '{', '|', '}', '~'}

// Emitted by the lexer on End of File
const eof rune = -1

// Function prototype for scanner functions
type stateFn func(*lexer) stateFn

// Struct for tokens emitted by the scanning process
type item struct {
	Id            `json:"id"`
	Type          itemElement `json:"type"`
	Text          interface{} `json:"text"`
	Line          `json:"line"`
	StartPosition `json:"startPosition"`
	Length        int `json:"length"`
}

// The lexer struct tracks the state of the lexer
type lexer struct {
	name             string    // The name of the current lexer
	input            string    // The input text
	state            stateFn   // The current state of the lexer
	index            int       // Position in input
	start            int       // The start of the current token
	width            int       // The width of the current position
	items            chan item // The channel items are emitted to
	lastItem         *item     // The last item emitted to the channel
	lastItemPosition StartPosition
	id               int // Unique id for each item emitted
}

// lex is the entry point of the lexer
func lex(name, input string) *lexer {
	log.Debugln("Start")
	if !norm.NFC.IsNormalString(input) {
		input = norm.NFC.String(input)
	}
	l := &lexer{
		name:  name,
		input: input,
		items: make(chan item),
	}
	go l.run()
	log.Debugln("End")
	return l
}

// run is the engine of the lexing process.
func (l *lexer) run() {
	log.Debugln("Start")
	for l.state = lexStart; l.state != nil; {
		l.state = l.state(l)
	}
	log.Debugln("End")
}

// emit passes an item back to the client.
func (l *lexer) emit(t itemElement) {
	if l.start == l.index && int(l.index) < len(l.input) {
		l.index += 1
	}
	log.Infof("#### %s: %q start: %d pos: %d line: %d\n", t,
		l.input[l.start:l.index], l.start, l.index, l.lineNumber())
	l.id++
	length := utf8.RuneCountInString(l.input[l.start:l.index])
	nItem := item{
		Id:            Id(l.id),
		Type:          t,
		Text:          l.input[l.start:l.index],
		Line:          Line(l.lineNumber()),
		StartPosition: StartPosition(l.start + 1), // Positions start at 1 not 0
		Length:        length,
	}
	l.items <- nItem
	l.lastItem = &nItem
	l.start = l.index
}

// backup backs up the lexer by one position using the width of the last rune retrieved from the
// input.
func (l *lexer) backup(pos int) {
	for i := 0; i < pos; i++ {
		l.index -= l.width
	}
}

// current returns the rune at the current position in the input.
func (l *lexer) current() rune {
	r, _ := utf8.DecodeRuneInString(l.input[l.index:])
	return r
}

// peek looks ahead in the input by one position and returns the rune.
func (l *lexer) peek() rune {
	r := l.next()
	l.backup(1)
	return r
}

// advance fast forwards the lexer to the next rune in the input specified by "to".
func (l *lexer) advance(to rune) {
	for {
		if l.next() == eof || to == l.current() {
			break
		}
	}
}

// next advances the position of the lexer by one rune and returns that rune.
func (l *lexer) next() rune {
	if int(l.index) >= len(l.input) {
		log.Debugln("Reached eof!")
		l.width = 0
		return eof
	}
	r, w := utf8.DecodeRuneInString(l.input[l.index:])
	l.width = w
	l.index += l.width
	if unicode.In(r, unicode.Diacritic) {
		log.Debugf("DIACRITIC char found: %#U width: %d\n", r, w)
	}
	log.Debugf("cur: %q start: %d pos: %d\n", r, l.start, l.index)
	return r
}

// nextItem returns the next item from the input.
func (l *lexer) nextItem() *item {
	item := <-l.items
	l.lastItemPosition = item.StartPosition
	return &item

}

func (l *lexer) lineNumber() int {
	return strings.Count(l.input[:l.index-1], "\n") + 1
}

// isStartOfLine calculates if the current position of the lexer in the input is the beginning of a
// new line.
func (l *lexer) isStartOfLine() bool {
	return (l.index - l.start) == 0
}

// isSpace reports whether r is a space character.
func isSpace(r rune) bool {
	return r == ' ' || r == '\t' || r == '\n' || r == '\r'
}

// isEndOfLine reports whether r is an end-of-line character.
func isEndOfLine(r rune) bool {
	return r == '\r' || r == '\n'
}

// isSection compares a number of positions (skipping whitespace) to
// determine if the runes are sectionAdornments and returns a true if the
// positions match each other. Rune comparison begins at the current lexer
// position. isSection returns false if there is a blank line between the
// positions or if there is a rune mismatch between positions.
func isSection(l *lexer) bool {
	log.Debugln("Start")
	var lookPositions = 2
	var newLineNum int
	var runePositions []rune
	var matchCount int
	cPos := l.index

	exit := func(value bool) bool {
		l.index = cPos
		log.Debugln("Returning", value)
		return value
	}

	log.Debugln("Looking ahead", lookPositions, "position(s) for sectionAdornments...")

	// Check two runes to see if they are section adornments, if not, we will check the next
	// line (after whitespace).
	if isSectionAdornment(l.current()) && isSectionAdornment(l.peek()) &&
		l.current() == l.peek() {
		return true
	}

	// Advance to the end of the line
	l.advance('\n')

	for j := 0; j < lookPositions; j++ {
		for isSpace(l.current()) {
			if isEndOfLine(l.current()) {
				if newLineNum == 1 {
					log.Debugln("Too many newlines!")
					return exit(false)
				}
				newLineNum += 1
				log.Debugln("newLineNum:", newLineNum)
			}
			l.next()
		}
		if isSectionAdornment(l.current()) {
			log.Debugf("Found adornment: \"%s\" pos: %d\n", string(l.current()), l.index)
			runePositions = append(runePositions, l.current())
			matchCount += 1
		}
		if l.next() == eof {
			return exit(false)
		}
	}

	if len(runePositions) == 0 || matchCount != lookPositions {
		return exit(false)
	}

	return exit(true)
}

// isSectionAdornment returns true if r matches a section adornment.
func isSectionAdornment(r rune) bool {
	for _, a := range sectionAdornments {
		if a == r {
			return true
		}
	}
	return false
}

// lexStart is the first stateFn called by run(). From here other stateFn's are called depending on
// the input. When this function returns nil, the lexing is finished and run() will exit.
func lexStart(l *lexer) stateFn {
	log.Debugln("Start")
	for {
		var tokenLength = l.index - l.start
		r := l.current()
		// log.Debugln("tokenLength:", tokenLength, l.width)
		if tokenLength <= l.width && l.width > 0 && !isEndOfLine(r) {
			log.Debugln("Start of new token")
			if isSection(l) {
				return lexSection
			} else if isSpace(r) {
				return lexSpace
			}
		} else if isEndOfLine(r) {
			log.Debugln("isEndOfLine == true")
			if l.index > l.start {
				l.emit(itemParagraph)
				l.start += 1 // Skip the new line
			} else if l.start == l.index {
				l.emit(itemBlankLine)
			}
		}
		if l.next() == eof {
			break
		}
	}

	// Correctly reached eof.
	l.emit(itemEOF)
	log.Debugln("End")
	return nil
}

// lexSpace consumes space characters (space and tab) in the input and emits a itemSpace token.
func lexSpace(l *lexer) stateFn {
	log.Debugln("Start")
	for isSpace(l.current()) {
		l.next()
	}
	if l.start < l.index {
		l.emit(itemSpace)
		l.next()
	}
	log.Debugln("End")
	return lexStart
}

// lexSection is used after isSection() has determined that the next runes of input are section.
// From here, the lexTitle() and lexSectionAdornment() are called based on the input.
func lexSection(l *lexer) stateFn {
	log.Debugln("Start")
	// The order of the case statements matter here
	switch r := l.next(); {
	case isSectionAdornment(r):
		if l.lastItem != nil && l.lastItem.Type != itemTitle {
			return lexSectionAdornment
		}
		lexSectionAdornment(l)
	case unicode.IsPrint(r):
		return lexTitle
	}
	log.Debugln("Exit")
	return lexStart
}

// lexTitle consumes input until newline and emits an itemTitle token. If spaces are detected at the
// start of the line, an itemSpace is emitted. Spaces after the title (and before newline) are
// ignored. On completion control is returned to lexSection.
func lexTitle(l *lexer) stateFn {
	log.Debugln("Start")
	l.backup(2) // Start from the newline of the previous line
	for {
		if isSpace(l.current()) && l.isStartOfLine() {
			return lexSpace
		}
		l.next()
		if l.peek() == '\n' {
			l.emit(itemTitle)
			l.start += 1
			l.index += 1
			break
		}
	}
	log.Debugln("End")
	return lexSection
}

// lexSectionAdornment advances the lexer until a newline is encountered and emits a
// itemSectionAdornment token. Control is returned to lexSection() on completion.
func lexSectionAdornment(l *lexer) stateFn {
	log.Debugln("Start")
	for {
		//TODO: Add adornment rune check
		l.next()
		if l.peek() == '\n' {
			l.emit(itemSectionAdornment)
			l.start += 1
			l.index += 1
			break
		}
	}
	log.Debugln("End")
	return lexSection
}
<commit_msg>lex.go: Add String() methods to types<commit_after>// go-rst - A reStructuredText parser for Go
// 2014 (c) The go-rst Authors
// MIT Licensed. See LICENSE for details.

package parse

import (
	"code.google.com/p/go.text/unicode/norm"
	"github.com/demizer/go-elog"
	"strings"
	"unicode"
	"unicode/utf8"
	"strconv"
)

type Id int

func (i Id) IdNumber() Id { return i }

func (i Id) String() string { return strconv.Itoa(int(i)) }

// The line number of an item in the input string
type Line int

func (l Line) LineNumber() Line { return l }

func (l Line) String() string { return strconv.Itoa(int(l)) }

// The begining location of an item in the input.
type StartPosition int

func (s StartPosition) Position() StartPosition { return s }

func (s StartPosition) String() string { return strconv.Itoa(int(s)) }

// itemElement are the types that are emitted by the lexer.
type itemElement int

const (
	itemEOF itemElement = iota
	itemError
	itemTitle
	itemSectionAdornment
	itemParagraph
	itemBlockquote
	itemLiteralBlock
	itemSystemMessage
	itemSpace
	itemBlankLine
)

var elements = [...]string{
	"itemEOF",
	"itemError",
	"itemTitle",
	"itemSectionAdornment",
	"itemParagraph",
	"itemBlockquote",
	"itemLiteralBlock",
	"itemSystemMessage",
	"itemSpace",
	"itemBlankLine",
}

// String implements the Stringer interface for printing itemElement types.
func (t itemElement) String() string { return elements[t] }

func (t *itemElement) UnmarshalJSON(data []byte) error {
	for num, elm := range elements {
		if elm == string(data[1:len(data)-1]) {
			*t = itemElement(num)
		}
	}
	return nil
}

// Valid section adornment runes
var sectionAdornments = []rune{'!', '"', '#', '$', '\'', '%', '&', '(', ')', '*',
	'+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\',
	']', '^', '_', '`', '{', '|', '}', '~'}

// Emitted by the lexer on End of File
const eof rune = -1

// Function prototype for scanner functions
type stateFn func(*lexer) stateFn

// Struct for tokens emitted by the scanning process
type item struct {
	Id            `json:"id"`
	Type          itemElement `json:"type"`
	Text          interface{} `json:"text"`
	Line          `json:"line"`
	StartPosition `json:"startPosition"`
	Length        int `json:"length"`
}

// The lexer struct tracks the state of the lexer
type lexer struct {
	name             string    // The name of the current lexer
	input            string    // The input text
	state            stateFn   // The current state of the lexer
	index            int       // Position in input
	start            int       // The start of the current token
	width            int       // The width of the current position
	items            chan item // The channel items are emitted to
	lastItem         *item     // The last item emitted to the channel
	lastItemPosition StartPosition
	id               int // Unique id for each item emitted
}

// lex is the entry point of the lexer
func lex(name, input string) *lexer {
	log.Debugln("Start")
	if !norm.NFC.IsNormalString(input) {
		input = norm.NFC.String(input)
	}
	l := &lexer{
		name:  name,
		input: input,
		items: make(chan item),
	}
	go l.run()
	log.Debugln("End")
	return l
}

// run is the engine of the lexing process.
func (l *lexer) run() {
	log.Debugln("Start")
	for l.state = lexStart; l.state != nil; {
		l.state = l.state(l)
	}
	log.Debugln("End")
}

// emit passes an item back to the client.
func (l *lexer) emit(t itemElement) {
	if l.start == l.index && int(l.index) < len(l.input) {
		l.index += 1
	}
	log.Infof("#### %s: %q start: %d pos: %d line: %d\n", t,
		l.input[l.start:l.index], l.start, l.index, l.lineNumber())
	l.id++
	length := utf8.RuneCountInString(l.input[l.start:l.index])
	nItem := item{
		Id:            Id(l.id),
		Type:          t,
		Text:          l.input[l.start:l.index],
		Line:          Line(l.lineNumber()),
		StartPosition: StartPosition(l.start + 1), // Positions start at 1 not 0
		Length:        length,
	}
	l.items <- nItem
	l.lastItem = &nItem
	l.start = l.index
}

// backup backs up the lexer by one position using the width of the last rune retrieved from the
// input.
func (l *lexer) backup(pos int) {
	for i := 0; i < pos; i++ {
		l.index -= l.width
	}
}

// current returns the rune at the current position in the input.
func (l *lexer) current() rune {
	r, _ := utf8.DecodeRuneInString(l.input[l.index:])
	return r
}

// peek looks ahead in the input by one position and returns the rune.
func (l *lexer) peek() rune {
	r := l.next()
	l.backup(1)
	return r
}

// advance fast forwards the lexer to the next rune in the input specified by "to".
func (l *lexer) advance(to rune) {
	for {
		if l.next() == eof || to == l.current() {
			break
		}
	}
}

// next advances the position of the lexer by one rune and returns that rune.
func (l *lexer) next() rune {
	if int(l.index) >= len(l.input) {
		log.Debugln("Reached eof!")
		l.width = 0
		return eof
	}
	r, w := utf8.DecodeRuneInString(l.input[l.index:])
	l.width = w
	l.index += l.width
	if unicode.In(r, unicode.Diacritic) {
		log.Debugf("DIACRITIC char found: %#U width: %d\n", r, w)
	}
	log.Debugf("cur: %q start: %d pos: %d\n", r, l.start, l.index)
	return r
}

// nextItem returns the next item from the input.
func (l *lexer) nextItem() *item {
	item := <-l.items
	l.lastItemPosition = item.StartPosition
	return &item

}

func (l *lexer) lineNumber() int {
	return strings.Count(l.input[:l.index-1], "\n") + 1
}

// isStartOfLine calculates if the current position of the lexer in the input is the beginning of a
// new line.
func (l *lexer) isStartOfLine() bool {
	return (l.index - l.start) == 0
}

// isSpace reports whether r is a space character.
func isSpace(r rune) bool {
	return r == ' ' || r == '\t' || r == '\n' || r == '\r'
}

// isEndOfLine reports whether r is an end-of-line character.
func isEndOfLine(r rune) bool {
	return r == '\r' || r == '\n'
}

// isSection compares a number of positions (skipping whitespace) to
// determine if the runes are sectionAdornments and returns a true if the
// positions match each other. Rune comparison begins at the current lexer
// position. isSection returns false if there is a blank line between the
// positions or if there is a rune mismatch between positions.
func isSection(l *lexer) bool {
	log.Debugln("Start")
	var lookPositions = 2
	var newLineNum int
	var runePositions []rune
	var matchCount int
	cPos := l.index

	exit := func(value bool) bool {
		l.index = cPos
		log.Debugln("Returning", value)
		return value
	}

	log.Debugln("Looking ahead", lookPositions, "position(s) for sectionAdornments...")

	// Check two runes to see if they are section adornments, if not, we will check the next
	// line (after whitespace).
	if isSectionAdornment(l.current()) && isSectionAdornment(l.peek()) &&
		l.current() == l.peek() {
		return true
	}

	// Advance to the end of the line
	l.advance('\n')

	for j := 0; j < lookPositions; j++ {
		for isSpace(l.current()) {
			if isEndOfLine(l.current()) {
				if newLineNum == 1 {
					log.Debugln("Too many newlines!")
					return exit(false)
				}
				newLineNum += 1
				log.Debugln("newLineNum:", newLineNum)
			}
			l.next()
		}
		if isSectionAdornment(l.current()) {
			log.Debugf("Found adornment: \"%s\" pos: %d\n", string(l.current()), l.index)
			runePositions = append(runePositions, l.current())
			matchCount += 1
		}
		if l.next() == eof {
			return exit(false)
		}
	}

	if len(runePositions) == 0 || matchCount != lookPositions {
		return exit(false)
	}

	return exit(true)
}

// isSectionAdornment returns true if r matches a section adornment.
func isSectionAdornment(r rune) bool {
	for _, a := range sectionAdornments {
		if a == r {
			return true
		}
	}
	return false
}

// lexStart is the first stateFn called by run(). From here other stateFn's are called depending on
// the input. When this function returns nil, the lexing is finished and run() will exit.
func lexStart(l *lexer) stateFn {
	log.Debugln("Start")
	for {
		var tokenLength = l.index - l.start
		r := l.current()
		// log.Debugln("tokenLength:", tokenLength, l.width)
		if tokenLength <= l.width && l.width > 0 && !isEndOfLine(r) {
			log.Debugln("Start of new token")
			if isSection(l) {
				return lexSection
			} else if isSpace(r) {
				return lexSpace
			}
		} else if isEndOfLine(r) {
			log.Debugln("isEndOfLine == true")
			if l.index > l.start {
				l.emit(itemParagraph)
				l.start += 1 // Skip the new line
			} else if l.start == l.index {
				l.emit(itemBlankLine)
			}
		}
		if l.next() == eof {
			break
		}
	}

	// Correctly reached eof.
	l.emit(itemEOF)
	log.Debugln("End")
	return nil
}

// lexSpace consumes space characters (space and tab) in the input and emits a itemSpace token.
func lexSpace(l *lexer) stateFn {
	log.Debugln("Start")
	for isSpace(l.current()) {
		l.next()
	}
	if l.start < l.index {
		l.emit(itemSpace)
		l.next()
	}
	log.Debugln("End")
	return lexStart
}

// lexSection is used after isSection() has determined that the next runes of input are section.
// From here, the lexTitle() and lexSectionAdornment() are called based on the input.
func lexSection(l *lexer) stateFn {
	log.Debugln("Start")
	// The order of the case statements matter here
	switch r := l.next(); {
	case isSectionAdornment(r):
		if l.lastItem != nil && l.lastItem.Type != itemTitle {
			return lexSectionAdornment
		}
		lexSectionAdornment(l)
	case unicode.IsPrint(r):
		return lexTitle
	}
	log.Debugln("Exit")
	return lexStart
}

// lexTitle consumes input until newline and emits an itemTitle token. If spaces are detected at the
// start of the line, an itemSpace is emitted. Spaces after the title (and before newline) are
// ignored. On completion control is returned to lexSection.
func lexTitle(l *lexer) stateFn {
	log.Debugln("Start")
	l.backup(2) // Start from the newline of the previous line
	for {
		if isSpace(l.current()) && l.isStartOfLine() {
			return lexSpace
		}
		l.next()
		if l.peek() == '\n' {
			l.emit(itemTitle)
			l.start += 1
			l.index += 1
			break
		}
	}
	log.Debugln("End")
	return lexSection
}

// lexSectionAdornment advances the lexer until a newline is encountered and emits a
// itemSectionAdornment token. Control is returned to lexSection() on completion.
func lexSectionAdornment(l *lexer) stateFn {
	log.Debugln("Start")
	for {
		//TODO: Add adornment rune check
		l.next()
		if l.peek() == '\n' {
			l.emit(itemSectionAdornment)
			l.start += 1
			l.index += 1
			break
		}
	}
	log.Debugln("End")
	return lexSection
}
<|endoftext|>


<commit_before>package godirwalk_test

import (
	"os"
	"path/filepath"
	"testing"

	"github.com/karrick/godirwalk"
)

const testScratchBufferSize = 16 * 1024

func helperFilepathWalk(tb testing.TB, osDirname string) []string {
	var entries []string
	err := filepath.Walk(osDirname, func(osPathname string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.Name() == "skip" {
			return filepath.SkipDir
		}
		// filepath.Walk invokes callback function with a slashed version of the
		// pathname, while godirwalk invokes callback function with the
		// os-specific pathname separator.
		entries = append(entries, filepath.ToSlash(osPathname))
		return nil
	})
	if err != nil {
		tb.Fatal(err)
	}
	return entries
}

func helperGodirwalkWalk(tb testing.TB, osDirname string) []string {
	var entries []string
	err := godirwalk.Walk(osDirname, &godirwalk.Options{
		Callback: func(osPathname string, dirent *godirwalk.Dirent) error {
			if dirent.Name() == "skip" {
				return filepath.SkipDir
			}
			// filepath.Walk invokes callback function with a slashed version of
			// the pathname, while godirwalk invokes callback function with the
			// os-specific pathname separator.
			entries = append(entries, filepath.ToSlash(osPathname))
			return nil
		},
		ScratchBuffer: make([]byte, testScratchBufferSize),
	})
	if err != nil {
		tb.Fatal(err)
	}
	return entries
}

func symlinkAbs(oldname, newname string) error {
	absDir, err := filepath.Abs(oldname)
	if err != nil {
		return err
	}

	if err = os.Symlink(absDir, newname); err != nil {
		return err
	}

	return nil
}

func TestWalkSkipDir(t *testing.T) {
	// Ensure the results from calling filepath.Walk exactly match the results
	// for calling this library's walk function.

	test := func(t *testing.T, osDirname string) {
		expected := helperFilepathWalk(t, osDirname)
		actual := helperGodirwalkWalk(t, osDirname)

		if got, want := len(actual), len(expected); got != want {
			t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
		}

		for i := 0; i < len(actual); i++ {
			if got, want := actual[i], expected[i]; got != want {
				t.Errorf("(GOT) %v; (WNT) %v", got, want)
			}
		}
	}

	// Test cases for encountering the filepath.SkipDir error at different times
	// from the call.

	t.Run("SkipFileAtRoot", func(t *testing.T) {
		test(t, "testdata/dir1/dir1a")
	})

	t.Run("SkipFileUnderRoot", func(t *testing.T) {
		test(t, "testdata/dir1")
	})

	t.Run("SkipDirAtRoot", func(t *testing.T) {
		test(t, "testdata/dir2/skip")
	})

	t.Run("SkipDirUnderRoot", func(t *testing.T) {
		test(t, "testdata/dir2")
	})

	t.Run("SkipDirOnSymlink", func(t *testing.T) {
		osDirname := "testdata/dir3"
		actual := helperGodirwalkWalk(t, osDirname)

		expected := []string{
			"testdata/dir3",
			"testdata/dir3/aaa.txt",
			"testdata/dir3/zzz",
			"testdata/dir3/zzz/aaa.txt",
		}

		if got, want := len(actual), len(expected); got != want {
			t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
		}

		for i := 0; i < len(actual); i++ {
			if got, want := actual[i], expected[i]; got != want {
				t.Errorf("(GOT) %v; (WNT) %v", got, want)
			}
		}
	})
}

func TestWalkFollowSymbolicLinksFalse(t *testing.T) {
	const (
		osDirname = "testdata/dir4"
		symlink   = "testdata/dir4/symlinkToAbsDirectory"
	)

	if err := symlinkAbs("testdata/dir4/zzz", symlink); err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := os.Remove(symlink); err != nil {
			t.Error(err)
		}
	}()

	var actual []string
	err := godirwalk.Walk(osDirname, &godirwalk.Options{
		Callback: func(osPathname string, dirent *godirwalk.Dirent) error {
			if dirent.Name() == "skip" {
				return filepath.SkipDir
			}
			// filepath.Walk invokes callback function with a slashed version of
			// the pathname, while godirwalk invokes callback function with the
			// os-specific pathname separator.
			actual = append(actual, filepath.ToSlash(osPathname))
			return nil
		},
	})
	if err != nil {
		t.Fatal(err)
	}

	expected := []string{
		"testdata/dir4",
		"testdata/dir4/aaa.txt",
		"testdata/dir4/symlinkToAbsDirectory",
		"testdata/dir4/symlinkToDirectory",
		"testdata/dir4/symlinkToFile",
		"testdata/dir4/zzz",
		"testdata/dir4/zzz/aaa.txt",
	}

	if got, want := len(actual), len(expected); got != want {
		t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
	}

	for i := 0; i < len(actual); i++ {
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

func TestWalkFollowSymbolicLinksTrue(t *testing.T) {
	const (
		osDirname = "testdata/dir4"
		symlink   = "testdata/dir4/symlinkToAbsDirectory"
	)

	if err := symlinkAbs("testdata/dir4/zzz", symlink); err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := os.Remove(symlink); err != nil {
			t.Error(err)
		}
	}()

	var actual []string
	err := godirwalk.Walk(osDirname, &godirwalk.Options{
		FollowSymbolicLinks: true,
		Callback: func(osPathname string, dirent *godirwalk.Dirent) error {
			if dirent.Name() == "skip" {
				return filepath.SkipDir
			}
			// filepath.Walk invokes callback function with a slashed version of
			// the pathname, while godirwalk invokes callback function with the
			// os-specific pathname separator.
			actual = append(actual, filepath.ToSlash(osPathname))
			return nil
		},
	})
	if err != nil {
		t.Fatal(err)
	}

	expected := []string{
		"testdata/dir4",
		"testdata/dir4/aaa.txt",
		"testdata/dir4/symlinkToAbsDirectory",
		"testdata/dir4/symlinkToAbsDirectory/aaa.txt",
		"testdata/dir4/symlinkToDirectory",
		"testdata/dir4/symlinkToDirectory/aaa.txt",
		"testdata/dir4/symlinkToFile",
		"testdata/dir4/zzz",
		"testdata/dir4/zzz/aaa.txt",
	}

	if got, want := len(actual), len(expected); got != want {
		t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
	}

	for i := 0; i < len(actual); i++ {
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

func TestPostChildrenCallback(t *testing.T) {
	const osDirname = "testdata/dir5"

	var actual []string

	err := godirwalk.Walk(osDirname, &godirwalk.Options{
		ScratchBuffer: make([]byte, testScratchBufferSize),
		Callback: func(osPathname string, _ *godirwalk.Dirent) error {
			t.Logf("walk in: %s", osPathname)
			return nil
		},
		PostChildrenCallback: func(osPathname string, de *godirwalk.Dirent) error {
			t.Logf("walk out: %s", osPathname)
			actual = append(actual, osPathname)
			return nil
		},
	})
	if err != nil {
		t.Errorf("(GOT): %v; (WNT): %v", err, nil)
	}

	expected := []string{
		"testdata/dir5/a2/a2a",
		"testdata/dir5/a2",
		"testdata/dir5",
	}

	if got, want := len(actual), len(expected); got != want {
		t.Errorf("(GOT) %v; (WNT) %v", got, want)
	}

	for i := 0; i < len(actual); i++ {
		if i >= len(expected) {
			t.Fatalf("(GOT) %v; (WNT): %v", actual[i], nil)
		}
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

var goPrefix = filepath.Join(os.Getenv("GOPATH"), "src")

func BenchmarkFilepathWalk(b *testing.B) {
	if testing.Short() {
		b.Skip("Skipping benchmark using user's Go source directory")
	}

	for i := 0; i < b.N; i++ {
		_ = helperFilepathWalk(b, goPrefix)
	}
}

func BenchmarkGoDirWalk(b *testing.B) {
	if testing.Short() {
		b.Skip("Skipping benchmark using user's Go source directory")
	}

	for i := 0; i < b.N; i++ {
		_ = helperGodirwalkWalk(b, goPrefix)
	}
}

const flameIterations = 10

func BenchmarkFlameGraphFilepathWalk(b *testing.B) {
	for i := 0; i < flameIterations; i++ {
		_ = helperFilepathWalk(b, goPrefix)
	}
}

func BenchmarkFlameGraphGoDirWalk(b *testing.B) {
	for i := 0; i < flameIterations; i++ {
		_ = helperGodirwalkWalk(b, goPrefix)
	}
}
<commit_msg>more Go idiomatic<commit_after>package godirwalk_test

import (
	"os"
	"path/filepath"
	"testing"

	"github.com/karrick/godirwalk"
)

const testScratchBufferSize = 16 * 1024

func helperFilepathWalk(tb testing.TB, osDirname string) []string {
	var entries []string
	err := filepath.Walk(osDirname, func(osPathname string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.Name() == "skip" {
			return filepath.SkipDir
		}
		// filepath.Walk invokes callback function with a slashed version of the
		// pathname, while godirwalk invokes callback function with the
		// os-specific pathname separator.
		entries = append(entries, filepath.ToSlash(osPathname))
		return nil
	})
	if err != nil {
		tb.Fatal(err)
	}
	return entries
}

func helperGodirwalkWalk(tb testing.TB, osDirname string) []string {
	var entries []string
	err := godirwalk.Walk(osDirname, &godirwalk.Options{
		Callback: func(osPathname string, dirent *godirwalk.Dirent) error {
			if dirent.Name() == "skip" {
				return filepath.SkipDir
			}
			// filepath.Walk invokes callback function with a slashed version of
			// the pathname, while godirwalk invokes callback function with the
			// os-specific pathname separator.
			entries = append(entries, filepath.ToSlash(osPathname))
			return nil
		},
		ScratchBuffer: make([]byte, testScratchBufferSize),
	})
	if err != nil {
		tb.Fatal(err)
	}
	return entries
}

func symlinkAbs(oldname, newname string) error {
	absDir, err := filepath.Abs(oldname)
	if err != nil {
		return err
	}
	return os.Symlink(absDir, newname)
}

func TestWalkSkipDir(t *testing.T) {
	// Ensure the results from calling filepath.Walk exactly match the results
	// for calling this library's walk function.

	test := func(t *testing.T, osDirname string) {
		expected := helperFilepathWalk(t, osDirname)
		actual := helperGodirwalkWalk(t, osDirname)

		if got, want := len(actual), len(expected); got != want {
			t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
		}

		for i := 0; i < len(actual); i++ {
			if got, want := actual[i], expected[i]; got != want {
				t.Errorf("(GOT) %v; (WNT) %v", got, want)
			}
		}
	}

	// Test cases for encountering the filepath.SkipDir error at different times
	// from the call.

	t.Run("SkipFileAtRoot", func(t *testing.T) {
		test(t, "testdata/dir1/dir1a")
	})

	t.Run("SkipFileUnderRoot", func(t *testing.T) {
		test(t, "testdata/dir1")
	})

	t.Run("SkipDirAtRoot", func(t *testing.T) {
		test(t, "testdata/dir2/skip")
	})

	t.Run("SkipDirUnderRoot", func(t *testing.T) {
		test(t, "testdata/dir2")
	})

	t.Run("SkipDirOnSymlink", func(t *testing.T) {
		osDirname := "testdata/dir3"
		actual := helperGodirwalkWalk(t, osDirname)

		expected := []string{
			"testdata/dir3",
			"testdata/dir3/aaa.txt",
			"testdata/dir3/zzz",
			"testdata/dir3/zzz/aaa.txt",
		}

		if got, want := len(actual), len(expected); got != want {
			t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
		}

		for i := 0; i < len(actual); i++ {
			if got, want := actual[i], expected[i]; got != want {
				t.Errorf("(GOT) %v; (WNT) %v", got, want)
			}
		}
	})
}

func TestWalkFollowSymbolicLinksFalse(t *testing.T) {
	const (
		osDirname = "testdata/dir4"
		symlink   = "testdata/dir4/symlinkToAbsDirectory"
	)

	if err := symlinkAbs("testdata/dir4/zzz", symlink); err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := os.Remove(symlink); err != nil {
			t.Error(err)
		}
	}()

	var actual []string
	err := godirwalk.Walk(osDirname, &godirwalk.Options{
		Callback: func(osPathname string, dirent *godirwalk.Dirent) error {
			if dirent.Name() == "skip" {
				return filepath.SkipDir
			}
			// filepath.Walk invokes callback function with a slashed version of
			// the pathname, while godirwalk invokes callback function with the
			// os-specific pathname separator.
			actual = append(actual, filepath.ToSlash(osPathname))
			return nil
		},
	})
	if err != nil {
		t.Fatal(err)
	}

	expected := []string{
		"testdata/dir4",
		"testdata/dir4/aaa.txt",
		"testdata/dir4/symlinkToAbsDirectory",
		"testdata/dir4/symlinkToDirectory",
		"testdata/dir4/symlinkToFile",
		"testdata/dir4/zzz",
		"testdata/dir4/zzz/aaa.txt",
	}

	if got, want := len(actual), len(expected); got != want {
		t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
	}

	for i := 0; i < len(actual); i++ {
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

func TestWalkFollowSymbolicLinksTrue(t *testing.T) {
	const (
		osDirname = "testdata/dir4"
		symlink   = "testdata/dir4/symlinkToAbsDirectory"
	)

	if err := symlinkAbs("testdata/dir4/zzz", symlink); err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := os.Remove(symlink); err != nil {
			t.Error(err)
		}
	}()

	var actual []string
	err := godirwalk.Walk(osDirname, &godirwalk.Options{
		FollowSymbolicLinks: true,
		Callback: func(osPathname string, dirent *godirwalk.Dirent) error {
			if dirent.Name() == "skip" {
				return filepath.SkipDir
			}
			// filepath.Walk invokes callback function with a slashed version of
			// the pathname, while godirwalk invokes callback function with the
			// os-specific pathname separator.
			actual = append(actual, filepath.ToSlash(osPathname))
			return nil
		},
	})
	if err != nil {
		t.Fatal(err)
	}

	expected := []string{
		"testdata/dir4",
		"testdata/dir4/aaa.txt",
		"testdata/dir4/symlinkToAbsDirectory",
		"testdata/dir4/symlinkToAbsDirectory/aaa.txt",
		"testdata/dir4/symlinkToDirectory",
		"testdata/dir4/symlinkToDirectory/aaa.txt",
		"testdata/dir4/symlinkToFile",
		"testdata/dir4/zzz",
		"testdata/dir4/zzz/aaa.txt",
	}

	if got, want := len(actual), len(expected); got != want {
		t.Fatalf("\n(GOT)\n\t%#v\n(WNT)\n\t%#v", actual, expected)
	}

	for i := 0; i < len(actual); i++ {
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

func TestPostChildrenCallback(t *testing.T) {
	const osDirname = "testdata/dir5"

	var actual []string

	err := godirwalk.Walk(osDirname, &godirwalk.Options{
		ScratchBuffer: make([]byte, testScratchBufferSize),
		Callback: func(osPathname string, _ *godirwalk.Dirent) error {
			t.Logf("walk in: %s", osPathname)
			return nil
		},
		PostChildrenCallback: func(osPathname string, de *godirwalk.Dirent) error {
			t.Logf("walk out: %s", osPathname)
			actual = append(actual, osPathname)
			return nil
		},
	})
	if err != nil {
		t.Errorf("(GOT): %v; (WNT): %v", err, nil)
	}

	expected := []string{
		"testdata/dir5/a2/a2a",
		"testdata/dir5/a2",
		"testdata/dir5",
	}

	if got, want := len(actual), len(expected); got != want {
		t.Errorf("(GOT) %v; (WNT) %v", got, want)
	}

	for i := 0; i < len(actual); i++ {
		if i >= len(expected) {
			t.Fatalf("(GOT) %v; (WNT): %v", actual[i], nil)
		}
		if got, want := actual[i], expected[i]; got != want {
			t.Errorf("(GOT) %v; (WNT) %v", got, want)
		}
	}
}

var goPrefix = filepath.Join(os.Getenv("GOPATH"), "src")

func BenchmarkFilepathWalk(b *testing.B) {
	if testing.Short() {
		b.Skip("Skipping benchmark using user's Go source directory")
	}

	for i := 0; i < b.N; i++ {
		_ = helperFilepathWalk(b, goPrefix)
	}
}

func BenchmarkGoDirWalk(b *testing.B) {
	if testing.Short() {
		b.Skip("Skipping benchmark using user's Go source directory")
	}

	for i := 0; i < b.N; i++ {
		_ = helperGodirwalkWalk(b, goPrefix)
	}
}

const flameIterations = 10

func BenchmarkFlameGraphFilepathWalk(b *testing.B) {
	for i := 0; i < flameIterations; i++ {
		_ = helperFilepathWalk(b, goPrefix)
	}
}

func BenchmarkFlameGraphGoDirWalk(b *testing.B) {
	for i := 0; i < flameIterations; i++ {
		_ = helperGodirwalkWalk(b, goPrefix)
	}
}
<|endoftext|>