<commit_before>// Copyright 2016 Hajime Hoshi
//
// 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.

// +build !js

package glfw

import (
	"fmt"
	"unsafe"

	"golang.org/x/sys/windows"

	"github.com/hajimehoshi/ebiten/internal/glfw"
)

const (
	smCyCaption             = 4
	monitorDefaultToNearest = 2
)

type rect struct {
	left   int32
	top    int32
	right  int32
	bottom int32
}

type monitorInfo struct {
	cbSize    uint32
	rcMonitor rect
	rcWork    rect
	dwFlags   uint32
}

var (
	// user32 is defined at hideconsole_windows.go
	procGetSystemMetrics    = user32.NewProc("GetSystemMetrics")
	procGetActiveWindow     = user32.NewProc("GetActiveWindow")
	procGetForegroundWindow = user32.NewProc("GetForegroundWindow")
	procMonitorFromWindow   = user32.NewProc("MonitorFromWindow")
	procGetMonitorInfoW     = user32.NewProc("GetMonitorInfoW")
)

func getSystemMetrics(nIndex int) (int, error) {
	r, _, e := procGetSystemMetrics.Call(uintptr(nIndex))
	if e != nil && e.(windows.Errno) != 0 {
		return 0, fmt.Errorf("ui: GetSystemMetrics failed: error code: %d", e)
	}
	return int(r), nil
}

func getActiveWindow() (uintptr, error) {
	r, _, e := procGetActiveWindow.Call()
	if e != nil && e.(windows.Errno) != 0 {
		return 0, fmt.Errorf("ui: GetActiveWindow failed: error code: %d", e)
	}
	return r, nil
}

func getForegroundWindow() (uintptr, error) {
	r, _, e := procGetForegroundWindow.Call()
	if e != nil && e.(windows.Errno) != 0 {
		return 0, fmt.Errorf("ui: GetForegroundWindow failed: error code: %d", e)
	}
	return r, nil
}

func monitorFromWindow(hwnd uintptr, dwFlags uint32) (uintptr, error) {
	r, _, e := procMonitorFromWindow.Call(hwnd, uintptr(dwFlags))
	if e != nil && e.(windows.Errno) != 0 {
		return 0, fmt.Errorf("ui: MonitorFromWindow failed: error code: %d", e)
	}
	if r == 0 {
		return 0, fmt.Errorf("ui: MonitorFromWindow failed: returned value: %d", r)
	}
	return r, nil
}

func getMonitorInfoW(hMonitor uintptr, lpmi *monitorInfo) error {
	r, _, e := procGetMonitorInfoW.Call(hMonitor, uintptr(unsafe.Pointer(lpmi)))
	if e != nil && e.(windows.Errno) != 0 {
		return fmt.Errorf("ui: GetMonitorInfoW failed: error code: %d", e)
	}
	if r == 0 {
		return fmt.Errorf("ui: GetMonitorInfoW failed: returned value: %d", r)
	}
	return nil
}

func (u *UserInterface) glfwScale() float64 {
	return u.deviceScaleFactor()
}

func (u *UserInterface) adjustWindowPosition(x, y int) (int, int) {
	mx, my := u.currentMonitor().GetPos()
	// As the video width/height might be wrong,
	// adjust x/y at least to enable to handle the window (#328)
	if x < mx {
		x = mx
	}
	t, err := getSystemMetrics(smCyCaption)
	if err != nil {
		panic(err)
	}
	if y < my+t {
		y = my + t
	}
	return x, y
}

func (u *UserInterface) currentMonitorFromPosition() *glfw.Monitor {
	// TODO: Should we use u.window.GetWin32Window() here?
	w, err := getActiveWindow()
	if err != nil {
		panic(err)
	}
	if w == 0 {
		// There is no window at launching, but there is a hidden initialized window.
		// Get the foreground window, that is common among multiple processes.
		w, err = getForegroundWindow()
		if err != nil {
			panic(err)
		}
		if w == 0 {
			// GetForegroundWindow can return null according to the document. Use
			// the primary monitor instead.
			return glfw.GetPrimaryMonitor()
		}
	}

	// Get the current monitor by the window handle instead of the window position. It is because the window
	// position is not relaiable in some cases e.g. when the window is put across multiple monitors.

	m, err := monitorFromWindow(w, monitorDefaultToNearest)
	if err != nil {
		panic(err)
	}

	mi := monitorInfo{}
	mi.cbSize = uint32(unsafe.Sizeof(mi))
	if err := getMonitorInfoW(m, &mi); err != nil {
		panic(err)
	}

	x, y := int(mi.rcMonitor.left), int(mi.rcMonitor.top)
	for _, m := range glfw.GetMonitors() {
		mx, my := m.GetPos()
		if mx == x && my == y {
			return m
		}
	}
	return glfw.GetPrimaryMonitor()
}

func (u *UserInterface) nativeWindow() unsafe.Pointer {
	return u.window.GetWin32Window()
}
<commit_msg>uidriver/glfw: Bug fix: Crash on Wine when getting a monitor<commit_after>// Copyright 2016 Hajime Hoshi
//
// 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.

// +build !js

package glfw

import (
	"fmt"
	"unsafe"

	"golang.org/x/sys/windows"

	"github.com/hajimehoshi/ebiten/internal/glfw"
)

const (
	smCyCaption             = 4
	monitorDefaultToNearest = 2
)

type rect struct {
	left   int32
	top    int32
	right  int32
	bottom int32
}

type monitorInfo struct {
	cbSize    uint32
	rcMonitor rect
	rcWork    rect
	dwFlags   uint32
}

var (
	// user32 is defined at hideconsole_windows.go
	procGetSystemMetrics    = user32.NewProc("GetSystemMetrics")
	procGetActiveWindow     = user32.NewProc("GetActiveWindow")
	procGetForegroundWindow = user32.NewProc("GetForegroundWindow")
	procMonitorFromWindow   = user32.NewProc("MonitorFromWindow")
	procGetMonitorInfoW     = user32.NewProc("GetMonitorInfoW")
)

func getSystemMetrics(nIndex int) (int, error) {
	r, _, e := procGetSystemMetrics.Call(uintptr(nIndex))
	if e != nil && e.(windows.Errno) != 0 {
		return 0, fmt.Errorf("ui: GetSystemMetrics failed: error code: %d", e)
	}
	return int(r), nil
}

func getActiveWindow() (uintptr, error) {
	r, _, e := procGetActiveWindow.Call()
	if e != nil && e.(windows.Errno) != 0 {
		return 0, fmt.Errorf("ui: GetActiveWindow failed: error code: %d", e)
	}
	return r, nil
}

func getForegroundWindow() (uintptr, error) {
	r, _, e := procGetForegroundWindow.Call()
	if e != nil && e.(windows.Errno) != 0 {
		return 0, fmt.Errorf("ui: GetForegroundWindow failed: error code: %d", e)
	}
	return r, nil
}

func monitorFromWindow(hwnd uintptr, dwFlags uint32) (uintptr, error) {
	r, _, e := procMonitorFromWindow.Call(hwnd, uintptr(dwFlags))
	if e != nil && e.(windows.Errno) != 0 {
		return 0, fmt.Errorf("ui: MonitorFromWindow failed: error code: %d", e)
	}
	if r == 0 {
		return 0, fmt.Errorf("ui: MonitorFromWindow failed: returned value: %d", r)
	}
	return r, nil
}

func getMonitorInfoW(hMonitor uintptr, lpmi *monitorInfo) error {
	r, _, e := procGetMonitorInfoW.Call(hMonitor, uintptr(unsafe.Pointer(lpmi)))
	if e != nil && e.(windows.Errno) != 0 {
		return fmt.Errorf("ui: GetMonitorInfoW failed: error code: %d", e)
	}
	if r == 0 {
		return fmt.Errorf("ui: GetMonitorInfoW failed: returned value: %d", r)
	}
	return nil
}

func (u *UserInterface) glfwScale() float64 {
	return u.deviceScaleFactor()
}

func (u *UserInterface) adjustWindowPosition(x, y int) (int, int) {
	mx, my := u.currentMonitor().GetPos()
	// As the video width/height might be wrong,
	// adjust x/y at least to enable to handle the window (#328)
	if x < mx {
		x = mx
	}
	t, err := getSystemMetrics(smCyCaption)
	if err != nil {
		panic(err)
	}
	if y < my+t {
		y = my + t
	}
	return x, y
}

func (u *UserInterface) currentMonitorFromPosition() *glfw.Monitor {
	// TODO: Should we use u.window.GetWin32Window() here?
	w, err := getActiveWindow()
	if err != nil {
		panic(err)
	}
	if w == 0 {
		// There is no window at launching, but there is a hidden initialized window.
		// Get the foreground window, that is common among multiple processes.
		w, err = getForegroundWindow()
		if err != nil {
			panic(err)
		}
		if w == 0 {
			// GetForegroundWindow can return null according to the document. Use
			// the primary monitor instead.
			return glfw.GetPrimaryMonitor()
		}
	}

	// Get the current monitor by the window handle instead of the window position. It is because the window
	// position is not relaiable in some cases e.g. when the window is put across multiple monitors.

	m, err := monitorFromWindow(w, monitorDefaultToNearest)
	if err != nil {
		// monitorFromWindow can return error on Wine. Ignore this.
		return glfw.GetPrimaryMonitor()
	}

	mi := monitorInfo{}
	mi.cbSize = uint32(unsafe.Sizeof(mi))
	if err := getMonitorInfoW(m, &mi); err != nil {
		panic(err)
	}

	x, y := int(mi.rcMonitor.left), int(mi.rcMonitor.top)
	for _, m := range glfw.GetMonitors() {
		mx, my := m.GetPos()
		if mx == x && my == y {
			return m
		}
	}
	return glfw.GetPrimaryMonitor()
}

func (u *UserInterface) nativeWindow() unsafe.Pointer {
	return u.window.GetWin32Window()
}
<|endoftext|>


<commit_before>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains the code dealing with package directory trees.

package main

import (
	"bytes"
	"go/doc"
	"go/parser"
	"go/token"
	"log"
	"os"
	"path/filepath"
	"strings"
	"unicode"
)

type Directory struct {
	Depth int
	Path  string // includes Name
	Name  string
	Text  string       // package documentation, if any
	Dirs  []*Directory // subdirectories
}

func isGoFile(fi os.FileInfo) bool {
	name := fi.Name()
	return !fi.IsDir() &&
		len(name) > 0 && name[0] != '.' && // ignore .files
		filepath.Ext(name) == ".go"
}

func isPkgFile(fi os.FileInfo) bool {
	return isGoFile(fi) &&
		!strings.HasSuffix(fi.Name(), "_test.go") // ignore test files
}

func isPkgDir(fi os.FileInfo) bool {
	name := fi.Name()
	return fi.IsDir() && len(name) > 0 &&
		name[0] != '_' && name[0] != '.' // ignore _files and .files
}

func firstSentence(s string) string {
	i := -1 // index+1 of first terminator (punctuation ending a sentence)
	j := -1 // index+1 of first terminator followed by white space
	prev := 'A'
	for k, ch := range s {
		k1 := k + 1
		if ch == '.' || ch == '!' || ch == '?' {
			if i < 0 {
				i = k1 // first terminator
			}
			if k1 < len(s) && s[k1] <= ' ' {
				if j < 0 {
					j = k1 // first terminator followed by white space
				}
				if !unicode.IsUpper(prev) {
					j = k1
					break
				}
			}
		}
		prev = ch
	}

	if j < 0 {
		// use the next best terminator
		j = i
		if j < 0 {
			// no terminator at all, use the entire string
			j = len(s)
		}
	}

	return s[0:j]
}

type treeBuilder struct {
	pathFilter func(string) bool
	maxDepth   int
}

func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth int) *Directory {
	if b.pathFilter != nil && !b.pathFilter(path) {
		return nil
	}

	if depth >= b.maxDepth {
		// return a dummy directory so that the parent directory
		// doesn't get discarded just because we reached the max
		// directory depth
		return &Directory{depth, path, name, "", nil}
	}

	list, err := fs.ReadDir(path)
	if err != nil {
		// newDirTree is called with a path that should be a package
		// directory; errors here should not happen, but if they do,
		// we want to know about them
		log.Printf("ReadDir(%s): %s", path, err)
	}

	// determine number of subdirectories and if there are package files
	ndirs := 0
	hasPkgFiles := false
	var synopses [4]string // prioritized package documentation (0 == highest priority)
	for _, d := range list {
		switch {
		case isPkgDir(d):
			ndirs++
		case isPkgFile(d):
			// looks like a package file, but may just be a file ending in ".go";
			// don't just count it yet (otherwise we may end up with hasPkgFiles even
			// though the directory doesn't contain any real package files - was bug)
			if synopses[0] == "" {
				// no "optimal" package synopsis yet; continue to collect synopses
				file, err := parseFile(fset, filepath.Join(path, d.Name()),
					parser.ParseComments|parser.PackageClauseOnly)
				if err == nil {
					hasPkgFiles = true
					if file.Doc != nil {
						// prioritize documentation
						i := -1
						switch file.Name.Name {
						case name:
							i = 0 // normal case: directory name matches package name
						case fakePkgName:
							i = 1 // synopses for commands
						case "main":
							i = 2 // directory contains a main package
						default:
							i = 3 // none of the above
						}
						if 0 <= i && i < len(synopses) && synopses[i] == "" {
							synopses[i] = doc.Synopsis(file.Doc.Text())
						}
					}
				}
			}
		}
	}

	// create subdirectory tree
	var dirs []*Directory
	if ndirs > 0 {
		dirs = make([]*Directory, ndirs)
		i := 0
		for _, d := range list {
			if isPkgDir(d) {
				name := d.Name()
				dd := b.newDirTree(fset, filepath.Join(path, name), name, depth+1)
				if dd != nil {
					dirs[i] = dd
					i++
				}
			}
		}
		dirs = dirs[0:i]
	}

	// if there are no package files and no subdirectories
	// containing package files, ignore the directory
	if !hasPkgFiles && len(dirs) == 0 {
		return nil
	}

	// select the highest-priority synopsis for the directory entry, if any
	synopsis := ""
	for _, synopsis = range synopses {
		if synopsis != "" {
			break
		}
	}

	return &Directory{depth, path, name, synopsis, dirs}
}

// newDirectory creates a new package directory tree with at most maxDepth
// levels, anchored at root. The result tree is pruned such that it only
// contains directories that contain package files or that contain
// subdirectories containing package files (transitively). If a non-nil
// pathFilter is provided, directory paths additionally must be accepted
// by the filter (i.e., pathFilter(path) must be true). If a value >= 0 is
// provided for maxDepth, nodes at larger depths are pruned as well; they
// are assumed to contain package files even if their contents are not known
// (i.e., in this case the tree may contain directories w/o any package files).
//
func newDirectory(root string, pathFilter func(string) bool, maxDepth int) *Directory {
	// The root could be a symbolic link so use Stat not Lstat.
	d, err := fs.Stat(root)
	// If we fail here, report detailed error messages; otherwise
	// is is hard to see why a directory tree was not built.
	switch {
	case err != nil:
		log.Printf("newDirectory(%s): %s", root, err)
		return nil
	case !isPkgDir(d):
		log.Printf("newDirectory(%s): not a package directory", root)
		return nil
	}
	if maxDepth < 0 {
		maxDepth = 1e6 // "infinity"
	}
	b := treeBuilder{pathFilter, maxDepth}
	// the file set provided is only for local parsing, no position
	// information escapes and thus we don't need to save the set
	return b.newDirTree(token.NewFileSet(), root, d.Name(), 0)
}

func (dir *Directory) writeLeafs(buf *bytes.Buffer) {
	if dir != nil {
		if len(dir.Dirs) == 0 {
			buf.WriteString(dir.Path)
			buf.WriteByte('\n')
			return
		}

		for _, d := range dir.Dirs {
			d.writeLeafs(buf)
		}
	}
}

func (dir *Directory) walk(c chan<- *Directory, skipRoot bool) {
	if dir != nil {
		if !skipRoot {
			c <- dir
		}
		for _, d := range dir.Dirs {
			d.walk(c, false)
		}
	}
}

func (dir *Directory) iter(skipRoot bool) <-chan *Directory {
	c := make(chan *Directory)
	go func() {
		dir.walk(c, skipRoot)
		close(c)
	}()
	return c
}

func (dir *Directory) lookupLocal(name string) *Directory {
	for _, d := range dir.Dirs {
		if d.Name == name {
			return d
		}
	}
	return nil
}

// lookup looks for the *Directory for a given path, relative to dir.
func (dir *Directory) lookup(path string) *Directory {
	d := strings.Split(dir.Path, string(filepath.Separator))
	p := strings.Split(path, string(filepath.Separator))
	i := 0
	for i < len(d) {
		if i >= len(p) || d[i] != p[i] {
			return nil
		}
		i++
	}
	for dir != nil && i < len(p) {
		dir = dir.lookupLocal(p[i])
		i++
	}
	return dir
}

// DirEntry describes a directory entry. The Depth and Height values
// are useful for presenting an entry in an indented fashion.
//
type DirEntry struct {
	Depth    int    // >= 0
	Height   int    // = DirList.MaxHeight - Depth, > 0
	Path     string // includes Name, relative to DirList root
	Name     string
	Synopsis string
}

type DirList struct {
	MaxHeight int // directory tree height, > 0
	List      []DirEntry
}

// listing creates a (linear) directory listing from a directory tree.
// If skipRoot is set, the root directory itself is excluded from the list.
//
func (root *Directory) listing(skipRoot bool) *DirList {
	if root == nil {
		return nil
	}

	// determine number of entries n and maximum height
	n := 0
	minDepth := 1 << 30 // infinity
	maxDepth := 0
	for d := range root.iter(skipRoot) {
		n++
		if minDepth > d.Depth {
			minDepth = d.Depth
		}
		if maxDepth < d.Depth {
			maxDepth = d.Depth
		}
	}
	maxHeight := maxDepth - minDepth + 1

	if n == 0 {
		return nil
	}

	// create list
	list := make([]DirEntry, n)
	i := 0
	for d := range root.iter(skipRoot) {
		p := &list[i]
		p.Depth = d.Depth - minDepth
		p.Height = maxHeight - p.Depth
		// the path is relative to root.Path - remove the root.Path
		// prefix (the prefix should always be present but avoid
		// crashes and check)
		path := d.Path
		if strings.HasPrefix(d.Path, root.Path) {
			path = d.Path[len(root.Path):]
		}
		// remove trailing separator if any - path must be relative
		if len(path) > 0 && path[0] == filepath.Separator {
			path = path[1:]
		}
		p.Path = path
		p.Name = d.Name
		p.Synopsis = d.Text
		i++
	}

	return &DirList{maxHeight, list}
}
<commit_msg>godoc: remove dead code<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains the code dealing with package directory trees.

package main

import (
	"bytes"
	"go/doc"
	"go/parser"
	"go/token"
	"log"
	"os"
	"path/filepath"
	"strings"
)

type Directory struct {
	Depth int
	Path  string // includes Name
	Name  string
	Text  string       // package documentation, if any
	Dirs  []*Directory // subdirectories
}

func isGoFile(fi os.FileInfo) bool {
	name := fi.Name()
	return !fi.IsDir() &&
		len(name) > 0 && name[0] != '.' && // ignore .files
		filepath.Ext(name) == ".go"
}

func isPkgFile(fi os.FileInfo) bool {
	return isGoFile(fi) &&
		!strings.HasSuffix(fi.Name(), "_test.go") // ignore test files
}

func isPkgDir(fi os.FileInfo) bool {
	name := fi.Name()
	return fi.IsDir() && len(name) > 0 &&
		name[0] != '_' && name[0] != '.' // ignore _files and .files
}

type treeBuilder struct {
	pathFilter func(string) bool
	maxDepth   int
}

func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth int) *Directory {
	if b.pathFilter != nil && !b.pathFilter(path) {
		return nil
	}

	if depth >= b.maxDepth {
		// return a dummy directory so that the parent directory
		// doesn't get discarded just because we reached the max
		// directory depth
		return &Directory{depth, path, name, "", nil}
	}

	list, err := fs.ReadDir(path)
	if err != nil {
		// newDirTree is called with a path that should be a package
		// directory; errors here should not happen, but if they do,
		// we want to know about them
		log.Printf("ReadDir(%s): %s", path, err)
	}

	// determine number of subdirectories and if there are package files
	ndirs := 0
	hasPkgFiles := false
	var synopses [4]string // prioritized package documentation (0 == highest priority)
	for _, d := range list {
		switch {
		case isPkgDir(d):
			ndirs++
		case isPkgFile(d):
			// looks like a package file, but may just be a file ending in ".go";
			// don't just count it yet (otherwise we may end up with hasPkgFiles even
			// though the directory doesn't contain any real package files - was bug)
			if synopses[0] == "" {
				// no "optimal" package synopsis yet; continue to collect synopses
				file, err := parseFile(fset, filepath.Join(path, d.Name()),
					parser.ParseComments|parser.PackageClauseOnly)
				if err == nil {
					hasPkgFiles = true
					if file.Doc != nil {
						// prioritize documentation
						i := -1
						switch file.Name.Name {
						case name:
							i = 0 // normal case: directory name matches package name
						case fakePkgName:
							i = 1 // synopses for commands
						case "main":
							i = 2 // directory contains a main package
						default:
							i = 3 // none of the above
						}
						if 0 <= i && i < len(synopses) && synopses[i] == "" {
							synopses[i] = doc.Synopsis(file.Doc.Text())
						}
					}
				}
			}
		}
	}

	// create subdirectory tree
	var dirs []*Directory
	if ndirs > 0 {
		dirs = make([]*Directory, ndirs)
		i := 0
		for _, d := range list {
			if isPkgDir(d) {
				name := d.Name()
				dd := b.newDirTree(fset, filepath.Join(path, name), name, depth+1)
				if dd != nil {
					dirs[i] = dd
					i++
				}
			}
		}
		dirs = dirs[0:i]
	}

	// if there are no package files and no subdirectories
	// containing package files, ignore the directory
	if !hasPkgFiles && len(dirs) == 0 {
		return nil
	}

	// select the highest-priority synopsis for the directory entry, if any
	synopsis := ""
	for _, synopsis = range synopses {
		if synopsis != "" {
			break
		}
	}

	return &Directory{depth, path, name, synopsis, dirs}
}

// newDirectory creates a new package directory tree with at most maxDepth
// levels, anchored at root. The result tree is pruned such that it only
// contains directories that contain package files or that contain
// subdirectories containing package files (transitively). If a non-nil
// pathFilter is provided, directory paths additionally must be accepted
// by the filter (i.e., pathFilter(path) must be true). If a value >= 0 is
// provided for maxDepth, nodes at larger depths are pruned as well; they
// are assumed to contain package files even if their contents are not known
// (i.e., in this case the tree may contain directories w/o any package files).
//
func newDirectory(root string, pathFilter func(string) bool, maxDepth int) *Directory {
	// The root could be a symbolic link so use Stat not Lstat.
	d, err := fs.Stat(root)
	// If we fail here, report detailed error messages; otherwise
	// is is hard to see why a directory tree was not built.
	switch {
	case err != nil:
		log.Printf("newDirectory(%s): %s", root, err)
		return nil
	case !isPkgDir(d):
		log.Printf("newDirectory(%s): not a package directory", root)
		return nil
	}
	if maxDepth < 0 {
		maxDepth = 1e6 // "infinity"
	}
	b := treeBuilder{pathFilter, maxDepth}
	// the file set provided is only for local parsing, no position
	// information escapes and thus we don't need to save the set
	return b.newDirTree(token.NewFileSet(), root, d.Name(), 0)
}

func (dir *Directory) writeLeafs(buf *bytes.Buffer) {
	if dir != nil {
		if len(dir.Dirs) == 0 {
			buf.WriteString(dir.Path)
			buf.WriteByte('\n')
			return
		}

		for _, d := range dir.Dirs {
			d.writeLeafs(buf)
		}
	}
}

func (dir *Directory) walk(c chan<- *Directory, skipRoot bool) {
	if dir != nil {
		if !skipRoot {
			c <- dir
		}
		for _, d := range dir.Dirs {
			d.walk(c, false)
		}
	}
}

func (dir *Directory) iter(skipRoot bool) <-chan *Directory {
	c := make(chan *Directory)
	go func() {
		dir.walk(c, skipRoot)
		close(c)
	}()
	return c
}

func (dir *Directory) lookupLocal(name string) *Directory {
	for _, d := range dir.Dirs {
		if d.Name == name {
			return d
		}
	}
	return nil
}

// lookup looks for the *Directory for a given path, relative to dir.
func (dir *Directory) lookup(path string) *Directory {
	d := strings.Split(dir.Path, string(filepath.Separator))
	p := strings.Split(path, string(filepath.Separator))
	i := 0
	for i < len(d) {
		if i >= len(p) || d[i] != p[i] {
			return nil
		}
		i++
	}
	for dir != nil && i < len(p) {
		dir = dir.lookupLocal(p[i])
		i++
	}
	return dir
}

// DirEntry describes a directory entry. The Depth and Height values
// are useful for presenting an entry in an indented fashion.
//
type DirEntry struct {
	Depth    int    // >= 0
	Height   int    // = DirList.MaxHeight - Depth, > 0
	Path     string // includes Name, relative to DirList root
	Name     string
	Synopsis string
}

type DirList struct {
	MaxHeight int // directory tree height, > 0
	List      []DirEntry
}

// listing creates a (linear) directory listing from a directory tree.
// If skipRoot is set, the root directory itself is excluded from the list.
//
func (root *Directory) listing(skipRoot bool) *DirList {
	if root == nil {
		return nil
	}

	// determine number of entries n and maximum height
	n := 0
	minDepth := 1 << 30 // infinity
	maxDepth := 0
	for d := range root.iter(skipRoot) {
		n++
		if minDepth > d.Depth {
			minDepth = d.Depth
		}
		if maxDepth < d.Depth {
			maxDepth = d.Depth
		}
	}
	maxHeight := maxDepth - minDepth + 1

	if n == 0 {
		return nil
	}

	// create list
	list := make([]DirEntry, n)
	i := 0
	for d := range root.iter(skipRoot) {
		p := &list[i]
		p.Depth = d.Depth - minDepth
		p.Height = maxHeight - p.Depth
		// the path is relative to root.Path - remove the root.Path
		// prefix (the prefix should always be present but avoid
		// crashes and check)
		path := d.Path
		if strings.HasPrefix(d.Path, root.Path) {
			path = d.Path[len(root.Path):]
		}
		// remove trailing separator if any - path must be relative
		if len(path) > 0 && path[0] == filepath.Separator {
			path = path[1:]
		}
		p.Path = path
		p.Name = d.Name
		p.Synopsis = d.Text
		i++
	}

	return &DirList{maxHeight, list}
}
<|endoftext|>


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

package tikv

import (
	"bytes"
	"sync"

	"github.com/golang/protobuf/proto"
	"github.com/juju/errors"
	"github.com/ngaut/log"
	pb "github.com/pingcap/kvproto/pkg/kvrpcpb"
	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/terror"
)

type txnCommitter struct {
	store       *tikvStore
	txn         *tikvTxn
	startTS     uint64
	keys        [][]byte
	mutations   map[string]*pb.Mutation
	commitTS    uint64
	mu          sync.RWMutex
	writtenKeys [][]byte
	committed   bool
}

func newTxnCommitter(txn *tikvTxn) (*txnCommitter, error) {
	var keys [][]byte
	mutations := make(map[string]*pb.Mutation)
	err := txn.us.WalkBuffer(func(k kv.Key, v []byte) error {
		if len(v) > 0 {
			mutations[string(k)] = &pb.Mutation{
				Op:    pb.Op_Put.Enum(),
				Key:   k,
				Value: v,
			}
		} else {
			mutations[string(k)] = &pb.Mutation{
				Op:  pb.Op_Del.Enum(),
				Key: k,
			}
		}
		keys = append(keys, k)
		return nil
	})
	if err != nil {
		return nil, errors.Trace(err)
	}
	// Transactions without Put/Del, only Locks are readonly.
	// We can skip commit directly.
	if len(keys) == 0 {
		return nil, nil
	}
	for _, lockKey := range txn.lockKeys {
		if _, ok := mutations[string(lockKey)]; !ok {
			mutations[string(lockKey)] = &pb.Mutation{
				Op:  pb.Op_Lock.Enum(),
				Key: lockKey,
			}
			keys = append(keys, lockKey)
		}
	}
	return &txnCommitter{
		store:     txn.store,
		txn:       txn,
		startTS:   txn.StartTS(),
		keys:      keys,
		mutations: mutations,
	}, nil
}

func (c *txnCommitter) primary() []byte {
	return c.keys[0]
}

// iterKeys groups keys into batches, then applies `f` to them. If the flag
// asyncNonPrimary is set, it will return as soon as the primary batch is
// processed.
func (c *txnCommitter) iterKeys(keys [][]byte, f func(batchKeys) error, sizeFn func([]byte) int, asyncNonPrimary bool) error {
	if len(keys) == 0 {
		return nil
	}
	groups, firstRegion, err := c.store.regionCache.GroupKeysByRegion(keys)
	if err != nil {
		return errors.Trace(err)
	}
	firstIsPrimary := bytes.Equal(keys[0], c.primary())

	var batches []batchKeys
	// Make sure the group that contains primary key goes first.
	if firstIsPrimary {
		batches = appendBatchBySize(batches, firstRegion, groups[firstRegion], sizeFn, txnCommitBatchSize)
		delete(groups, firstRegion)
	}
	for id, g := range groups {
		batches = appendBatchBySize(batches, id, g, sizeFn, txnCommitBatchSize)
	}

	if firstIsPrimary {
		err = c.doBatches(batches[:1], f)
		if err != nil {
			return errors.Trace(err)
		}
		batches = batches[1:]
	}
	if asyncNonPrimary {
		go func() {
			c.doBatches(batches, f)
		}()
		return nil
	}
	err = c.doBatches(batches, f)
	return errors.Trace(err)
}

// doBatches applies f to batches parallelly.
func (c *txnCommitter) doBatches(batches []batchKeys, f func(batchKeys) error) error {
	if len(batches) == 0 {
		return nil
	}
	if len(batches) == 1 {
		e := f(batches[0])
		if e != nil {
			log.Warnf("txnCommitter doBatches failed: %v, tid: %d", e, c.startTS)
		}
		return errors.Trace(e)
	}

	// TODO: For prewrite, stop sending other requests after receiving first error.
	ch := make(chan error)
	for _, batch := range batches {
		go func(batch batchKeys) {
			ch <- f(batch)
		}(batch)
	}
	var err error
	for i := 0; i < len(batches); i++ {
		if e := <-ch; e != nil {
			log.Warnf("txnCommitter doBatches failed: %v, tid: %d", e, c.startTS)
			err = e
		}
	}
	return errors.Trace(err)
}

func (c *txnCommitter) keyValueSize(key []byte) int {
	size := c.keySize(key)
	if mutation := c.mutations[string(key)]; mutation != nil {
		size += len(mutation.Value)
	}
	return size
}

func (c *txnCommitter) keySize(key []byte) int {
	return len(key)
}

func (c *txnCommitter) prewriteSingleRegion(batch batchKeys) error {
	mutations := make([]*pb.Mutation, len(batch.keys))
	for i, k := range batch.keys {
		mutations[i] = c.mutations[string(k)]
	}
	req := &pb.Request{
		Type: pb.MessageType_CmdPrewrite.Enum(),
		CmdPrewriteReq: &pb.CmdPrewriteRequest{
			Mutations:    mutations,
			PrimaryLock:  c.primary(),
			StartVersion: proto.Uint64(c.startTS),
		},
	}

	var backoffErr error
	for backoff := txnLockBackoff(); backoffErr == nil; backoffErr = backoff() {
		resp, err := c.store.SendKVReq(req, batch.region)
		if err != nil {
			return errors.Trace(err)
		}
		if regionErr := resp.GetRegionError(); regionErr != nil {
			// re-split keys and prewrite again.
			// TODO: The recursive maybe not able to exit if TiKV &
			// PD are implemented incorrectly. A possible fix is
			// introducing a 'max backoff time'.
			err = c.prewriteKeys(batch.keys)
			return errors.Trace(err)
		}
		prewriteResp := resp.GetCmdPrewriteResp()
		if prewriteResp == nil {
			return errors.Trace(errBodyMissing)
		}
		keyErrs := prewriteResp.GetErrors()
		if len(keyErrs) == 0 {
			// We need to cleanup all written keys if transaction aborts.
			c.mu.Lock()
			defer c.mu.Unlock()
			c.writtenKeys = append(c.writtenKeys, batch.keys...)
			return nil
		}
		for _, keyErr := range keyErrs {
			lockInfo, err := extractLockInfoFromKeyErr(keyErr)
			if err != nil {
				// It could be `Retryable` or `Abort`.
				return errors.Trace(err)
			}
			lock := newLock(c.store, lockInfo.GetPrimaryLock(), lockInfo.GetLockVersion(), lockInfo.GetKey(), c.startTS)
			_, err = lock.cleanup()
			if err != nil && terror.ErrorNotEqual(err, errInnerRetryable) {
				return errors.Trace(err)
			}
		}
	}
	return errors.Annotate(backoffErr, txnRetryableMark)
}

func (c *txnCommitter) commitSingleRegion(batch batchKeys) error {
	req := &pb.Request{
		Type: pb.MessageType_CmdCommit.Enum(),
		CmdCommitReq: &pb.CmdCommitRequest{
			StartVersion:  proto.Uint64(c.startTS),
			Keys:          batch.keys,
			CommitVersion: proto.Uint64(c.commitTS),
		},
	}

	resp, err := c.store.SendKVReq(req, batch.region)
	if err != nil {
		return errors.Trace(err)
	}
	if regionErr := resp.GetRegionError(); regionErr != nil {
		// re-split keys and commit again.
		err = c.commitKeys(batch.keys)
		return errors.Trace(err)
	}
	commitResp := resp.GetCmdCommitResp()
	if commitResp == nil {
		return errors.Trace(errBodyMissing)
	}
	if keyErr := commitResp.GetError(); keyErr != nil {
		c.mu.RLock()
		defer c.mu.RUnlock()
		err = errors.Errorf("commit failed: %v", keyErr.String())
		if c.committed {
			// No secondary key could be rolled back after it's primary key is committed.
			// There must be a serious bug somewhere.
			log.Errorf("txn failed commit key after primary key committed: %v, tid: %d", err, c.startTS)
			return errors.Trace(err)
		}
		// The transaction maybe rolled back by concurrent transactions.
		log.Warnf("txn failed commit primary key: %v, retry later, tid: %d", err, c.startTS)
		return errors.Annotate(err, txnRetryableMark)
	}

	c.mu.Lock()
	defer c.mu.Unlock()
	// Group that contains primary key is always the first.
	// We mark transaction's status committed when we receive the first success response.
	c.committed = true
	return nil
}

func (c *txnCommitter) cleanupSingleRegion(batch batchKeys) error {
	req := &pb.Request{
		Type: pb.MessageType_CmdBatchRollback.Enum(),
		CmdBatchRollbackReq: &pb.CmdBatchRollbackRequest{
			Keys:         batch.keys,
			StartVersion: proto.Uint64(c.startTS),
		},
	}
	resp, err := c.store.SendKVReq(req, batch.region)
	if err != nil {
		return errors.Trace(err)
	}
	if regionErr := resp.GetRegionError(); regionErr != nil {
		err = c.cleanupKeys(batch.keys)
		return errors.Trace(err)
	}
	if keyErr := resp.GetCmdBatchRollbackResp().GetError(); keyErr != nil {
		err = errors.Errorf("cleanup failed: %s", keyErr)
		log.Errorf("txn failed cleanup key: %v, tid: %d", err, c.startTS)
		return errors.Trace(err)
	}
	return nil
}

func (c *txnCommitter) prewriteKeys(keys [][]byte) error {
	return c.iterKeys(keys, c.prewriteSingleRegion, c.keyValueSize, false)
}

func (c *txnCommitter) commitKeys(keys [][]byte) error {
	return c.iterKeys(keys, c.commitSingleRegion, c.keySize, true)
}

func (c *txnCommitter) cleanupKeys(keys [][]byte) error {
	return c.iterKeys(keys, c.cleanupSingleRegion, c.keySize, false)
}

func (c *txnCommitter) Commit() error {
	err := c.prewriteKeys(c.keys)
	if err != nil {
		log.Warnf("txn commit failed on prewrite: %v, tid: %d", err, c.startTS)
		go func() {
			c.cleanupKeys(c.writtenKeys)
		}()
		return errors.Trace(err)
	}

	commitTS, err := c.store.getTimestampWithRetry()
	if err != nil {
		return errors.Trace(err)
	}
	c.commitTS = commitTS

	err = c.commitKeys(c.keys)
	if err != nil {
		if !c.committed {
			go func() {
				c.cleanupKeys(c.writtenKeys)
			}()
			return errors.Trace(err)
		}
		log.Warnf("txn commit succeed with error: %v, tid: %d", err, c.startTS)
	}
	return nil
}

// TiKV recommends each RPC packet should be less than ~1MB. We keep each packet's
// Key+Value size below 512KB.
const txnCommitBatchSize = 512 * 1024

// batchKeys is a batch of keys in the same region.
type batchKeys struct {
	region RegionVerID
	keys   [][]byte
}

// appendBatchBySize appends keys to []batchKeys. It may split the keys to make
// sure each batch's size does not exceed the limit.
func appendBatchBySize(b []batchKeys, region RegionVerID, keys [][]byte, sizeFn func([]byte) int, limit int) []batchKeys {
	var start, end int
	for start = 0; start < len(keys); start = end {
		var size int
		for end = start; end < len(keys) && size < limit; end++ {
			size += sizeFn(keys[end])
		}
		b = append(b, batchKeys{
			region: region,
			keys:   keys[start:end],
		})
	}
	return b
}
<commit_msg>store/tikv: use defer to cleanup keys. (#1474)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package tikv

import (
	"bytes"
	"sync"

	"github.com/golang/protobuf/proto"
	"github.com/juju/errors"
	"github.com/ngaut/log"
	pb "github.com/pingcap/kvproto/pkg/kvrpcpb"
	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/terror"
)

type txnCommitter struct {
	store       *tikvStore
	txn         *tikvTxn
	startTS     uint64
	keys        [][]byte
	mutations   map[string]*pb.Mutation
	commitTS    uint64
	mu          sync.RWMutex
	writtenKeys [][]byte
	committed   bool
}

func newTxnCommitter(txn *tikvTxn) (*txnCommitter, error) {
	var keys [][]byte
	mutations := make(map[string]*pb.Mutation)
	err := txn.us.WalkBuffer(func(k kv.Key, v []byte) error {
		if len(v) > 0 {
			mutations[string(k)] = &pb.Mutation{
				Op:    pb.Op_Put.Enum(),
				Key:   k,
				Value: v,
			}
		} else {
			mutations[string(k)] = &pb.Mutation{
				Op:  pb.Op_Del.Enum(),
				Key: k,
			}
		}
		keys = append(keys, k)
		return nil
	})
	if err != nil {
		return nil, errors.Trace(err)
	}
	// Transactions without Put/Del, only Locks are readonly.
	// We can skip commit directly.
	if len(keys) == 0 {
		return nil, nil
	}
	for _, lockKey := range txn.lockKeys {
		if _, ok := mutations[string(lockKey)]; !ok {
			mutations[string(lockKey)] = &pb.Mutation{
				Op:  pb.Op_Lock.Enum(),
				Key: lockKey,
			}
			keys = append(keys, lockKey)
		}
	}
	return &txnCommitter{
		store:     txn.store,
		txn:       txn,
		startTS:   txn.StartTS(),
		keys:      keys,
		mutations: mutations,
	}, nil
}

func (c *txnCommitter) primary() []byte {
	return c.keys[0]
}

// iterKeys groups keys into batches, then applies `f` to them. If the flag
// asyncNonPrimary is set, it will return as soon as the primary batch is
// processed.
func (c *txnCommitter) iterKeys(keys [][]byte, f func(batchKeys) error, sizeFn func([]byte) int, asyncNonPrimary bool) error {
	if len(keys) == 0 {
		return nil
	}
	groups, firstRegion, err := c.store.regionCache.GroupKeysByRegion(keys)
	if err != nil {
		return errors.Trace(err)
	}
	firstIsPrimary := bytes.Equal(keys[0], c.primary())

	var batches []batchKeys
	// Make sure the group that contains primary key goes first.
	if firstIsPrimary {
		batches = appendBatchBySize(batches, firstRegion, groups[firstRegion], sizeFn, txnCommitBatchSize)
		delete(groups, firstRegion)
	}
	for id, g := range groups {
		batches = appendBatchBySize(batches, id, g, sizeFn, txnCommitBatchSize)
	}

	if firstIsPrimary {
		err = c.doBatches(batches[:1], f)
		if err != nil {
			return errors.Trace(err)
		}
		batches = batches[1:]
	}
	if asyncNonPrimary {
		go func() {
			c.doBatches(batches, f)
		}()
		return nil
	}
	err = c.doBatches(batches, f)
	return errors.Trace(err)
}

// doBatches applies f to batches parallelly.
func (c *txnCommitter) doBatches(batches []batchKeys, f func(batchKeys) error) error {
	if len(batches) == 0 {
		return nil
	}
	if len(batches) == 1 {
		e := f(batches[0])
		if e != nil {
			log.Warnf("txnCommitter doBatches failed: %v, tid: %d", e, c.startTS)
		}
		return errors.Trace(e)
	}

	// TODO: For prewrite, stop sending other requests after receiving first error.
	ch := make(chan error)
	for _, batch := range batches {
		go func(batch batchKeys) {
			ch <- f(batch)
		}(batch)
	}
	var err error
	for i := 0; i < len(batches); i++ {
		if e := <-ch; e != nil {
			log.Warnf("txnCommitter doBatches failed: %v, tid: %d", e, c.startTS)
			err = e
		}
	}
	return errors.Trace(err)
}

func (c *txnCommitter) keyValueSize(key []byte) int {
	size := c.keySize(key)
	if mutation := c.mutations[string(key)]; mutation != nil {
		size += len(mutation.Value)
	}
	return size
}

func (c *txnCommitter) keySize(key []byte) int {
	return len(key)
}

func (c *txnCommitter) prewriteSingleRegion(batch batchKeys) error {
	mutations := make([]*pb.Mutation, len(batch.keys))
	for i, k := range batch.keys {
		mutations[i] = c.mutations[string(k)]
	}
	req := &pb.Request{
		Type: pb.MessageType_CmdPrewrite.Enum(),
		CmdPrewriteReq: &pb.CmdPrewriteRequest{
			Mutations:    mutations,
			PrimaryLock:  c.primary(),
			StartVersion: proto.Uint64(c.startTS),
		},
	}

	var backoffErr error
	for backoff := txnLockBackoff(); backoffErr == nil; backoffErr = backoff() {
		resp, err := c.store.SendKVReq(req, batch.region)
		if err != nil {
			return errors.Trace(err)
		}
		if regionErr := resp.GetRegionError(); regionErr != nil {
			// re-split keys and prewrite again.
			// TODO: The recursive maybe not able to exit if TiKV &
			// PD are implemented incorrectly. A possible fix is
			// introducing a 'max backoff time'.
			err = c.prewriteKeys(batch.keys)
			return errors.Trace(err)
		}
		prewriteResp := resp.GetCmdPrewriteResp()
		if prewriteResp == nil {
			return errors.Trace(errBodyMissing)
		}
		keyErrs := prewriteResp.GetErrors()
		if len(keyErrs) == 0 {
			// We need to cleanup all written keys if transaction aborts.
			c.mu.Lock()
			defer c.mu.Unlock()
			c.writtenKeys = append(c.writtenKeys, batch.keys...)
			return nil
		}
		for _, keyErr := range keyErrs {
			lockInfo, err := extractLockInfoFromKeyErr(keyErr)
			if err != nil {
				// It could be `Retryable` or `Abort`.
				return errors.Trace(err)
			}
			lock := newLock(c.store, lockInfo.GetPrimaryLock(), lockInfo.GetLockVersion(), lockInfo.GetKey(), c.startTS)
			_, err = lock.cleanup()
			if err != nil && terror.ErrorNotEqual(err, errInnerRetryable) {
				return errors.Trace(err)
			}
		}
	}
	return errors.Annotate(backoffErr, txnRetryableMark)
}

func (c *txnCommitter) commitSingleRegion(batch batchKeys) error {
	req := &pb.Request{
		Type: pb.MessageType_CmdCommit.Enum(),
		CmdCommitReq: &pb.CmdCommitRequest{
			StartVersion:  proto.Uint64(c.startTS),
			Keys:          batch.keys,
			CommitVersion: proto.Uint64(c.commitTS),
		},
	}

	resp, err := c.store.SendKVReq(req, batch.region)
	if err != nil {
		return errors.Trace(err)
	}
	if regionErr := resp.GetRegionError(); regionErr != nil {
		// re-split keys and commit again.
		err = c.commitKeys(batch.keys)
		return errors.Trace(err)
	}
	commitResp := resp.GetCmdCommitResp()
	if commitResp == nil {
		return errors.Trace(errBodyMissing)
	}
	if keyErr := commitResp.GetError(); keyErr != nil {
		c.mu.RLock()
		defer c.mu.RUnlock()
		err = errors.Errorf("commit failed: %v", keyErr.String())
		if c.committed {
			// No secondary key could be rolled back after it's primary key is committed.
			// There must be a serious bug somewhere.
			log.Errorf("txn failed commit key after primary key committed: %v, tid: %d", err, c.startTS)
			return errors.Trace(err)
		}
		// The transaction maybe rolled back by concurrent transactions.
		log.Warnf("txn failed commit primary key: %v, retry later, tid: %d", err, c.startTS)
		return errors.Annotate(err, txnRetryableMark)
	}

	c.mu.Lock()
	defer c.mu.Unlock()
	// Group that contains primary key is always the first.
	// We mark transaction's status committed when we receive the first success response.
	c.committed = true
	return nil
}

func (c *txnCommitter) cleanupSingleRegion(batch batchKeys) error {
	req := &pb.Request{
		Type: pb.MessageType_CmdBatchRollback.Enum(),
		CmdBatchRollbackReq: &pb.CmdBatchRollbackRequest{
			Keys:         batch.keys,
			StartVersion: proto.Uint64(c.startTS),
		},
	}
	resp, err := c.store.SendKVReq(req, batch.region)
	if err != nil {
		return errors.Trace(err)
	}
	if regionErr := resp.GetRegionError(); regionErr != nil {
		err = c.cleanupKeys(batch.keys)
		return errors.Trace(err)
	}
	if keyErr := resp.GetCmdBatchRollbackResp().GetError(); keyErr != nil {
		err = errors.Errorf("cleanup failed: %s", keyErr)
		log.Errorf("txn failed cleanup key: %v, tid: %d", err, c.startTS)
		return errors.Trace(err)
	}
	return nil
}

func (c *txnCommitter) prewriteKeys(keys [][]byte) error {
	return c.iterKeys(keys, c.prewriteSingleRegion, c.keyValueSize, false)
}

func (c *txnCommitter) commitKeys(keys [][]byte) error {
	return c.iterKeys(keys, c.commitSingleRegion, c.keySize, true)
}

func (c *txnCommitter) cleanupKeys(keys [][]byte) error {
	return c.iterKeys(keys, c.cleanupSingleRegion, c.keySize, false)
}

func (c *txnCommitter) Commit() error {
	defer func() {
		// Always clean up all written keys if the txn does not commit.
		if !c.committed {
			go func() {
				c.cleanupKeys(c.writtenKeys)
				log.Infof("txn clean up done, tid: %d", c.startTS)
			}()
		}
	}()

	err := c.prewriteKeys(c.keys)
	if err != nil {
		log.Warnf("txn commit failed on prewrite: %v, tid: %d", err, c.startTS)
		return errors.Trace(err)
	}

	commitTS, err := c.store.getTimestampWithRetry()
	if err != nil {
		log.Warnf("txn get commitTS failed: %v, tid: %d", err, c.startTS)
		return errors.Trace(err)
	}
	c.commitTS = commitTS

	err = c.commitKeys(c.keys)
	if err != nil {
		if !c.committed {
			log.Warnf("txn commit failed on commit: %v, tid: %d", err, c.startTS)
			return errors.Trace(err)
		}
		log.Warnf("txn commit succeed with error: %v, tid: %d", err, c.startTS)
	}
	return nil
}

// TiKV recommends each RPC packet should be less than ~1MB. We keep each packet's
// Key+Value size below 512KB.
const txnCommitBatchSize = 512 * 1024

// batchKeys is a batch of keys in the same region.
type batchKeys struct {
	region RegionVerID
	keys   [][]byte
}

// appendBatchBySize appends keys to []batchKeys. It may split the keys to make
// sure each batch's size does not exceed the limit.
func appendBatchBySize(b []batchKeys, region RegionVerID, keys [][]byte, sizeFn func([]byte) int, limit int) []batchKeys {
	var start, end int
	for start = 0; start < len(keys); start = end {
		var size int
		for end = start; end < len(keys) && size < limit; end++ {
			size += sizeFn(keys[end])
		}
		b = append(b, batchKeys{
			region: region,
			keys:   keys[start:end],
		})
	}
	return b
}
<|endoftext|>


<commit_before>package amazonproduct

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"
)

type AmazonProductAPI struct {
	AccessKey    string
	SecretKey    string
	AssociateTag string
	Host         string
}

/*
ItemSearchByKeyword takes a string containg keywords and returns the search results
*/
func (api AmazonProductAPI) ItemSearchByKeyword(Keywords string, page int) (string, error) {
	params := map[string]string{
		"Keywords":      Keywords,
		"ResponseGroup": "Images,ItemAttributes,Small,EditorialReview",
		"ItemPage":      strconv.FormatInt(int64(page), 10),
	}
	return api.ItemSearch("All", params)
}

func (api AmazonProductAPI) ItemSearchByKeywordWithResponseGroup(Keywords string, ResponseGroup string) (string, error) {
	params := map[string]string{
		"Keywords":      Keywords,
		"ResponseGroup": ResponseGroup,
	}
	return api.ItemSearch("All", params)
}

func (api AmazonProductAPI) ItemSearch(SearchIndex string, Parameters map[string]string) (string, error) {
	Parameters["SearchIndex"] = SearchIndex
	genUrl, err := GenerateAmazonUrl(api, "ItemSearch", Parameters)
	if err != nil {
		return "", err
	}

	SetTimestamp(genUrl)

	signedurl, err := SignAmazonUrl(genUrl, api)
	if err != nil {
		return "", err
	}

	resp, err := http.Get(signedurl)
	if err != nil {
		return "", err
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	return string(body), nil
}

func GenerateAmazonUrl(api AmazonProductAPI, Operation string, Parameters map[string]string) (finalUrl *url.URL, err error) {

	result, err := url.Parse(api.Host)
	if err != nil {
		return nil, err
	}

	result.Host = api.Host
	result.Scheme = "http"
	result.Path = "/onca/xml"

	values := url.Values{}
	values.Add("Operation", Operation)
	values.Add("Service", "AWSECommerceService")
	values.Add("AWSAccessKeyId", api.AccessKey)
	values.Add("Version", "2009-01-01")
	values.Add("AssociateTag", api.AssociateTag)

	for k, v := range Parameters {
		values.Set(k, v)
	}

	params := values.Encode()
	result.RawQuery = params

	return result, nil
}

func SetTimestamp(origUrl *url.URL) (err error) {
	values, err := url.ParseQuery(origUrl.RawQuery)
	if err != nil {
		return err
	}
	values.Set("Timestamp", time.Now().UTC().Format(time.RFC3339))
	origUrl.RawQuery = values.Encode()

	return nil
}

func SignAmazonUrl(origUrl *url.URL, api AmazonProductAPI) (signedUrl string, err error) {

	escapeUrl := strings.Replace(origUrl.RawQuery, ",", "%2C", -1)
	escapeUrl = strings.Replace(escapeUrl, ":", "%3A", -1)

	params := strings.Split(escapeUrl, "&")
	sort.Strings(params)
	sortedParams := strings.Join(params, "&")

	toSign := fmt.Sprintf("GET\n%s\n%s\n%s", origUrl.Host, origUrl.Path, sortedParams)

	hasher := hmac.New(sha256.New, []byte(api.SecretKey))
	_, err = hasher.Write([]byte(toSign))
	if err != nil {
		return "", err
	}

	hash := base64.StdEncoding.EncodeToString(hasher.Sum(nil))

	hash = url.QueryEscape(hash)

	newParams := fmt.Sprintf("%s&Signature=%s", sortedParams, hash)

	origUrl.RawQuery = newParams

	return origUrl.String(), nil
}
<commit_msg>Revert "ItemPage support"<commit_after>package amazonproduct

import (
	"net/url"
	"sort"
	"fmt"
	"strings"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"time"
	"net/http"
	"io/ioutil"
)

type AmazonProductAPI struct {
	AccessKey string
	SecretKey string
	AssociateTag string
	Host string
}

func (api AmazonProductAPI) ItemSearchByKeyword(Keywords string) (string, error) {
	params := map[string] string {
		"Keywords": Keywords,
		"ResponseGroup" : "Images,ItemAttributes,Small,EditorialReview",
	}
	return api.ItemSearch("All", params)
}

func (api AmazonProductAPI) ItemSearchByKeywordWithResponseGroup(Keywords string, ResponseGroup string) (string, error) {
	params := map[string] string {
		"Keywords": Keywords,
		"ResponseGroup" : ResponseGroup,
	}
	return api.ItemSearch("All", params)
}

func (api AmazonProductAPI) ItemSearch(SearchIndex string, Parameters map[string] string) (string,error){
	Parameters["SearchIndex"] = SearchIndex
	genUrl, err := GenerateAmazonUrl(api, "ItemSearch", Parameters)
	if (err != nil) {
		return "", err
	}

	SetTimestamp(genUrl)

	signedurl,err := SignAmazonUrl(genUrl, api)
	if (err != nil) {
		return "", err
	}

	resp, err := http.Get(signedurl)
	if (err != nil) {
		return "", err
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if (err != nil) {
		return "", err
	}

	return string(body), nil
}

func GenerateAmazonUrl(api AmazonProductAPI, Operation string, Parameters map[string] string) (finalUrl *url.URL, err error) {

	result,err := url.Parse(api.Host)
	if (err != nil) {
		return nil, err
	}

	result.Host = api.Host
	result.Scheme = "http"
	result.Path = "/onca/xml"

	values := url.Values{}
	values.Add("Operation", Operation)
	values.Add("Service", "AWSECommerceService")
	values.Add("AWSAccessKeyId", api.AccessKey)
	values.Add("Version", "2009-01-01")
	values.Add("AssociateTag", api.AssociateTag)

	for k, v := range Parameters {
		values.Set(k, v)
	}

	params := values.Encode()
	result.RawQuery = params

	return result, nil
}

func SetTimestamp(origUrl *url.URL) (err error) {
	values, err := url.ParseQuery(origUrl.RawQuery)
	if (err != nil) {
		return err
	}
	values.Set("Timestamp", time.Now().UTC().Format(time.RFC3339))
	origUrl.RawQuery = values.Encode()

	return nil
}

func SignAmazonUrl(origUrl *url.URL, api AmazonProductAPI) (signedUrl string , err error){

	escapeUrl := strings.Replace(origUrl.RawQuery, ",", "%2C", -1)
	escapeUrl = strings.Replace(escapeUrl, ":", "%3A", -1)

	params := strings.Split(escapeUrl, "&")
	sort.Strings(params)
	sortedParams := strings.Join(params, "&")

	toSign := fmt.Sprintf("GET\n%s\n%s\n%s", origUrl.Host, origUrl.Path, sortedParams)

	hasher := hmac.New(sha256.New, []byte(api.SecretKey))
	_, err = hasher.Write([]byte(toSign))
	if (err != nil) {
		return "", err
	}

	hash := base64.StdEncoding.EncodeToString(hasher.Sum(nil))

	hash = url.QueryEscape(hash)

	newParams := fmt.Sprintf("%s&Signature=%s", sortedParams, hash)

	origUrl.RawQuery = newParams

	return origUrl.String(), nil
}
<|endoftext|>


<commit_before>package pinentry

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"

	"github.com/keybase/client/go/logger"
	keybase1 "github.com/keybase/client/protocol/go"
)

//
// some borrowed from here:
//
//  https://github.com/bradfitz/camlistore/blob/master/pkg/misc/pinentry/pinentry.go
//
// Under the Apache 2.0 license
//

type Pinentry struct {
	initRes *error
	path    string
	term    string
	tty     string
	prog    string
	log     *logger.Logger
}

func New(envprog string, log *logger.Logger) *Pinentry {
	return &Pinentry{
		prog: envprog,
		log:  log,
	}
}

func (pe *Pinentry) Init() (error, error) {
	if pe.initRes != nil {
		return *pe.initRes, nil
	}
	err, fatalerr := pe.FindProgram()
	if err == nil {
		err = pe.GetTerminalName()
	}
	pe.term = os.Getenv("TERM")
	pe.initRes = &err
	return err, fatalerr
}

func (pe *Pinentry) SetInitError(e error) {
	pe.initRes = &e
}

func (pe *Pinentry) FindProgram() (error, error) {
	prog := pe.prog
	var err, fatalerr error
	if len(prog) > 0 {
		if err = canExec(prog); err == nil {
			pe.path = prog
		} else {
			err = fmt.Errorf("Can't execute given pinentry program '%s': %s",
				prog, err)
			fatalerr = err
		}
	} else if prog, err = FindPinentry(pe.log); err == nil {
		pe.path = prog
	}
	return err, fatalerr
}

func (pe *Pinentry) GetTerminalName() error {
	tty, err := os.Readlink("/proc/self/fd/0")
	if err != nil {
		pe.log.Debug("| Can't find terminal name via /proc lookup: %s", err)
	} else {
		pe.log.Debug("| found tty=%s", tty)
		pe.tty = tty
	}
	// Tis not a fatal error.  In particular, it won't work on OSX
	return nil
}

func (pe *Pinentry) Get(arg keybase1.SecretEntryArg) (res *keybase1.SecretEntryRes, err error) {

	pe.log.Debug("+ Pinentry::Get()")

	// Do a lazy initialization
	if err, _ = pe.Init(); err != nil {
		return
	}

	inst := pinentryInstance{parent: pe}
	defer inst.Close()

	if err = inst.Init(); err != nil {
		// We probably shouldn't try to use this thing again if we failed
		// to set it up.
		pe.SetInitError(err)
		return
	}
	res, err = inst.Run(arg)
	pe.log.Debug("- Pinentry::Get() -> %v", err)
	return
}

func (pi *pinentryInstance) Close() {
	pi.stdin.Close()
	pi.cmd.Wait()
}

type pinentryInstance struct {
	parent *Pinentry
	cmd    *exec.Cmd
	stdout io.ReadCloser
	stdin  io.WriteCloser
	br     *bufio.Reader
}

func (pi *pinentryInstance) Set(cmd, val string, errp *error) {
	if val == "" {
		return
	}
	fmt.Fprintf(pi.stdin, "%s %s\n", cmd, val)
	line, _, err := pi.br.ReadLine()
	if err != nil {
		*errp = err
		return
	}
	if string(line) != "OK" {
		*errp = fmt.Errorf("Response to " + cmd + " was " + string(line))
	}
	return
}

func (pi *pinentryInstance) Init() (err error) {

	parent := pi.parent

	parent.log.Debug("+ pinentryInstance::Init()")

	pi.cmd = exec.Command(parent.path)
	pi.stdin, _ = pi.cmd.StdinPipe()
	pi.stdout, _ = pi.cmd.StdoutPipe()

	if err = pi.cmd.Start(); err != nil {
		parent.log.Warning("unexpected error running pinentry (%s): %s", parent.path, err)
		return
	}

	pi.br = bufio.NewReader(pi.stdout)
	lineb, _, err := pi.br.ReadLine()

	if err != nil {
		err = fmt.Errorf("Failed to get getpin greeting: %s", err)
		return
	}

	line := string(lineb)
	if !strings.HasPrefix(line, "OK") {
		err = fmt.Errorf("getpin greeting didn't say 'OK', said: %q", line)
		return
	}

	if len(parent.tty) > 0 {
		pi.Set("OPTION", "ttyname="+parent.tty, &err)
	}
	if len(parent.term) > 0 {
		pi.Set("OPTION", "ttytype="+parent.term, &err)
	}

	parent.log.Debug("- pinentryInstance::Init() -> %v", err)
	return
}

func descEncode(s string) string {
	s = strings.Replace(s, "%", "%%", -1)
	s = strings.Replace(s, "\n", "%0A", -1)
	return s
}

func resDecode(s string) string {
	s = strings.Replace(s, "%25", "%", -1)
	return s
}

func (pi *pinentryInstance) Run(arg keybase1.SecretEntryArg) (res *keybase1.SecretEntryRes, err error) {

	pi.Set("SETPROMPT", arg.Prompt, &err)
	pi.Set("SETDESC", descEncode(arg.Desc), &err)
	pi.Set("SETOK", arg.Ok, &err)
	pi.Set("SETCANCEL", arg.Cancel, &err)
	pi.Set("SETERROR", arg.Err, &err)

	if err != nil {
		return
	}

	pinentrySecretStoreInfo, err := pi.useSecretStore(arg.UseSecretStore)
	if err != nil {
		return
	}

	fmt.Fprintf(pi.stdin, "GETPIN\n")
	var lineb []byte
	lineb, _, err = pi.br.ReadLine()
	if err != nil {
		err = fmt.Errorf("Failed to read line after GETPIN: %v", err)
		return
	}
	line := string(lineb)
	switch {
	case strings.HasPrefix(line, "D "):
		res = &keybase1.SecretEntryRes{Text: resDecode(line[2:])}
	case strings.HasPrefix(line, "ERR 83886179 canceled"):
		res = &keybase1.SecretEntryRes{Canceled: true}
	case line == "OK":
		res = &keybase1.SecretEntryRes{}
	default:
		return nil, fmt.Errorf("GETPIN response didn't start with D; got %q", line)
	}

	res.StoreSecret = pi.shouldStoreSecret(pinentrySecretStoreInfo)

	return
}
<commit_msg>fixed GetTerminalName on OSs that don't have /proc but do have /dev/tty.<commit_after>package pinentry

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"

	"github.com/keybase/client/go/logger"
	keybase1 "github.com/keybase/client/protocol/go"
)

//
// some borrowed from here:
//
//  https://github.com/bradfitz/camlistore/blob/master/pkg/misc/pinentry/pinentry.go
//
// Under the Apache 2.0 license
//

type Pinentry struct {
	initRes *error
	path    string
	term    string
	tty     string
	prog    string
	log     *logger.Logger
}

func New(envprog string, log *logger.Logger) *Pinentry {
	return &Pinentry{
		prog: envprog,
		log:  log,
	}
}

func (pe *Pinentry) Init() (error, error) {
	if pe.initRes != nil {
		return *pe.initRes, nil
	}
	err, fatalerr := pe.FindProgram()
	if err == nil {
		pe.GetTerminalName()
	}
	pe.term = os.Getenv("TERM")
	pe.initRes = &err
	return err, fatalerr
}

func (pe *Pinentry) SetInitError(e error) {
	pe.initRes = &e
}

func (pe *Pinentry) FindProgram() (error, error) {
	prog := pe.prog
	var err, fatalerr error
	if len(prog) > 0 {
		if err = canExec(prog); err == nil {
			pe.path = prog
		} else {
			err = fmt.Errorf("Can't execute given pinentry program '%s': %s",
				prog, err)
			fatalerr = err
		}
	} else if prog, err = FindPinentry(pe.log); err == nil {
		pe.path = prog
	}
	return err, fatalerr
}

func (pe *Pinentry) GetTerminalName() {
	tty, err := os.Readlink("/proc/self/fd/0")
	if err != nil {
		pe.log.Debug("| Can't find terminal name via /proc lookup: %s", err)

		// try /dev/tty
		tty = "/dev/tty"
		_, err = os.Stat("/dev/tty")
		if err != nil {
			pe.log.Debug("| stat /dev/tty failed: %s", err)
			return
		}
	}

	pe.log.Debug("| found tty=%s", tty)
	pe.tty = tty
}

func (pe *Pinentry) Get(arg keybase1.SecretEntryArg) (res *keybase1.SecretEntryRes, err error) {

	pe.log.Debug("+ Pinentry::Get()")

	// Do a lazy initialization
	if err, _ = pe.Init(); err != nil {
		return
	}

	inst := pinentryInstance{parent: pe}
	defer inst.Close()

	if err = inst.Init(); err != nil {
		// We probably shouldn't try to use this thing again if we failed
		// to set it up.
		pe.SetInitError(err)
		return
	}
	res, err = inst.Run(arg)
	pe.log.Debug("- Pinentry::Get() -> %v", err)
	return
}

func (pi *pinentryInstance) Close() {
	pi.stdin.Close()
	pi.cmd.Wait()
}

type pinentryInstance struct {
	parent *Pinentry
	cmd    *exec.Cmd
	stdout io.ReadCloser
	stdin  io.WriteCloser
	br     *bufio.Reader
}

func (pi *pinentryInstance) Set(cmd, val string, errp *error) {
	if val == "" {
		return
	}
	fmt.Fprintf(pi.stdin, "%s %s\n", cmd, val)
	line, _, err := pi.br.ReadLine()
	if err != nil {
		*errp = err
		return
	}
	if string(line) != "OK" {
		*errp = fmt.Errorf("Response to " + cmd + " was " + string(line))
	}
	return
}

func (pi *pinentryInstance) Init() (err error) {

	parent := pi.parent

	parent.log.Debug("+ pinentryInstance::Init()")

	pi.cmd = exec.Command(parent.path)
	pi.stdin, _ = pi.cmd.StdinPipe()
	pi.stdout, _ = pi.cmd.StdoutPipe()

	if err = pi.cmd.Start(); err != nil {
		parent.log.Warning("unexpected error running pinentry (%s): %s", parent.path, err)
		return
	}

	pi.br = bufio.NewReader(pi.stdout)
	lineb, _, err := pi.br.ReadLine()

	if err != nil {
		err = fmt.Errorf("Failed to get getpin greeting: %s", err)
		return
	}

	line := string(lineb)
	if !strings.HasPrefix(line, "OK") {
		err = fmt.Errorf("getpin greeting didn't say 'OK', said: %q", line)
		return
	}

	if len(parent.tty) > 0 {
		parent.log.Debug("setting ttyname to %s", parent.tty)
		pi.Set("OPTION", "ttyname="+parent.tty, &err)
		if err != nil {
			parent.log.Debug("error setting ttyname: %s", err)
		}
	}
	if len(parent.term) > 0 {
		parent.log.Info("setting ttytype to %s", parent.term)
		pi.Set("OPTION", "ttytype="+parent.term, &err)
		if err != nil {
			parent.log.Debug("error setting ttytype: %s", err)
		}
	}

	parent.log.Debug("- pinentryInstance::Init() -> %v", err)
	return
}

func descEncode(s string) string {
	s = strings.Replace(s, "%", "%%", -1)
	s = strings.Replace(s, "\n", "%0A", -1)
	return s
}

func resDecode(s string) string {
	s = strings.Replace(s, "%25", "%", -1)
	return s
}

func (pi *pinentryInstance) Run(arg keybase1.SecretEntryArg) (res *keybase1.SecretEntryRes, err error) {

	pi.Set("SETPROMPT", arg.Prompt, &err)
	pi.Set("SETDESC", descEncode(arg.Desc), &err)
	pi.Set("SETOK", arg.Ok, &err)
	pi.Set("SETCANCEL", arg.Cancel, &err)
	pi.Set("SETERROR", arg.Err, &err)

	if err != nil {
		return
	}

	pinentrySecretStoreInfo, err := pi.useSecretStore(arg.UseSecretStore)
	if err != nil {
		return
	}

	fmt.Fprintf(pi.stdin, "GETPIN\n")
	var lineb []byte
	lineb, _, err = pi.br.ReadLine()
	if err != nil {
		err = fmt.Errorf("Failed to read line after GETPIN: %v", err)
		return
	}
	line := string(lineb)
	switch {
	case strings.HasPrefix(line, "D "):
		res = &keybase1.SecretEntryRes{Text: resDecode(line[2:])}
	case strings.HasPrefix(line, "ERR 83886179 canceled"):
		res = &keybase1.SecretEntryRes{Canceled: true}
	case line == "OK":
		res = &keybase1.SecretEntryRes{}
	default:
		return nil, fmt.Errorf("GETPIN response didn't start with D; got %q", line)
	}

	res.StoreSecret = pi.shouldStoreSecret(pinentrySecretStoreInfo)

	return
}
<|endoftext|>


<commit_before>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package runtime

import "internal/cpu"

const (
	// bit masks taken from bits/hwcap.h
	_HWCAP_S390_ZARCH  = 2
	_HWCAP_S390_STFLE  = 4
	_HWCAP_S390_MSA    = 8
	_HWCAP_S390_LDISP  = 16
	_HWCAP_S390_EIMM   = 32
	_HWCAP_S390_DFP    = 64
	_HWCAP_S390_ETF3EH = 256
	_HWCAP_S390_VX     = 2048 // vector facility
)

func archauxv(tag, val uintptr) {
	switch tag {
	case _AT_HWCAP: // CPU capability bit flags
		cpu.S390X.HasZArch = val&_HWCAP_S390_ZARCH != 0
		cpu.S390X.HasSTFLE = val&_HWCAP_S390_STFLE != 0
		cpu.S390X.HasMSA = val&_HWCAP_S390_MSA != 0
		cpu.S390X.HasLDisp = val&_HWCAP_S390_LDISP != 0
		cpu.S390X.HasEImm = val&_HWCAP_S390_EIMM != 0
		cpu.S390X.HasDFP = val&_HWCAP_S390_DFP != 0
		cpu.S390X.HasETF3Enhanced = val&_HWCAP_S390_ETF3EH != 0
		cpu.S390X.HasVX = val&_HWCAP_S390_VX != 0
	}
}
<commit_msg>runtime: correct facilities names in s390 CPU support<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package runtime

import "internal/cpu"

const (
	// bit masks taken from bits/hwcap.h
	_HWCAP_S390_ZARCH  = 2
	_HWCAP_S390_STFLE  = 4
	_HWCAP_S390_MSA    = 8
	_HWCAP_S390_LDISP  = 16
	_HWCAP_S390_EIMM   = 32
	_HWCAP_S390_DFP    = 64
	_HWCAP_S390_ETF3EH = 256
	_HWCAP_S390_VX     = 2048 // vector facility
	_HWCAP_S390_VXE    = 8192
)

func archauxv(tag, val uintptr) {
	switch tag {
	case _AT_HWCAP: // CPU capability bit flags
		cpu.S390X.HasZARCH = val&_HWCAP_S390_ZARCH != 0
		cpu.S390X.HasSTFLE = val&_HWCAP_S390_STFLE != 0
		cpu.S390X.HasLDISP = val&_HWCAP_S390_LDISP != 0
		cpu.S390X.HasEIMM = val&_HWCAP_S390_EIMM != 0
		cpu.S390X.HasDFP = val&_HWCAP_S390_DFP != 0
		cpu.S390X.HasETF3EH = val&_HWCAP_S390_ETF3EH != 0
		cpu.S390X.HasMSA = val&_HWCAP_S390_MSA != 0
		cpu.S390X.HasVX = val&_HWCAP_S390_VX != 0
		cpu.S390X.HasVXE = val&_HWCAP_S390_VXE != 0
	}
}
<|endoftext|>


<commit_before>// UVa 423 - MPI Maelstrom

package main

import (
	"fmt"
	"math"
	"os"
	"strconv"
)

var n int

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func floydWarshall(matrix [][]int) {
	for k := 0; k < n; k++ {
		for i := 0; i < n; i++ {
			for j := 0; j < n; j++ {
				matrix[i][j] = min(matrix[i][j], matrix[i][k]+matrix[k][j])
			}
		}
	}
}

func main() {
	in, _ := os.Open("423.in")
	defer in.Close()
	out, _ := os.Create("423.out")
	defer out.Close()

	fmt.Fscanf(in, "%d", &n)
	matrix := make([][]int, n)
	for i := range matrix {
		matrix[i] = make([]int, n)
	}
	var token string
	for i := 1; i < n; i++ {
		for j := 0; j < i; j++ {
			fmt.Fscanf(in, "%s", &token)
			if token == "x" {
				matrix[i][j] = math.MaxInt32
			} else {
				matrix[i][j], _ = strconv.Atoi(token)
			}
			matrix[j][i] = matrix[i][j]
		}
	}
	floydWarshall(matrix)
	var max int
	for i := 1; i < n; i++ {
		if matrix[0][i] > max {
			max = matrix[0][i]
		}
	}
	fmt.Fprintln(out, max)
}
<commit_msg>clean up<commit_after>// UVa 423 - MPI Maelstrom

package main

import (
	"fmt"
	"math"
	"os"
	"strconv"
)

var n int

func floydWarshall(matrix [][]int) {
	for k := 0; k < n; k++ {
		for i := 0; i < n; i++ {
			for j := 0; j < n; j++ {
				if matrix[i][k]+matrix[k][j] < matrix[i][j] {
					matrix[i][j] = matrix[i][k] + matrix[k][j]
				}
			}
		}
	}
}

func main() {
	in, _ := os.Open("423.in")
	defer in.Close()
	out, _ := os.Create("423.out")
	defer out.Close()

	fmt.Fscanf(in, "%d", &n)
	matrix := make([][]int, n)
	for i := range matrix {
		matrix[i] = make([]int, n)
	}
	var token string
	for i := 1; i < n; i++ {
		for j := 0; j < i; j++ {
			fmt.Fscanf(in, "%s", &token)
			if token == "x" {
				matrix[i][j] = math.MaxInt32
			} else {
				matrix[i][j], _ = strconv.Atoi(token)
			}
			matrix[j][i] = matrix[i][j]
		}
	}
	floydWarshall(matrix)
	var max int
	for i := 1; i < n; i++ {
		if matrix[0][i] > max {
			max = matrix[0][i]
		}
	}
	fmt.Fprintln(out, max)
}
<|endoftext|>


<commit_before>package models

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/laincloud/hagrid/config"
	"github.com/mijia/sweb/log"
)

type Alert struct {
	ID        int        `gorm:"primary_key"`
	Name      string     `gorm:"type:varchar(64);not null;unique"`
	Enabled   bool       `gorm:"not null"`
	Services  []Service  `gorm:"ForeignKey:AlertID"`
	Templates []Template `gorm:"ForeignKey:AlertID"`
	Notifiers []User     `gorm:"many2many:alert_to_user_notify"`
	Admins    []User     `gorm:"many2many:alert_to_user_admin"`

	CreatedAt time.Time
}

type Icinga2Apply struct {
	Name             string
	NotificationType string
	Users            string
	ServiceName      string
	Interval         int
}

type Icinga2Service struct {
	ID            string
	Name          string
	Warning       string
	Critical      string
	CheckAttempts int
	ResendTime    int
	MetricURL     string
	MetricType    string
}

var metricType = map[string]string{
	Greater:  "greater",
	Less:     "less",
	NotEqual: "notequal",
	Equal:    "equal",
}

func (s *Icinga2Service) generateApplies(notifiersStr string) []Icinga2Apply {
	var icinga2Applies []Icinga2Apply
	for _, notificationType := range config.GetIcinga2NotificationTypes() {
		newNotification := Icinga2Apply{
			Name:             fmt.Sprintf("%s[%s]", s.Name, notificationType),
			NotificationType: notificationType,
			Users:            notifiersStr,
			ServiceName:      s.Name,
			Interval:         60 * s.ResendTime, //The metric of interval is second here
		}
		icinga2Applies = append(icinga2Applies, newNotification)
	}
	return icinga2Applies
}

func (al *Alert) generateIcinga2Config() ([]Icinga2Apply, []Icinga2Service) {
	notifiersList := make([]string, 0, len(al.Notifiers))
	for _, notifier := range al.Notifiers {
		notifiersList = append(notifiersList, notifier.Name)
	}
	notifiersBytes, _ := json.Marshal(notifiersList)
	notifiersStr := string(notifiersBytes)
	var icinga2Applies []Icinga2Apply
	var icinga2Services []Icinga2Service
	for _, service := range al.Services {
		if service.Enabled {
			if !strings.ContainsRune(service.Metric, '$') {
				// This service is not using template
				newService := Icinga2Service{
					ID:            strconv.Itoa(service.ID),
					Name:          fmt.Sprintf("%s-%s", al.Name, service.Name),
					Warning:       service.Warning,
					Critical:      service.Critical,
					CheckAttempts: service.CheckAttempts,
					ResendTime:    service.ResendTime,
					MetricURL:     fmt.Sprintf("%s/render?target=%s", config.GetSource(), service.Metric),
					MetricType:    metricType[service.CheckType],
				}
				icinga2Services = append(icinga2Services, newService)
				icinga2Applies = append(icinga2Applies, newService.generateApplies(notifiersStr)...)

			} else {
				// This service is using template
				for _, tmpl := range al.Templates {
					replacedStr := "$" + strings.TrimSpace(tmpl.Name)
					if strings.Contains(service.Metric, replacedStr) {
						for _, value := range strings.Split(tmpl.Values, ",") {
							trimedValue := strings.TrimSpace(value)
							if trimedValue != "" {
								realMetric := strings.Replace(service.Metric, replacedStr, trimedValue, -1)
								newService := Icinga2Service{
									ID:            fmt.Sprintf("%d-%d-%s", service.ID, tmpl.ID, trimedValue),
									Name:          fmt.Sprintf("%s-%s[%s]", al.Name, service.Name, trimedValue),
									Warning:       service.Warning,
									Critical:      service.Critical,
									CheckAttempts: service.CheckAttempts,
									ResendTime:    service.ResendTime,
									MetricURL:     fmt.Sprintf("%s/render?target=%s", config.GetSource(), realMetric),
									MetricType:    metricType[service.CheckType],
								}
								icinga2Services = append(icinga2Services, newService)
								icinga2Applies = append(icinga2Applies, newService.generateApplies(notifiersStr)...)
							}
						}
					}
				}
			}
		}
	}
	return icinga2Applies, icinga2Services
}

func SaveAlert(alert *Alert) error {
	if err := db.Save(alert).Error; err != nil {
		log.Errorf("Saving alert failed: %s", err.Error())
		return fmt.Errorf("Saving alert failed. Ask admin for help")
	}
	return nil
}

func DeleteAlert(id int) error {
	if err := db.Delete(Alert{}, "id = ?", id).Error; err != nil {
		log.Errorf("Deleting alert failed: %s", err.Error())
		return fmt.Errorf("Deleting alert failed. Ask admin for help")
	}
	return nil
}

func GetAlert(alert *Alert, id int) error {
	if err := db.First(alert, id).Error; err != nil {
		log.Errorf("Getting alert %d failed: %s", id, err.Error())
		return fmt.Errorf("Getting alert failed. Ask admin for help")
	}
	return nil
}

func GetAllAlerts(alerts *[]Alert) error {
	if err := db.Find(alerts).Error; err != nil {
		log.Errorf("Getting all alerts failed: %s", err.Error())
		return fmt.Errorf("Getting all alerts failed. Ask admin for help")
	}
	return nil
}

func IsAlertDuplicated(name string) bool {
	var count int
	err := db.Model(&Alert{}).Where("name = ?", name).Count(&count).Error
	return count != 0 || err != nil
}

func GetDetailedAlert(alert *Alert, id int) error {
	if err := GetAlert(alert, id); err != nil {
		return err
	}
	if err := db.Model(alert).Association("Services").Find(&(alert.Services)).Error; err != nil {
		log.Errorf("Getting associated services of %d failed: %s", id, err.Error())
		return fmt.Errorf("Getting associated services failed. Ask admin for help")
	}
	if err := db.Model(alert).Association("Templates").Find(&(alert.Templates)).Error; err != nil {
		log.Errorf("Getting associated templates of %d failed: %s", id, err.Error())
		return fmt.Errorf("Getting associated templates failed. Ask admin for help")
	}
	if err := db.Model(alert).Association("Notifiers").Find(&(alert.Notifiers)).Error; err != nil {
		log.Errorf("Getting associated notifiers of %d failed: %s", id, err.Error())
		return fmt.Errorf("Getting notifiers services failed. Ask admin for help")
	}
	if err := db.Model(alert).Association("Admins").Find(&(alert.Admins)).Error; err != nil {
		log.Errorf("Getting associated admins of %d failed: %s", id, err.Error())
		return fmt.Errorf("Getting associated admins failed. Ask admin for help")
	}
	return nil
}

func SynchronizeAlert(id int) error {
	syncLock.Lock()
	defer syncLock.Unlock()
	var (
		appliesFile  string
		servicesFile string
		err          error
		newStage     string
	)
	alert := &Alert{}
	if err = GetDetailedAlert(alert, id); err != nil {
		return err
	}
	pkgName := alertPackagePrefix + strconv.Itoa(id)
	// If the alert is not enabled, nothing will sync to icinga2, we only need to remove the package
	if !alert.Enabled || config.GetSource() == "" {
		icinga2Client.DeletePackage(pkgName)
		return nil
	}
	icinga2Client.CreatePackage(pkgName)
	icinga2Applies, icinga2Services := alert.generateIcinga2Config()
	appliesFile, err = renderTemplate("applies.tmpl", map[string][]Icinga2Apply{"Applies": icinga2Applies})
	if err != nil {
		log.Errorf("Rendering template applies.tmpl failed: %s", err.Error())
		return fmt.Errorf("Rendering template failed. Ask admin for help")
	}
	servicesFile, err = renderTemplate("services.tmpl", map[string][]Icinga2Service{"Services": icinga2Services})
	if err != nil {
		log.Errorf("Rendering template services.tmpl failed: %s", err.Error())
		return fmt.Errorf("Rendering template failed. Ask admin for help")
	}

	files := make(map[string]string)
	files["conf.d/applies.conf"] = appliesFile
	files["conf.d/services.conf"] = servicesFile
	if newStage, err = icinga2Client.UploadFiles(pkgName, files); err != nil {
		log.Errorf("Uploading alert config files failed: %s", err.Error())
		return fmt.Errorf("Uploading alert config files failed. Ask admin for help")
	}

	// Clear old packages
	pkg, _ := icinga2Client.GetPackage(pkgName)
	if pkg != nil {
		for _, stage := range pkg.Stages {
			if stage != newStage && stage != pkg.ActiveStage {
				icinga2Client.DeleteStage(pkgName, stage)
			}
		}
	}
	return nil
}
<commit_msg>Fix comparing template members of metric<commit_after>package models

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/laincloud/hagrid/config"
	"github.com/mijia/sweb/log"
)

type Alert struct {
	ID        int        `gorm:"primary_key"`
	Name      string     `gorm:"type:varchar(64);not null;unique"`
	Enabled   bool       `gorm:"not null"`
	Services  []Service  `gorm:"ForeignKey:AlertID"`
	Templates []Template `gorm:"ForeignKey:AlertID"`
	Notifiers []User     `gorm:"many2many:alert_to_user_notify"`
	Admins    []User     `gorm:"many2many:alert_to_user_admin"`

	CreatedAt time.Time
}

type Icinga2Apply struct {
	Name             string
	NotificationType string
	Users            string
	ServiceName      string
	Interval         int
}

type Icinga2Service struct {
	ID            string
	Name          string
	Warning       string
	Critical      string
	CheckAttempts int
	ResendTime    int
	MetricURL     string
	MetricType    string
}

var metricType = map[string]string{
	Greater:  "greater",
	Less:     "less",
	NotEqual: "notequal",
	Equal:    "equal",
}

func (s *Icinga2Service) generateApplies(notifiersStr string) []Icinga2Apply {
	var icinga2Applies []Icinga2Apply
	for _, notificationType := range config.GetIcinga2NotificationTypes() {
		newNotification := Icinga2Apply{
			Name:             fmt.Sprintf("%s[%s]", s.Name, notificationType),
			NotificationType: notificationType,
			Users:            notifiersStr,
			ServiceName:      s.Name,
			Interval:         60 * s.ResendTime, //The metric of interval is second here
		}
		icinga2Applies = append(icinga2Applies, newNotification)
	}
	return icinga2Applies
}

func (al *Alert) generateIcinga2Config() ([]Icinga2Apply, []Icinga2Service) {
	notifiersList := make([]string, 0, len(al.Notifiers))
	for _, notifier := range al.Notifiers {
		notifiersList = append(notifiersList, notifier.Name)
	}
	notifiersBytes, _ := json.Marshal(notifiersList)
	notifiersStr := string(notifiersBytes)
	var icinga2Applies []Icinga2Apply
	var icinga2Services []Icinga2Service
	for _, service := range al.Services {
		if service.Enabled {
			if !strings.ContainsRune(service.Metric, '$') {
				// This service is not using template
				newService := Icinga2Service{
					ID:            strconv.Itoa(service.ID),
					Name:          fmt.Sprintf("%s-%s", al.Name, service.Name),
					Warning:       service.Warning,
					Critical:      service.Critical,
					CheckAttempts: service.CheckAttempts,
					ResendTime:    service.ResendTime,
					MetricURL:     fmt.Sprintf("%s/render?target=%s", config.GetSource(), service.Metric),
					MetricType:    metricType[service.CheckType],
				}
				icinga2Services = append(icinga2Services, newService)
				icinga2Applies = append(icinga2Applies, newService.generateApplies(notifiersStr)...)

			} else {
				// This service is using template
				for _, tmpl := range al.Templates {
					replacedStr := "$" + strings.TrimSpace(tmpl.Name)
					if strings.Contains(service.Metric, replacedStr + ".") || strings.HasSuffix(service.Metric, replacedStr) {
						for _, value := range strings.Split(tmpl.Values, ",") {
							trimedValue := strings.TrimSpace(value)
							if trimedValue != "" {
								realMetric := strings.Replace(service.Metric, replacedStr, trimedValue, -1)
								newService := Icinga2Service{
									ID:            fmt.Sprintf("%d-%d-%s", service.ID, tmpl.ID, trimedValue),
									Name:          fmt.Sprintf("%s-%s[%s]", al.Name, service.Name, trimedValue),
									Warning:       service.Warning,
									Critical:      service.Critical,
									CheckAttempts: service.CheckAttempts,
									ResendTime:    service.ResendTime,
									MetricURL:     fmt.Sprintf("%s/render?target=%s", config.GetSource(), realMetric),
									MetricType:    metricType[service.CheckType],
								}
								icinga2Services = append(icinga2Services, newService)
								icinga2Applies = append(icinga2Applies, newService.generateApplies(notifiersStr)...)
							}
						}
					}
				}
			}
		}
	}
	return icinga2Applies, icinga2Services
}

func SaveAlert(alert *Alert) error {
	if err := db.Save(alert).Error; err != nil {
		log.Errorf("Saving alert failed: %s", err.Error())
		return fmt.Errorf("Saving alert failed. Ask admin for help")
	}
	return nil
}

func DeleteAlert(id int) error {
	if err := db.Delete(Alert{}, "id = ?", id).Error; err != nil {
		log.Errorf("Deleting alert failed: %s", err.Error())
		return fmt.Errorf("Deleting alert failed. Ask admin for help")
	}
	return nil
}

func GetAlert(alert *Alert, id int) error {
	if err := db.First(alert, id).Error; err != nil {
		log.Errorf("Getting alert %d failed: %s", id, err.Error())
		return fmt.Errorf("Getting alert failed. Ask admin for help")
	}
	return nil
}

func GetAllAlerts(alerts *[]Alert) error {
	if err := db.Find(alerts).Error; err != nil {
		log.Errorf("Getting all alerts failed: %s", err.Error())
		return fmt.Errorf("Getting all alerts failed. Ask admin for help")
	}
	return nil
}

func IsAlertDuplicated(name string) bool {
	var count int
	err := db.Model(&Alert{}).Where("name = ?", name).Count(&count).Error
	return count != 0 || err != nil
}

func GetDetailedAlert(alert *Alert, id int) error {
	if err := GetAlert(alert, id); err != nil {
		return err
	}
	if err := db.Model(alert).Association("Services").Find(&(alert.Services)).Error; err != nil {
		log.Errorf("Getting associated services of %d failed: %s", id, err.Error())
		return fmt.Errorf("Getting associated services failed. Ask admin for help")
	}
	if err := db.Model(alert).Association("Templates").Find(&(alert.Templates)).Error; err != nil {
		log.Errorf("Getting associated templates of %d failed: %s", id, err.Error())
		return fmt.Errorf("Getting associated templates failed. Ask admin for help")
	}
	if err := db.Model(alert).Association("Notifiers").Find(&(alert.Notifiers)).Error; err != nil {
		log.Errorf("Getting associated notifiers of %d failed: %s", id, err.Error())
		return fmt.Errorf("Getting notifiers services failed. Ask admin for help")
	}
	if err := db.Model(alert).Association("Admins").Find(&(alert.Admins)).Error; err != nil {
		log.Errorf("Getting associated admins of %d failed: %s", id, err.Error())
		return fmt.Errorf("Getting associated admins failed. Ask admin for help")
	}
	return nil
}

func SynchronizeAlert(id int) error {
	syncLock.Lock()
	defer syncLock.Unlock()
	var (
		appliesFile  string
		servicesFile string
		err          error
		newStage     string
	)
	alert := &Alert{}
	if err = GetDetailedAlert(alert, id); err != nil {
		return err
	}
	pkgName := alertPackagePrefix + strconv.Itoa(id)
	// If the alert is not enabled, nothing will sync to icinga2, we only need to remove the package
	if !alert.Enabled || config.GetSource() == "" {
		icinga2Client.DeletePackage(pkgName)
		return nil
	}
	icinga2Client.CreatePackage(pkgName)
	icinga2Applies, icinga2Services := alert.generateIcinga2Config()
	appliesFile, err = renderTemplate("applies.tmpl", map[string][]Icinga2Apply{"Applies": icinga2Applies})
	if err != nil {
		log.Errorf("Rendering template applies.tmpl failed: %s", err.Error())
		return fmt.Errorf("Rendering template failed. Ask admin for help")
	}
	servicesFile, err = renderTemplate("services.tmpl", map[string][]Icinga2Service{"Services": icinga2Services})
	if err != nil {
		log.Errorf("Rendering template services.tmpl failed: %s", err.Error())
		return fmt.Errorf("Rendering template failed. Ask admin for help")
	}

	files := make(map[string]string)
	files["conf.d/applies.conf"] = appliesFile
	files["conf.d/services.conf"] = servicesFile
	if newStage, err = icinga2Client.UploadFiles(pkgName, files); err != nil {
		log.Errorf("Uploading alert config files failed: %s", err.Error())
		return fmt.Errorf("Uploading alert config files failed. Ask admin for help")
	}

	// Clear old packages
	pkg, _ := icinga2Client.GetPackage(pkgName)
	if pkg != nil {
		for _, stage := range pkg.Stages {
			if stage != newStage && stage != pkg.ActiveStage {
				icinga2Client.DeleteStage(pkgName, stage)
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package brain

import (
	"github.com/BytemarkHosting/bytemark-client/lib/output"
)

// TODO (tom): Add pretty print & test file for this function
// VMDefaultSpec represents a VM Default specification.
type VMDefaultSpec struct {
	VMDefault VMDefault     `json:"vm_default,omitempty"`
	Discs     []Disc        `json:"disc,omitempty"`
	Reimage   *ImageInstall `json:"reimage,omitempty"` // may want to be null, so is a pointer
}

// DefaultFields returns the list of default fields to feed to github.com/BytemarkHosting/row.From for this type.
func (spec VMDefaultSpec) DefaultFields(f output.Format) string {
	switch f {
	case output.List:
		return "VirtualMachine, Discs, Reimage"
	}
	return "VirtualMachine, Discs, Reimage"
}<commit_msg>added pretty print function for the vm default specification<commit_after>package brain

import (
	"io"

	"github.com/BytemarkHosting/bytemark-client/lib/output"
	"github.com/BytemarkHosting/bytemark-client/lib/output/prettyprint"
)

// TODO(tom): Add pretty print & test file for this function
// VMDefaultSpec represents a VM Default specification.
type VMDefaultSpec struct {
	VMDefault VMDefault     `json:"vm_default,omitempty"`
	Discs     []Disc        `json:"disc,omitempty"`
	Reimage   *ImageInstall `json:"reimage,omitempty"` // may want to be null, so is a pointer
}

// DefaultFields returns the list of default fields to feed to github.com/BytemarkHosting/row.From for this type.
func (spec VMDefaultSpec) DefaultFields(f output.Format) string {
	switch f {
	case output.List:
		return "VirtualMachine, Discs, Reimage"
	}
	return "VirtualMachine, Discs, Reimage"
}

// TODO(tom): add test file for this
// TODO(tom): add backup schedules to prettyprint

// PrettyPrint outputs a nice human-readable overview of the VM Default Specification to the given writer.
func (spec VMDefaultSpec) PrettyPrint(wr io.Writer, detail prettyprint.DetailLevel) error {
	const template = `{{ define "vmdspec_sgl" }} ▸ {{.VMDefault.Name }} in {{capitalize .VMDefault.ZoneName}}{{ end }}
{{ define "vmdspec_vmd" }} {{ pluralize "core" "cores" .VMDefault.Cores }}, {{ mibgib .VMDefault.Memory }}, {{pluralize "disc" "discs"  }}{{ else }}no discs{{ end }}{{ end }}
{{ define "vmdspec_reimage" }} {{ .Reimage.Distribution }}{{ end }}

{{ define "vmdspec_discs"  }}
{{- if .Discs }}    discs:
{{- range .VMDefault.Discs }}
      • {{ prettysprint . "_sgl" }}
{{- end }}
{{ end -}}
{{ end }}

{{ define "vmdspec_medium" }}{{ template "vmdspec_sgl" . }}
{{ template "vmdspec_vmd" . }}{{ end }}

{{ define "vmdspec_full" -}}
{{ template "vmdspec_medium" . }}

{{ template "vmdspec_vmd" . }}
{{ template "vmdspec_discs" . -}}
{{ template "vmdspec_reimage" . }}
{{ end }}`
	return prettyprint.Run(wr, template, "vmdspec"+string(detail), spec)
}
<|endoftext|>


<commit_before>// Copyright © 2013-2017 Pierre Neidhardt <ambrevar@gmail.com>
// Use of this file is governed by the license that can be found in LICENSE.

package main

import (
	"io/ioutil"
	"testing"

	"github.com/ambrevar/demlo/cuesheet"
)

const (
	sampleCuesheet    = "cuesheet/testdata/sample.cue"
	scriptCase        = "scripts/30-case.lua"
	scriptPunctuation = "scripts/40-punctuation.lua"
)

func TestFixPunctuation(t *testing.T) {
	input := inputInfo{}
	output := outputInfo{
		Tags: map[string]string{
			"a b": "a_b",
			".a":  ".a",
			"a (": "a(",
			"(a":  "( a",
			"a c": "a 	c",
			"a": "	 a 	",
			"Some i.n.i.t.i.a.l.s.": "Some i.n.i.t.i.a.l.s.",
		},
	}

	buf, err := ioutil.ReadFile(scriptPunctuation)
	if err != nil {
		t.Fatal("Script is not readable", err)
	}

	// Compile scripts.
	L, err := MakeSandbox(nil)
	SandboxCompileScript(L, "punctuation", string(buf))
	if err != nil {
		t.Fatal("Spurious sandbox", err)
	}
	defer L.Close()

	err = RunScript(L, "punctuation", &input, &output)
	if err != nil {
		t.Fatalf("script punctuation: %s", err)
	}

	for want, got := range output.Tags {
		if got != want {
			t.Errorf(`Got "%v", want "%v"`, got, want)
		}
	}
}

func TestTitleCase(t *testing.T) {
	input := inputInfo{}
	output := outputInfo{
		Tags: map[string]string{
			"All Lowercase Words":                     "all lowercase words",
			"All Uppercase Words":                     "ALL UPPERCASE WORDS",
			"All Crazy Case Words":                    "aLl cRaZY cASE WordS",
			"With Common Preps in a CD Into the Box.": "With common preps in a cd INTO the box.",
			"Feat and feat. The Machines.":            "Feat and Feat. the machines.",
			"Unicode Apos´trophe":                     "unicode apos´trophe",
			"...":                                                      "...",
			".'?":                                                      ".'?",
			"I'll Be Ill'":                                             "i'll be ill'",
			"Names Like O'Hara, D’Arcy":                                "Names like o'hara, d’arcy",
			"Names Like McDonald and MacNeil":                          "Names like mcdonald and macneil",
			"Éléanor":                                                  "élÉanor",
			"XIV LIV Xiv Liv. Liv. Xiv.":                               "XIV LIV xiv liv. liv. xiv.",
			"A Start With a Lowercase Constant":                        "a start with a lowercase constant",
			`"A Double Quoted Sentence" and 'One Single Quoted'.`:      `"a double quoted sentence" and 'one single quoted'.`,
			`Another "Double Quoted Sentence", and "A Sentence More".`: `another "double quoted sentence", and "a sentence more".`,
			"Some I.N.I.T.I.A.L.S.":                                    "Some i.n.i.t.i.a.l.s.",
		},
	}

	buf, err := ioutil.ReadFile(scriptCase)
	if err != nil {
		t.Fatal("Script is not readable", err)
	}

	// Compile scripts.
	L, err := MakeSandbox(nil)
	SandboxCompileScript(L, "case", string(buf))
	if err != nil {
		t.Fatal("Spurious sandbox", err)
	}
	defer L.Close()

	err = RunScript(L, "case", &input, &output)
	if err != nil {
		t.Fatalf("script case: %s", err)
	}

	for want, got := range output.Tags {
		if got != want {
			t.Errorf(`Got "%v", want "%v"`, got, want)
		}
	}
}

func TestSentenceCase(t *testing.T) {
	input := inputInfo{}
	output := outputInfo{
		Tags: map[string]string{
			"Capitalized words":               "capitalized words",
			"Machine":                         "machine",
			"Rise of the machines":            "Rise Of The Machines",
			"Chanson d'avant":                 "Chanson D'Avant",
			"Names like o'hara, d’arcy":       "Names LIKE O'HARA, D’ARCY",
			"Names like McDonald and MacNeil": "Names LIKE MCDONALD AND MACNEIL",
			"XIV LIV xiv liv. Liv. Xiv.":      "XIV LIV xiv liv. liv. xiv.",
		},
	}

	buf, err := ioutil.ReadFile(scriptCase)
	if err != nil {
		t.Fatal("Script is not readable", err)
	}

	// Compile scripts.
	L, err := MakeSandbox(nil)
	SandboxCompileScript(L, "case", string(buf))
	if err != nil {
		t.Fatal("Spurious sandbox", err)
	}
	defer L.Close()

	// Set setencecase.
	L.PushBoolean(true)
	L.SetGlobal("scase")

	err = RunScript(L, "case", &input, &output)
	if err != nil {
		t.Fatalf("script case: %s", err)
	}

	for want, got := range output.Tags {
		if got != want {
			t.Errorf(`Got "%v", want "%v"`, got, want)
		}
	}
}

func TestStringNorm(t *testing.T) {
	want := []struct {
		s    string
		norm string
	}{
		{s: "A", norm: "a"},
		{s: "0a", norm: "a"},
		{s: "00a", norm: "a"},
		{s: "a0", norm: "a0"},
		{s: "a.0", norm: "a"},
		{s: "a0a", norm: "a0a"},
		{s: "a.0a", norm: "aa"},
		{s: "10", norm: "10"},
		{s: "01", norm: "1"},
		{s: ".a", norm: "a"},
		{s: "..a", norm: "a"},
	}

	for _, v := range want {
		n := stringNorm(v.s)
		if n != v.norm {
			t.Errorf(`Got "%v", want norm("%v")=="%v"`, n, v.s, v.norm)
		}
	}
}

func TestStringRel(t *testing.T) {
	want := []struct {
		a   string
		b   string
		rel float64
	}{
		{a: "foo", b: "bar", rel: 0.0},
		{a: "foo", b: "foo", rel: 1.0},
		{a: "foobar", b: "foobaz", rel: 1 - float64(1)/float64(6)},
		{a: "", b: "b", rel: 0.0},
		{a: "a", b: "", rel: 0.0},
		{a: "", b: "", rel: 1.0},
		{a: "ab", b: "ba", rel: 0.5},
		{a: "abba", b: "aba", rel: 0.75},
		{a: "aba", b: "abba", rel: 0.75},
		{a: "résumé", b: "resume", rel: 1 - float64(2)/float64(6)},
	}

	for _, v := range want {
		r := stringRel(v.a, v.b)
		if r != v.rel {
			t.Errorf(`Got %v, want rel("%v", "%v")==%v`, r, v.a, v.b, v.rel)
		}
	}
}

func TestFFmpegSplitTimes(t *testing.T) {
	// We need to make up last track's duration: 3 minutes.
	totaltime := float64(17*60 + 4 + 3*60)

	want := []struct {
		track    int
		start    string
		duration string
	}{
		{track: 0, start: "00:00:00.000", duration: "00:06:40.360"},
		{track: 1, start: "00:06:40.360", duration: "00:04:13.640"},
		{track: 3, start: "00:17:04.000", duration: "00:03:00.000"},
		{track: 4, start: "", duration: ""},
		{track: 8, start: "", duration: ""},
	}

	buf, err := ioutil.ReadFile(sampleCuesheet)
	if err != nil {
		panic(err)
	}
	sheet, err := cuesheet.New(buf)
	if err != nil {
		panic(err)
	}

	for _, v := range want {
		start, duration := ffmpegSplitTimes(sheet, "Faithless - Live in Berlin (CD1).mp3", v.track, totaltime)
		if start != v.start || duration != v.duration {
			t.Errorf("Got {start: %v, duration: %v}, want {start: %v, duration: %v}", start, duration, v.start, v.duration)
		}
	}
}
<commit_msg>Fix MakeSandbox result count in tests<commit_after>// Copyright © 2013-2017 Pierre Neidhardt <ambrevar@gmail.com>
// Use of this file is governed by the license that can be found in LICENSE.

package main

import (
	"io/ioutil"
	"testing"

	"github.com/ambrevar/demlo/cuesheet"
)

const (
	sampleCuesheet    = "cuesheet/testdata/sample.cue"
	scriptCase        = "scripts/30-case.lua"
	scriptPunctuation = "scripts/40-punctuation.lua"
)

func TestFixPunctuation(t *testing.T) {
	input := inputInfo{}
	output := outputInfo{
		Tags: map[string]string{
			"a b": "a_b",
			".a":  ".a",
			"a (": "a(",
			"(a":  "( a",
			"a c": "a 	c",
			"a": "	 a 	",
			"Some i.n.i.t.i.a.l.s.": "Some i.n.i.t.i.a.l.s.",
		},
	}

	buf, err := ioutil.ReadFile(scriptPunctuation)
	if err != nil {
		t.Fatal("Script is not readable", err)
	}

	// Compile scripts.
	L := MakeSandbox(nil)
	SandboxCompileScript(L, "punctuation", string(buf))
	if err != nil {
		t.Fatal("Spurious sandbox", err)
	}
	defer L.Close()

	err = RunScript(L, "punctuation", &input, &output)
	if err != nil {
		t.Fatalf("script punctuation: %s", err)
	}

	for want, got := range output.Tags {
		if got != want {
			t.Errorf(`Got "%v", want "%v"`, got, want)
		}
	}
}

func TestTitleCase(t *testing.T) {
	input := inputInfo{}
	output := outputInfo{
		Tags: map[string]string{
			"All Lowercase Words":                     "all lowercase words",
			"All Uppercase Words":                     "ALL UPPERCASE WORDS",
			"All Crazy Case Words":                    "aLl cRaZY cASE WordS",
			"With Common Preps in a CD Into the Box.": "With common preps in a cd INTO the box.",
			"Feat and feat. The Machines.":            "Feat and Feat. the machines.",
			"Unicode Apos´trophe":                     "unicode apos´trophe",
			"...":                                                      "...",
			".'?":                                                      ".'?",
			"I'll Be Ill'":                                             "i'll be ill'",
			"Names Like O'Hara, D’Arcy":                                "Names like o'hara, d’arcy",
			"Names Like McDonald and MacNeil":                          "Names like mcdonald and macneil",
			"Éléanor":                                                  "élÉanor",
			"XIV LIV Xiv Liv. Liv. Xiv.":                               "XIV LIV xiv liv. liv. xiv.",
			"A Start With a Lowercase Constant":                        "a start with a lowercase constant",
			`"A Double Quoted Sentence" and 'One Single Quoted'.`:      `"a double quoted sentence" and 'one single quoted'.`,
			`Another "Double Quoted Sentence", and "A Sentence More".`: `another "double quoted sentence", and "a sentence more".`,
			"Some I.N.I.T.I.A.L.S.":                                    "Some i.n.i.t.i.a.l.s.",
		},
	}

	buf, err := ioutil.ReadFile(scriptCase)
	if err != nil {
		t.Fatal("Script is not readable", err)
	}

	// Compile scripts.
	L := MakeSandbox(nil)
	SandboxCompileScript(L, "case", string(buf))
	if err != nil {
		t.Fatal("Spurious sandbox", err)
	}
	defer L.Close()

	err = RunScript(L, "case", &input, &output)
	if err != nil {
		t.Fatalf("script case: %s", err)
	}

	for want, got := range output.Tags {
		if got != want {
			t.Errorf(`Got "%v", want "%v"`, got, want)
		}
	}
}

func TestSentenceCase(t *testing.T) {
	input := inputInfo{}
	output := outputInfo{
		Tags: map[string]string{
			"Capitalized words":               "capitalized words",
			"Machine":                         "machine",
			"Rise of the machines":            "Rise Of The Machines",
			"Chanson d'avant":                 "Chanson D'Avant",
			"Names like o'hara, d’arcy":       "Names LIKE O'HARA, D’ARCY",
			"Names like McDonald and MacNeil": "Names LIKE MCDONALD AND MACNEIL",
			"XIV LIV xiv liv. Liv. Xiv.":      "XIV LIV xiv liv. liv. xiv.",
		},
	}

	buf, err := ioutil.ReadFile(scriptCase)
	if err != nil {
		t.Fatal("Script is not readable", err)
	}

	// Compile scripts.
	L := MakeSandbox(nil)
	SandboxCompileScript(L, "case", string(buf))
	if err != nil {
		t.Fatal("Spurious sandbox", err)
	}
	defer L.Close()

	// Set setencecase.
	L.PushBoolean(true)
	L.SetGlobal("scase")

	err = RunScript(L, "case", &input, &output)
	if err != nil {
		t.Fatalf("script case: %s", err)
	}

	for want, got := range output.Tags {
		if got != want {
			t.Errorf(`Got "%v", want "%v"`, got, want)
		}
	}
}

func TestStringNorm(t *testing.T) {
	want := []struct {
		s    string
		norm string
	}{
		{s: "A", norm: "a"},
		{s: "0a", norm: "a"},
		{s: "00a", norm: "a"},
		{s: "a0", norm: "a0"},
		{s: "a.0", norm: "a"},
		{s: "a0a", norm: "a0a"},
		{s: "a.0a", norm: "aa"},
		{s: "10", norm: "10"},
		{s: "01", norm: "1"},
		{s: ".a", norm: "a"},
		{s: "..a", norm: "a"},
	}

	for _, v := range want {
		n := stringNorm(v.s)
		if n != v.norm {
			t.Errorf(`Got "%v", want norm("%v")=="%v"`, n, v.s, v.norm)
		}
	}
}

func TestStringRel(t *testing.T) {
	want := []struct {
		a   string
		b   string
		rel float64
	}{
		{a: "foo", b: "bar", rel: 0.0},
		{a: "foo", b: "foo", rel: 1.0},
		{a: "foobar", b: "foobaz", rel: 1 - float64(1)/float64(6)},
		{a: "", b: "b", rel: 0.0},
		{a: "a", b: "", rel: 0.0},
		{a: "", b: "", rel: 1.0},
		{a: "ab", b: "ba", rel: 0.5},
		{a: "abba", b: "aba", rel: 0.75},
		{a: "aba", b: "abba", rel: 0.75},
		{a: "résumé", b: "resume", rel: 1 - float64(2)/float64(6)},
	}

	for _, v := range want {
		r := stringRel(v.a, v.b)
		if r != v.rel {
			t.Errorf(`Got %v, want rel("%v", "%v")==%v`, r, v.a, v.b, v.rel)
		}
	}
}

func TestFFmpegSplitTimes(t *testing.T) {
	// We need to make up last track's duration: 3 minutes.
	totaltime := float64(17*60 + 4 + 3*60)

	want := []struct {
		track    int
		start    string
		duration string
	}{
		{track: 0, start: "00:00:00.000", duration: "00:06:40.360"},
		{track: 1, start: "00:06:40.360", duration: "00:04:13.640"},
		{track: 3, start: "00:17:04.000", duration: "00:03:00.000"},
		{track: 4, start: "", duration: ""},
		{track: 8, start: "", duration: ""},
	}

	buf, err := ioutil.ReadFile(sampleCuesheet)
	if err != nil {
		panic(err)
	}
	sheet, err := cuesheet.New(buf)
	if err != nil {
		panic(err)
	}

	for _, v := range want {
		start, duration := ffmpegSplitTimes(sheet, "Faithless - Live in Berlin (CD1).mp3", v.track, totaltime)
		if start != v.start || duration != v.duration {
			t.Errorf("Got {start: %v, duration: %v}, want {start: %v, duration: %v}", start, duration, v.start, v.duration)
		}
	}
}
<|endoftext|>