<commit_before>package stringutils

import (
	"crypto/md5"
	"crypto/rand"
	"fmt"
	"io"
	"math"
	r "math/rand"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
	"unicode/utf16"
)

func GetAfter(src string, find string) (string, bool) {
	index := strings.Index(src, find)
	if index > -1 && index+len(find) < len(src) {
		return src[(index + len(find)):], true
	}
	return src, false
}

func GetBefore(src string, find string) (string, bool) {
	index := strings.Index(src, find)
	if index > -1 && index < len(src) {
		return src[:index], true
	}
	return src, false
}

func UnicodeDecode(text string) string {
	regex, err := regexp.Compile(`(\\u[a-fA-F0-9]{4})`)
	if err != nil {
		return text
	}

	text = regex.ReplaceAllStringFunc(text, func(match string) string {
		_txt := match[2:]
		char, err := strconv.ParseInt(_txt, 16, 32)
		if err != nil {
			return match
		}
		return string(rune(int(char)))
	})

	regex, err = regexp.Compile(`(&#[\d]{2,6})`)
	if err != nil {
		return text
	}

	text = regex.ReplaceAllStringFunc(text, func(match string) string {
		_txt := match[2:]
		char, err := strconv.ParseInt(_txt, 10, 32)
		if err != nil {
			return match
		}
		return string(rune(int(char)))
	})

	return text
}

func UnicodeEncode(str string) (js, html string) {
	rs := []rune(str)
	js = ""
	html = ""
	for _, r := range rs {
		rint := int(r)
		if rint < 128 {
			js += string(r)
			html += string(r)
		} else {
			js += `\u` + strconv.FormatInt(int64(rint), 16) // json
			html += `&#` + strconv.Itoa(int(r)) + ";"       // 网页
		}
	}
	fmt.Printf("JSON: %s\n", js)
	fmt.Printf("HTML: %s\n", html)
	return
}

//比较是否在切片中存在，不区分大小写
func ExistSliceFold(srcSlice []string, elem string) bool {
	for _, v := range srcSlice {
		if strings.EqualFold(v, elem) {
			return true
		}
	}

	return false
}

//比较是否在切片中存在,区分大小写
func ExistSlice(srcSlice []string, elem string) bool {
	for _, v := range srcSlice {
		if v == elem {
			return true
		}
	}

	return false
}

//字符串转换uint16
func StringToUTF16(s string) []uint16 {
	return utf16.Encode([]rune(s + "\x00"))
}

func Float64IsZero(s float64) bool {
	if math.Abs(s) < 0.0001 {
		return true
	}
	return false
}

//取子串
func GetBetweenStr(str, start, end string) string {
	n := strings.Index(str, start)
	if n == -1 {
		n = 0
	}
	str = string([]byte(str)[n:])
	m := strings.Index(str, end)
	if m == -1 {
		m = len(str)
	}
	str = string([]byte(str)[:m])
	return str
}

//取子串
func Substr(str string, start, length int) string {
	rs := []rune(str)
	rl := len(rs)
	end := 0

	if start < 0 {
		start = rl - 1 + start
	}
	end = start + length

	if start > end {
		start, end = end, start
	}

	if start < 0 {
		start = 0
	}
	if start > rl {
		start = rl
	}
	if end < 0 {
		end = 0
	}
	if end > rl {
		end = rl
	}

	return string(rs[start:end])
}

//删除slice中的元素
func RemoveSliceElement(val interface{}, index int) interface{} {

	if reflect.TypeOf(val).Kind() != reflect.Slice {
		fmt.Println("val类型非slice")
		return nil
	}

	s := reflect.ValueOf(val)
	if index < 0 || index >= s.Len() {
		fmt.Println("传入参数有误")
		return nil
	}

	prev := s.Index(index)
	for i := index + 1; i < s.Len(); i++ {
		value := s.Index(i)
		prev.Set(value)
		prev = value
	}

	return s.Slice(0, s.Len()-1).Interface()
}

// RandomCreateBytes generate random []byte by specify chars.
func RandomCreateBytes(n int, alphabets ...byte) []byte {
	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	var bytes = make([]byte, n)
	var randby bool
	if num, err := rand.Read(bytes); num != n || err != nil {
		r.Seed(time.Now().UnixNano())
		randby = true
	}
	for i, b := range bytes {
		if len(alphabets) == 0 {
			if randby {
				bytes[i] = alphanum[r.Intn(len(alphanum))]
			} else {
				bytes[i] = alphanum[b%byte(len(alphanum))]
			}
		} else {
			if randby {
				bytes[i] = alphabets[r.Intn(len(alphabets))]
			} else {
				bytes[i] = alphabets[b%byte(len(alphabets))]
			}
		}
	}
	return bytes
}

func SumMd5(txtInput string) string {
	h := md5.New()
	io.WriteString(h, txtInput)
	return fmt.Sprintf("%x", h.Sum(nil))
}
<commit_msg>修改取子串函数实现<commit_after>package stringutils

import (
	"crypto/md5"
	"crypto/rand"
	"fmt"
	"io"
	"math"
	r "math/rand"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
	"unicode/utf16"
)

func GetAfter(src string, find string) (string, bool) {
	index := strings.Index(src, find)
	if index > -1 && index+len(find) < len(src) {
		return src[(index + len(find)):], true
	}
	return src, false
}

func GetBefore(src string, find string) (string, bool) {
	index := strings.Index(src, find)
	if index > -1 && index < len(src) {
		return src[:index], true
	}
	return src, false
}

func UnicodeDecode(text string) string {
	regex, err := regexp.Compile(`(\\u[a-fA-F0-9]{4})`)
	if err != nil {
		return text
	}

	text = regex.ReplaceAllStringFunc(text, func(match string) string {
		_txt := match[2:]
		char, err := strconv.ParseInt(_txt, 16, 32)
		if err != nil {
			return match
		}
		return string(rune(int(char)))
	})

	regex, err = regexp.Compile(`(&#[\d]{2,6})`)
	if err != nil {
		return text
	}

	text = regex.ReplaceAllStringFunc(text, func(match string) string {
		_txt := match[2:]
		char, err := strconv.ParseInt(_txt, 10, 32)
		if err != nil {
			return match
		}
		return string(rune(int(char)))
	})

	return text
}

func UnicodeEncode(str string) (js, html string) {
	rs := []rune(str)
	js = ""
	html = ""
	for _, r := range rs {
		rint := int(r)
		if rint < 128 {
			js += string(r)
			html += string(r)
		} else {
			js += `\u` + strconv.FormatInt(int64(rint), 16) // json
			html += `&#` + strconv.Itoa(int(r)) + ";"       // 网页
		}
	}
	fmt.Printf("JSON: %s\n", js)
	fmt.Printf("HTML: %s\n", html)
	return
}

//比较是否在切片中存在，不区分大小写
func ExistSliceFold(srcSlice []string, elem string) bool {
	for _, v := range srcSlice {
		if strings.EqualFold(v, elem) {
			return true
		}
	}

	return false
}

//比较是否在切片中存在,区分大小写
func ExistSlice(srcSlice []string, elem string) bool {
	for _, v := range srcSlice {
		if v == elem {
			return true
		}
	}

	return false
}

//字符串转换uint16
func StringToUTF16(s string) []uint16 {
	return utf16.Encode([]rune(s + "\x00"))
}

func Float64IsZero(s float64) bool {
	if math.Abs(s) < 0.0001 {
		return true
	}
	return false
}

//取子串
func Substr(s string, start, length int) string {
	bt := []rune(s)
	if start < 0 {
		start = 0
	}
	if start > len(bt) {
		start = start % len(bt)
	}
	var end int
	if (start + length) > (len(bt) - 1) {
		end = len(bt)
	} else {
		end = start + length
	}
	return string(bt[start:end])
}

//删除slice中的元素
func RemoveSliceElement(val interface{}, index int) interface{} {

	if reflect.TypeOf(val).Kind() != reflect.Slice {
		fmt.Println("val类型非slice")
		return nil
	}

	s := reflect.ValueOf(val)
	if index < 0 || index >= s.Len() {
		fmt.Println("传入参数有误")
		return nil
	}

	prev := s.Index(index)
	for i := index + 1; i < s.Len(); i++ {
		value := s.Index(i)
		prev.Set(value)
		prev = value
	}

	return s.Slice(0, s.Len()-1).Interface()
}

// RandomCreateBytes generate random []byte by specify chars.
func RandomCreateBytes(n int, alphabets ...byte) []byte {
	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	var bytes = make([]byte, n)
	var randby bool
	if num, err := rand.Read(bytes); num != n || err != nil {
		r.Seed(time.Now().UnixNano())
		randby = true
	}
	for i, b := range bytes {
		if len(alphabets) == 0 {
			if randby {
				bytes[i] = alphanum[r.Intn(len(alphanum))]
			} else {
				bytes[i] = alphanum[b%byte(len(alphanum))]
			}
		} else {
			if randby {
				bytes[i] = alphabets[r.Intn(len(alphabets))]
			} else {
				bytes[i] = alphabets[b%byte(len(alphabets))]
			}
		}
	}
	return bytes
}

func SumMd5(txtInput string) string {
	h := md5.New()
	io.WriteString(h, txtInput)
	return fmt.Sprintf("%x", h.Sum(nil))
}
<|endoftext|>


<commit_before>package path

import (
	"os"
	"path/filepath"
	"strings"

	. "gopkg.in/check.v1"
)

var _ = Suite(&PathTests{})

type PathTests struct {
	oldDir string
	dir string
}

func (t *PathTests) SetUpTest(c *C) {
	var err error
	t.oldDir, err = os.Getwd()
	c.Assert(err, IsNil)
	// Fix for OSX systems. Temporary folder lies in a symlink directory
	// /var/folders which is actually at /private/var/folders
	t.dir, err = filepath.EvalSymlinks(c.MkDir())
	c.Assert(err, IsNil)
	err = os.Chdir(t.dir)
	c.Assert(err, IsNil)
}

func (t *PathTests) TearDownTest(c *C) {
	os.Chdir(t.oldDir)
}

func (t *PathTests) TestNormalizeAbs(c *C) {
	err := os.Chdir(filepath.Join(t.dir, ".."))
	c.Assert(err, IsNil)
	base := filepath.Base(t.dir)
	n, err := Normalize(base)
	c.Assert(err, IsNil)
	c.Assert(n, Equals, t.dir)
}

func (t *PathTests) TestNormalizeRedundantChar(c *C) {
	if runtime.GOOS == "windows" {
		c.Skip("Incompatible with windows")
		return
	}

	n, err := Normalize(string(filepath.Separator) + t.dir)
	c.Assert(err, IsNil)
	c.Assert(n, Equals, t.dir)
}

func (t *PathTests) TestIsBelow(c *C) {
	basePath := t.dir
	belowPath := filepath.Dir(t.dir)
	basePath = strings.Replace(basePath, string(filepath.Separator),
		string(filepath.Separator) + string(filepath.Separator), -1)

	is, err := IsBelow(basePath, belowPath)
	c.Assert(err, IsNil)
	c.Assert(is, Equals, true)
}
<commit_msg>helpers/path: make redundant char check useful on windows<commit_after>package path

import (
	"os"
	"path/filepath"
	"strings"

	. "gopkg.in/check.v1"
)

var _ = Suite(&PathTests{})

type PathTests struct {
	oldDir string
	dir    string
}

func (t *PathTests) SetUpTest(c *C) {
	var err error
	t.oldDir, err = os.Getwd()
	c.Assert(err, IsNil)
	// Fix for OSX systems. Temporary folder lies in a symlink directory
	// /var/folders which is actually at /private/var/folders
	t.dir, err = filepath.EvalSymlinks(c.MkDir())
	c.Assert(err, IsNil)
	err = os.Chdir(t.dir)
	c.Assert(err, IsNil)
}

func (t *PathTests) TearDownTest(c *C) {
	os.Chdir(t.oldDir)
}

func (t *PathTests) TestNormalizeAbs(c *C) {
	err := os.Chdir(filepath.Join(t.dir, ".."))
	c.Assert(err, IsNil)
	base := filepath.Base(t.dir)
	n, err := Normalize(base)
	c.Assert(err, IsNil)
	c.Assert(n, Equals, t.dir)
}

func (t *PathTests) TestNormalizeRedundantChar(c *C) {
	path := strings.Replace(t.dir, string(filepath.Separator),
		string(filepath.Separator)+string(filepath.Separator), -1)
	c.Assert(path, Not(Equals), t.dir)
	n, err := Normalize(path)
	c.Assert(err, IsNil)
	c.Assert(n, Equals, t.dir)
}

func (t *PathTests) TestIsBelow(c *C) {
	basePath := t.dir
	belowPath := filepath.Dir(t.dir)

	is, err := IsBelow(basePath, belowPath)
	c.Assert(err, IsNil)
	c.Assert(is, Equals, true)
}
<|endoftext|>


<commit_before>package bassh

import (
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"
	"syscall"

	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/terminal"
)

//SSHParams contains params to setup the Session
type SSHParams struct {
	Env    []string
	Stdin  io.Reader
	Stdout io.Writer
	Stderr io.Writer
}

//SSHClient wraps the ssh client configuration and the host/port information
type SSHClient struct {
	Session *ssh.Session
	Config  *ssh.ClientConfig
	Host    string
	Port    int
}

//InitSession returns a session initialised with the given params
func (client *SSHClient) InitSession(params *SSHParams) error {
	var (
		session *ssh.Session
		err     error
	)

	if session, err = client.newSession(); err != nil {
		return err
	}

	if err = client.prepareCommand(session, params); err != nil {
		return err
	}

	client.Session = session

	return nil
}

//CloseSession closes the session for the client
func (client *SSHClient) CloseSession() {
	client.Session.Close()
}

func (client *SSHClient) prepareCommand(session *ssh.Session, params *SSHParams) error {
	for _, env := range params.Env {
		variable := strings.Split(env, "=")
		if len(variable) != 2 {
			continue
		}
		fmt.Println("Setting env variable ", variable[0], " to ", variable[1])
		if err := session.Setenv(variable[0], variable[1]); err != nil {
			fmt.Println("The remote system doesn't accept the setEnv command: ", err)
			return err
		}
	}

	if params.Stdin != nil {
		stdin, err := session.StdinPipe()
		if err != nil {
			return fmt.Errorf("Unable to setup stdin for session: %v", err)
		}
		go io.Copy(stdin, params.Stdin)
	}

	if params.Stdout != nil {
		stdout, err := session.StdoutPipe()
		if err != nil {
			return fmt.Errorf("Unable to setup stdout for session: %v", err)
		}
		go io.Copy(params.Stdout, stdout)
	}

	if params.Stderr != nil {
		stderr, err := session.StderrPipe()
		if err != nil {
			return fmt.Errorf("Unable to setup stderr for session: %v", err)
		}
		go io.Copy(params.Stderr, stderr)
	}

	return nil
}

func (client *SSHClient) newSession() (*ssh.Session, error) {
	connection, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", client.Host, client.Port), client.Config)
	if err != nil {
		return nil, fmt.Errorf("Failed to dial: %s", err)
	}

	session, err := connection.NewSession()
	if err != nil {
		return nil, fmt.Errorf("Failed to create session: %s", err)
	}

	modes := ssh.TerminalModes{
		// ssh.ECHO:          0,     // disable echoing
		ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
	}

	if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
		session.Close()
		return nil, fmt.Errorf("request for pseudo terminal failed: %s", err)
	}

	return session, nil
}

func decodeKeyForAuthMethod(file string) (ssh.AuthMethod, error) {
	fmt.Printf("Private key is at: %s\n", file)
	buffer, err := ioutil.ReadFile(file)
	if err != nil {
		fmt.Println("Error while reading the file: ", err)
		return nil, errors.New("Error while reading the .pem file from disk!")
	}
	decryptedBuffer, err := decryptIfEncrypted(buffer)
	if err != nil {
		return nil, err
	}
	key, err := ssh.ParsePrivateKey(decryptedBuffer)
	if err != nil {
		fmt.Println("Error while parsing private key: ", err)
		return nil, err
	}
	fmt.Println("Private key succesfully decripted and decoded.")

	return ssh.PublicKeys(key), nil
}

func decryptIfEncrypted(buffer []byte) ([]byte, error) {
	//  Decode the key extracting the pem.Block structure
	block, _ := pem.Decode(buffer)
	if block == nil {
		return nil, errors.New("Failed to decode certificate buffer")
	}
	//  Verify if the pem.block is Encrypted
	if x509.IsEncryptedPEMBlock(block) {
		fmt.Println("Key is encrypted, specify decrypt passphrase: ")
		bytePassword, err := terminal.ReadPassword(int(syscall.Stdin))
		if err == nil {
			fmt.Println("\nPassword typed: " + string(bytePassword))
		}
		passphrase := string(bytePassword)
		decryptedPem, err := x509.DecryptPEMBlock(block, []byte(strings.TrimSpace(passphrase)))
		if err != nil {
			fmt.Println("Error while reading the file: ", err)
			return nil, err
		}
		//  Recreating the decoded block to be returned
		var newBlock pem.Block
		newBlock.Type = block.Type
		newBlock.Headers = block.Headers
		newBlock.Bytes = decryptedPem
		//  Encoding block into []byte and returning
		return pem.EncodeToMemory(&newBlock), nil
	}
	fmt.Println("Key is not encrypted.")

	return buffer, nil
}

//func sshAgent() ssh.AuthMethod {
//	if sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
//		return ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers)
//	}
//	return nil
//}

//ConfigureCredentials returns the ClientConfig struct to be used as part of the
//SSHClient definition
func ConfigureCredentials(username string, keypath string) (*ssh.ClientConfig, error) {
	var config ssh.ClientConfig
	config.User = username
	pemKeyCommand := keypath
	authMethod, err := decodeKeyForAuthMethod(pemKeyCommand)
	if err != nil {
		return nil, err
	}
	config.Auth = []ssh.AuthMethod{authMethod}
	return &config, nil
}

//CreateClient takes a *ssh.ClientConfig struct as input, ipAddress of the target
//machine and the ssh port, and returns an *SSHClient where a command can be Run on
func CreateClient(sshConfig *ssh.ClientConfig, ipAddr string, port int) *SSHClient {
	return &SSHClient{
		Config: sshConfig,
		Host:   ipAddr,
		Port:   port,
	}
}

//Run opens an SSH session and Runs the command passed as an argument
func (client *SSHClient) Run(command string) {
	defer client.CloseSession()
	if err := client.Session.Run(command); err != nil {
		fmt.Fprintf(os.Stderr, "command run error: %s\n", err)
		if client.Session == nil {
			fmt.Println("Session not initialised.")
		}
		os.Exit(1)
	}
	return
}

//RunBash runs /bin/bash on the client
func (client *SSHClient) RunBash() {
	params := &SSHParams{
		Env:    []string{""},
		Stdin:  os.Stdin,
		Stdout: os.Stdout,
		Stderr: os.Stderr,
	}

	if err := client.InitSession(params); err == nil {
		client.Run("/bin/bash")
	}
}
<commit_msg>Fix Run function<commit_after>package bassh

import (
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"
	"syscall"

	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/terminal"
)

//SSHParams contains params to setup the Session
type SSHParams struct {
	Env    []string
	Stdin  io.Reader
	Stdout io.Writer
	Stderr io.Writer
}

//SSHClient wraps the ssh client configuration and the host/port information
type SSHClient struct {
	Session *ssh.Session
	Config  *ssh.ClientConfig
	Host    string
	Port    int
}

//InitSession returns a session initialised with the given params
func (client *SSHClient) InitSession(params *SSHParams) error {
	var (
		session *ssh.Session
		err     error
	)

	if session, err = client.newSession(); err != nil {
		return err
	}

	if err = client.prepareCommand(session, params); err != nil {
		return err
	}

	client.Session = session

	return nil
}

//CloseSession closes the session for the client
func (client *SSHClient) CloseSession() {
	client.Session.Close()
}

func (client *SSHClient) prepareCommand(session *ssh.Session, params *SSHParams) error {
	for _, env := range params.Env {
		variable := strings.Split(env, "=")
		if len(variable) != 2 {
			continue
		}
		fmt.Println("Setting env variable ", variable[0], " to ", variable[1])
		if err := session.Setenv(variable[0], variable[1]); err != nil {
			fmt.Println("The remote system doesn't accept the setEnv command: ", err)
			return err
		}
	}

	if params.Stdin != nil {
		stdin, err := session.StdinPipe()
		if err != nil {
			return fmt.Errorf("Unable to setup stdin for session: %v", err)
		}
		go io.Copy(stdin, params.Stdin)
	}

	if params.Stdout != nil {
		stdout, err := session.StdoutPipe()
		if err != nil {
			return fmt.Errorf("Unable to setup stdout for session: %v", err)
		}
		go io.Copy(params.Stdout, stdout)
	}

	if params.Stderr != nil {
		stderr, err := session.StderrPipe()
		if err != nil {
			return fmt.Errorf("Unable to setup stderr for session: %v", err)
		}
		go io.Copy(params.Stderr, stderr)
	}

	return nil
}

func (client *SSHClient) newSession() (*ssh.Session, error) {
	connection, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", client.Host, client.Port), client.Config)
	if err != nil {
		return nil, fmt.Errorf("Failed to dial: %s", err)
	}

	session, err := connection.NewSession()
	if err != nil {
		return nil, fmt.Errorf("Failed to create session: %s", err)
	}

	modes := ssh.TerminalModes{
		// ssh.ECHO:          0,     // disable echoing
		ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
	}

	if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
		session.Close()
		return nil, fmt.Errorf("request for pseudo terminal failed: %s", err)
	}

	return session, nil
}

func decodeKeyForAuthMethod(file string) (ssh.AuthMethod, error) {
	fmt.Printf("Private key is at: %s\n", file)
	buffer, err := ioutil.ReadFile(file)
	if err != nil {
		fmt.Println("Error while reading the file: ", err)
		return nil, errors.New("Error while reading the .pem file from disk!")
	}
	decryptedBuffer, err := decryptIfEncrypted(buffer)
	if err != nil {
		return nil, err
	}
	key, err := ssh.ParsePrivateKey(decryptedBuffer)
	if err != nil {
		fmt.Println("Error while parsing private key: ", err)
		return nil, err
	}
	fmt.Println("Private key succesfully decripted and decoded.")

	return ssh.PublicKeys(key), nil
}

func decryptIfEncrypted(buffer []byte) ([]byte, error) {
	//  Decode the key extracting the pem.Block structure
	block, _ := pem.Decode(buffer)
	if block == nil {
		return nil, errors.New("Failed to decode certificate buffer")
	}
	//  Verify if the pem.block is Encrypted
	if x509.IsEncryptedPEMBlock(block) {
		fmt.Println("Key is encrypted, specify decrypt passphrase: ")
		bytePassword, err := terminal.ReadPassword(int(syscall.Stdin))
		if err == nil {
			fmt.Println("\nPassword typed: " + string(bytePassword))
		}
		passphrase := string(bytePassword)
		decryptedPem, err := x509.DecryptPEMBlock(block, []byte(strings.TrimSpace(passphrase)))
		if err != nil {
			fmt.Println("Error while reading the file: ", err)
			return nil, err
		}
		//  Recreating the decoded block to be returned
		var newBlock pem.Block
		newBlock.Type = block.Type
		newBlock.Headers = block.Headers
		newBlock.Bytes = decryptedPem
		//  Encoding block into []byte and returning
		return pem.EncodeToMemory(&newBlock), nil
	}
	fmt.Println("Key is not encrypted.")

	return buffer, nil
}

//func sshAgent() ssh.AuthMethod {
//	if sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
//		return ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers)
//	}
//	return nil
//}

//ConfigureCredentials returns the ClientConfig struct to be used as part of the
//SSHClient definition
func ConfigureCredentials(username string, keypath string) (*ssh.ClientConfig, error) {
	var config ssh.ClientConfig
	config.User = username
	pemKeyCommand := keypath
	authMethod, err := decodeKeyForAuthMethod(pemKeyCommand)
	if err != nil {
		return nil, err
	}
	config.Auth = []ssh.AuthMethod{authMethod}
	return &config, nil
}

//CreateClient takes a *ssh.ClientConfig struct as input, ipAddress of the target
//machine and the ssh port, and returns an *SSHClient where a command can be Run on
func CreateClient(sshConfig *ssh.ClientConfig, ipAddr string, port int) *SSHClient {
	return &SSHClient{
		Config: sshConfig,
		Host:   ipAddr,
		Port:   port,
	}
}

//Run opens an SSH session and Runs the command passed as an argument
func (client *SSHClient) Run(command string) {
	params := &SSHParams{
		Env:    []string{""},
		Stdin:  os.Stdin,
		Stdout: os.Stdout,
		Stderr: os.Stderr,
	}

	if err := client.InitSession(params); err != nil {
		fmt.Errorf("Error while initialising SSH session! Error was: %s", err)
	}
	defer client.CloseSession()
	if err := client.Session.Run(command); err != nil {
		fmt.Fprintf(os.Stderr, "command run error: %s\n", err)
		if client.Session == nil {
			fmt.Println("Session not initialised.")
		}
		os.Exit(1)
	}
	return
}

//RunBash runs /bin/bash on the client
func (client *SSHClient) RunBash() {
	params := &SSHParams{
		Env:    []string{""},
		Stdin:  os.Stdin,
		Stdout: os.Stdout,
		Stderr: os.Stderr,
	}

	if err := client.InitSession(params); err == nil {
		client.Run("/bin/bash")
	}
}
<|endoftext|>


<commit_before>package espsdk

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

	"github.com/Sirupsen/logrus"
	"github.com/dysolution/sleepwalker"
)

// A Batch is a container for Contributions of the same type and
// any Releases that may be associated with them.
type Batch struct {
	AssignmentID                     string     `json:"assignment_id,omitempty"`
	BatchTags                        []string   `json:"batch_tags,omitempty"`
	BriefID                          string     `json:"brief_id,omitempty"`
	ContributionsAwaitingReviewCount int        `json:"contributions_awaiting_review_count,omitempty"`
	ContributionsCount               int        `json:"contributions_count,omitempty"`
	CreatedAt                        *time.Time `json:"created_at,omitempty"`
	CreatedBy                        string     `json:"created_by,omitempty"`
	CreatorIstockUsername            string     `json:"creator_istock_username,omitempty"`
	EventID                          string     `json:"event_id,omitempty"`
	ID                               int        `json:"id,omitempty"`
	IsGetty                          bool       `json:"is_getty,omitempty"`
	IsIstock                         bool       `json:"is_istock,omitempty"`
	IstockExclusive                  bool       `json:"istock_exclusive,omitempty"`
	LastContributionSubmittedAt      *time.Time `json:"last_contribution_submitted_at,omitempty"`
	LastSubmittedAt                  *time.Time `json:"last_submitted_at,omitempty"`
	Note                             string     `json:"note,omitempty"`
	ProfileID                        int        `json:"profile_id,omitempty"`
	ReviewedContributionsCount       int        `json:"reviewed_contributions_count,omitempty"`
	RevisableContributionsCount      int        `json:"revisable_contributions_count,omitempty"`
	SaveExtractedMetadata            bool       `json:"save_extracted_metadata,omitempty"`
	Status                           string     `json:"status,omitempty"`
	SubmissionName                   string     `json:"submission_name,omitempty"`
	SubmissionType                   string     `json:"submission_type,omitempty"`
	SubmittedContributionsCount      int        `json:"submitted_contributions_count,omitempty"`
	UpdatedAt                        *time.Time `json:"updated_at,omitempty"`
	UserID                           string     `json:"user_id,omitempty"`
}

// Index requests a list of all Batches for the account.
func (b Batch) Index(client sleepwalker.RESTClient) BatchList {
	desc := "Batch.Index"
	result, err := client.Get(b)
	if err != nil {
		result.Log().Error(desc)
		return BatchList{}
	}
	if result.StatusCode == 404 {
		result.Log().Error(desc)
		return BatchList{}
	}
	result.Log().Info(desc)
	batchList, _ := BatchList{}.Unmarshal(result.Payload)
	return batchList
}

// NameIsValid provides validation for a proposed SubmissionName.
func (b Batch) NameIsValid() bool { return len(b.SubmissionName) > 0 }

// TypeIsValid reports whether a proposed type is valid for ESP.
func (b *Batch) TypeIsValid() bool { return batchTypeIsValid[b.SubmissionType] }

// ValidTypes are the BatchTypes supported by ESP.
func (b Batch) ValidTypes() []string {
	keys := make([]string, len(batchTypeIsValid))
	i := 0
	for k := range batchTypeIsValid {
		keys[i] = k
		i++
	}
	return keys
}

// Path returns the path for the Batch. If the Batch has no ID, Path returns
// the root for all Batches (the Batch Index).
func (b Batch) Path() string {
	if b.ID == 0 {
		return Batches
	}
	return fmt.Sprintf("%s/%d", Batches, b.ID)
}

// Marshal serializes the Batch into a byte slice.
func (b Batch) Marshal() ([]byte, error) {
	return sleepwalker.Marshal(b)
}

// Unmarshal serializes the Batch into a byte slice.
func (b Batch) Unmarshal(payload []byte) (*Batch, error) {
	var batch *Batch
	err := json.Unmarshal(payload, &batch)
	if err != nil {
		return batch, err
	}
	return batch, nil
}

// A BatchUpdate contains a Batch. This matches the
// structure of the JSON payload the API expects during a PUT.
type BatchUpdate struct {
	Batch Batch `json:"submission_batch"`
}

// Path returns the path of the batch being updated.
func (bu BatchUpdate) Path() string { return bu.Batch.Path() }

// Marshal serializes a BatchUpdate into a byte slice.
func (bu BatchUpdate) Marshal() ([]byte, error) {
	return sleepwalker.Marshal(bu)
}

var batchTypeIsValid = map[string]bool{
	"getty_creative_video":  true,
	"getty_editorial_video": true,
	"getty_creative_still":  true,
	"getty_editorial_still": true,
	"istock_creative_video": true,
}

// A BatchList matches the structure of the JSON payload returned
// by the GET (all) Batches API endpoint.
type BatchList struct {
	Items             []Batch `json:"items,omitempty"`
	batchListMetadata `json:"meta,omitempty"`
}

type batchListMetadata struct {
	TotalItems int `json:"total_items,omitempty"`
}

// Unmarshal attempts to deserialize the provided JSON payload
// into the complete metadata returned by a request to the Index (GET all)
// API endpoint.
func (bl BatchList) Unmarshal(payload []byte) (BatchList, error) {
	var batchList BatchList
	if err := json.Unmarshal(payload, &batchList); err != nil {
		var errResponse interface{}
		json.Unmarshal(payload, &errResponse)
		Log.WithFields(logrus.Fields{
			"error":   err,
			"payload": errResponse,
		}).Error("BatchList.Unmarshal")
		return BatchList{}, err
	}
	return batchList, nil
}

// Last returns the most recently-created batch.
func (bl BatchList) Last() Batch {
	Log.Debugf("getting most recent of %d batches", bl.TotalItems)
	return bl.Items[0]
}
<commit_msg>refactor: lower severity<commit_after>package espsdk

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

	"github.com/Sirupsen/logrus"
	"github.com/dysolution/sleepwalker"
)

// A Batch is a container for Contributions of the same type and
// any Releases that may be associated with them.
type Batch struct {
	AssignmentID                     string     `json:"assignment_id,omitempty"`
	BatchTags                        []string   `json:"batch_tags,omitempty"`
	BriefID                          string     `json:"brief_id,omitempty"`
	ContributionsAwaitingReviewCount int        `json:"contributions_awaiting_review_count,omitempty"`
	ContributionsCount               int        `json:"contributions_count,omitempty"`
	CreatedAt                        *time.Time `json:"created_at,omitempty"`
	CreatedBy                        string     `json:"created_by,omitempty"`
	CreatorIstockUsername            string     `json:"creator_istock_username,omitempty"`
	EventID                          string     `json:"event_id,omitempty"`
	ID                               int        `json:"id,omitempty"`
	IsGetty                          bool       `json:"is_getty,omitempty"`
	IsIstock                         bool       `json:"is_istock,omitempty"`
	IstockExclusive                  bool       `json:"istock_exclusive,omitempty"`
	LastContributionSubmittedAt      *time.Time `json:"last_contribution_submitted_at,omitempty"`
	LastSubmittedAt                  *time.Time `json:"last_submitted_at,omitempty"`
	Note                             string     `json:"note,omitempty"`
	ProfileID                        int        `json:"profile_id,omitempty"`
	ReviewedContributionsCount       int        `json:"reviewed_contributions_count,omitempty"`
	RevisableContributionsCount      int        `json:"revisable_contributions_count,omitempty"`
	SaveExtractedMetadata            bool       `json:"save_extracted_metadata,omitempty"`
	Status                           string     `json:"status,omitempty"`
	SubmissionName                   string     `json:"submission_name,omitempty"`
	SubmissionType                   string     `json:"submission_type,omitempty"`
	SubmittedContributionsCount      int        `json:"submitted_contributions_count,omitempty"`
	UpdatedAt                        *time.Time `json:"updated_at,omitempty"`
	UserID                           string     `json:"user_id,omitempty"`
}

// Index requests a list of all Batches for the account.
func (b Batch) Index(client sleepwalker.RESTClient) BatchList {
	desc := "Batch.Index"
	result, err := client.Get(b)
	if err != nil {
		result.Log().Error(desc)
		return BatchList{}
	}
	if result.StatusCode == 404 {
		result.Log().Error(desc)
		return BatchList{}
	}
	result.Log().Debug(desc)
	batchList, _ := BatchList{}.Unmarshal(result.Payload)
	return batchList
}

// NameIsValid provides validation for a proposed SubmissionName.
func (b Batch) NameIsValid() bool { return len(b.SubmissionName) > 0 }

// TypeIsValid reports whether a proposed type is valid for ESP.
func (b *Batch) TypeIsValid() bool { return batchTypeIsValid[b.SubmissionType] }

// ValidTypes are the BatchTypes supported by ESP.
func (b Batch) ValidTypes() []string {
	keys := make([]string, len(batchTypeIsValid))
	i := 0
	for k := range batchTypeIsValid {
		keys[i] = k
		i++
	}
	return keys
}

// Path returns the path for the Batch. If the Batch has no ID, Path returns
// the root for all Batches (the Batch Index).
func (b Batch) Path() string {
	if b.ID == 0 {
		return Batches
	}
	return fmt.Sprintf("%s/%d", Batches, b.ID)
}

// Marshal serializes the Batch into a byte slice.
func (b Batch) Marshal() ([]byte, error) {
	return sleepwalker.Marshal(b)
}

// Unmarshal serializes the Batch into a byte slice.
func (b Batch) Unmarshal(payload []byte) (*Batch, error) {
	var batch *Batch
	err := json.Unmarshal(payload, &batch)
	if err != nil {
		return batch, err
	}
	return batch, nil
}

// A BatchUpdate contains a Batch. This matches the
// structure of the JSON payload the API expects during a PUT.
type BatchUpdate struct {
	Batch Batch `json:"submission_batch"`
}

// Path returns the path of the batch being updated.
func (bu BatchUpdate) Path() string { return bu.Batch.Path() }

// Marshal serializes a BatchUpdate into a byte slice.
func (bu BatchUpdate) Marshal() ([]byte, error) {
	return sleepwalker.Marshal(bu)
}

var batchTypeIsValid = map[string]bool{
	"getty_creative_video":  true,
	"getty_editorial_video": true,
	"getty_creative_still":  true,
	"getty_editorial_still": true,
	"istock_creative_video": true,
}

// A BatchList matches the structure of the JSON payload returned
// by the GET (all) Batches API endpoint.
type BatchList struct {
	Items             []Batch `json:"items,omitempty"`
	batchListMetadata `json:"meta,omitempty"`
}

type batchListMetadata struct {
	TotalItems int `json:"total_items,omitempty"`
}

// Unmarshal attempts to deserialize the provided JSON payload
// into the complete metadata returned by a request to the Index (GET all)
// API endpoint.
func (bl BatchList) Unmarshal(payload []byte) (BatchList, error) {
	var batchList BatchList
	if err := json.Unmarshal(payload, &batchList); err != nil {
		var errResponse interface{}
		json.Unmarshal(payload, &errResponse)
		Log.WithFields(logrus.Fields{
			"error":   err,
			"payload": errResponse,
		}).Error("BatchList.Unmarshal")
		return BatchList{}, err
	}
	return batchList, nil
}

// Last returns the most recently-created batch.
func (bl BatchList) Last() Batch {
	Log.Debugf("getting most recent of %d batches", bl.TotalItems)
	return bl.Items[0]
}
<|endoftext|>


<commit_before>package vault
<commit_msg>add TestMode_CheckMultiFlagSet<commit_after>package vault

import "testing"

func b(b bool) *bool {
	return &b
}

func TestMode_CheckMultiFlagSet(t *testing.T) {
	tests := []struct {
		in   mode
		want bool
	}{
		{
			mode{
				b(true),
				b(true),
				b(true),
				b(true)},
			true,
		},
		{
			mode{
				b(true),
				b(true),
				b(true),
				b(false)},
			true,
		},
		{
			mode{
				b(true),
				b(true),
				b(false),
				b(true)},
			true,
		},
		{
			mode{
				b(true),
				b(true),
				b(false),
				b(false)},
			true,
		},
		{
			mode{
				b(true),
				b(false),
				b(true),
				b(true)},
			true,
		},
		{
			mode{
				b(true),
				b(false),
				b(true),
				b(false)},
			true,
		},
		{
			mode{
				b(true),
				b(false),
				b(false),
				b(true)},
			true,
		},
		{
			mode{
				b(true),
				b(false),
				b(false),
				b(false)},
			false,
		},
		{
			mode{
				b(false),
				b(true),
				b(true),
				b(true)},
			true,
		},
		{
			mode{
				b(false),
				b(true),
				b(true),
				b(false)},
			true,
		},
		{
			mode{
				b(false),
				b(true),
				b(false),
				b(true)},
			true,
		},
		{
			mode{
				b(false),
				b(true),
				b(false),
				b(false)},
			false,
		},
		{
			mode{
				b(false),
				b(false),
				b(true),
				b(true)},
			true,
		},
		{
			mode{
				b(false),
				b(false),
				b(true),
				b(false)},
			false,
		},
		{
			mode{
				b(false),
				b(false),
				b(false),
				b(true)},
			false,
		},
		{
			mode{
				b(false),
				b(false),
				b(false),
				b(false)},
			false,
		},
	}

	for _, test := range tests {
		got := test.in.checkMultiFlagSet()

		if test.want != got {
			t.Fatalf("want %q, but %q:", test.want, got)
		}
	}
}
<|endoftext|>


<commit_before>package main

import "flag"
import "fmt"
import "log"
import "os"
import "strconv"
import "strings"
import "time"

import c "github.com/couchbase/indexing/secondary/common"
import "github.com/couchbase/indexing/secondary/dataport"
import "github.com/couchbase/indexing/secondary/projector"
import projc "github.com/couchbase/indexing/secondary/projector/client"
import protobuf "github.com/couchbase/indexing/secondary/protobuf/projector"

var pooln = "default"

var options struct {
	buckets       []string // buckets to connect
	endpoints     []string // list of endpoint daemon to start
	coordEndpoint string   // co-ordinator endpoint
	stat          string   // periodic timeout to print dataport statistics
	timeout       string   // timeout for dataport to exit
	maxVbno       int      // maximum number of vbuckets
	projector     bool     // start projector, useful in debug mode.
	debug         bool
	trace         bool
}

func argParse() []string {
	buckets := "default"
	endpoints := "localhost:9020"
	coordEndpoint := "localhost:9021"

	flag.StringVar(&buckets, "buckets", buckets,
		"buckets to connect")
	flag.StringVar(&endpoints, "endpoints", endpoints,
		"list of endpoint daemon to start")
	flag.StringVar(&options.coordEndpoint, "coorendp", coordEndpoint,
		"co-ordinator endpoint")
	flag.StringVar(&options.stat, "stat", "1000",
		"periodic timeout to print dataport statistics")
	flag.StringVar(&options.timeout, "timeout", "0",
		"timeout for dataport to exit")
	flag.IntVar(&options.maxVbno, "maxvb", 1024,
		"maximum number of vbuckets")
	flag.BoolVar(&options.projector, "projector", false,
		"start projector for debug mode")
	flag.BoolVar(&options.debug, "debug", false,
		"run in debug mode")
	flag.BoolVar(&options.trace, "trace", false,
		"run in trace mode")

	flag.Parse()

	options.buckets = strings.Split(buckets, ",")
	options.endpoints = strings.Split(endpoints, ",")
	if options.debug {
		c.SetLogLevel(c.LogLevelDebug)
	} else if options.trace {
		c.SetLogLevel(c.LogLevelTrace)
	} else {
		c.SetLogLevel(c.LogLevelInfo)
	}

	args := flag.Args()
	if len(args) < 1 || len(options.buckets) < 1 {
		usage()
		os.Exit(1)
	}
	return strings.Split(args[0], ",")
}

func usage() {
	fmt.Fprintf(os.Stderr, "Usage : %s [OPTIONS] <addr> \n", os.Args[0])
	flag.PrintDefaults()
}

var projectors = make(map[string]*projc.Client)

func main() {
	clusters := argParse()

	maxvbs := c.SystemConfig["maxVbuckets"].Int()
	dconf := c.SystemConfig.SectionConfig("projector.dataport.indexer.", true)

	// start dataport servers.
	for _, endpoint := range options.endpoints {
		stat, _ := strconv.Atoi(options.stat)
		timeout, _ := strconv.Atoi(options.timeout)
		go dataport.Application(
			endpoint, stat, timeout, maxvbs, dconf,
			func(addr string, msg interface{}) bool { return true })
	}
	go dataport.Application(options.coordEndpoint, 0, 0, maxvbs, dconf, nil)

	for _, cluster := range clusters {
		adminport := getProjectorAdminport(cluster, "default")
		if options.projector {
			config := c.SystemConfig.SectionConfig("projector.", true)
			config.SetValue("clusterAddr", cluster)
			config.SetValue("adminport.listenAddr", adminport)
			econf := c.SystemConfig.SectionConfig("endpoint.dataport.", true)
			epfactory := NewEndpointFactory(cluster, maxvbs, econf)
			config.SetValue("routerEndpointFactory", epfactory)
			projector.NewProjector(maxvbs, config) // start projector daemon
		}

		// projector-client
		cconfig := c.SystemConfig.SectionConfig("projector.client.", true)
		projectors[cluster] = projc.NewClient(adminport, maxvbs, cconfig)
	}

	// index instances for specified buckets.
	instances := protobuf.ExampleIndexInstances(
		options.buckets, options.endpoints, options.coordEndpoint)

	// start backfill stream on each projector
	for _, client := range projectors {
		// start backfill stream on each projector
		_, err := client.InitialTopicRequest(
			"backfill" /*topic*/, "default", /*pooln*/
			"dataport" /*endpointType*/, instances)
		if err != nil {
			log.Fatal(err)
		}
	}

	time.Sleep(1000 * time.Second)
	//<-make(chan bool) // wait for ever
}

func getProjectorAdminport(cluster, pooln string) string {
	cinfo := c.NewClusterInfoCache(cluster, pooln)
	if err := cinfo.Fetch(); err != nil {
		log.Fatal(err)
	}
	nodeID := cinfo.GetCurrentNode()
	adminport, err := cinfo.GetServiceAddress(nodeID, "projector")
	if err != nil {
		log.Fatal(err)
	}
	return adminport
}

func mf(err error, msg string) {
	if err != nil {
		log.Fatalf("%v: %v", msg, err)
	}
}

// NewEndpointFactory to create endpoint instances based on config.
func NewEndpointFactory(
	cluster string, maxvbs int, econf c.Config) c.RouterEndpointFactory {

	return func(topic, endpointType, addr string) (c.RouterEndpoint, error) {
		switch endpointType {
		case "dataport":
			return dataport.NewRouterEndpoint(cluster, topic, addr, maxvbs, econf)
		default:
			log.Fatal("Unknown endpoint type")
		}
		return nil, nil
	}
}
<commit_msg>Removed maxvb cmdline switch from datapath.go script.<commit_after>package main

import "flag"
import "fmt"
import "log"
import "os"
import "strconv"
import "strings"
import "time"

import c "github.com/couchbase/indexing/secondary/common"
import "github.com/couchbase/indexing/secondary/dataport"
import "github.com/couchbase/indexing/secondary/projector"
import projc "github.com/couchbase/indexing/secondary/projector/client"
import protobuf "github.com/couchbase/indexing/secondary/protobuf/projector"

var pooln = "default"

var options struct {
	buckets       []string // buckets to connect
	endpoints     []string // list of endpoint daemon to start
	coordEndpoint string   // co-ordinator endpoint
	stat          string   // periodic timeout to print dataport statistics
	timeout       string   // timeout for dataport to exit
	projector     bool     // start projector, useful in debug mode.
	debug         bool
	trace         bool
}

func argParse() []string {
	buckets := "default"
	endpoints := "localhost:9020"
	coordEndpoint := "localhost:9021"

	flag.StringVar(&buckets, "buckets", buckets,
		"buckets to connect")
	flag.StringVar(&endpoints, "endpoints", endpoints,
		"list of endpoint daemon to start")
	flag.StringVar(&options.coordEndpoint, "coorendp", coordEndpoint,
		"co-ordinator endpoint")
	flag.StringVar(&options.stat, "stat", "1000",
		"periodic timeout to print dataport statistics")
	flag.StringVar(&options.timeout, "timeout", "0",
		"timeout for dataport to exit")
	flag.BoolVar(&options.projector, "projector", false,
		"start projector for debug mode")
	flag.BoolVar(&options.debug, "debug", false,
		"run in debug mode")
	flag.BoolVar(&options.trace, "trace", false,
		"run in trace mode")

	flag.Parse()

	options.buckets = strings.Split(buckets, ",")
	options.endpoints = strings.Split(endpoints, ",")
	if options.debug {
		c.SetLogLevel(c.LogLevelDebug)
	} else if options.trace {
		c.SetLogLevel(c.LogLevelTrace)
	} else {
		c.SetLogLevel(c.LogLevelInfo)
	}

	args := flag.Args()
	if len(args) < 1 || len(options.buckets) < 1 {
		usage()
		os.Exit(1)
	}
	return strings.Split(args[0], ",")
}

func usage() {
	fmt.Fprintf(os.Stderr, "Usage : %s [OPTIONS] <addr> \n", os.Args[0])
	flag.PrintDefaults()
}

var projectors = make(map[string]*projc.Client)

func main() {
	clusters := argParse()

	maxvbs := c.SystemConfig["maxVbuckets"].Int()
	dconf := c.SystemConfig.SectionConfig("projector.dataport.indexer.", true)

	// start dataport servers.
	for _, endpoint := range options.endpoints {
		stat, _ := strconv.Atoi(options.stat)
		timeout, _ := strconv.Atoi(options.timeout)
		go dataport.Application(
			endpoint, stat, timeout, maxvbs, dconf,
			func(addr string, msg interface{}) bool { return true })
	}
	go dataport.Application(options.coordEndpoint, 0, 0, maxvbs, dconf, nil)

	for _, cluster := range clusters {
		adminport := getProjectorAdminport(cluster, "default")
		if options.projector {
			config := c.SystemConfig.SectionConfig("projector.", true)
			config.SetValue("clusterAddr", cluster)
			config.SetValue("adminport.listenAddr", adminport)
			econf := c.SystemConfig.SectionConfig("endpoint.dataport.", true)
			epfactory := NewEndpointFactory(cluster, maxvbs, econf)
			config.SetValue("routerEndpointFactory", epfactory)
			projector.NewProjector(maxvbs, config) // start projector daemon
		}

		// projector-client
		cconfig := c.SystemConfig.SectionConfig("projector.client.", true)
		projectors[cluster] = projc.NewClient(adminport, maxvbs, cconfig)
	}

	// index instances for specified buckets.
	instances := protobuf.ExampleIndexInstances(
		options.buckets, options.endpoints, options.coordEndpoint)

	// start backfill stream on each projector
	for _, client := range projectors {
		// start backfill stream on each projector
		_, err := client.InitialTopicRequest(
			"backfill" /*topic*/, "default", /*pooln*/
			"dataport" /*endpointType*/, instances)
		if err != nil {
			log.Fatal(err)
		}
	}

	time.Sleep(1000 * time.Second)
	//<-make(chan bool) // wait for ever
}

func getProjectorAdminport(cluster, pooln string) string {
	cinfo := c.NewClusterInfoCache(cluster, pooln)
	if err := cinfo.Fetch(); err != nil {
		log.Fatal(err)
	}
	nodeID := cinfo.GetCurrentNode()
	adminport, err := cinfo.GetServiceAddress(nodeID, "projector")
	if err != nil {
		log.Fatal(err)
	}
	return adminport
}

func mf(err error, msg string) {
	if err != nil {
		log.Fatalf("%v: %v", msg, err)
	}
}

// NewEndpointFactory to create endpoint instances based on config.
func NewEndpointFactory(
	cluster string, maxvbs int, econf c.Config) c.RouterEndpointFactory {

	return func(topic, endpointType, addr string) (c.RouterEndpoint, error) {
		switch endpointType {
		case "dataport":
			return dataport.NewRouterEndpoint(cluster, topic, addr, maxvbs, econf)
		default:
			log.Fatal("Unknown endpoint type")
		}
		return nil, nil
	}
}
<|endoftext|>


<commit_before>package caveat

import (
	"crypto/ecdsa"
	"crypto/rand"
	"crypto/sha256"
	"encoding/binary"
	"fmt"
	"time"

	vsecurity "veyron/security"

	"veyron2/security"
	"veyron2/security/wire"
	"veyron2/vom"
)

// nonceLength specifies the length in bytes of the random nonce used
// in publicKeyCaveat and publicKeyDischarge.
//
// TODO(ataly, ashankar): Nonce length of 16 bytes was chosen very conservatively.
// The purpose of the random nonce is essentially to separate caveats that have
// the same restriction and validation key (in order to prevent discharge replay).
// Can we use 4bytes instead?.
const nonceLength = 16

// publicKeyCaveat implements security.ThirdPartyCaveat. It specifies a restriction,
// a validation key and the location (object name) of the third-party responsible
// for discharging the caveat. A discharge for this caveat is a signed assertion whose
// signature can be verified using the validation key.
type publicKeyCaveat struct {
	// RandNonce specifies a cryptographically random nonce (of fixed length) that
	// uniquely identifies the caveat.
	RandNonce []uint8
	// DischargeMintingCaveat specifies the caveat that has to be validated
	// before minting a discharge for a publicKeyCaveat.
	DischargeMintingCaveat []byte
	// ValidationKey specifies the public key of the discharging-party.
	ValidationKey wire.PublicKey
	// ThirdPartyLocation specifies the object name of the discharging-party.
	ThirdPartyLocation string
	// ThirdPartyRequirements specify the information wanted in order to issue a discharge.
	ThirdPartyRequirements security.ThirdPartyRequirements
}

// ID returns a unique 32bytes long identity for the caveat based on the random nonce
// and SHA-256 hash of the restriction embedded in the caveat.
//
// TODO(ataly, ashankar): A 256bit hash is probably much stronger that what we need
// here. Can we truncate the hash to 96bits?
func (c *publicKeyCaveat) ID() string {
	return id(c.RandNonce, c.DischargeMintingCaveat)
}

func (c *publicKeyCaveat) Location() string {
	return c.ThirdPartyLocation
}

func (c *publicKeyCaveat) String() string {
	return fmt.Sprintf("publicKeyCaveat{DischargeMintingCaveat: (%v bytes), ThirdPartyLocation: %q}", len(c.DischargeMintingCaveat), c.ThirdPartyLocation)
}

func (c *publicKeyCaveat) Requirements() security.ThirdPartyRequirements {
	return c.ThirdPartyRequirements
}

func (c *publicKeyCaveat) Validate(ctx security.Context) error {
	// Check whether the context has a dicharge matching the caveat's ID.
	dis, ok := ctx.Discharges()[c.ID()]
	if !ok {
		return fmt.Errorf("missing discharge")
	}

	pkDischarge, ok := dis.(*publicKeyDischarge)
	if !ok {
		return fmt.Errorf("caveat of type %T cannot be validated with discharge of type %T", c, dis)
	}

	// Validate all caveats present on the discharge.
	validators, err := vsecurity.CaveatValidators(pkDischarge.Caveats...)
	if err != nil {
		return err
	}
	for _, v := range validators {
		if err := v.Validate(ctx); err != nil {
			return err
		}
	}

	// Check the discharge signature with the validation key from the caveat.
	key, err := c.ValidationKey.Decode()
	if err != nil {
		return err
	}
	if !pkDischarge.Signature.Verify(key, pkDischarge.contentHash()) {
		return fmt.Errorf("discharge %v has invalid signature", dis)
	}
	return nil
}

// publicKeyDischarge implements security.Discharge. It specifies a discharge
// for a publicKeyCaveat, and includes a signature that can be verified with
// the validation key of the publicKeyCaveat. Additionally, the discharge may
// also include caveats which must all validate in order for the discharge to
// be considered valid.
type publicKeyDischarge struct {
	// CaveatID is used to match this Discharge to the the ThirdPartyCaveat it is for.
	CaveatID string

	// Caveats under which this Discharge is valid.
	Caveats [][]byte

	// Signature on the contents of the discharge that can be verified using the
	// validaton key in the publicKeycaveat this discharge is for.
	Signature security.Signature
}

func (d *publicKeyDischarge) ID() string {
	return d.CaveatID
}

func (d *publicKeyDischarge) ThirdPartyCaveats() []security.ThirdPartyCaveat {
	return vsecurity.ThirdPartyCaveats(d.Caveats...)
}

func (d *publicKeyDischarge) sign(signer security.Signer) (err error) {
	d.Signature, err = signer.Sign(d.contentHash())
	return
}

func (d *publicKeyDischarge) contentHash() []byte {
	h := sha256.New()
	tmp := make([]byte, binary.MaxVarintLen64)

	wire.WriteString(h, tmp, d.CaveatID)
	for _, cav := range d.Caveats {
		wire.WriteBytes(h, tmp, cav)
	}
	return h.Sum(nil)
}

// NewPublicKeyCaveat returns a security.ThirdPartyCaveat which requires a
// discharge from a principal identified by the public key 'key' and present
// at the object name 'location'. This discharging principal is expected to
// validate 'caveat' before issuing a discharge.
func NewPublicKeyCaveat(caveat security.Caveat, key *ecdsa.PublicKey, location string, requirements security.ThirdPartyRequirements) (security.ThirdPartyCaveat, error) {
	nonce := make([]uint8, nonceLength)
	if _, err := rand.Read(nonce); err != nil {
		return nil, err
	}

	var validationKey wire.PublicKey
	if err := validationKey.Encode(key); err != nil {
		return nil, err
	}

	return &publicKeyCaveat{
		RandNonce:              nonce,
		DischargeMintingCaveat: caveat.Bytes(),
		ValidationKey:          validationKey,
		ThirdPartyLocation:     location,
		ThirdPartyRequirements: requirements,
	}, nil
}

// NewPublicKeyDischarge returns a new discharge for the provided 'tp'
// after validating any restrictions specified in it under context 'ctx'.
//
// The ID of the discharge is the same as the ID of 'caveat', and the discharge
// will be usable for the provided 'duration' only if:
// (1) 'caveats' are met when using the discharge.
// (2) 'tp' was obtained from NewPublicKeyCaveat using a key that is the same as
// the provided signer's PublicKey.
func NewPublicKeyDischarge(signer security.Signer, tp security.ThirdPartyCaveat, ctx security.Context, duration time.Duration, caveats []security.Caveat) (security.Discharge, error) {
	pkCaveat, ok := tp.(*publicKeyCaveat)
	if !ok {
		return nil, fmt.Errorf("cannot mint discharges for %T", tp)
	}

	v, err := vsecurity.CaveatValidators(pkCaveat.DischargeMintingCaveat)
	if err != nil {
		return nil, err
	}
	if err := v[0].Validate(ctx); err != nil {
		return nil, err
	}

	now := time.Now()
	expiryCaveat, err := security.NewCaveat(&Expiry{IssueTime: now, ExpiryTime: now.Add(duration)})
	if err != nil {
		return nil, err
	}

	caveats = append(caveats, expiryCaveat)
	discharge := &publicKeyDischarge{
		CaveatID: tp.ID(),
		Caveats:  vsecurity.CaveatBytes(caveats...),
	}

	// TODO(ashankar,ataly): Should signer necessarily be the same as ctx.LocalID()?
	// If so, need the PrivateID object corresponding to ctx.LocalID.
	if err := discharge.sign(signer); err != nil {
		return nil, err
	}
	return discharge, nil
}

// id calculates the sha256 hash of length-delimited byte slices
func id(slices ...[]byte) string {
	h := sha256.New()
	tmp := make([]byte, binary.MaxVarintLen64)

	for _, slice := range slices {
		wire.WriteBytes(h, tmp, slice)
	}
	return string(h.Sum(nil))
}

func init() {
	vom.Register(publicKeyCaveat{})
	vom.Register(publicKeyDischarge{})
}
<commit_msg>TBR: veyron2/security: Build and backward compatibility fix.<commit_after>package caveat

import (
	"crypto/rand"
	"crypto/sha256"
	"encoding/binary"
	"fmt"
	"time"

	vsecurity "veyron/security"

	"veyron2/security"
	"veyron2/security/wire"
	"veyron2/vom"
)

// nonceLength specifies the length in bytes of the random nonce used
// in publicKeyCaveat and publicKeyDischarge.
//
// TODO(ataly, ashankar): Nonce length of 16 bytes was chosen very conservatively.
// The purpose of the random nonce is essentially to separate caveats that have
// the same restriction and validation key (in order to prevent discharge replay).
// Can we use 4bytes instead?.
const nonceLength = 16

// publicKeyCaveat implements security.ThirdPartyCaveat. It specifies a restriction,
// a validation key and the location (object name) of the third-party responsible
// for discharging the caveat. A discharge for this caveat is a signed assertion whose
// signature can be verified using the validation key.
type publicKeyCaveat struct {
	// RandNonce specifies a cryptographically random nonce (of fixed length) that
	// uniquely identifies the caveat.
	RandNonce []uint8
	// DischargeMintingCaveat specifies the caveat that has to be validated
	// before minting a discharge for a publicKeyCaveat.
	DischargeMintingCaveat []byte
	// ValidationKey specifies the public key of the discharging-party.
	ValidationKey wire.PublicKey
	// ThirdPartyLocation specifies the object name of the discharging-party.
	ThirdPartyLocation string
	// ThirdPartyRequirements specify the information wanted in order to issue a discharge.
	ThirdPartyRequirements security.ThirdPartyRequirements
}

// ID returns a unique 32bytes long identity for the caveat based on the random nonce
// and SHA-256 hash of the restriction embedded in the caveat.
//
// TODO(ataly, ashankar): A 256bit hash is probably much stronger that what we need
// here. Can we truncate the hash to 96bits?
func (c *publicKeyCaveat) ID() string {
	return id(c.RandNonce, c.DischargeMintingCaveat)
}

func (c *publicKeyCaveat) Location() string {
	return c.ThirdPartyLocation
}

func (c *publicKeyCaveat) String() string {
	return fmt.Sprintf("publicKeyCaveat{DischargeMintingCaveat: (%v bytes), ThirdPartyLocation: %q}", len(c.DischargeMintingCaveat), c.ThirdPartyLocation)
}

func (c *publicKeyCaveat) Requirements() security.ThirdPartyRequirements {
	return c.ThirdPartyRequirements
}

func (c *publicKeyCaveat) Validate(ctx security.Context) error {
	// Check whether the context has a dicharge matching the caveat's ID.
	dis, ok := ctx.Discharges()[c.ID()]
	if !ok {
		return fmt.Errorf("missing discharge")
	}

	pkDischarge, ok := dis.(*publicKeyDischarge)
	if !ok {
		return fmt.Errorf("caveat of type %T cannot be validated with discharge of type %T", c, dis)
	}

	// Validate all caveats present on the discharge.
	validators, err := vsecurity.CaveatValidators(pkDischarge.Caveats...)
	if err != nil {
		return err
	}
	for _, v := range validators {
		if err := v.Validate(ctx); err != nil {
			return err
		}
	}

	// Check the discharge signature with the validation key from the caveat.
	key, err := c.ValidationKey.Decode()
	if err != nil {
		return err
	}
	if !pkDischarge.Signature.Verify(key, pkDischarge.contentHash()) {
		return fmt.Errorf("discharge %v has invalid signature", dis)
	}
	return nil
}

// publicKeyDischarge implements security.Discharge. It specifies a discharge
// for a publicKeyCaveat, and includes a signature that can be verified with
// the validation key of the publicKeyCaveat. Additionally, the discharge may
// also include caveats which must all validate in order for the discharge to
// be considered valid.
type publicKeyDischarge struct {
	// CaveatID is used to match this Discharge to the the ThirdPartyCaveat it is for.
	CaveatID string

	// Caveats under which this Discharge is valid.
	Caveats [][]byte

	// Signature on the contents of the discharge that can be verified using the
	// validaton key in the publicKeycaveat this discharge is for.
	Signature security.Signature
}

func (d *publicKeyDischarge) ID() string {
	return d.CaveatID
}

func (d *publicKeyDischarge) ThirdPartyCaveats() []security.ThirdPartyCaveat {
	return vsecurity.ThirdPartyCaveats(d.Caveats...)
}

func (d *publicKeyDischarge) sign(signer security.Signer) (err error) {
	d.Signature, err = signer.Sign(d.contentHash())
	return
}

func (d *publicKeyDischarge) contentHash() []byte {
	h := sha256.New()
	tmp := make([]byte, binary.MaxVarintLen64)

	wire.WriteString(h, tmp, d.CaveatID)
	for _, cav := range d.Caveats {
		wire.WriteBytes(h, tmp, cav)
	}
	return h.Sum(nil)
}

// NewPublicKeyCaveat returns a security.ThirdPartyCaveat which requires a
// discharge from a principal identified by the public key 'key' and present
// at the object name 'location'. This discharging principal is expected to
// validate 'caveat' before issuing a discharge.
func NewPublicKeyCaveat(caveat security.Caveat, key security.PublicKey, location string, requirements security.ThirdPartyRequirements) (security.ThirdPartyCaveat, error) {
	nonce := make([]uint8, nonceLength)
	if _, err := rand.Read(nonce); err != nil {
		return nil, err
	}

	var validationKey wire.PublicKey
	if err := validationKey.Encode(key); err != nil {
		return nil, err
	}

	return &publicKeyCaveat{
		RandNonce:              nonce,
		DischargeMintingCaveat: caveat.Bytes(),
		ValidationKey:          validationKey,
		ThirdPartyLocation:     location,
		ThirdPartyRequirements: requirements,
	}, nil
}

// NewPublicKeyDischarge returns a new discharge for the provided 'tp'
// after validating any restrictions specified in it under context 'ctx'.
//
// The ID of the discharge is the same as the ID of 'caveat', and the discharge
// will be usable for the provided 'duration' only if:
// (1) 'caveats' are met when using the discharge.
// (2) 'tp' was obtained from NewPublicKeyCaveat using a key that is the same as
// the provided signer's PublicKey.
func NewPublicKeyDischarge(signer security.Signer, tp security.ThirdPartyCaveat, ctx security.Context, duration time.Duration, caveats []security.Caveat) (security.Discharge, error) {
	pkCaveat, ok := tp.(*publicKeyCaveat)
	if !ok {
		return nil, fmt.Errorf("cannot mint discharges for %T", tp)
	}

	v, err := vsecurity.CaveatValidators(pkCaveat.DischargeMintingCaveat)
	if err != nil {
		return nil, err
	}
	if err := v[0].Validate(ctx); err != nil {
		return nil, err
	}

	now := time.Now()
	expiryCaveat, err := security.NewCaveat(&Expiry{IssueTime: now, ExpiryTime: now.Add(duration)})
	if err != nil {
		return nil, err
	}

	caveats = append(caveats, expiryCaveat)
	discharge := &publicKeyDischarge{
		CaveatID: tp.ID(),
		Caveats:  vsecurity.CaveatBytes(caveats...),
	}

	// TODO(ashankar,ataly): Should signer necessarily be the same as ctx.LocalID()?
	// If so, need the PrivateID object corresponding to ctx.LocalID.
	if err := discharge.sign(signer); err != nil {
		return nil, err
	}
	return discharge, nil
}

// id calculates the sha256 hash of length-delimited byte slices
func id(slices ...[]byte) string {
	h := sha256.New()
	tmp := make([]byte, binary.MaxVarintLen64)

	for _, slice := range slices {
		wire.WriteBytes(h, tmp, slice)
	}
	return string(h.Sum(nil))
}

func init() {
	vom.Register(publicKeyCaveat{})
	vom.Register(publicKeyDischarge{})
}
<|endoftext|>


<commit_before>package types

import (
	"github.com/dougfort/gocards"
)

// StackType reresesnts on stack of cards in the Tableau
type StackType struct {
	HiddenCount int
	Cards       gocards.Cards
}

// TableauWidth is the number of stacks in the Tableau
const TableauWidth = 10

// Tableau is the game layout
type Tableau []StackType
<commit_msg>define Game types<commit_after>package types

import (
	"github.com/dougfort/gocards"
)

// StackType represents one stack of cards in the Tableau
type StackType struct {
	HiddenCount int
	Cards       gocards.Cards
}

// TableauWidth is the number of stacks in the Tableau
const TableauWidth = 10

// Tableau is the outer (visible) game layout
type Tableau [TableauWidth]StackType

// HiddenCards represents the Cards that are not visible in the Tableau
type HiddenCards [TableauWidth]gocards.Card

// Game represents a complete, playable, game
type Game struct {
	Deck        gocards.PlayableDeck
	Tableau     Tableau
	HiddenCards HiddenCards
}
<|endoftext|>


<commit_before>// Copyright 2016 Google LLC.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package bundler supports bundling (batching) of items. Bundling amortizes an
// action with fixed costs over multiple items. For example, if an API provides
// an RPC that accepts a list of items as input, but clients would prefer
// adding items one at a time, then a Bundler can accept individual items from
// the client and bundle many of them into a single RPC.
//
// This package is experimental and subject to change without notice.
package bundler

import (
	"context"
	"errors"
	"reflect"
	"sync"
	"time"

	"golang.org/x/sync/semaphore"
)

const (
	DefaultDelayThreshold       = time.Second
	DefaultBundleCountThreshold = 10
	DefaultBundleByteThreshold  = 1e6 // 1M
	DefaultBufferedByteLimit    = 1e9 // 1G
)

var (
	// ErrOverflow indicates that Bundler's stored bytes exceeds its BufferedByteLimit.
	ErrOverflow = errors.New("bundler reached buffered byte limit")

	// ErrOversizedItem indicates that an item's size exceeds the maximum bundle size.
	ErrOversizedItem = errors.New("item size exceeds bundle byte limit")
)

// A Bundler collects items added to it into a bundle until the bundle
// exceeds a given size, then calls a user-provided function to handle the
// bundle.
//
// The exported fields are only safe to modify prior to the first call to Add
// or AddWait.
type Bundler struct {
	// Starting from the time that the first message is added to a bundle, once
	// this delay has passed, handle the bundle. The default is DefaultDelayThreshold.
	DelayThreshold time.Duration

	// Once a bundle has this many items, handle the bundle. Since only one
	// item at a time is added to a bundle, no bundle will exceed this
	// threshold, so it also serves as a limit. The default is
	// DefaultBundleCountThreshold.
	BundleCountThreshold int

	// Once the number of bytes in current bundle reaches this threshold, handle
	// the bundle. The default is DefaultBundleByteThreshold. This triggers handling,
	// but does not cap the total size of a bundle.
	BundleByteThreshold int

	// The maximum size of a bundle, in bytes. Zero means unlimited.
	BundleByteLimit int

	// The maximum number of bytes that the Bundler will keep in memory before
	// returning ErrOverflow. The default is DefaultBufferedByteLimit.
	BufferedByteLimit int

	// The maximum number of handler invocations that can be running at once.
	// The default is 1.
	HandlerLimit int

	handler       func(interface{}) // called to handle a bundle
	itemSliceZero reflect.Value     // nil (zero value) for slice of items

	mu           sync.Mutex          // guards access to fields below
	flushTimer   *time.Timer         // implements DelayThreshold
	handlerCount int                 // # of bundles currently being handled (i.e. handler is invoked on them)
	sem          *semaphore.Weighted // enforces BufferedByteLimit
	semOnce      sync.Once           // guards semaphore initialization

	// The current bundle we're adding items to. Not yet in the queue.
	// Appended to the queue once the flushTimer fires or the bundle
	// thresholds/limits are reached. If curBundle is nil and tail is
	// not, we first try to add items to tail. Once tail is full or handled,
	// we create a new curBundle for the incoming item.
	curBundle *bundle

	// The next bundle in the queue to be handled. Nil if the queue is
	// empty.
	head *bundle

	// The last bundle in the queue to be handled. Nil if the queue is
	// empty. If curBundle is nil and tail isn't, we attempt to add new
	// items to the tail until if becomes full or has been passed to the
	// handler.
	tail *bundle

	curFlush  *sync.WaitGroup // counts outstanding bundles since last flush
	prevFlush chan bool       // signal used to wait for prior flush

	// TODO: consider alternative queue implementation for head/tail bundle. see:
	// https://code-review.googlesource.com/c/google-api-go-client/+/47991/4/support/bundler/bundler.go#74
}

// A bundle is a group of items that were added individually and will be passed
// to a handler as a slice.
type bundle struct {
	items reflect.Value   // slice of T
	size  int             // size in bytes of all items
	next  *bundle         // bundles are handled in order as a linked list queue
	flush *sync.WaitGroup // the counter that tracks flush completion
}

// add appends item to this bundle and increments the total size. It requires
// that b.mu is locked.
func (bu *bundle) add(item interface{}, size int) {
	bu.items = reflect.Append(bu.items, reflect.ValueOf(item))
	bu.size += size
}

// NewBundler creates a new Bundler.
//
// itemExample is a value of the type that will be bundled. For example, if you
// want to create bundles of *Entry, you could pass &Entry{} for itemExample.
//
// handler is a function that will be called on each bundle. If itemExample is
// of type T, the argument to handler is of type []T. handler is always called
// sequentially for each bundle, and never in parallel.
//
// Configure the Bundler by setting its thresholds and limits before calling
// any of its methods.
func NewBundler(itemExample interface{}, handler func(interface{})) *Bundler {
	b := &Bundler{
		DelayThreshold:       DefaultDelayThreshold,
		BundleCountThreshold: DefaultBundleCountThreshold,
		BundleByteThreshold:  DefaultBundleByteThreshold,
		BufferedByteLimit:    DefaultBufferedByteLimit,
		HandlerLimit:         1,

		handler:       handler,
		itemSliceZero: reflect.Zero(reflect.SliceOf(reflect.TypeOf(itemExample))),
		curFlush:      &sync.WaitGroup{},
	}
	return b
}

func (b *Bundler) initSemaphores() {
	// Create the semaphores lazily, because the user may set limits
	// after NewBundler.
	b.semOnce.Do(func() {
		b.sem = semaphore.NewWeighted(int64(b.BufferedByteLimit))
	})
}

// enqueueCurBundle moves curBundle to the end of the queue. The bundle may be
// handled immediately if we are below handlerLimit. It requires that b.mu is
// locked.
func (b *Bundler) enqueueCurBundle() {
	// We don't require callers to check if there is a pending bundle. It
	// may have already been appended to the queue. If so, return early.
	if b.curBundle == nil {
		return
	}
	// If we are below the HandlerLimit, the queue must be empty. Handle
	// immediately with a new goroutine.
	if b.handlerCount < b.HandlerLimit {
		b.handlerCount++
		go b.handle(b.curBundle)
	} else if b.tail != nil {
		// There are bundles on the queue, so append to the end
		b.tail.next = b.curBundle
		b.tail = b.curBundle
	} else {
		// The queue is empty, so initialize the queue
		b.head = b.curBundle
		b.tail = b.curBundle
	}
	b.curBundle = nil
	if b.flushTimer != nil {
		b.flushTimer.Stop()
		b.flushTimer = nil
	}
}

// canFit returns true if bu can fit an additional item of size bytes based
// on the limits of Bundler b.
func (b *Bundler) canFit(bu *bundle, size int) bool {
	return (b.BundleByteLimit <= 0 || bu.size+size <= b.BundleByteLimit) &&
		(b.BundleCountThreshold <= 0 || bu.items.Len() < b.BundleCountThreshold)
}

// Add adds item to the current bundle. It marks the bundle for handling and
// starts a new one if any of the thresholds or limits are exceeded.
//
// If the item's size exceeds the maximum bundle size (Bundler.BundleByteLimit), then
// the item can never be handled. Add returns ErrOversizedItem in this case.
//
// If adding the item would exceed the maximum memory allowed
// (Bundler.BufferedByteLimit) or an AddWait call is blocked waiting for
// memory, Add returns ErrOverflow.
//
// Add never blocks.
func (b *Bundler) Add(item interface{}, size int) error {
	// If this item exceeds the maximum size of a bundle,
	// we can never send it.
	if b.BundleByteLimit > 0 && size > b.BundleByteLimit {
		return ErrOversizedItem
	}

	// If adding this item would exceed our allotted memory
	// footprint, we can't accept it.
	// (TryAcquire also returns false if anything is waiting on the semaphore,
	// so calls to Add and AddWait shouldn't be mixed.)
	b.initSemaphores()
	if !b.sem.TryAcquire(int64(size)) {
		return ErrOverflow
	}

	b.mu.Lock()
	defer b.mu.Unlock()
	return b.add(item, size)
}

// add adds item to the tail of the bundle queue or curBundle depending on space
// and nil-ness (see inline comments). It marks curBundle for handling (by
// appending it to the queue) if any of the thresholds or limits are exceeded.
// curBundle is lazily initialized. It requires that b.mu is locked.
func (b *Bundler) add(item interface{}, size int) error {
	// If we don't have a curBundle, see if we can add to the queue tail.
	if b.tail != nil && b.curBundle == nil && b.canFit(b.tail, size) {
		b.tail.add(item, size)
		return nil
	}

	// If we can't fit in the existing curBundle, move it onto the queue.
	if b.curBundle != nil && !b.canFit(b.curBundle, size) {
		b.enqueueCurBundle()
	}

	// Create a curBundle if we don't have one.
	if b.curBundle == nil {
		b.curFlush.Add(1)
		b.curBundle = &bundle{
			items: b.itemSliceZero,
			flush: b.curFlush,
		}
	}

	// Add the item.
	b.curBundle.add(item, size)

	// If curBundle is ready for handling, move it to the queue.
	if b.curBundle.size >= b.BundleByteThreshold ||
		b.curBundle.items.Len() == b.BundleCountThreshold {
		b.enqueueCurBundle()
	}

	// If we created a new bundle and it wasn't immediately handled, set a timer
	if b.curBundle != nil && b.flushTimer == nil {
		b.flushTimer = time.AfterFunc(b.DelayThreshold, b.tryHandleBundles)
	}

	return nil
}

// tryHandleBundles is the timer callback that handles or queues any current
// bundle after DelayThreshold time, even if the bundle isn't completely full.
func (b *Bundler) tryHandleBundles() {
	b.mu.Lock()
	b.enqueueCurBundle()
	b.mu.Unlock()
}

// next returns the next bundle that is ready for handling and removes it from
// the internal queue. It requires that b.mu is locked.
func (b *Bundler) next() *bundle {
	if b.head == nil {
		return nil
	}
	out := b.head
	b.head = b.head.next
	if b.head == nil {
		b.tail = nil
	}
	out.next = nil
	return out
}

// handle calls the user-specified handler on the given bundle. handle is
// intended to be run as a goroutine. After the handler returns, we update the
// byte total. handle continues processing additional bundles that are ready.
// If no more bundles are ready, the handler count is decremented and the
// goroutine ends.
func (b *Bundler) handle(bu *bundle) {
	for bu != nil {
		b.handler(bu.items.Interface())
		bu = b.postHandle(bu)
	}
	b.mu.Lock()
	b.handlerCount--
	b.mu.Unlock()
}

func (b *Bundler) postHandle(bu *bundle) *bundle {
	b.mu.Lock()
	defer b.mu.Unlock()
	b.sem.Release(int64(bu.size))
	bu.flush.Done()
	return b.next()
}

// AddWait adds item to the current bundle. It marks the bundle for handling and
// starts a new one if any of the thresholds or limits are exceeded.
//
// If the item's size exceeds the maximum bundle size (Bundler.BundleByteLimit), then
// the item can never be handled. AddWait returns ErrOversizedItem in this case.
//
// If adding the item would exceed the maximum memory allowed (Bundler.BufferedByteLimit),
// AddWait blocks until space is available or ctx is done.
//
// Calls to Add and AddWait should not be mixed on the same Bundler.
func (b *Bundler) AddWait(ctx context.Context, item interface{}, size int) error {
	// If this item exceeds the maximum size of a bundle,
	// we can never send it.
	if b.BundleByteLimit > 0 && size > b.BundleByteLimit {
		return ErrOversizedItem
	}
	// If adding this item would exceed our allotted memory footprint, block
	// until space is available. The semaphore is FIFO, so there will be no
	// starvation.
	b.initSemaphores()
	if err := b.sem.Acquire(ctx, int64(size)); err != nil {
		return err
	}

	b.mu.Lock()
	defer b.mu.Unlock()
	return b.add(item, size)
}

// Flush invokes the handler for all remaining items in the Bundler and waits
// for it to return.
func (b *Bundler) Flush() {
	b.mu.Lock()

	// If a curBundle is pending, move it to the queue.
	b.enqueueCurBundle()

	// Store a pointer to the WaitGroup that counts outstanding bundles
	// in the current flush and create a new one to track the next flush.
	wg := b.curFlush
	b.curFlush = &sync.WaitGroup{}

	// Flush must wait for all prior, outstanding flushes to complete.
	// We use a channel to communicate completion between each flush in
	// the sequence.
	prev := b.prevFlush
	next := make(chan bool)
	b.prevFlush = next

	b.mu.Unlock()

	// Wait until the previous flush is finished.
	if prev != nil {
		<-prev
	}

	// Wait until this flush is finished.
	wg.Wait()

	// Allow the next flush to finish.
	close(next)
}
<commit_msg>bundler: remove whitespace to clarify mutex guard<commit_after>// Copyright 2016 Google LLC.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package bundler supports bundling (batching) of items. Bundling amortizes an
// action with fixed costs over multiple items. For example, if an API provides
// an RPC that accepts a list of items as input, but clients would prefer
// adding items one at a time, then a Bundler can accept individual items from
// the client and bundle many of them into a single RPC.
//
// This package is experimental and subject to change without notice.
package bundler

import (
	"context"
	"errors"
	"reflect"
	"sync"
	"time"

	"golang.org/x/sync/semaphore"
)

const (
	DefaultDelayThreshold       = time.Second
	DefaultBundleCountThreshold = 10
	DefaultBundleByteThreshold  = 1e6 // 1M
	DefaultBufferedByteLimit    = 1e9 // 1G
)

var (
	// ErrOverflow indicates that Bundler's stored bytes exceeds its BufferedByteLimit.
	ErrOverflow = errors.New("bundler reached buffered byte limit")

	// ErrOversizedItem indicates that an item's size exceeds the maximum bundle size.
	ErrOversizedItem = errors.New("item size exceeds bundle byte limit")
)

// A Bundler collects items added to it into a bundle until the bundle
// exceeds a given size, then calls a user-provided function to handle the
// bundle.
//
// The exported fields are only safe to modify prior to the first call to Add
// or AddWait.
type Bundler struct {
	// Starting from the time that the first message is added to a bundle, once
	// this delay has passed, handle the bundle. The default is DefaultDelayThreshold.
	DelayThreshold time.Duration

	// Once a bundle has this many items, handle the bundle. Since only one
	// item at a time is added to a bundle, no bundle will exceed this
	// threshold, so it also serves as a limit. The default is
	// DefaultBundleCountThreshold.
	BundleCountThreshold int

	// Once the number of bytes in current bundle reaches this threshold, handle
	// the bundle. The default is DefaultBundleByteThreshold. This triggers handling,
	// but does not cap the total size of a bundle.
	BundleByteThreshold int

	// The maximum size of a bundle, in bytes. Zero means unlimited.
	BundleByteLimit int

	// The maximum number of bytes that the Bundler will keep in memory before
	// returning ErrOverflow. The default is DefaultBufferedByteLimit.
	BufferedByteLimit int

	// The maximum number of handler invocations that can be running at once.
	// The default is 1.
	HandlerLimit int

	handler       func(interface{}) // called to handle a bundle
	itemSliceZero reflect.Value     // nil (zero value) for slice of items

	mu           sync.Mutex          // guards access to fields below
	flushTimer   *time.Timer         // implements DelayThreshold
	handlerCount int                 // # of bundles currently being handled (i.e. handler is invoked on them)
	sem          *semaphore.Weighted // enforces BufferedByteLimit
	semOnce      sync.Once           // guards semaphore initialization
	// The current bundle we're adding items to. Not yet in the queue.
	// Appended to the queue once the flushTimer fires or the bundle
	// thresholds/limits are reached. If curBundle is nil and tail is
	// not, we first try to add items to tail. Once tail is full or handled,
	// we create a new curBundle for the incoming item.
	curBundle *bundle
	// The next bundle in the queue to be handled. Nil if the queue is
	// empty.
	head *bundle
	// The last bundle in the queue to be handled. Nil if the queue is
	// empty. If curBundle is nil and tail isn't, we attempt to add new
	// items to the tail until if becomes full or has been passed to the
	// handler.
	tail      *bundle
	curFlush  *sync.WaitGroup // counts outstanding bundles since last flush
	prevFlush chan bool       // signal used to wait for prior flush

	// TODO: consider alternative queue implementation for head/tail bundle. see:
	// https://code-review.googlesource.com/c/google-api-go-client/+/47991/4/support/bundler/bundler.go#74
}

// A bundle is a group of items that were added individually and will be passed
// to a handler as a slice.
type bundle struct {
	items reflect.Value   // slice of T
	size  int             // size in bytes of all items
	next  *bundle         // bundles are handled in order as a linked list queue
	flush *sync.WaitGroup // the counter that tracks flush completion
}

// add appends item to this bundle and increments the total size. It requires
// that b.mu is locked.
func (bu *bundle) add(item interface{}, size int) {
	bu.items = reflect.Append(bu.items, reflect.ValueOf(item))
	bu.size += size
}

// NewBundler creates a new Bundler.
//
// itemExample is a value of the type that will be bundled. For example, if you
// want to create bundles of *Entry, you could pass &Entry{} for itemExample.
//
// handler is a function that will be called on each bundle. If itemExample is
// of type T, the argument to handler is of type []T. handler is always called
// sequentially for each bundle, and never in parallel.
//
// Configure the Bundler by setting its thresholds and limits before calling
// any of its methods.
func NewBundler(itemExample interface{}, handler func(interface{})) *Bundler {
	b := &Bundler{
		DelayThreshold:       DefaultDelayThreshold,
		BundleCountThreshold: DefaultBundleCountThreshold,
		BundleByteThreshold:  DefaultBundleByteThreshold,
		BufferedByteLimit:    DefaultBufferedByteLimit,
		HandlerLimit:         1,

		handler:       handler,
		itemSliceZero: reflect.Zero(reflect.SliceOf(reflect.TypeOf(itemExample))),
		curFlush:      &sync.WaitGroup{},
	}
	return b
}

func (b *Bundler) initSemaphores() {
	// Create the semaphores lazily, because the user may set limits
	// after NewBundler.
	b.semOnce.Do(func() {
		b.sem = semaphore.NewWeighted(int64(b.BufferedByteLimit))
	})
}

// enqueueCurBundle moves curBundle to the end of the queue. The bundle may be
// handled immediately if we are below handlerLimit. It requires that b.mu is
// locked.
func (b *Bundler) enqueueCurBundle() {
	// We don't require callers to check if there is a pending bundle. It
	// may have already been appended to the queue. If so, return early.
	if b.curBundle == nil {
		return
	}
	// If we are below the HandlerLimit, the queue must be empty. Handle
	// immediately with a new goroutine.
	if b.handlerCount < b.HandlerLimit {
		b.handlerCount++
		go b.handle(b.curBundle)
	} else if b.tail != nil {
		// There are bundles on the queue, so append to the end
		b.tail.next = b.curBundle
		b.tail = b.curBundle
	} else {
		// The queue is empty, so initialize the queue
		b.head = b.curBundle
		b.tail = b.curBundle
	}
	b.curBundle = nil
	if b.flushTimer != nil {
		b.flushTimer.Stop()
		b.flushTimer = nil
	}
}

// canFit returns true if bu can fit an additional item of size bytes based
// on the limits of Bundler b.
func (b *Bundler) canFit(bu *bundle, size int) bool {
	return (b.BundleByteLimit <= 0 || bu.size+size <= b.BundleByteLimit) &&
		(b.BundleCountThreshold <= 0 || bu.items.Len() < b.BundleCountThreshold)
}

// Add adds item to the current bundle. It marks the bundle for handling and
// starts a new one if any of the thresholds or limits are exceeded.
//
// If the item's size exceeds the maximum bundle size (Bundler.BundleByteLimit), then
// the item can never be handled. Add returns ErrOversizedItem in this case.
//
// If adding the item would exceed the maximum memory allowed
// (Bundler.BufferedByteLimit) or an AddWait call is blocked waiting for
// memory, Add returns ErrOverflow.
//
// Add never blocks.
func (b *Bundler) Add(item interface{}, size int) error {
	// If this item exceeds the maximum size of a bundle,
	// we can never send it.
	if b.BundleByteLimit > 0 && size > b.BundleByteLimit {
		return ErrOversizedItem
	}

	// If adding this item would exceed our allotted memory
	// footprint, we can't accept it.
	// (TryAcquire also returns false if anything is waiting on the semaphore,
	// so calls to Add and AddWait shouldn't be mixed.)
	b.initSemaphores()
	if !b.sem.TryAcquire(int64(size)) {
		return ErrOverflow
	}

	b.mu.Lock()
	defer b.mu.Unlock()
	return b.add(item, size)
}

// add adds item to the tail of the bundle queue or curBundle depending on space
// and nil-ness (see inline comments). It marks curBundle for handling (by
// appending it to the queue) if any of the thresholds or limits are exceeded.
// curBundle is lazily initialized. It requires that b.mu is locked.
func (b *Bundler) add(item interface{}, size int) error {
	// If we don't have a curBundle, see if we can add to the queue tail.
	if b.tail != nil && b.curBundle == nil && b.canFit(b.tail, size) {
		b.tail.add(item, size)
		return nil
	}

	// If we can't fit in the existing curBundle, move it onto the queue.
	if b.curBundle != nil && !b.canFit(b.curBundle, size) {
		b.enqueueCurBundle()
	}

	// Create a curBundle if we don't have one.
	if b.curBundle == nil {
		b.curFlush.Add(1)
		b.curBundle = &bundle{
			items: b.itemSliceZero,
			flush: b.curFlush,
		}
	}

	// Add the item.
	b.curBundle.add(item, size)

	// If curBundle is ready for handling, move it to the queue.
	if b.curBundle.size >= b.BundleByteThreshold ||
		b.curBundle.items.Len() == b.BundleCountThreshold {
		b.enqueueCurBundle()
	}

	// If we created a new bundle and it wasn't immediately handled, set a timer
	if b.curBundle != nil && b.flushTimer == nil {
		b.flushTimer = time.AfterFunc(b.DelayThreshold, b.tryHandleBundles)
	}

	return nil
}

// tryHandleBundles is the timer callback that handles or queues any current
// bundle after DelayThreshold time, even if the bundle isn't completely full.
func (b *Bundler) tryHandleBundles() {
	b.mu.Lock()
	b.enqueueCurBundle()
	b.mu.Unlock()
}

// next returns the next bundle that is ready for handling and removes it from
// the internal queue. It requires that b.mu is locked.
func (b *Bundler) next() *bundle {
	if b.head == nil {
		return nil
	}
	out := b.head
	b.head = b.head.next
	if b.head == nil {
		b.tail = nil
	}
	out.next = nil
	return out
}

// handle calls the user-specified handler on the given bundle. handle is
// intended to be run as a goroutine. After the handler returns, we update the
// byte total. handle continues processing additional bundles that are ready.
// If no more bundles are ready, the handler count is decremented and the
// goroutine ends.
func (b *Bundler) handle(bu *bundle) {
	for bu != nil {
		b.handler(bu.items.Interface())
		bu = b.postHandle(bu)
	}
	b.mu.Lock()
	b.handlerCount--
	b.mu.Unlock()
}

func (b *Bundler) postHandle(bu *bundle) *bundle {
	b.mu.Lock()
	defer b.mu.Unlock()
	b.sem.Release(int64(bu.size))
	bu.flush.Done()
	return b.next()
}

// AddWait adds item to the current bundle. It marks the bundle for handling and
// starts a new one if any of the thresholds or limits are exceeded.
//
// If the item's size exceeds the maximum bundle size (Bundler.BundleByteLimit), then
// the item can never be handled. AddWait returns ErrOversizedItem in this case.
//
// If adding the item would exceed the maximum memory allowed (Bundler.BufferedByteLimit),
// AddWait blocks until space is available or ctx is done.
//
// Calls to Add and AddWait should not be mixed on the same Bundler.
func (b *Bundler) AddWait(ctx context.Context, item interface{}, size int) error {
	// If this item exceeds the maximum size of a bundle,
	// we can never send it.
	if b.BundleByteLimit > 0 && size > b.BundleByteLimit {
		return ErrOversizedItem
	}
	// If adding this item would exceed our allotted memory footprint, block
	// until space is available. The semaphore is FIFO, so there will be no
	// starvation.
	b.initSemaphores()
	if err := b.sem.Acquire(ctx, int64(size)); err != nil {
		return err
	}

	b.mu.Lock()
	defer b.mu.Unlock()
	return b.add(item, size)
}

// Flush invokes the handler for all remaining items in the Bundler and waits
// for it to return.
func (b *Bundler) Flush() {
	b.mu.Lock()

	// If a curBundle is pending, move it to the queue.
	b.enqueueCurBundle()

	// Store a pointer to the WaitGroup that counts outstanding bundles
	// in the current flush and create a new one to track the next flush.
	wg := b.curFlush
	b.curFlush = &sync.WaitGroup{}

	// Flush must wait for all prior, outstanding flushes to complete.
	// We use a channel to communicate completion between each flush in
	// the sequence.
	prev := b.prevFlush
	next := make(chan bool)
	b.prevFlush = next

	b.mu.Unlock()

	// Wait until the previous flush is finished.
	if prev != nil {
		<-prev
	}

	// Wait until this flush is finished.
	wg.Wait()

	// Allow the next flush to finish.
	close(next)
}
<|endoftext|>


<commit_before>package git

/*
#include <git2.h>
*/
import "C"
import "runtime"

type BlameOptions struct {
	Flags              BlameOptionsFlag
	MinMatchCharacters uint16
	NewestCommit       *Oid
	OldestCommit       *Oid
	MinLine            uint32
	MaxLine            uint32
}

func DefaultBlameOptions() (BlameOptions, error) {
	opts := C.git_blame_options{}
	ecode := C.git_blame_init_options(&opts, C.GIT_BLAME_OPTIONS_VERSION)
	if ecode < 0 {
		return BlameOptions{}, MakeGitError(ecode)
	}

	return BlameOptions{
		Flags:              BlameOptionsFlag(opts.flags),
		MinMatchCharacters: uint16(opts.min_match_characters),
		NewestCommit:       newOidFromC(&opts.newest_commit),
		OldestCommit:       newOidFromC(&opts.oldest_commit),
		MinLine:            uint32(opts.min_line),
		MaxLine:            uint32(opts.max_line),
	}, nil
}

type BlameOptionsFlag uint32

const (
	BlameNormal                      BlameOptionsFlag = C.GIT_BLAME_NORMAL
	BlameTrackCopiesSameFile         BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_FILE
	BlameTrackCopiesSameCommitMoves  BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES
	BlameTrackCopiesSameCommitCopies BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES
	BlameTrackCopiesAnyCommitCopies  BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES
	BlameFirstParent                 BlameOptionsFlag = C.GIT_BLAME_FIRST_PARENT
)

func (v *Repository) BlameFile(path string, opts *BlameOptions) (*Blame, error) {
	var blamePtr *C.git_blame

	var copts *C.git_blame_options
	if opts != nil {
		copts = &C.git_blame_options{
			version:              C.GIT_BLAME_OPTIONS_VERSION,
			flags:                C.uint32_t(opts.Flags),
			min_match_characters: C.uint16_t(opts.MinMatchCharacters),
			min_line:             C.uint32_t(opts.MinLine),
			max_line:             C.uint32_t(opts.MaxLine),
		}
		if opts.NewestCommit != nil {
			copts.newest_commit = *opts.NewestCommit.toC()
		}
		if opts.OldestCommit != nil {
			copts.oldest_commit = *opts.OldestCommit.toC()
		}
	}

	ecode := C.git_blame_file(&blamePtr, v.ptr, C.CString(path), copts)
	if ecode < 0 {
		return nil, MakeGitError(ecode)
	}

	return newBlameFromC(blamePtr), nil
}

type Blame struct {
	ptr *C.git_blame
}

func (blame *Blame) HunkCount() int {
	return int(C.git_blame_get_hunk_count(blame.ptr))
}

func (blame *Blame) HunkByIndex(index int) (BlameHunk, error) {
	ptr := C.git_blame_get_hunk_byindex(blame.ptr, C.uint32_t(index))
	if ptr == nil {
		return BlameHunk{}, ErrInvalid
	}
	return blameHunkFromC(ptr), nil
}

func newBlameFromC(ptr *C.git_blame) *Blame {
	if ptr == nil {
		return nil
	}

	blame := &Blame{
		ptr: ptr,
	}

	runtime.SetFinalizer(blame, (*Blame).Free)
	return blame
}

func (blame *Blame) Free() error {
	if blame.ptr == nil {
		return ErrInvalid
	}
	runtime.SetFinalizer(blame, nil)
	C.git_blame_free(blame.ptr)
	blame.ptr = nil
	return nil
}

type BlameHunk struct {
	LinesInHunk          uint16
	FinalCommitId        *Oid
	FinalStartLineNumber uint16
	FinalSignature       *Signature
	OrigCommitId         *Oid
	OrigPath             string
	OrigStartLineNumber  uint16
	OrigSignature        *Signature
	Boundary             bool
}

func blameHunkFromC(hunk *C.git_blame_hunk) BlameHunk {
	return BlameHunk{
		LinesInHunk:          uint16(hunk.lines_in_hunk),
		FinalCommitId:        newOidFromC(&hunk.final_commit_id),
		FinalStartLineNumber: uint16(hunk.final_start_line_number),
		FinalSignature:       newSignatureFromC(hunk.final_signature),
		OrigCommitId:         newOidFromC(&hunk.orig_commit_id),
		OrigPath:             C.GoString(hunk.orig_path),
		OrigStartLineNumber:  uint16(hunk.orig_start_line_number),
		OrigSignature:        newSignatureFromC(hunk.orig_signature),
		Boundary:             hunk.boundary == 1,
	}
}
<commit_msg>lock OS thread when MakeGitError might be called<commit_after>package git

/*
#include <git2.h>
*/
import "C"
import "runtime"

type BlameOptions struct {
	Flags              BlameOptionsFlag
	MinMatchCharacters uint16
	NewestCommit       *Oid
	OldestCommit       *Oid
	MinLine            uint32
	MaxLine            uint32
}

func DefaultBlameOptions() (BlameOptions, error) {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	opts := C.git_blame_options{}
	ecode := C.git_blame_init_options(&opts, C.GIT_BLAME_OPTIONS_VERSION)
	if ecode < 0 {
		return BlameOptions{}, MakeGitError(ecode)
	}

	return BlameOptions{
		Flags:              BlameOptionsFlag(opts.flags),
		MinMatchCharacters: uint16(opts.min_match_characters),
		NewestCommit:       newOidFromC(&opts.newest_commit),
		OldestCommit:       newOidFromC(&opts.oldest_commit),
		MinLine:            uint32(opts.min_line),
		MaxLine:            uint32(opts.max_line),
	}, nil
}

type BlameOptionsFlag uint32

const (
	BlameNormal                      BlameOptionsFlag = C.GIT_BLAME_NORMAL
	BlameTrackCopiesSameFile         BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_FILE
	BlameTrackCopiesSameCommitMoves  BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES
	BlameTrackCopiesSameCommitCopies BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES
	BlameTrackCopiesAnyCommitCopies  BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES
	BlameFirstParent                 BlameOptionsFlag = C.GIT_BLAME_FIRST_PARENT
)

func (v *Repository) BlameFile(path string, opts *BlameOptions) (*Blame, error) {
	var blamePtr *C.git_blame

	var copts *C.git_blame_options
	if opts != nil {
		copts = &C.git_blame_options{
			version:              C.GIT_BLAME_OPTIONS_VERSION,
			flags:                C.uint32_t(opts.Flags),
			min_match_characters: C.uint16_t(opts.MinMatchCharacters),
			min_line:             C.uint32_t(opts.MinLine),
			max_line:             C.uint32_t(opts.MaxLine),
		}
		if opts.NewestCommit != nil {
			copts.newest_commit = *opts.NewestCommit.toC()
		}
		if opts.OldestCommit != nil {
			copts.oldest_commit = *opts.OldestCommit.toC()
		}
	}

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ecode := C.git_blame_file(&blamePtr, v.ptr, C.CString(path), copts)
	if ecode < 0 {
		return nil, MakeGitError(ecode)
	}

	return newBlameFromC(blamePtr), nil
}

type Blame struct {
	ptr *C.git_blame
}

func (blame *Blame) HunkCount() int {
	return int(C.git_blame_get_hunk_count(blame.ptr))
}

func (blame *Blame) HunkByIndex(index int) (BlameHunk, error) {
	ptr := C.git_blame_get_hunk_byindex(blame.ptr, C.uint32_t(index))
	if ptr == nil {
		return BlameHunk{}, ErrInvalid
	}
	return blameHunkFromC(ptr), nil
}

func newBlameFromC(ptr *C.git_blame) *Blame {
	if ptr == nil {
		return nil
	}

	blame := &Blame{
		ptr: ptr,
	}

	runtime.SetFinalizer(blame, (*Blame).Free)
	return blame
}

func (blame *Blame) Free() error {
	if blame.ptr == nil {
		return ErrInvalid
	}
	runtime.SetFinalizer(blame, nil)
	C.git_blame_free(blame.ptr)
	blame.ptr = nil
	return nil
}

type BlameHunk struct {
	LinesInHunk          uint16
	FinalCommitId        *Oid
	FinalStartLineNumber uint16
	FinalSignature       *Signature
	OrigCommitId         *Oid
	OrigPath             string
	OrigStartLineNumber  uint16
	OrigSignature        *Signature
	Boundary             bool
}

func blameHunkFromC(hunk *C.git_blame_hunk) BlameHunk {
	return BlameHunk{
		LinesInHunk:          uint16(hunk.lines_in_hunk),
		FinalCommitId:        newOidFromC(&hunk.final_commit_id),
		FinalStartLineNumber: uint16(hunk.final_start_line_number),
		FinalSignature:       newSignatureFromC(hunk.final_signature),
		OrigCommitId:         newOidFromC(&hunk.orig_commit_id),
		OrigPath:             C.GoString(hunk.orig_path),
		OrigStartLineNumber:  uint16(hunk.orig_start_line_number),
		OrigSignature:        newSignatureFromC(hunk.orig_signature),
		Boundary:             hunk.boundary == 1,
	}
}
<|endoftext|>