<commit_before>package commands

import (
	"bufio"
	"fmt"
	"github.com/jingweno/gh/cmd"
	"github.com/jingweno/gh/git"
	"github.com/jingweno/gh/github"
	"github.com/jingweno/gh/utils"
	"io"
	"io/ioutil"
	"os"
	"reflect"
	"regexp"
	"strings"
)

var cmdPullRequest = &Command{
	Run:   pullRequest,
	Usage: "pull-request [-f] [-m <MESSAGE>|-F <FILE>|-i <ISSUE>|<ISSUE-URL>] [-b <BASE>] [-h <HEAD>] ",
	Short: "Open a pull request on GitHub",
	Long: `Opens a pull request on GitHub for the project that the "origin" remote
points to. The default head of the pull request is the current branch.
Both base and head of the pull request can be explicitly given in one of
the following formats: "branch", "owner:branch", "owner/repo:branch".
This command will abort operation if it detects that the current topic
branch has local commits that are not yet pushed to its upstream branch
on the remote. To skip this check, use "-f".

Without <MESSAGE> or <FILE>, a text editor will open in which title and body
of the pull request can be entered in the same manner as git commit message.
Pull request message can also be passed via stdin with "-F -".

If instead of normal <TITLE> an issue number is given with "-i", the pull
request will be attached to an existing GitHub issue. Alternatively, instead
of title you can paste a full URL to an issue on GitHub.
`,
}

var (
	flagPullRequestBase,
	flagPullRequestHead,
	flagPullRequestIssue,
	flagPullRequestMessage,
	flagPullRequestFile string
	flagPullRequestForce bool
)

func init() {
	cmdPullRequest.Flag.StringVar(&flagPullRequestBase, "b", "", "BASE")
	cmdPullRequest.Flag.StringVar(&flagPullRequestHead, "h", "", "HEAD")
	cmdPullRequest.Flag.StringVar(&flagPullRequestIssue, "i", "", "ISSUE")
	cmdPullRequest.Flag.StringVar(&flagPullRequestMessage, "m", "", "MESSAGE")
	cmdPullRequest.Flag.BoolVar(&flagPullRequestForce, "f", false, "FORCE")
	cmdPullRequest.Flag.StringVar(&flagPullRequestFile, "F", "", "FILE")
	cmdPullRequest.Flag.StringVar(&flagPullRequestFile, "file", "", "FILE")
}

/*
  # while on a topic branch called "feature":
  $ gh pull-request
  [ opens text editor to edit title & body for the request ]
  [ opened pull request on GitHub for "YOUR_USER:feature" ]

  # explicit pull base & head:
  $ gh pull-request -b jingweno:master -h jingweno:feature

  $ gh pull-request -m "title\n\nbody"
  [ create pull request with title & body  ]

  $ gh pull-request -i 123
  [ attached pull request to issue #123 ]

  $ gh pull-request https://github.com/jingweno/gh/pull/123
  [ attached pull request to issue #123 ]

  $ gh pull-request -F FILE
  [ create pull request with title & body from FILE ]
*/
func pullRequest(cmd *Command, args *Args) {
	localRepo := github.LocalRepo()

	currentBranch, err := localRepo.CurrentBranch()
	utils.Check(err)

	baseProject, err := localRepo.MainProject()
	utils.Check(err)

	headProject, err := localRepo.CurrentProject()
	utils.Check(err)

	var (
		base, head           string
		force, explicitOwner bool
	)

	force = flagPullRequestForce

	if flagPullRequestBase != "" {
		baseProject, base = parsePullRequestProject(baseProject, flagPullRequestBase)
	}

	if flagPullRequestHead != "" {
		headProject, head = parsePullRequestProject(headProject, flagPullRequestHead)
		explicitOwner = strings.Contains(flagPullRequestHead, ":")
	}

	if args.ParamsSize() == 1 {
		arg := args.RemoveParam(0)
		flagPullRequestIssue = parsePullRequestIssueNumber(arg)
	}

	if base == "" {
		masterBranch, err := localRepo.MasterBranch()
		utils.Check(err)
		base = masterBranch.ShortName()
	}

	trackedBranch, _ := currentBranch.Upstream()
	if head == "" {
		if trackedBranch != nil && trackedBranch.IsRemote() {
			if reflect.DeepEqual(baseProject, headProject) && base == trackedBranch.ShortName() {
				e := fmt.Errorf(`Aborted: head branch is the same as base ("%s")`, base)
				e = fmt.Errorf("%s\n(use `-h <branch>` to specify an explicit pull request head)", e)
				utils.Check(e)
			}
		} else {
			// the current branch tracking another branch
			// pretend there's no upstream at all
			trackedBranch = nil
		}

		if trackedBranch == nil {
			head = currentBranch.ShortName()
		} else {
			head = trackedBranch.ShortName()
		}
	}

	client := github.NewClient(baseProject.Host)

	// when no tracking, assume remote branch is published under active user's fork
	if trackedBranch == nil && !explicitOwner && client.Credentials.User != headProject.Owner {
		// disable this on gh
		//headProject = github.NewProject("", headProject.Name, headProject.Host)
	}

	var title, body string

	if flagPullRequestMessage != "" {
		title, body = readMsg(flagPullRequestMessage)
	}

	if flagPullRequestFile != "" {
		var (
			content []byte
			err     error
		)
		if flagPullRequestFile == "-" {
			content, err = ioutil.ReadAll(os.Stdin)
		} else {
			content, err = ioutil.ReadFile(flagPullRequestFile)
		}
		utils.Check(err)
		title, body = readMsg(string(content))
	}

	fullBase := fmt.Sprintf("%s:%s", baseProject.Owner, base)
	fullHead := fmt.Sprintf("%s:%s", headProject.Owner, head)

	commits, _ := git.RefList(base, head)
	if !force && trackedBranch != nil && len(commits) > 0 {
		err = fmt.Errorf("Aborted: %d commits are not yet pushed to %s", len(commits), trackedBranch.LongName())
		err = fmt.Errorf("%s\n(use `-f` to force submit a pull request anyway)", err)
		utils.Check(err)
	}

	if title == "" && flagPullRequestIssue == "" {
		t, b, err := writePullRequestTitleAndBody(base, head, fullBase, fullHead, commits)
		utils.Check(err)
		title = t
		body = b
	}

	if title == "" && flagPullRequestIssue == "" {
		utils.Check(fmt.Errorf("Aborting due to empty pull request title"))
	}

	var pullRequestURL string
	if args.Noop {
		args.Before(fmt.Sprintf("Would request a pull request to %s from %s", fullBase, fullHead), "")
		pullRequestURL = "PULL_REQUEST_URL"
	} else {
		if title != "" {
			pr, err := client.CreatePullRequest(baseProject, base, fullHead, title, body)
			utils.Check(err)
			pullRequestURL = pr.HTMLURL
		}

		if flagPullRequestIssue != "" {
			pr, err := client.CreatePullRequestForIssue(baseProject, base, fullHead, flagPullRequestIssue)
			utils.Check(err)
			pullRequestURL = pr.HTMLURL
		}
	}

	args.Replace("echo", "", pullRequestURL)
}

func writePullRequestTitleAndBody(base, head, fullBase, fullHead string, commits []string) (title, body string, err error) {
	messageFile, err := git.PullReqMsgFile()
	if err != nil {
		return
	}
	defer os.Remove(messageFile)

	err = writePullRequestChanges(base, head, fullBase, fullHead, commits, messageFile)
	if err != nil {
		return
	}

	editor, err := git.Editor()
	if err != nil {
		return
	}

	err = editTitleAndBody(editor, messageFile)
	if err != nil {
		err = fmt.Errorf("error using text editor for pull request message")
		return
	}

	title, body, err = readTitleAndBody(messageFile)
	if err != nil {
		return
	}

	return
}

func writePullRequestChanges(base, head, fullBase, fullHead string, commits []string, messageFile string) error {
	var defaultMsg, commitSummary string
	if len(commits) == 1 {
		msg, err := git.Show(commits[0])
		if err != nil {
			return err
		}
		defaultMsg = fmt.Sprintf("%s\n", msg)
	} else if len(commits) > 1 {
		commitLogs, err := git.Log(base, head)
		if err != nil {
			return err
		}

		if len(commitLogs) > 0 {
			startRegexp := regexp.MustCompilePOSIX("^")
			endRegexp := regexp.MustCompilePOSIX(" +$")

			commitLogs = strings.TrimSpace(commitLogs)
			commitLogs = startRegexp.ReplaceAllString(commitLogs, "# ")
			commitLogs = endRegexp.ReplaceAllString(commitLogs, "")
			commitSummary = `
#
# Changes:
#
%s`
			commitSummary = fmt.Sprintf(commitSummary, commitLogs)
		}
	}

	message := `%s
# Requesting a pull to %s from %s
#
# Write a message for this pull request. The first block
# of the text is the title and the rest is description.%s
`
	message = fmt.Sprintf(message, defaultMsg, fullBase, fullHead, commitSummary)

	return ioutil.WriteFile(messageFile, []byte(message), 0644)
}

func editTitleAndBody(editor, messageFile string) error {
	editCmd := cmd.New(editor)
	r := regexp.MustCompile("[mg]?vi[m]$")
	if r.MatchString(editor) {
		editCmd.WithArg("-c")
		editCmd.WithArg("set ft=gitcommit tw=0 wrap lbr")
	}
	editCmd.WithArg(messageFile)

	return editCmd.Exec()
}

func readTitleAndBody(messageFile string) (title, body string, err error) {
	f, err := os.Open(messageFile)
	defer f.Close()
	if err != nil {
		return "", "", err
	}

	reader := bufio.NewReader(f)

	return readTitleAndBodyFrom(reader)
}

func readTitleAndBodyFrom(reader *bufio.Reader) (title, body string, err error) {
	r := regexp.MustCompile("\\S")
	var titleParts, bodyParts []string

	line, err := readLine(reader)
	for err == nil {
		if strings.HasPrefix(line, "#") {
			break
		}

		if len(bodyParts) == 0 && r.MatchString(line) {
			titleParts = append(titleParts, line)
		} else {
			bodyParts = append(bodyParts, line)
		}

		line, err = readLine(reader)
	}

	if err == io.EOF {
		err = nil
	}

	title = strings.Join(titleParts, " ")
	title = strings.TrimSpace(title)

	body = strings.Join(bodyParts, "\n")
	body = strings.TrimSpace(body)

	return
}

func readLine(r *bufio.Reader) (string, error) {
	var (
		isPrefix = true
		err      error
		line, ln []byte
	)

	for isPrefix && err == nil {
		line, isPrefix, err = r.ReadLine()
		ln = append(ln, line...)
	}

	return string(ln), err
}

func readMsg(msg string) (title, body string) {
	split := strings.SplitN(msg, "\n\n", 2)
	title = strings.TrimSpace(split[0])
	if len(split) > 1 {
		body = strings.TrimSpace(split[1])
	}

	return
}

func parsePullRequestProject(context *github.Project, s string) (p *github.Project, ref string) {
	p = context
	ref = s

	if strings.Contains(s, ":") {
		split := strings.SplitN(s, ":", 2)
		ref = split[1]
		var name string
		if !strings.Contains(split[0], "/") {
			name = context.Name
		}
		p = github.NewProject(split[0], name, context.Host)
	}

	return
}

func parsePullRequestIssueNumber(url string) string {
	u, e := github.ParseURL(url)
	if e != nil {
		return ""
	}

	r := regexp.MustCompile(`^issues\/(\d+)`)
	p := u.ProjectPath()
	if r.MatchString(p) {
		return r.FindStringSubmatch(p)[1]
	}

	return ""
}
<commit_msg>Fix bug that warning unpunished commits<commit_after>package commands

import (
	"bufio"
	"fmt"
	"github.com/jingweno/gh/cmd"
	"github.com/jingweno/gh/git"
	"github.com/jingweno/gh/github"
	"github.com/jingweno/gh/utils"
	"io"
	"io/ioutil"
	"os"
	"reflect"
	"regexp"
	"strings"
)

var cmdPullRequest = &Command{
	Run:   pullRequest,
	Usage: "pull-request [-f] [-m <MESSAGE>|-F <FILE>|-i <ISSUE>|<ISSUE-URL>] [-b <BASE>] [-h <HEAD>] ",
	Short: "Open a pull request on GitHub",
	Long: `Opens a pull request on GitHub for the project that the "origin" remote
points to. The default head of the pull request is the current branch.
Both base and head of the pull request can be explicitly given in one of
the following formats: "branch", "owner:branch", "owner/repo:branch".
This command will abort operation if it detects that the current topic
branch has local commits that are not yet pushed to its upstream branch
on the remote. To skip this check, use "-f".

Without <MESSAGE> or <FILE>, a text editor will open in which title and body
of the pull request can be entered in the same manner as git commit message.
Pull request message can also be passed via stdin with "-F -".

If instead of normal <TITLE> an issue number is given with "-i", the pull
request will be attached to an existing GitHub issue. Alternatively, instead
of title you can paste a full URL to an issue on GitHub.
`,
}

var (
	flagPullRequestBase,
	flagPullRequestHead,
	flagPullRequestIssue,
	flagPullRequestMessage,
	flagPullRequestFile string
	flagPullRequestForce bool
)

func init() {
	cmdPullRequest.Flag.StringVar(&flagPullRequestBase, "b", "", "BASE")
	cmdPullRequest.Flag.StringVar(&flagPullRequestHead, "h", "", "HEAD")
	cmdPullRequest.Flag.StringVar(&flagPullRequestIssue, "i", "", "ISSUE")
	cmdPullRequest.Flag.StringVar(&flagPullRequestMessage, "m", "", "MESSAGE")
	cmdPullRequest.Flag.BoolVar(&flagPullRequestForce, "f", false, "FORCE")
	cmdPullRequest.Flag.StringVar(&flagPullRequestFile, "F", "", "FILE")
	cmdPullRequest.Flag.StringVar(&flagPullRequestFile, "file", "", "FILE")
}

/*
  # while on a topic branch called "feature":
  $ gh pull-request
  [ opens text editor to edit title & body for the request ]
  [ opened pull request on GitHub for "YOUR_USER:feature" ]

  # explicit pull base & head:
  $ gh pull-request -b jingweno:master -h jingweno:feature

  $ gh pull-request -m "title\n\nbody"
  [ create pull request with title & body  ]

  $ gh pull-request -i 123
  [ attached pull request to issue #123 ]

  $ gh pull-request https://github.com/jingweno/gh/pull/123
  [ attached pull request to issue #123 ]

  $ gh pull-request -F FILE
  [ create pull request with title & body from FILE ]
*/
func pullRequest(cmd *Command, args *Args) {
	localRepo := github.LocalRepo()

	currentBranch, err := localRepo.CurrentBranch()
	utils.Check(err)

	baseProject, err := localRepo.MainProject()
	utils.Check(err)

	headProject, err := localRepo.CurrentProject()
	utils.Check(err)

	var (
		base, head           string
		force, explicitOwner bool
	)

	force = flagPullRequestForce

	if flagPullRequestBase != "" {
		baseProject, base = parsePullRequestProject(baseProject, flagPullRequestBase)
	}

	if flagPullRequestHead != "" {
		headProject, head = parsePullRequestProject(headProject, flagPullRequestHead)
		explicitOwner = strings.Contains(flagPullRequestHead, ":")
	}

	if args.ParamsSize() == 1 {
		arg := args.RemoveParam(0)
		flagPullRequestIssue = parsePullRequestIssueNumber(arg)
	}

	if base == "" {
		masterBranch, err := localRepo.MasterBranch()
		utils.Check(err)
		base = masterBranch.ShortName()
	}

	trackedBranch, _ := currentBranch.Upstream()
	if head == "" {
		if trackedBranch != nil && trackedBranch.IsRemote() {
			if reflect.DeepEqual(baseProject, headProject) && base == trackedBranch.ShortName() {
				e := fmt.Errorf(`Aborted: head branch is the same as base ("%s")`, base)
				e = fmt.Errorf("%s\n(use `-h <branch>` to specify an explicit pull request head)", e)
				utils.Check(e)
			}
		} else {
			// the current branch tracking another branch
			// pretend there's no upstream at all
			trackedBranch = nil
		}

		if trackedBranch == nil {
			head = currentBranch.ShortName()
		} else {
			head = trackedBranch.ShortName()
		}
	}

	client := github.NewClient(baseProject.Host)

	// when no tracking, assume remote branch is published under active user's fork
	if trackedBranch == nil && !explicitOwner && client.Credentials.User != headProject.Owner {
		// disable this on gh
		//headProject = github.NewProject("", headProject.Name, headProject.Host)
	}

	var title, body string

	if flagPullRequestMessage != "" {
		title, body = readMsg(flagPullRequestMessage)
	}

	if flagPullRequestFile != "" {
		var (
			content []byte
			err     error
		)
		if flagPullRequestFile == "-" {
			content, err = ioutil.ReadAll(os.Stdin)
		} else {
			content, err = ioutil.ReadFile(flagPullRequestFile)
		}
		utils.Check(err)
		title, body = readMsg(string(content))
	}

	fullBase := fmt.Sprintf("%s:%s", baseProject.Owner, base)
	fullHead := fmt.Sprintf("%s:%s", headProject.Owner, head)

	if !force && trackedBranch != nil {
		remoteCommits, _ := git.RefList(trackedBranch.LongName(), "")
		if len(remoteCommits) > 0 {
			err = fmt.Errorf("Aborted: %d commits are not yet pushed to %s", len(remoteCommits), trackedBranch.LongName())
			err = fmt.Errorf("%s\n(use `-f` to force submit a pull request anyway)", err)
			utils.Check(err)
		}
	}

	if title == "" && flagPullRequestIssue == "" {
		commits, _ := git.RefList(base, head)
		t, b, err := writePullRequestTitleAndBody(base, head, fullBase, fullHead, commits)
		utils.Check(err)
		title = t
		body = b
	}

	if title == "" && flagPullRequestIssue == "" {
		utils.Check(fmt.Errorf("Aborting due to empty pull request title"))
	}

	var pullRequestURL string
	if args.Noop {
		args.Before(fmt.Sprintf("Would request a pull request to %s from %s", fullBase, fullHead), "")
		pullRequestURL = "PULL_REQUEST_URL"
	} else {
		if title != "" {
			pr, err := client.CreatePullRequest(baseProject, base, fullHead, title, body)
			utils.Check(err)
			pullRequestURL = pr.HTMLURL
		}

		if flagPullRequestIssue != "" {
			pr, err := client.CreatePullRequestForIssue(baseProject, base, fullHead, flagPullRequestIssue)
			utils.Check(err)
			pullRequestURL = pr.HTMLURL
		}
	}

	args.Replace("echo", "", pullRequestURL)
}

func writePullRequestTitleAndBody(base, head, fullBase, fullHead string, commits []string) (title, body string, err error) {
	messageFile, err := git.PullReqMsgFile()
	if err != nil {
		return
	}
	defer os.Remove(messageFile)

	err = writePullRequestChanges(base, head, fullBase, fullHead, commits, messageFile)
	if err != nil {
		return
	}

	editor, err := git.Editor()
	if err != nil {
		return
	}

	err = editTitleAndBody(editor, messageFile)
	if err != nil {
		err = fmt.Errorf("error using text editor for pull request message")
		return
	}

	title, body, err = readTitleAndBody(messageFile)
	if err != nil {
		return
	}

	return
}

func writePullRequestChanges(base, head, fullBase, fullHead string, commits []string, messageFile string) error {
	var defaultMsg, commitSummary string
	if len(commits) == 1 {
		msg, err := git.Show(commits[0])
		if err != nil {
			return err
		}
		defaultMsg = fmt.Sprintf("%s\n", msg)
	} else if len(commits) > 1 {
		commitLogs, err := git.Log(base, head)
		if err != nil {
			return err
		}

		if len(commitLogs) > 0 {
			startRegexp := regexp.MustCompilePOSIX("^")
			endRegexp := regexp.MustCompilePOSIX(" +$")

			commitLogs = strings.TrimSpace(commitLogs)
			commitLogs = startRegexp.ReplaceAllString(commitLogs, "# ")
			commitLogs = endRegexp.ReplaceAllString(commitLogs, "")
			commitSummary = `
#
# Changes:
#
%s`
			commitSummary = fmt.Sprintf(commitSummary, commitLogs)
		}
	}

	message := `%s
# Requesting a pull to %s from %s
#
# Write a message for this pull request. The first block
# of the text is the title and the rest is description.%s
`
	message = fmt.Sprintf(message, defaultMsg, fullBase, fullHead, commitSummary)

	return ioutil.WriteFile(messageFile, []byte(message), 0644)
}

func editTitleAndBody(editor, messageFile string) error {
	editCmd := cmd.New(editor)
	r := regexp.MustCompile("[mg]?vi[m]$")
	if r.MatchString(editor) {
		editCmd.WithArg("-c")
		editCmd.WithArg("set ft=gitcommit tw=0 wrap lbr")
	}
	editCmd.WithArg(messageFile)

	return editCmd.Exec()
}

func readTitleAndBody(messageFile string) (title, body string, err error) {
	f, err := os.Open(messageFile)
	defer f.Close()
	if err != nil {
		return "", "", err
	}

	reader := bufio.NewReader(f)

	return readTitleAndBodyFrom(reader)
}

func readTitleAndBodyFrom(reader *bufio.Reader) (title, body string, err error) {
	r := regexp.MustCompile("\\S")
	var titleParts, bodyParts []string

	line, err := readLine(reader)
	for err == nil {
		if strings.HasPrefix(line, "#") {
			break
		}

		if len(bodyParts) == 0 && r.MatchString(line) {
			titleParts = append(titleParts, line)
		} else {
			bodyParts = append(bodyParts, line)
		}

		line, err = readLine(reader)
	}

	if err == io.EOF {
		err = nil
	}

	title = strings.Join(titleParts, " ")
	title = strings.TrimSpace(title)

	body = strings.Join(bodyParts, "\n")
	body = strings.TrimSpace(body)

	return
}

func readLine(r *bufio.Reader) (string, error) {
	var (
		isPrefix = true
		err      error
		line, ln []byte
	)

	for isPrefix && err == nil {
		line, isPrefix, err = r.ReadLine()
		ln = append(ln, line...)
	}

	return string(ln), err
}

func readMsg(msg string) (title, body string) {
	split := strings.SplitN(msg, "\n\n", 2)
	title = strings.TrimSpace(split[0])
	if len(split) > 1 {
		body = strings.TrimSpace(split[1])
	}

	return
}

func parsePullRequestProject(context *github.Project, s string) (p *github.Project, ref string) {
	p = context
	ref = s

	if strings.Contains(s, ":") {
		split := strings.SplitN(s, ":", 2)
		ref = split[1]
		var name string
		if !strings.Contains(split[0], "/") {
			name = context.Name
		}
		p = github.NewProject(split[0], name, context.Host)
	}

	return
}

func parsePullRequestIssueNumber(url string) string {
	u, e := github.ParseURL(url)
	if e != nil {
		return ""
	}

	r := regexp.MustCompile(`^issues\/(\d+)`)
	p := u.ProjectPath()
	if r.MatchString(p) {
		return r.FindStringSubmatch(p)[1]
	}

	return ""
}
<|endoftext|>


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

package siv

import (
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
	"strconv"
	"testing"
)

func TestXorend(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func fromBinary(s string) byte {
	AssertEq(8, len(s), "%s", s)

	u, err := strconv.ParseUint(s, 2, 8)
	AssertEq(nil, err, "%s", s)

	return byte(u)
}

type XorendTest struct{}

func init() { RegisterTestSuite(&XorendTest{}) }

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *XorendTest) AIsShorterThanB() {
	a := []byte{0xde}
	b := []byte{0xde, 0xad}

	f := func() { xorend(a, b) }
	ExpectThat(f, Panics(HasSubstr("length")))
}

func (t *XorendTest) BothAreNil() {
	ExpectEq("TODO", "")
}

func (t *XorendTest) BIsNil() {
	ExpectEq("TODO", "")
}

func (t *XorendTest) BothAreEmpty() {
	ExpectEq("TODO", "")
}

func (t *XorendTest) BIsEmpty() {
	ExpectEq("TODO", "")
}

func (t *XorendTest) BIsNonEmpty() {
	ExpectEq("TODO", "")
}
<commit_msg>XorendTest.BothAreNil<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package siv

import (
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
	"strconv"
	"testing"
)

func TestXorend(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func fromBinary(s string) byte {
	AssertEq(8, len(s), "%s", s)

	u, err := strconv.ParseUint(s, 2, 8)
	AssertEq(nil, err, "%s", s)

	return byte(u)
}

type XorendTest struct{}

func init() { RegisterTestSuite(&XorendTest{}) }

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *XorendTest) AIsShorterThanB() {
	a := []byte{0xde}
	b := []byte{0xde, 0xad}

	f := func() { xorend(a, b) }
	ExpectThat(f, Panics(HasSubstr("length")))
}

func (t *XorendTest) BothAreNil() {
	a := []byte(nil)
	b := []byte(nil)

	expected := []byte{}
	ExpectThat(xorend(a, b), DeepEquals(expected))
}

func (t *XorendTest) BIsNil() {
	ExpectEq("TODO", "")
}

func (t *XorendTest) BothAreEmpty() {
	ExpectEq("TODO", "")
}

func (t *XorendTest) BIsEmpty() {
	ExpectEq("TODO", "")
}

func (t *XorendTest) BIsNonEmpty() {
	ExpectEq("TODO", "")
}
<|endoftext|>


<commit_before>// Copyright 2014 Simon Zimmermann. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package server

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"time"

	"github.com/simonz05/carry/types"
	"github.com/simonz05/util/httputil"
	"github.com/simonz05/util/log"
)

func (c *context) createStat(rw http.ResponseWriter, req *http.Request) {
	if req.ContentLength > MaxStatSize {
		httputil.ServeJSONCodeError(rw, "Request Entity Too Large", http.StatusRequestEntityTooLarge)
		return
	}

	stats := []*types.Stat{}
	d := json.NewDecoder(req.Body)
	err := d.Decode(&stats)

	if err != nil {
		log.Error(err)
		httputil.ServeJSONCodeError(rw, "JSON Decode Error", http.StatusBadRequest)
		return
	}

	if len(stats) > 0 {
		now := time.Now().Unix()

		for _, s := range stats {
			if s.Timestamp <= 0 {
				s.Timestamp = now
			}

			if s.Value < 0 {
				s.Value = 0
			}
		}
	}

	err = c.sto.ReceiveStats(stats)

	if err != nil {
		log.Error(err)
		httputil.ServeJSONCodeError(rw, "Internal Server Error", http.StatusInternalServerError)
		return
	}

	rw.WriteHeader(http.StatusCreated)
}

func (c *context) createStatGet(rw http.ResponseWriter, req *http.Request) {
	err := req.ParseForm()

	if err != nil {
		rw.WriteHeader(http.StatusInternalServerError)
		rw.Write([]byte("Internal Server Error"))
		return
	}

	keys, ok := req.Form["k"]
	n := len(keys)

	if !ok || n == 0 {
		httputil.BadRequestError(rw, "No stats")
		return
	}

	stats := make([]*types.Stat, 0, len(keys))

	for _, key := range keys {
		stats = append(stats, &types.Stat{Key: key})
	}

	values, ok := req.Form["v"]

	if !ok || n != len(values) {
		httputil.BadRequestError(rw, fmt.Sprintf("bad value part ok %v, len %d", ok, len(values)))
		return
	}

	for i, value := range values {
		v, err := strconv.ParseFloat(value, 64)
		if err != nil {
			httputil.BadRequestError(rw, fmt.Sprintf("bad value part %s %s", value, err))
			return
		}

		if v < 0 {
			v = 0.0
		}

		stats[i].Value = v
	}

	times, ok := req.Form["t"]

	if !ok || n != len(times) {
		httputil.BadRequestError(rw, "bad timestamp part")
		return
	}

	for i, ts := range times {
		t, err := strconv.ParseInt(ts, 10, 64)
		if err != nil {
			httputil.BadRequestError(rw, "bad timestamp part")
			return
		}

		if t < 0 {
			t = 0
		}

		stats[i].Timestamp = t
	}

	kinds, ok := req.Form["c"]

	if !ok || n != len(kinds) {
		httputil.BadRequestError(rw, "bad stat kind part")
		return
	}

	for i, value := range kinds {
		kind := new(types.StatKind)
		err := kind.UnmarshalText([]byte(value))

		if err != nil {
			httputil.BadRequestError(rw, "bad stat kind part")
			return
		}
		stats[i].Type = *kind
	}

	err = c.sto.ReceiveStats(stats)

	if err != nil {
		log.Error(err)
		rw.WriteHeader(http.StatusInternalServerError)
		rw.Write([]byte("Internal Server Error"))
		return
	}

	rw.Header().Add("Cache-Control", "private, no-cache, no-cache=Set-Cookie, proxy-revalidate")
	rw.Header().Add("Pragma", "no-cache")
	rw.WriteHeader(http.StatusNoContent)
}

func (c *context) headStat(rw http.ResponseWriter, req *http.Request) {
	rw.Header().Set("Access-Control-Allow-Methods", "PUT, DELETE, GET, POST, HEAD, OPTIONS")
	rw.Header().Set("Access-Control-Allow-Headers", "Content-Type, Accept, *")
	//rw.Header().Add("Access-Control-Allow-Headers", "origin, x-csrftoken, content-type, accept")
	rw.WriteHeader(http.StatusOK)
}
<commit_msg>move some error types to info<commit_after>// Copyright 2014 Simon Zimmermann. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package server

import (
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"strconv"
	"time"

	"github.com/simonz05/carry/types"
	"github.com/simonz05/util/httputil"
	"github.com/simonz05/util/log"
)

func (c *context) createStat(rw http.ResponseWriter, req *http.Request) {
	if req.ContentLength > MaxStatSize {
		httputil.ServeJSONCodeError(rw, "Request Entity Too Large", http.StatusRequestEntityTooLarge)
		return
	}

	stats := []*types.Stat{}
	d := json.NewDecoder(req.Body)
	err := d.Decode(&stats)

	if err != nil {
		operr, ok := err.(*net.OpError)
		if !(ok && operr.Temporary()) && err != io.EOF {
			log.Error(err)
		} else {
			log.Print(err)
		}
		httputil.ServeJSONCodeError(rw, "JSON Decode Error", http.StatusBadRequest)
		return
	}

	if len(stats) > 0 {
		now := time.Now().Unix()

		for _, s := range stats {
			if s.Timestamp <= 0 {
				s.Timestamp = now
			}

			if s.Value < 0 {
				s.Value = 0
			}
		}
	}

	err = c.sto.ReceiveStats(stats)

	if err != nil {
		log.Error(err)
		httputil.ServeJSONCodeError(rw, "Internal Server Error", http.StatusInternalServerError)
		return
	}

	rw.WriteHeader(http.StatusCreated)
}

func (c *context) createStatGet(rw http.ResponseWriter, req *http.Request) {
	err := req.ParseForm()

	if err != nil {
		rw.WriteHeader(http.StatusInternalServerError)
		rw.Write([]byte("Internal Server Error"))
		return
	}

	keys, ok := req.Form["k"]
	n := len(keys)

	if !ok || n == 0 {
		httputil.BadRequestError(rw, "No stats")
		return
	}

	stats := make([]*types.Stat, 0, len(keys))

	for _, key := range keys {
		stats = append(stats, &types.Stat{Key: key})
	}

	values, ok := req.Form["v"]

	if !ok || n != len(values) {
		httputil.BadRequestError(rw, fmt.Sprintf("bad value part ok %v, len %d", ok, len(values)))
		return
	}

	for i, value := range values {
		v, err := strconv.ParseFloat(value, 64)
		if err != nil {
			httputil.BadRequestError(rw, fmt.Sprintf("bad value part %s %s", value, err))
			return
		}

		if v < 0 {
			v = 0.0
		}

		stats[i].Value = v
	}

	times, ok := req.Form["t"]

	if !ok || n != len(times) {
		httputil.BadRequestError(rw, "bad timestamp part")
		return
	}

	for i, ts := range times {
		t, err := strconv.ParseInt(ts, 10, 64)
		if err != nil {
			httputil.BadRequestError(rw, "bad timestamp part")
			return
		}

		if t < 0 {
			t = 0
		}

		stats[i].Timestamp = t
	}

	kinds, ok := req.Form["c"]

	if !ok || n != len(kinds) {
		httputil.BadRequestError(rw, "bad stat kind part")
		return
	}

	for i, value := range kinds {
		kind := new(types.StatKind)
		err := kind.UnmarshalText([]byte(value))

		if err != nil {
			httputil.BadRequestError(rw, "bad stat kind part")
			return
		}
		stats[i].Type = *kind
	}

	err = c.sto.ReceiveStats(stats)

	if err != nil {
		log.Error(err)
		rw.WriteHeader(http.StatusInternalServerError)
		rw.Write([]byte("Internal Server Error"))
		return
	}

	rw.Header().Add("Cache-Control", "private, no-cache, no-cache=Set-Cookie, proxy-revalidate")
	rw.Header().Add("Pragma", "no-cache")
	rw.WriteHeader(http.StatusNoContent)
}

func (c *context) headStat(rw http.ResponseWriter, req *http.Request) {
	rw.Header().Set("Access-Control-Allow-Methods", "PUT, DELETE, GET, POST, HEAD, OPTIONS")
	rw.Header().Set("Access-Control-Allow-Headers", "Content-Type, Accept, *")
	//rw.Header().Add("Access-Control-Allow-Headers", "origin, x-csrftoken, content-type, accept")
	rw.WriteHeader(http.StatusOK)
}
<|endoftext|>


<commit_before>package server

import (
	"net/http"

	"github.com/gin-gonic/gin"

	"github.com/drone/drone-exp/shared/crypto"
	"github.com/drone/drone/cache"
	"github.com/drone/drone/router/middleware/session"
	"github.com/drone/drone/shared/token"
	"github.com/drone/drone/store"
)

func GetSelf(c *gin.Context) {
	c.JSON(200, session.User(c))
}

func GetFeed(c *gin.Context) {
	repos, err := cache.GetRepos(c, session.User(c))
	if err != nil {
		c.String(500, "Error fetching repository list. %s", err)
		return
	}

	feed, err := store.GetUserFeed(c, repos)
	if err != nil {
		c.String(500, "Error fetching feed. %s", err)
		return
	}
	c.JSON(200, feed)
}

func GetRepos(c *gin.Context) {
	repos, err := cache.GetRepos(c, session.User(c))
	if err != nil {
		c.String(500, "Error fetching repository list. %s", err)
		return
	}

	repos_, err := store.GetRepoListOf(c, repos)
	if err != nil {
		c.String(500, "Error fetching repository list. %s", err)
		return
	}
	c.JSON(http.StatusOK, repos_)
}

func GetRemoteRepos(c *gin.Context) {
	repos, err := cache.GetRepos(c, session.User(c))
	if err != nil {
		c.String(500, "Error fetching repository list. %s", err)
		return
	}
	c.JSON(http.StatusOK, repos)
}

func PostToken(c *gin.Context) {
	user := session.User(c)

	token := token.New(token.UserToken, user.Login)
	tokenstr, err := token.Sign(user.Hash)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}
	c.String(http.StatusOK, tokenstr)
}

func DeleteToken(c *gin.Context) {
	user := session.User(c)
	user.Hash = crypto.Rand()
	if err := store.UpdateUser(c, user); err != nil {
		c.String(500, "Error revoking tokens. %s", err)
		return
	}

	token := token.New(token.UserToken, user.Login)
	tokenstr, err := token.Sign(user.Hash)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}
	c.String(http.StatusOK, tokenstr)
}
<commit_msg>removed import that was mistakenly auto-added<commit_after>package server

import (
	"encoding/base32"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/securecookie"

	"github.com/drone/drone/cache"
	"github.com/drone/drone/router/middleware/session"
	"github.com/drone/drone/shared/token"
	"github.com/drone/drone/store"
)

func GetSelf(c *gin.Context) {
	c.JSON(200, session.User(c))
}

func GetFeed(c *gin.Context) {
	repos, err := cache.GetRepos(c, session.User(c))
	if err != nil {
		c.String(500, "Error fetching repository list. %s", err)
		return
	}

	feed, err := store.GetUserFeed(c, repos)
	if err != nil {
		c.String(500, "Error fetching feed. %s", err)
		return
	}
	c.JSON(200, feed)
}

func GetRepos(c *gin.Context) {
	repos, err := cache.GetRepos(c, session.User(c))
	if err != nil {
		c.String(500, "Error fetching repository list. %s", err)
		return
	}

	repos_, err := store.GetRepoListOf(c, repos)
	if err != nil {
		c.String(500, "Error fetching repository list. %s", err)
		return
	}
	c.JSON(http.StatusOK, repos_)
}

func GetRemoteRepos(c *gin.Context) {
	repos, err := cache.GetRepos(c, session.User(c))
	if err != nil {
		c.String(500, "Error fetching repository list. %s", err)
		return
	}
	c.JSON(http.StatusOK, repos)
}

func PostToken(c *gin.Context) {
	user := session.User(c)

	token := token.New(token.UserToken, user.Login)
	tokenstr, err := token.Sign(user.Hash)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}
	c.String(http.StatusOK, tokenstr)
}

func DeleteToken(c *gin.Context) {
	user := session.User(c)
	user.Hash = base32.StdEncoding.EncodeToString(
		securecookie.GenerateRandomKey(32),
	)
	if err := store.UpdateUser(c, user); err != nil {
		c.String(500, "Error revoking tokens. %s", err)
		return
	}

	token := token.New(token.UserToken, user.Login)
	tokenstr, err := token.Sign(user.Hash)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}
	c.String(http.StatusOK, tokenstr)
}
<|endoftext|>


<commit_before>package gonvim

import (
	"fmt"

	"github.com/dzhou121/ui"
)

// Border is the border of area
type Border struct {
	color *RGBA
	width int
}

// AreaHandler is
type AreaHandler struct {
	area         *ui.Area
	width        int
	height       int
	borderTop    *Border
	borderRight  *Border
	borderLeft   *Border
	borderBottom *Border
	bg           *RGBA
	x, y         int
	shown        bool
}

func drawRect(dp *ui.AreaDrawParams, x, y, width, height int, color *RGBA) {
	p := ui.NewPath(ui.Winding)
	p.AddRectangle(float64(x), float64(y), float64(width), float64(height))
	p.End()
	dp.Context.Fill(p, &ui.Brush{
		Type: ui.Solid,
		R:    color.R,
		G:    color.G,
		B:    color.B,
		A:    color.A,
	})
	p.Free()
}

// Draw the area
func (ah *AreaHandler) Draw(a *ui.Area, dp *ui.AreaDrawParams) {
	if ah.bg == nil {
		return
	}
	bg := ah.bg
	p := ui.NewPath(ui.Winding)
	p.AddRectangle(dp.ClipX, dp.ClipY, dp.ClipWidth, dp.ClipHeight)
	p.End()

	dp.Context.Fill(p, &ui.Brush{
		Type: ui.Solid,
		R:    bg.R,
		G:    bg.G,
		B:    bg.B,
		A:    bg.A,
	})
	p.Free()

	ah.drawBorder(dp)
}

func (ah *AreaHandler) drawBorder(dp *ui.AreaDrawParams) {
	if ah.borderBottom != nil {
		drawRect(dp, 0, ah.height-ah.borderBottom.width, ah.width, ah.borderBottom.width, ah.borderBottom.color)
	}
	if ah.borderRight != nil {
		drawRect(dp, ah.width-ah.borderRight.width, 0, ah.borderRight.width, ah.height, ah.borderRight.color)
	}
	if ah.borderTop != nil {
		drawRect(dp, 0, 0, ah.width, ah.borderTop.width, ah.borderTop.color)
	}
	if ah.borderLeft != nil {
		drawRect(dp, 0, 0, ah.borderLeft.width, ah.height, ah.borderLeft.color)
	}
}

// MouseEvent is
func (ah *AreaHandler) MouseEvent(a *ui.Area, me *ui.AreaMouseEvent) {
}

// MouseCrossed is
func (ah *AreaHandler) MouseCrossed(a *ui.Area, left bool) {
}

// DragBroken is
func (ah *AreaHandler) DragBroken(a *ui.Area) {
}

// KeyEvent is
func (ah *AreaHandler) KeyEvent(a *ui.Area, key *ui.AreaKeyEvent) (handled bool) {
	if key.Up {
		return false
	}
	if key.Key == 0 && key.ExtKey == 0 {
		return false
	}
	namedKey := ""
	mod := ""

	switch key.Modifiers {
	case ui.Ctrl:
		mod = "C-"
	case ui.Alt:
		mod = "A-"
	case ui.Super:
		mod = "M-"
	}

	switch key.ExtKey {
	case ui.Escape:
		namedKey = "Esc"
	case ui.Insert:
		namedKey = "Insert"
	case ui.Delete:
		namedKey = "Del"
	case ui.Left:
		namedKey = "Left"
	case ui.Right:
		namedKey = "Right"
	case ui.Down:
		namedKey = "Down"
	case ui.Up:
		namedKey = "Up"
	}

	char := ""
	char = string(key.Key)
	if char == "\n" || char == "\r" {
		namedKey = "Enter"
	} else if char == "\t" {
		namedKey = "Tab"
	} else if key.Key == 127 {
		namedKey = "BS"
	} else if char == "<" {
		namedKey = "LT"
	}

	input := ""
	if namedKey != "" {
		input = fmt.Sprintf("<%s>", namedKey)
	} else if mod != "" {
		input = fmt.Sprintf("<%s%s>", mod, char)
	} else {
		input = char
	}
	editor.nvim.Input(input)
	return true
}

func (ah *AreaHandler) setPosition(x, y int) {
	if x == ah.x && y == ah.y {
		return
	}
	ah.x = x
	ah.y = y
	ui.QueueMain(func() {
		ah.area.SetPosition(x, y)
	})
}

func (ah *AreaHandler) show() {
	if ah.shown {
		return
	}
	ah.shown = true
	ui.QueueMain(func() {
		ah.area.Show()
	})
}

func (ah *AreaHandler) hide() {
	if !ah.shown {
		return
	}
	ah.shown = false
	ui.QueueMain(func() {
		ah.area.Hide()
	})
}

func (ah *AreaHandler) setSize(width, height int) {
	if width == ah.width && height == ah.height {
		return
	}
	ah.width = width
	ah.height = height
	ui.QueueMain(func() {
		ah.area.SetSize(width, height)
	})
}
<commit_msg>key event fix<commit_after>package gonvim

import (
	"fmt"

	"github.com/dzhou121/ui"
)

// Border is the border of area
type Border struct {
	color *RGBA
	width int
}

// AreaHandler is
type AreaHandler struct {
	area         *ui.Area
	width        int
	height       int
	borderTop    *Border
	borderRight  *Border
	borderLeft   *Border
	borderBottom *Border
	bg           *RGBA
	x, y         int
	shown        bool
}

func drawRect(dp *ui.AreaDrawParams, x, y, width, height int, color *RGBA) {
	p := ui.NewPath(ui.Winding)
	p.AddRectangle(float64(x), float64(y), float64(width), float64(height))
	p.End()
	dp.Context.Fill(p, &ui.Brush{
		Type: ui.Solid,
		R:    color.R,
		G:    color.G,
		B:    color.B,
		A:    color.A,
	})
	p.Free()
}

// Draw the area
func (ah *AreaHandler) Draw(a *ui.Area, dp *ui.AreaDrawParams) {
	if ah.bg == nil {
		return
	}
	bg := ah.bg
	p := ui.NewPath(ui.Winding)
	p.AddRectangle(dp.ClipX, dp.ClipY, dp.ClipWidth, dp.ClipHeight)
	p.End()

	dp.Context.Fill(p, &ui.Brush{
		Type: ui.Solid,
		R:    bg.R,
		G:    bg.G,
		B:    bg.B,
		A:    bg.A,
	})
	p.Free()

	ah.drawBorder(dp)
}

func (ah *AreaHandler) drawBorder(dp *ui.AreaDrawParams) {
	if ah.borderBottom != nil {
		drawRect(dp, 0, ah.height-ah.borderBottom.width, ah.width, ah.borderBottom.width, ah.borderBottom.color)
	}
	if ah.borderRight != nil {
		drawRect(dp, ah.width-ah.borderRight.width, 0, ah.borderRight.width, ah.height, ah.borderRight.color)
	}
	if ah.borderTop != nil {
		drawRect(dp, 0, 0, ah.width, ah.borderTop.width, ah.borderTop.color)
	}
	if ah.borderLeft != nil {
		drawRect(dp, 0, 0, ah.borderLeft.width, ah.height, ah.borderLeft.color)
	}
}

// MouseEvent is
func (ah *AreaHandler) MouseEvent(a *ui.Area, me *ui.AreaMouseEvent) {
}

// MouseCrossed is
func (ah *AreaHandler) MouseCrossed(a *ui.Area, left bool) {
}

// DragBroken is
func (ah *AreaHandler) DragBroken(a *ui.Area) {
}

// KeyEvent is
func (ah *AreaHandler) KeyEvent(a *ui.Area, key *ui.AreaKeyEvent) (handled bool) {
	if editor == nil {
		return false
	}

	if key.Up {
		return false
	}
	if key.Key == 0 && key.ExtKey == 0 {
		return false
	}
	namedKey := ""
	mod := ""

	switch key.Modifiers {
	case ui.Ctrl:
		mod = "C-"
	case ui.Alt:
		mod = "A-"
	case ui.Super:
		mod = "M-"
	}

	switch key.ExtKey {
	case ui.Escape:
		namedKey = "Esc"
	case ui.Insert:
		namedKey = "Insert"
	case ui.Delete:
		namedKey = "Del"
	case ui.Left:
		namedKey = "Left"
	case ui.Right:
		namedKey = "Right"
	case ui.Down:
		namedKey = "Down"
	case ui.Up:
		namedKey = "Up"
	}

	char := ""
	char = string(key.Key)
	if char == "\n" || char == "\r" {
		namedKey = "Enter"
	} else if char == "\t" {
		namedKey = "Tab"
	} else if key.Key == 127 {
		namedKey = "BS"
	} else if char == "<" {
		namedKey = "LT"
	}

	input := ""
	if namedKey != "" {
		input = fmt.Sprintf("<%s>", namedKey)
	} else if mod != "" {
		input = fmt.Sprintf("<%s%s>", mod, char)
	} else {
		input = char
	}
	editor.nvim.Input(input)
	return true
}

func (ah *AreaHandler) setPosition(x, y int) {
	if x == ah.x && y == ah.y {
		return
	}
	ah.x = x
	ah.y = y
	ui.QueueMain(func() {
		ah.area.SetPosition(x, y)
	})
}

func (ah *AreaHandler) show() {
	if ah.shown {
		return
	}
	ah.shown = true
	ui.QueueMain(func() {
		ah.area.Show()
	})
}

func (ah *AreaHandler) hide() {
	if !ah.shown {
		return
	}
	ah.shown = false
	ui.QueueMain(func() {
		ah.area.Hide()
	})
}

func (ah *AreaHandler) setSize(width, height int) {
	if width == ah.width && height == ah.height {
		return
	}
	ah.width = width
	ah.height = height
	ui.QueueMain(func() {
		ah.area.SetSize(width, height)
	})
}
<|endoftext|>


<commit_before>package gocouch

import (
	"bytes"
	"strings"
	"testing"
)

func getConnection(t *testing.T) *Server {
	srv, err := Connect("localhost", 5984, nil, 0)
	if err != nil {
		t.Logf("Error: %v", err)
		t.Fail()
	}
	return srv
}

func TestInfo(t *testing.T) {
	srv := getConnection(t)
	info, err := srv.Info()
	if err != nil {
		t.Logf("Error: %v", err)
		t.Fail()
	}
	// check struct parse
	if info.Message == "" || info.UUID == "" || info.Version == "" {
		t.Logf("Incorrect struct returned: %#v", info)
		t.Fail()
	}
}

func TestActiveTasks(t *testing.T) {
	srv := getConnection(t)
	var result []map[string]interface{}
	err := srv.GetActiveTasks(&result)
	if err != nil {
		t.Logf("Error: %v", err)
		t.Fail()
	}
	// todo: add checking authorisation and some task appearance (continious replication)
}

func TestGetAllDbs(t *testing.T) {
	srv := getConnection(t)
	var result []string
	err := srv.GetAllDbs(&result)
	if err != nil {
		t.Logf("Error: %v", err)
		t.Fail()
	}
	if len(result) < 1 {
		t.Log("Len of db names less than 1")
		t.Fail()
	}
}

func TestGetDBEvent(t *testing.T) {
	srv := getConnection(t)
	var result map[string]interface{}
	if err := srv.GetDBEvent(&result, Options{"timeout": "1"}); err != nil {
		t.Logf("Error: %v", err)
		t.Fail()
	}
	// todo: add creating and deleting db tests
}

func TestGetDBEventChan(t *testing.T) {
	srv := getConnection(t)
	events, err := srv.GetDBEventChan(10)
	defer func() {
		close(events)
	}()
	if err != nil {
		t.Logf("Error: %v", err)
		t.Fail()
	}
	// todo: check blocking
	// todo: add tests with real events
	// todo: check resourses are released
	// todo: test error reporting
}

func TestGetMembership(t *testing.T) {
	srv := getConnection(t)
	var result map[string][]string
	if err := srv.GetMembership(&result); err != nil {
		// membership only supported by couchdb 2.0
		if !strings.Contains(err.Error(), "Not supported") {
			t.Logf("Error: %v", err)
			t.Fail()
		}
	}
}

func TestGetLog(t *testing.T) {
	srv := getConnection(t)
	result, err := srv.GetLog(10000)
	if err != nil {
		t.Logf("Error: %v", err)
		t.Fail()
	}
	// check for `info` records, most likely you will see them in log
	if !bytes.Contains(result.Bytes(), []byte("info")) {
		t.Log("Got empty log, it's most likely an error")
		t.Fail()
	}
}

func TestReplicate(t *testing.T) {
	srv := getConnection(t)
	result, err := srv.Replicate("testing", "testing2", Options{"create_target": true})
	if err != nil {
		t.Logf("Error: %v", err)
		t.Fail()
	}
	if !result.Ok {
		t.Logf("Request was unsuccessfull, %#v\n", result)
	}
}

// todo: find a way to schedule this test to the end
//func TestRestart(t *testing.T) {
//	srv := getConnection(t)
//	err := srv.Restart()
//	if err != nil {
//		t.Logf("Error: %v", err)
//		t.Fail()
//	}
//}

func TestStats(t *testing.T) {
	srv := getConnection(t)
	var stats map[string]interface{}
	if err := srv.Stats([]string{"couchdb", "request_time"}, &stats); err != nil {
		t.Logf("Error: %v", err)
		t.Fail()
	}
}

func TestUUIDs(t *testing.T) {
	srv := getConnection(t)
	uuids, err := srv.GetUUIDs(15)
	if err != nil {
		t.Logf("Error: %v", err)
		t.Fail()
	}
	if len(uuids) != 15 {
		t.Log("UUIDs length mismatch")
		t.Fail()
	}
}
<commit_msg>fixed: fix tests<commit_after>package gocouch

import (
	"bytes"
	"strings"
	"testing"
)

func getConnection(t *testing.T) *Server {
	srv, err := Connect("localhost", 5984, nil, 0)
	if err != nil {
		t.Logf("Error: %v", err)
		t.Fail()
	}
	return srv
}

func TestInfo(t *testing.T) {
	srv := getConnection(t)
	info, err := srv.Info()
	if err != nil {
		t.Logf("Error: %v", err)
		t.Fail()
	}
	// check struct parse
	if info.Message == "" || info.UUID == "" || info.Version == "" {
		t.Logf("Incorrect struct returned: %#v", info)
		t.Fail()
	}
}

func TestActiveTasks(t *testing.T) {
	srv := getConnection(t)
	var result []map[string]interface{}
	err := srv.GetActiveTasks(&result)
	if err != nil {
		t.Logf("Error: %v", err)
		t.Fail()
	}
	// todo: add checking authorisation and some task appearance (continious replication)
}

func TestGetAllDbs(t *testing.T) {
	srv := getConnection(t)
	var result []string
	err := srv.GetAllDbs(&result)
	if err != nil {
		t.Logf("Error: %v", err)
		t.Fail()
	}
	if len(result) < 1 {
		t.Log("Len of db names less than 1")
		t.Fail()
	}
}

func TestGetDBEvent(t *testing.T) {
	srv := getConnection(t)
	var result map[string]interface{}
	if err := srv.GetDBEvent(&result, Options{"timeout": "1"}); err != nil {
		t.Logf("Error: %v", err)
		t.Fail()
	}
	// todo: add creating and deleting db tests
}

func TestGetDBEventChan(t *testing.T) {
	srv := getConnection(t)
	events, err := srv.GetDBEventChan(10)
	defer func() {
		close(events)
	}()
	if err != nil {
		t.Logf("Error: %v", err)
		t.Fail()
	}
	// todo: check blocking
	// todo: add tests with real events
	// todo: check resourses are released
	// todo: test error reporting
}

func TestGetMembership(t *testing.T) {
	srv := getConnection(t)
	var result map[string][]string
	if err := srv.GetMembership(&result); err != nil {
		// membership only supported by couchdb 2.0
		if !strings.Contains(err.Error(), "Not supported") {
			t.Logf("Error: %v", err)
			t.Fail()
		}
	}
}

func TestGetLog(t *testing.T) {
	srv := getConnection(t)
	result, err := srv.GetLog(10000)
	if err != nil {
		t.Logf("Error: %v", err)
		t.Fail()
	}
	// check for `info` records, most likely you will see them in log
	if !bytes.Contains(result.Bytes(), []byte("info")) {
		t.Log("Got empty log, it's most likely an error")
		t.Fail()
	}
}

func TestReplicate(t *testing.T) {
	srv := getConnection(t)
	result, err := srv.Replicate("testing", "testing2", Options{"create_target": true})
	if err != nil {
		t.Logf("Error: %v", err)
		t.Fail()
	}
	if result != nil && !result.Ok {
		t.Logf("Request was unsuccessfull, %#v\n", result)
		t.Fail()
	}
}

// todo: find a way to schedule this test to the end
//func TestRestart(t *testing.T) {
//	srv := getConnection(t)
//	err := srv.Restart()
//	if err != nil {
//		t.Logf("Error: %v", err)
//		t.Fail()
//	}
//}

func TestStats(t *testing.T) {
	srv := getConnection(t)
	var stats map[string]interface{}
	if err := srv.Stats([]string{"couchdb", "request_time"}, &stats); err != nil {
		t.Logf("Error: %v", err)
		t.Fail()
	}
}

func TestUUIDs(t *testing.T) {
	srv := getConnection(t)
	uuids, err := srv.GetUUIDs(15)
	if err != nil {
		t.Logf("Error: %v", err)
		t.Fail()
	}
	if len(uuids) != 15 {
		t.Log("UUIDs length mismatch")
		t.Fail()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"
	"os"
	"bufio"
	"crypto/rand"
	"math"
	"runtime"
	"github.com/cloudfoundry/gosigar"
)

func main() {
	//var ip = flag.Int("flagname", 1234, "help message for flagname")
	//flag.Parse()
	//fmt.Printf("ip: %d\n", *ip)

	cores := runtime.NumCPU()
	fmt.Printf("cores: %d\n", cores)

	mem := sigar.Mem{}
	mem.Get()
	fmt.Printf("memory: %d GiB\n", mem.Total>>30)

	f, err := os.Create("/tmp/dat2")
	if err != nil {
		panic("couldn't open")
	}
	defer f.Close()

	b := make([]byte, 0x1<<16)
	_, err = rand.Read(b)
	if err != nil {
		panic("couldn't rand")
	}

	w := bufio.NewWriter(f)
	bytesWritten := 0
	start := time.Now()

	for i := 0; i < int(mem.Total); i += len(b) {
		n, err := w.Write(b)
		bytesWritten += n
		if err != nil {
			panic("couldn't write")
		}
	}

	w.Flush()
	finish := time.Now()
	duration := finish.Sub(start)
	fmt.Printf("wrote %d MiB\n", bytesWritten>>20)
	fmt.Printf("took %f seconds\n", duration.Seconds())
	fmt.Printf("throughput %0.2f MiB/s\n", float64(bytesWritten)/float64(duration.Seconds())/math.Exp2(20))
}
<commit_msg>Use a temporary director; don't hardcode to `/tmp/dat2`<commit_after>package main

import (
	"fmt"
	"time"
	"os"
	"bufio"
	"math/rand"
	"math"
	"runtime"
	"github.com/cloudfoundry/gosigar"
	"io/ioutil"
	"path"
)

const Blocksize = 0x1 << 16 // 65,536, 2^16

func main() {

	//var ip = flag.Int("flagname", 1234, "help message for flagname")
	//flag.Parse()
	//fmt.Printf("ip: %d\n", *ip)

	cores := runtime.NumCPU()
	fmt.Printf("cores: %d\n", cores)

	mem := sigar.Mem{}
	mem.Get()
	fmt.Printf("memory: %d MiB\n", mem.Total>>20)

	dir, err := ioutil.TempDir("", "bonniego")
	check(err)
	defer os.RemoveAll(dir)

	f, err := os.Create(path.Join(dir, "bonniego"))
	check(err)
	defer f.Close()
	fmt.Printf("directory: %s\n", dir)

	randomBlock := make([]byte, Blocksize)
	_, err = rand.Read(randomBlock)
	check(err)

	w := bufio.NewWriter(f)
	bytesWritten := 0
	start := time.Now()

	for i := 0; i < int(mem.Total>>4); i += len(randomBlock) { // fixme remove ">> 4"
		n, err := w.Write(randomBlock)
		bytesWritten += n
		check(err)
	}

	w.Flush()
	f.Close()
	finish := time.Now()
	duration := finish.Sub(start)
	fmt.Printf("wrote %d MiB\n", bytesWritten>>20)
	fmt.Printf("took %f seconds\n", duration.Seconds())
	fmt.Printf("throughput %0.2f MiB/s\n", float64(bytesWritten)/float64(duration.Seconds())/math.Exp2(20))

	f, err = os.Open(path.Join(dir, "bonniego"))
	check(err)
	defer f.Close()

	r := bufio.NewReader(f)
	bytesRead := 0
	readBuf := []byte{}

	rando := rand.New(rand.NewSource(time.Now().UnixNano()))
	var offset int
	start = time.Now()
	for n, err := r.Read(readBuf); n > 0; bytesRead += n {
		check(err)
		// let's do the comparison
		offset = rando.Int() % Blocksize
		if readBuf[offset] != randomBlock[offset] {
			panic("they didn't match")
		}
	}
	f.Close()
	finish = time.Now()

	duration = finish.Sub(start)
	fmt.Printf("read %d MiB\n", bytesRead>>20)
	fmt.Printf("took %f seconds\n", duration.Seconds())
	fmt.Printf("throughput %0.2f MiB/s\n", float64(bytesWritten)/float64(duration.Seconds())/math.Exp2(20))
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}
<|endoftext|>


<commit_before>package gorm

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
)

// preloadCallback used to preload associations
func preloadCallback(scope *Scope) {
	if scope.Search.preload == nil || scope.HasError() {
		return
	}

	var (
		preloadedMap = map[string]bool{}
		fields       = scope.Fields()
	)

	for _, preload := range scope.Search.preload {
		var (
			preloadFields = strings.Split(preload.schema, ".")
			currentScope  = scope
			currentFields = fields
		)

		for idx, preloadField := range preloadFields {
			var currentPreloadConditions []interface{}

			// if not preloaded
			if preloadKey := strings.Join(preloadFields[:idx+1], "."); !preloadedMap[preloadKey] {

				// assign search conditions to last preload
				if idx == len(preloadFields)-1 {
					currentPreloadConditions = preload.conditions
				}

				for _, field := range currentFields {
					if field.Name != preloadField || field.Relationship == nil {
						continue
					}

					switch field.Relationship.Kind {
					case "has_one":
						currentScope.handleHasOnePreload(field, currentPreloadConditions)
					case "has_many":
						currentScope.handleHasManyPreload(field, currentPreloadConditions)
					case "belongs_to":
						currentScope.handleBelongsToPreload(field, currentPreloadConditions)
					case "many_to_many":
						currentScope.handleManyToManyPreload(field, currentPreloadConditions)
					default:
						scope.Err(errors.New("unsupported relation"))
					}

					preloadedMap[preloadKey] = true
					break
				}

				if !preloadedMap[preloadKey] {
					scope.Err(fmt.Errorf("can't preload field %s for %s", preloadField, currentScope.GetModelStruct().ModelType))
					return
				}
			}

			// preload next level
			if idx < len(preloadFields)-1 {
				currentScope = currentScope.getColumnAsScope(preloadField)
				currentFields = currentScope.Fields()
			}
		}
	}
}

// handleHasOnePreload used to preload has one associations
func (scope *Scope) handleHasOnePreload(field *Field, conditions []interface{}) {
	relation := field.Relationship

	// get relations's primary keys
	primaryKeys := scope.getColumnAsArray(relation.AssociationForeignFieldNames, scope.Value)
	if len(primaryKeys) == 0 {
		return
	}

	// find relations
	results := makeSlice(field.Struct.Type)
	scope.Err(scope.NewDB().Where(fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relation.ForeignDBNames), toQueryMarks(primaryKeys)), toQueryValues(primaryKeys)...).Find(results, conditions...).Error)

	// assign find results
	var (
		resultsValue       = indirect(reflect.ValueOf(results))
		indirectScopeValue = scope.IndirectValue()
	)

	for i := 0; i < resultsValue.Len(); i++ {
		result := resultsValue.Index(i)
		if indirectScopeValue.Kind() == reflect.Slice {
			foreignValues := getValueFromFields(result, relation.ForeignFieldNames)
			for j := 0; j < indirectScopeValue.Len(); j++ {
				if indirectValue := indirect(indirectScopeValue.Index(j)); equalAsString(getValueFromFields(indirectValue, relation.AssociationForeignFieldNames), foreignValues) {
					indirectValue.FieldByName(field.Name).Set(result)
					break
				}
			}
		} else {
			scope.Err(field.Set(result))
		}
	}
}

// handleHasManyPreload used to preload has many associations
func (scope *Scope) handleHasManyPreload(field *Field, conditions []interface{}) {
	relation := field.Relationship

	// get relations's primary keys
	primaryKeys := scope.getColumnAsArray(relation.AssociationForeignFieldNames, scope.Value)
	if len(primaryKeys) == 0 {
		return
	}

	// find relations
	results := makeSlice(field.Struct.Type)
	scope.Err(scope.NewDB().Where(fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relation.ForeignDBNames), toQueryMarks(primaryKeys)), toQueryValues(primaryKeys)...).Find(results, conditions...).Error)

	// assign find results
	var (
		resultsValue       = indirect(reflect.ValueOf(results))
		indirectScopeValue = scope.IndirectValue()
	)

	if indirectScopeValue.Kind() == reflect.Slice {
		for i := 0; i < resultsValue.Len(); i++ {
			result := resultsValue.Index(i)
			foreignValues := getValueFromFields(result, relation.ForeignFieldNames)
			for j := 0; j < indirectScopeValue.Len(); j++ {
				object := indirect(indirectScopeValue.Index(j))
				if equalAsString(getValueFromFields(object, relation.AssociationForeignFieldNames), foreignValues) {
					objectField := object.FieldByName(field.Name)
					objectField.Set(reflect.Append(objectField, result))
					break
				}
			}
		}
	} else {
		scope.Err(field.Set(resultsValue))
	}
}

// handleBelongsToPreload used to preload belongs to associations
func (scope *Scope) handleBelongsToPreload(field *Field, conditions []interface{}) {
	relation := field.Relationship

	// get relations's primary keys
	primaryKeys := scope.getColumnAsArray(relation.ForeignFieldNames, scope.Value)
	if len(primaryKeys) == 0 {
		return
	}

	// find relations
	results := makeSlice(field.Struct.Type)
	scope.Err(scope.NewDB().Where(fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relation.AssociationForeignDBNames), toQueryMarks(primaryKeys)), toQueryValues(primaryKeys)...).Find(results, conditions...).Error)

	// assign find results
	var (
		resultsValue       = indirect(reflect.ValueOf(results))
		indirectScopeValue = scope.IndirectValue()
	)

	for i := 0; i < resultsValue.Len(); i++ {
		result := resultsValue.Index(i)
		if indirectScopeValue.Kind() == reflect.Slice {
			value := getValueFromFields(result, relation.AssociationForeignFieldNames)
			for j := 0; j < indirectScopeValue.Len(); j++ {
				object := indirect(indirectScopeValue.Index(j))
				if equalAsString(getValueFromFields(object, relation.ForeignFieldNames), value) {
					object.FieldByName(field.Name).Set(result)
				}
			}
		} else {
			scope.Err(field.Set(result))
		}
	}
}

// handleManyToManyPreload used to preload many to many associations
func (scope *Scope) handleManyToManyPreload(field *Field, conditions []interface{}) {
	var (
		relation         = field.Relationship
		joinTableHandler = relation.JoinTableHandler
		fieldType        = field.StructField.Struct.Type.Elem()
		foreignKeyValue  interface{}
		foreignKeyType   = reflect.ValueOf(&foreignKeyValue).Type()
		linkHash         = map[string][]reflect.Value{}
		isPtr            bool
	)

	if fieldType.Kind() == reflect.Ptr {
		isPtr = true
		fieldType = fieldType.Elem()
	}

	var sourceKeys = []string{}
	for _, key := range joinTableHandler.SourceForeignKeys() {
		sourceKeys = append(sourceKeys, key.DBName)
	}

	// generate query with join table
	preloadJoinDB := scope.NewDB().Table(scope.New(reflect.New(fieldType).Interface()).TableName()).Select("*")
	preloadJoinDB = joinTableHandler.JoinWith(joinTableHandler, preloadJoinDB, scope.Value)

	// preload inline conditions
	if len(conditions) > 0 {
		preloadJoinDB = preloadJoinDB.Where(conditions[0], conditions[1:]...)
	}

	rows, err := preloadJoinDB.Rows()

	if scope.Err(err) != nil {
		return
	}
	defer rows.Close()

	columns, _ := rows.Columns()
	for rows.Next() {
		var (
			elem   = reflect.New(fieldType).Elem()
			fields = scope.New(elem.Addr().Interface()).Fields()
		)

		// register foreign keys in join tables
		for _, sourceKey := range sourceKeys {
			fields[sourceKey] = &Field{Field: reflect.New(foreignKeyType).Elem()}
		}

		scope.scan(rows, columns, fields)

		// generate hashed forkey keys in join table
		var foreignKeys = make([]interface{}, len(sourceKeys))
		for idx, sourceKey := range sourceKeys {
			foreignKeys[idx] = fields[sourceKey].Field.Elem().Interface()
		}
		hashedSourceKeys := toString(foreignKeys)

		if isPtr {
			linkHash[hashedSourceKeys] = append(linkHash[hashedSourceKeys], elem.Addr())
		} else {
			linkHash[hashedSourceKeys] = append(linkHash[hashedSourceKeys], elem)
		}
	}

	// assign find results
	var (
		indirectScopeValue = scope.IndirectValue()
		fieldsSourceMap    = map[string]reflect.Value{}
		foreignFieldNames  = []string{}
		fields             = scope.Fields()
	)

	for _, dbName := range relation.ForeignFieldNames {
		if field, ok := fields[dbName]; ok {
			foreignFieldNames = append(foreignFieldNames, field.Name)
		}
	}

	if indirectScopeValue.Kind() == reflect.Slice {
		for j := 0; j < indirectScopeValue.Len(); j++ {
			object := indirect(indirectScopeValue.Index(j))
			fieldsSourceMap[toString(getValueFromFields(object, foreignFieldNames))] = object.FieldByName(field.Name)
		}
	} else if indirectScopeValue.IsValid() {
		fieldsSourceMap[toString(getValueFromFields(indirectScopeValue, foreignFieldNames))] = indirectScopeValue.FieldByName(field.Name)
	}

	for source, link := range linkHash {
		fieldsSourceMap[source].Set(reflect.Append(fieldsSourceMap[source], link...))
	}
}
<commit_msg>Order results when preload many2many relations<commit_after>package gorm

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
)

// preloadCallback used to preload associations
func preloadCallback(scope *Scope) {
	if scope.Search.preload == nil || scope.HasError() {
		return
	}

	var (
		preloadedMap = map[string]bool{}
		fields       = scope.Fields()
	)

	for _, preload := range scope.Search.preload {
		var (
			preloadFields = strings.Split(preload.schema, ".")
			currentScope  = scope
			currentFields = fields
		)

		for idx, preloadField := range preloadFields {
			var currentPreloadConditions []interface{}

			// if not preloaded
			if preloadKey := strings.Join(preloadFields[:idx+1], "."); !preloadedMap[preloadKey] {

				// assign search conditions to last preload
				if idx == len(preloadFields)-1 {
					currentPreloadConditions = preload.conditions
				}

				for _, field := range currentFields {
					if field.Name != preloadField || field.Relationship == nil {
						continue
					}

					switch field.Relationship.Kind {
					case "has_one":
						currentScope.handleHasOnePreload(field, currentPreloadConditions)
					case "has_many":
						currentScope.handleHasManyPreload(field, currentPreloadConditions)
					case "belongs_to":
						currentScope.handleBelongsToPreload(field, currentPreloadConditions)
					case "many_to_many":
						currentScope.handleManyToManyPreload(field, currentPreloadConditions)
					default:
						scope.Err(errors.New("unsupported relation"))
					}

					preloadedMap[preloadKey] = true
					break
				}

				if !preloadedMap[preloadKey] {
					scope.Err(fmt.Errorf("can't preload field %s for %s", preloadField, currentScope.GetModelStruct().ModelType))
					return
				}
			}

			// preload next level
			if idx < len(preloadFields)-1 {
				currentScope = currentScope.getColumnAsScope(preloadField)
				currentFields = currentScope.Fields()
			}
		}
	}
}

// handleHasOnePreload used to preload has one associations
func (scope *Scope) handleHasOnePreload(field *Field, conditions []interface{}) {
	relation := field.Relationship

	// get relations's primary keys
	primaryKeys := scope.getColumnAsArray(relation.AssociationForeignFieldNames, scope.Value)
	if len(primaryKeys) == 0 {
		return
	}

	// find relations
	results := makeSlice(field.Struct.Type)
	scope.Err(scope.NewDB().Where(fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relation.ForeignDBNames), toQueryMarks(primaryKeys)), toQueryValues(primaryKeys)...).Find(results, conditions...).Error)

	// assign find results
	var (
		resultsValue       = indirect(reflect.ValueOf(results))
		indirectScopeValue = scope.IndirectValue()
	)

	for i := 0; i < resultsValue.Len(); i++ {
		result := resultsValue.Index(i)
		if indirectScopeValue.Kind() == reflect.Slice {
			foreignValues := getValueFromFields(result, relation.ForeignFieldNames)
			for j := 0; j < indirectScopeValue.Len(); j++ {
				if indirectValue := indirect(indirectScopeValue.Index(j)); equalAsString(getValueFromFields(indirectValue, relation.AssociationForeignFieldNames), foreignValues) {
					indirectValue.FieldByName(field.Name).Set(result)
					break
				}
			}
		} else {
			scope.Err(field.Set(result))
		}
	}
}

// handleHasManyPreload used to preload has many associations
func (scope *Scope) handleHasManyPreload(field *Field, conditions []interface{}) {
	relation := field.Relationship

	// get relations's primary keys
	primaryKeys := scope.getColumnAsArray(relation.AssociationForeignFieldNames, scope.Value)
	if len(primaryKeys) == 0 {
		return
	}

	// find relations
	results := makeSlice(field.Struct.Type)
	scope.Err(scope.NewDB().Where(fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relation.ForeignDBNames), toQueryMarks(primaryKeys)), toQueryValues(primaryKeys)...).Find(results, conditions...).Error)

	// assign find results
	var (
		resultsValue       = indirect(reflect.ValueOf(results))
		indirectScopeValue = scope.IndirectValue()
	)

	if indirectScopeValue.Kind() == reflect.Slice {
		for i := 0; i < resultsValue.Len(); i++ {
			result := resultsValue.Index(i)
			foreignValues := getValueFromFields(result, relation.ForeignFieldNames)
			for j := 0; j < indirectScopeValue.Len(); j++ {
				object := indirect(indirectScopeValue.Index(j))
				if equalAsString(getValueFromFields(object, relation.AssociationForeignFieldNames), foreignValues) {
					objectField := object.FieldByName(field.Name)
					objectField.Set(reflect.Append(objectField, result))
					break
				}
			}
		}
	} else {
		scope.Err(field.Set(resultsValue))
	}
}

// handleBelongsToPreload used to preload belongs to associations
func (scope *Scope) handleBelongsToPreload(field *Field, conditions []interface{}) {
	relation := field.Relationship

	// get relations's primary keys
	primaryKeys := scope.getColumnAsArray(relation.ForeignFieldNames, scope.Value)
	if len(primaryKeys) == 0 {
		return
	}

	// find relations
	results := makeSlice(field.Struct.Type)
	scope.Err(scope.NewDB().Where(fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relation.AssociationForeignDBNames), toQueryMarks(primaryKeys)), toQueryValues(primaryKeys)...).Find(results, conditions...).Error)

	// assign find results
	var (
		resultsValue       = indirect(reflect.ValueOf(results))
		indirectScopeValue = scope.IndirectValue()
	)

	for i := 0; i < resultsValue.Len(); i++ {
		result := resultsValue.Index(i)
		if indirectScopeValue.Kind() == reflect.Slice {
			value := getValueFromFields(result, relation.AssociationForeignFieldNames)
			for j := 0; j < indirectScopeValue.Len(); j++ {
				object := indirect(indirectScopeValue.Index(j))
				if equalAsString(getValueFromFields(object, relation.ForeignFieldNames), value) {
					object.FieldByName(field.Name).Set(result)
				}
			}
		} else {
			scope.Err(field.Set(result))
		}
	}
}

// handleManyToManyPreload used to preload many to many associations
func (scope *Scope) handleManyToManyPreload(field *Field, conditions []interface{}) {
	var (
		relation         = field.Relationship
		joinTableHandler = relation.JoinTableHandler
		fieldType        = field.Struct.Type.Elem()
		foreignKeyValue  interface{}
		foreignKeyType   = reflect.ValueOf(&foreignKeyValue).Type()
		linkHash         = map[string][]reflect.Value{}
		isPtr            bool
	)

	if fieldType.Kind() == reflect.Ptr {
		isPtr = true
		fieldType = fieldType.Elem()
	}

	var sourceKeys = []string{}
	for _, key := range joinTableHandler.SourceForeignKeys() {
		sourceKeys = append(sourceKeys, key.DBName)
	}

	// generate query with join table
	newScope := scope.New(reflect.New(fieldType).Interface())
	preloadJoinDB := scope.NewDB().Table(newScope.TableName()).Select("*")
	preloadJoinDB = joinTableHandler.JoinWith(joinTableHandler, preloadJoinDB, scope.Value)

	if primaryField := newScope.PrimaryField(); primaryField != nil {
		preloadJoinDB = preloadJoinDB.Order(fmt.Sprintf("%v.%v %v", newScope.QuotedTableName(), newScope.Quote(primaryField.DBName), "ASC"))
	}

	// preload inline conditions
	if len(conditions) > 0 {
		preloadJoinDB = preloadJoinDB.Where(conditions[0], conditions[1:]...)
	}

	rows, err := preloadJoinDB.Rows()

	if scope.Err(err) != nil {
		return
	}
	defer rows.Close()

	columns, _ := rows.Columns()
	for rows.Next() {
		var (
			elem   = reflect.New(fieldType).Elem()
			fields = scope.New(elem.Addr().Interface()).Fields()
		)

		// register foreign keys in join tables
		for _, sourceKey := range sourceKeys {
			fields[sourceKey] = &Field{Field: reflect.New(foreignKeyType).Elem()}
		}

		scope.scan(rows, columns, fields)

		// generate hashed forkey keys in join table
		var foreignKeys = make([]interface{}, len(sourceKeys))
		for idx, sourceKey := range sourceKeys {
			foreignKeys[idx] = fields[sourceKey].Field.Elem().Interface()
		}
		hashedSourceKeys := toString(foreignKeys)

		if isPtr {
			linkHash[hashedSourceKeys] = append(linkHash[hashedSourceKeys], elem.Addr())
		} else {
			linkHash[hashedSourceKeys] = append(linkHash[hashedSourceKeys], elem)
		}
	}

	// assign find results
	var (
		indirectScopeValue = scope.IndirectValue()
		fieldsSourceMap    = map[string]reflect.Value{}
		foreignFieldNames  = []string{}
		fields             = scope.Fields()
	)

	for _, dbName := range relation.ForeignFieldNames {
		if field, ok := fields[dbName]; ok {
			foreignFieldNames = append(foreignFieldNames, field.Name)
		}
	}

	if indirectScopeValue.Kind() == reflect.Slice {
		for j := 0; j < indirectScopeValue.Len(); j++ {
			object := indirect(indirectScopeValue.Index(j))
			fieldsSourceMap[toString(getValueFromFields(object, foreignFieldNames))] = object.FieldByName(field.Name)
		}
	} else if indirectScopeValue.IsValid() {
		fieldsSourceMap[toString(getValueFromFields(indirectScopeValue, foreignFieldNames))] = indirectScopeValue.FieldByName(field.Name)
	}

	for source, link := range linkHash {
		fieldsSourceMap[source].Set(reflect.Append(fieldsSourceMap[source], link...))
	}
}
<|endoftext|>


<commit_before>//
// Rudimentary program for examining Android APK files. An APK file
// is basically a ZIP file that contains an Android manifest and a series
// of DEX files, strings, resources, bitmaps, and assorted other items.
// This specific reader looks only at the DEX files, not the other
// bits and pieces.
//
package main

import (
	"flag"
	"fmt"
	"log"
	"math/rand"
	"os"

	"github.com/thanm/cabi-testgen/generator"
)

var verbflag = flag.Int("v", 0, "Verbose trace output level")
var numitflag = flag.Int("n", 1000, "Number of tests to generate")
var seedflag = flag.Int64("s", 10101, "Random seed")
var tagflag = flag.String("t", "gen", "Prefix name of go files/pkgs to generate")
var outdirflag = flag.String("o", ".", "Output directory for generated files")
var pkgpathflag = flag.String("p", "", "Base package path for generated files")

func verb(vlevel int, s string, a ...interface{}) {
	if *verbflag >= vlevel {
		fmt.Printf(s, a...)
		fmt.Printf("\n")
	}
}

func usage(msg string) {
	if len(msg) > 0 {
		fmt.Fprintf(os.Stderr, "error: %s\n", msg)
	}
	fmt.Fprintf(os.Stderr, "usage: apkread [flags] <APK file>\n")
	flag.PrintDefaults()
	os.Exit(2)
}

func openOutputFile(filename string, pk string, imports []string, ipref string) *os.File {
	verb(1, "opening %s", filename)
	outf, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
	if err != nil {
		log.Fatal(err)
	}
	outf.WriteString(fmt.Sprintf("package %s\n\n", pk))
	for _, imp := range imports {
		outf.WriteString(fmt.Sprintf("import . \"%s%s\"\n", ipref, imp))
	}
	outf.WriteString("\n")
	return outf
}

func emitUtils(outf *os.File) {
	fmt.Fprintf(outf, "import \"fmt\"\n")
	fmt.Fprintf(outf, "import \"os\"\n\n")
	fmt.Fprintf(outf, "func NoteFailure(fidx int, pref string, parmNo int) {\n")
	fmt.Fprintf(outf, "  fmt.Fprintf(os.Stderr, ")
	fmt.Fprintf(outf, "\"Error: fail on func %%d %%s %%d\\n\", fidx, pref, parmNo)\n")
	fmt.Fprintf(outf, "}\n\n")
}

func emitMain(outf *os.File, numit int) {
	fmt.Fprintf(outf, "import \"fmt\"\n")
	fmt.Fprintf(outf, "import \"os\"\n\n")
	fmt.Fprintf(outf, "func main() {\n")
	fmt.Fprintf(outf, "  fmt.Fprintf(os.Stderr, \"starting main\\n\")\n")
	for i := 0; i < *numitflag; i++ {
		fmt.Fprintf(outf, "  Caller%d()\n", i)
	}
	fmt.Fprintf(outf, "  fmt.Fprintf(os.Stderr, \"finished %d tests\\n\")\n", numit)
	fmt.Fprintf(outf, "}\n")
}

func main() {
	log.SetFlags(0)
	log.SetPrefix("cabi-testgen: ")
	flag.Parse()
	generator.Verbctl = *verbflag
	rand.Seed(*seedflag)
	verb(1, "in main verblevel=%d", *verbflag)
	verb(1, "seed is %d", *seedflag)
	if flag.NArg() != 0 {
		usage("unknown extra arguments")
	}
	verb(1, "tag is %s", *tagflag)

	var ipref string
	if len(*pkgpathflag) > 0 {
		ipref = *pkgpathflag + "/"
	}

	callerpkg := *tagflag + "Caller"
	checkerpkg := *tagflag + "Checker"
	utilspkg := *tagflag + "Utils"
	mainpkg := *tagflag + "Main"

	os.Mkdir(*outdirflag+"/"+callerpkg, 0777)
	os.Mkdir(*outdirflag+"/"+checkerpkg, 0777)
	os.Mkdir(*outdirflag+"/"+utilspkg, 0777)

	callerfile := *outdirflag + "/" + callerpkg + "/" + callerpkg + ".go"
	checkerfile := *outdirflag + "/" + checkerpkg + "/" + checkerpkg + ".go"
	utilsfile := *outdirflag + "/" + utilspkg + "/" + utilspkg + ".go"
	mainfile := *outdirflag + "/" + mainpkg + ".go"

	calleroutfile := openOutputFile(callerfile, callerpkg,
		[]string{checkerpkg, utilspkg}, ipref)
	checkeroutfile := openOutputFile(checkerfile, checkerpkg,
		[]string{utilspkg}, ipref)
	utilsoutfile := openOutputFile(utilsfile, utilspkg, []string{}, "")
	mainoutfile := openOutputFile(mainfile, "main", []string{callerpkg}, ipref)

	emitUtils(utilsoutfile)
	emitMain(mainoutfile, *numitflag)
	for i := 0; i < *numitflag; i++ {
		generator.Generate(calleroutfile, checkeroutfile, i)
	}

	utilsoutfile.Close()
	calleroutfile.Close()
	checkeroutfile.Close()
	mainoutfile.Close()

	verb(1, "leaving main")
}
<commit_msg>remove stale comment<commit_after>// Program to generate test files for C ABI testing (insure that the
// compiler is putting things in registers or memory and/or casting
// as appropriate).

package main

import (
	"flag"
	"fmt"
	"log"
	"math/rand"
	"os"

	"github.com/thanm/cabi-testgen/generator"
)

var verbflag = flag.Int("v", 0, "Verbose trace output level")
var numitflag = flag.Int("n", 1000, "Number of tests to generate")
var seedflag = flag.Int64("s", 10101, "Random seed")
var tagflag = flag.String("t", "gen", "Prefix name of go files/pkgs to generate")
var outdirflag = flag.String("o", ".", "Output directory for generated files")
var pkgpathflag = flag.String("p", "", "Base package path for generated files")

func verb(vlevel int, s string, a ...interface{}) {
	if *verbflag >= vlevel {
		fmt.Printf(s, a...)
		fmt.Printf("\n")
	}
}

func usage(msg string) {
	if len(msg) > 0 {
		fmt.Fprintf(os.Stderr, "error: %s\n", msg)
	}
	fmt.Fprintf(os.Stderr, "usage: apkread [flags] <APK file>\n")
	flag.PrintDefaults()
	os.Exit(2)
}

func openOutputFile(filename string, pk string, imports []string, ipref string) *os.File {
	verb(1, "opening %s", filename)
	outf, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
	if err != nil {
		log.Fatal(err)
	}
	outf.WriteString(fmt.Sprintf("package %s\n\n", pk))
	for _, imp := range imports {
		outf.WriteString(fmt.Sprintf("import . \"%s%s\"\n", ipref, imp))
	}
	outf.WriteString("\n")
	return outf
}

func emitUtils(outf *os.File) {
	fmt.Fprintf(outf, "import \"fmt\"\n")
	fmt.Fprintf(outf, "import \"os\"\n\n")
	fmt.Fprintf(outf, "func NoteFailure(fidx int, pref string, parmNo int) {\n")
	fmt.Fprintf(outf, "  fmt.Fprintf(os.Stderr, ")
	fmt.Fprintf(outf, "\"Error: fail on func %%d %%s %%d\\n\", fidx, pref, parmNo)\n")
	fmt.Fprintf(outf, "}\n\n")
}

func emitMain(outf *os.File, numit int) {
	fmt.Fprintf(outf, "import \"fmt\"\n")
	fmt.Fprintf(outf, "import \"os\"\n\n")
	fmt.Fprintf(outf, "func main() {\n")
	fmt.Fprintf(outf, "  fmt.Fprintf(os.Stderr, \"starting main\\n\")\n")
	for i := 0; i < *numitflag; i++ {
		fmt.Fprintf(outf, "  Caller%d()\n", i)
	}
	fmt.Fprintf(outf, "  fmt.Fprintf(os.Stderr, \"finished %d tests\\n\")\n", numit)
	fmt.Fprintf(outf, "}\n")
}

func main() {
	log.SetFlags(0)
	log.SetPrefix("cabi-testgen: ")
	flag.Parse()
	generator.Verbctl = *verbflag
	rand.Seed(*seedflag)
	verb(1, "in main verblevel=%d", *verbflag)
	verb(1, "seed is %d", *seedflag)
	if flag.NArg() != 0 {
		usage("unknown extra arguments")
	}
	verb(1, "tag is %s", *tagflag)

	var ipref string
	if len(*pkgpathflag) > 0 {
		ipref = *pkgpathflag + "/"
	}

	callerpkg := *tagflag + "Caller"
	checkerpkg := *tagflag + "Checker"
	utilspkg := *tagflag + "Utils"
	mainpkg := *tagflag + "Main"

	os.Mkdir(*outdirflag+"/"+callerpkg, 0777)
	os.Mkdir(*outdirflag+"/"+checkerpkg, 0777)
	os.Mkdir(*outdirflag+"/"+utilspkg, 0777)

	callerfile := *outdirflag + "/" + callerpkg + "/" + callerpkg + ".go"
	checkerfile := *outdirflag + "/" + checkerpkg + "/" + checkerpkg + ".go"
	utilsfile := *outdirflag + "/" + utilspkg + "/" + utilspkg + ".go"
	mainfile := *outdirflag + "/" + mainpkg + ".go"

	calleroutfile := openOutputFile(callerfile, callerpkg,
		[]string{checkerpkg, utilspkg}, ipref)
	checkeroutfile := openOutputFile(checkerfile, checkerpkg,
		[]string{utilspkg}, ipref)
	utilsoutfile := openOutputFile(utilsfile, utilspkg, []string{}, "")
	mainoutfile := openOutputFile(mainfile, "main", []string{callerpkg}, ipref)

	emitUtils(utilsoutfile)
	emitMain(mainoutfile, *numitflag)
	for i := 0; i < *numitflag; i++ {
		generator.Generate(calleroutfile, checkeroutfile, i)
	}

	utilsoutfile.Close()
	calleroutfile.Close()
	checkeroutfile.Close()
	mainoutfile.Close()

	verb(1, "leaving main")
}
<|endoftext|>


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

// +build !js

package oto

// #cgo LDFLAGS: -framework AudioToolbox
//
// #import <AudioToolbox/AudioToolbox.h>
//
// void oto_render(void* inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer);
import "C"

import (
	"fmt"
	"runtime"
	"sync"
	"time"
	"unsafe"
)

const queueBufferSize = 4096

type driver struct {
	audioQueue      C.AudioQueueRef
	buf             []byte
	bufSize         int
	sampleRate      int
	channelNum      int
	bitDepthInBytes int
	buffers         []C.AudioQueueBufferRef

	err error

	chWrite   chan []byte
	chWritten chan int
}

var (
	theDriver *driver
	driverM   sync.Mutex
)

func setDriver(d *driver) {
	driverM.Lock()
	defer driverM.Unlock()

	if theDriver != nil && d != nil {
		panic("oto: at most one driver object can exist")
	}
	theDriver = d

	setNotificationHandler(d)
}

func getDriver() *driver {
	driverM.Lock()
	defer driverM.Unlock()

	return theDriver
}

// TOOD: Convert the error code correctly.
// See https://stackoverflow.com/questions/2196869/how-do-you-convert-an-iphone-osstatus-code-to-something-useful

func newDriver(sampleRate, channelNum, bitDepthInBytes, bufferSizeInBytes int) (tryWriteCloser, error) {
	flags := C.kAudioFormatFlagIsPacked
	if bitDepthInBytes != 1 {
		flags |= C.kAudioFormatFlagIsSignedInteger
	}
	desc := C.AudioStreamBasicDescription{
		mSampleRate:       C.double(sampleRate),
		mFormatID:         C.kAudioFormatLinearPCM,
		mFormatFlags:      C.UInt32(flags),
		mBytesPerPacket:   C.UInt32(channelNum * bitDepthInBytes),
		mFramesPerPacket:  1,
		mBytesPerFrame:    C.UInt32(channelNum * bitDepthInBytes),
		mChannelsPerFrame: C.UInt32(channelNum),
		mBitsPerChannel:   C.UInt32(8 * bitDepthInBytes),
	}
	var audioQueue C.AudioQueueRef
	if osstatus := C.AudioQueueNewOutput(
		&desc,
		(C.AudioQueueOutputCallback)(C.oto_render),
		nil,
		(C.CFRunLoopRef)(0),
		(C.CFStringRef)(0),
		0,
		&audioQueue); osstatus != C.noErr {
		return nil, fmt.Errorf("oto: AudioQueueNewFormat with StreamFormat failed: %d", osstatus)
	}

	nbuf := bufferSizeInBytes / queueBufferSize
	if nbuf <= 1 {
		nbuf = 2
	}

	d := &driver{
		audioQueue:      audioQueue,
		sampleRate:      sampleRate,
		channelNum:      channelNum,
		bitDepthInBytes: bitDepthInBytes,
		bufSize:         nbuf * queueBufferSize,
		buffers:         make([]C.AudioQueueBufferRef, nbuf),
		chWrite:         make(chan []byte),
		chWritten:       make(chan int),
	}
	runtime.SetFinalizer(d, (*driver).Close)
	// Set the driver before setting the rendering callback.
	setDriver(d)

	for i := 0; i < len(d.buffers); i++ {
		if osstatus := C.AudioQueueAllocateBuffer(audioQueue, C.UInt32(queueBufferSize), &d.buffers[i]); osstatus != C.noErr {
			return nil, fmt.Errorf("oto: AudioQueueAllocateBuffer failed: %d", osstatus)
		}
		d.buffers[i].mAudioDataByteSize = C.UInt32(queueBufferSize)
		for j := 0; j < queueBufferSize; j++ {
			*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(d.buffers[i].mAudioData)) + uintptr(j))) = 0
		}
		if osstatus := C.AudioQueueEnqueueBuffer(audioQueue, d.buffers[i], 0, nil); osstatus != C.noErr {
			return nil, fmt.Errorf("oto: AudioQueueEnqueueBuffer failed: %d", osstatus)
		}
	}

	if osstatus := C.AudioQueueStart(audioQueue, nil); osstatus != C.noErr {
		return nil, fmt.Errorf("oto: AudioQueueStart failed: %d", osstatus)
	}

	return d, nil
}

//export oto_render
func oto_render(inUserData unsafe.Pointer, inAQ C.AudioQueueRef, inBuffer C.AudioQueueBufferRef) {
	d := getDriver()

	var buf []byte
loop:
	for len(buf) < queueBufferSize {
		// Set the timer. When the application is in background or being switched, the driver's buffer is not
		// updated and it is needed to fill the buffer with zeros.
		s := time.Second * queueBufferSize / time.Duration(d.sampleRate*d.channelNum*d.bitDepthInBytes)
		t := time.NewTicker(s)
		defer t.Stop()

		select {
		case dbuf := <-d.chWrite:
			n := queueBufferSize - len(buf)
			if n > len(dbuf) {
				n = len(dbuf)
			}
			buf = append(buf, dbuf[:n]...)
			d.chWritten <- n
		case <-t.C:
			buf = append(buf, make([]byte, queueBufferSize-len(buf))...)
			break loop
		}
	}

	for i := 0; i < queueBufferSize; i++ {
		*(*byte)(unsafe.Pointer(uintptr(inBuffer.mAudioData) + uintptr(i))) = buf[i]
	}
	// Do not update mAudioDataByteSize, or the buffer is not used correctly any more.

	if osstatus := C.AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nil); osstatus != C.noErr {
		if d.err != nil {
			d.err = fmt.Errorf("oto: AudioQueueEnqueueBuffer at oto_render failed: %d", d.err)
		}
	}
}

func (d *driver) TryWrite(data []byte) (int, error) {
	if d.err != nil {
		return 0, d.err
	}

	n := d.bufSize - len(d.buf)
	if n > len(data) {
		n = len(data)
	}
	d.buf = append(d.buf, data[:n]...)
	// Use the buffer only when the buffer length is enough to avoid choppy sound.
	for len(d.buf) >= queueBufferSize {
		d.chWrite <- d.buf
		n := <-d.chWritten
		d.buf = d.buf[n:]
	}
	return n, nil
}

func (d *driver) Close() error {
	runtime.SetFinalizer(d, nil)

	for _, b := range d.buffers {
		if osstatus := C.AudioQueueFreeBuffer(d.audioQueue, b); osstatus != C.noErr {
			return fmt.Errorf("oto: AudioQueueFreeBuffer failed: %d", osstatus)
		}
	}

	if osstatus := C.AudioQueueStop(d.audioQueue, C.false); osstatus != C.noErr {
		return fmt.Errorf("oto: AudioQueueStop failed: %d", osstatus)
	}
	if osstatus := C.AudioQueueDispose(d.audioQueue, C.false); osstatus != C.noErr {
		return fmt.Errorf("oto: AudioQueueDispose failed: %d", osstatus)
	}
	d.audioQueue = nil
	setDriver(nil)
	return nil
}
<commit_msg>darwin: Adjust queueBufferSize for channels and bit depth in bytes<commit_after>// Copyright 2019 The Oto Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !js

package oto

// #cgo LDFLAGS: -framework AudioToolbox
//
// #import <AudioToolbox/AudioToolbox.h>
//
// void oto_render(void* inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer);
import "C"

import (
	"fmt"
	"runtime"
	"sync"
	"time"
	"unsafe"
)

const baseQueueBufferSize = 1024

type audioInfo struct {
	channelNum      int
	bitDepthInBytes int
}

type driver struct {
	audioQueue C.AudioQueueRef
	buf        []byte
	bufSize    int
	sampleRate int
	audioInfo  *audioInfo
	buffers    []C.AudioQueueBufferRef

	err error

	chWrite   chan []byte
	chWritten chan int
}

var (
	theDriver *driver
	driverM   sync.Mutex
)

func setDriver(d *driver) {
	driverM.Lock()
	defer driverM.Unlock()

	if theDriver != nil && d != nil {
		panic("oto: at most one driver object can exist")
	}
	theDriver = d

	setNotificationHandler(d)
}

func getDriver() *driver {
	driverM.Lock()
	defer driverM.Unlock()

	return theDriver
}

// TOOD: Convert the error code correctly.
// See https://stackoverflow.com/questions/2196869/how-do-you-convert-an-iphone-osstatus-code-to-something-useful

func newDriver(sampleRate, channelNum, bitDepthInBytes, bufferSizeInBytes int) (tryWriteCloser, error) {
	flags := C.kAudioFormatFlagIsPacked
	if bitDepthInBytes != 1 {
		flags |= C.kAudioFormatFlagIsSignedInteger
	}
	desc := C.AudioStreamBasicDescription{
		mSampleRate:       C.double(sampleRate),
		mFormatID:         C.kAudioFormatLinearPCM,
		mFormatFlags:      C.UInt32(flags),
		mBytesPerPacket:   C.UInt32(channelNum * bitDepthInBytes),
		mFramesPerPacket:  1,
		mBytesPerFrame:    C.UInt32(channelNum * bitDepthInBytes),
		mChannelsPerFrame: C.UInt32(channelNum),
		mBitsPerChannel:   C.UInt32(8 * bitDepthInBytes),
	}

	audioInfo := &audioInfo{
		channelNum:      channelNum,
		bitDepthInBytes: bitDepthInBytes,
	}

	var audioQueue C.AudioQueueRef
	if osstatus := C.AudioQueueNewOutput(
		&desc,
		(C.AudioQueueOutputCallback)(C.oto_render),
		unsafe.Pointer(audioInfo),
		(C.CFRunLoopRef)(0),
		(C.CFStringRef)(0),
		0,
		&audioQueue); osstatus != C.noErr {
		return nil, fmt.Errorf("oto: AudioQueueNewFormat with StreamFormat failed: %d", osstatus)
	}

	queueBufferSize := baseQueueBufferSize * channelNum * bitDepthInBytes
	nbuf := bufferSizeInBytes / queueBufferSize
	if nbuf <= 1 {
		nbuf = 2
	}

	d := &driver{
		audioQueue: audioQueue,
		sampleRate: sampleRate,
		audioInfo:  audioInfo,
		bufSize:    nbuf * queueBufferSize,
		buffers:    make([]C.AudioQueueBufferRef, nbuf),
		chWrite:    make(chan []byte),
		chWritten:  make(chan int),
	}
	runtime.SetFinalizer(d, (*driver).Close)
	// Set the driver before setting the rendering callback.
	setDriver(d)

	for i := 0; i < len(d.buffers); i++ {
		if osstatus := C.AudioQueueAllocateBuffer(audioQueue, C.UInt32(queueBufferSize), &d.buffers[i]); osstatus != C.noErr {
			return nil, fmt.Errorf("oto: AudioQueueAllocateBuffer failed: %d", osstatus)
		}
		d.buffers[i].mAudioDataByteSize = C.UInt32(queueBufferSize)
		for j := 0; j < queueBufferSize; j++ {
			*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(d.buffers[i].mAudioData)) + uintptr(j))) = 0
		}
		if osstatus := C.AudioQueueEnqueueBuffer(audioQueue, d.buffers[i], 0, nil); osstatus != C.noErr {
			return nil, fmt.Errorf("oto: AudioQueueEnqueueBuffer failed: %d", osstatus)
		}
	}

	if osstatus := C.AudioQueueStart(audioQueue, nil); osstatus != C.noErr {
		return nil, fmt.Errorf("oto: AudioQueueStart failed: %d", osstatus)
	}

	return d, nil
}

//export oto_render
func oto_render(inUserData unsafe.Pointer, inAQ C.AudioQueueRef, inBuffer C.AudioQueueBufferRef) {
	audioInfo := (*audioInfo)(inUserData)
	queueBufferSize := baseQueueBufferSize * audioInfo.channelNum * audioInfo.bitDepthInBytes

	d := getDriver()

	var buf []byte
loop:
	for len(buf) < queueBufferSize {
		// Set the timer. When the application is in background or being switched, the driver's buffer is not
		// updated and it is needed to fill the buffer with zeros.
		s := time.Second * time.Duration(queueBufferSize) / time.Duration(d.sampleRate*d.audioInfo.channelNum*d.audioInfo.bitDepthInBytes)
		t := time.NewTicker(s)
		defer t.Stop()

		select {
		case dbuf := <-d.chWrite:
			n := queueBufferSize - len(buf)
			if n > len(dbuf) {
				n = len(dbuf)
			}
			buf = append(buf, dbuf[:n]...)
			d.chWritten <- n
		case <-t.C:
			buf = append(buf, make([]byte, queueBufferSize-len(buf))...)
			break loop
		}
	}

	for i := 0; i < queueBufferSize; i++ {
		*(*byte)(unsafe.Pointer(uintptr(inBuffer.mAudioData) + uintptr(i))) = buf[i]
	}
	// Do not update mAudioDataByteSize, or the buffer is not used correctly any more.

	if osstatus := C.AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nil); osstatus != C.noErr {
		if d.err != nil {
			d.err = fmt.Errorf("oto: AudioQueueEnqueueBuffer at oto_render failed: %d", d.err)
		}
	}
}

func (d *driver) TryWrite(data []byte) (int, error) {
	if d.err != nil {
		return 0, d.err
	}

	n := d.bufSize - len(d.buf)
	if n > len(data) {
		n = len(data)
	}
	d.buf = append(d.buf, data[:n]...)
	// Use the buffer only when the buffer length is enough to avoid choppy sound.
	queueBufferSize := baseQueueBufferSize * d.audioInfo.channelNum * d.audioInfo.bitDepthInBytes
	for len(d.buf) >= queueBufferSize {
		d.chWrite <- d.buf
		n := <-d.chWritten
		d.buf = d.buf[n:]
	}
	return n, nil
}

func (d *driver) Close() error {
	runtime.SetFinalizer(d, nil)

	for _, b := range d.buffers {
		if osstatus := C.AudioQueueFreeBuffer(d.audioQueue, b); osstatus != C.noErr {
			return fmt.Errorf("oto: AudioQueueFreeBuffer failed: %d", osstatus)
		}
	}

	if osstatus := C.AudioQueueStop(d.audioQueue, C.false); osstatus != C.noErr {
		return fmt.Errorf("oto: AudioQueueStop failed: %d", osstatus)
	}
	if osstatus := C.AudioQueueDispose(d.audioQueue, C.false); osstatus != C.noErr {
		return fmt.Errorf("oto: AudioQueueDispose failed: %d", osstatus)
	}
	d.audioQueue = nil
	setDriver(nil)
	return nil
}
<|endoftext|>