<commit_before>package wallet

import (
	"sync"

	"github.com/btcsuite/btcd/btcec"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil"
)

// ChannelReservation...
type ChannelReservation struct {
	FundingType FundingType

	FundingAmount btcutil.Amount
	ReserveAmount btcutil.Amount
	MinFeePerKb   btcutil.Amount

	sync.RWMutex // All fields below owned by the wallet.

	theirInputs []*wire.TxIn
	ourInputs   []*wire.TxIn

	theirChange []*wire.TxOut
	ourChange   []*wire.TxOut

	ourKey   *btcec.PrivateKey
	theirKey *btcec.PublicKey

	// In order of sorted inputs. Sorting is done in accordance
	// to BIP-69: https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki.
	theirSigs [][]byte
	ourSigs   [][]byte

	normalizedTxID wire.ShaHash

	fundingTx *wire.MsgTx
	// TODO(roasbef): time locks, who pays fee etc.
	// TODO(roasbeef): record Bob's ln-ID?

	completedFundingTx *btcutil.Tx

	reservationID uint64
	wallet        *LightningWallet
}

// newChannelReservation...
func newChannelReservation(t FundingType, fundingAmt btcutil.Amount,
	minFeeRate btcutil.Amount, wallet *LightningWallet, id uint64) *ChannelReservation {
	return &ChannelReservation{
		FundingType:   t,
		FundingAmount: fundingAmt,
		MinFeePerKb:   minFeeRate,
		wallet:        wallet,
		reservationID: id,
	}
}

// OurFunds...
func (r *ChannelReservation) OurFunds() ([]*wire.TxIn, []*wire.TxOut, *btcec.PublicKey) {
	r.RLock()
	defer r.Unlock()
	return r.ourInputs, r.ourChange, r.ourKey.PubKey()
}

// AddCounterPartyFunds...
func (r *ChannelReservation) AddFunds(theirInputs []*wire.TxIn, theirChangeOutputs []*wire.TxOut, multiSigKey *btcec.PublicKey) error {
	errChan := make(chan error, 1)

	r.wallet.msgChan <- &addCounterPartyFundsMsg{
		pendingFundingID:   r.reservationID,
		theirInputs:        theirInputs,
		theirChangeOutputs: theirChangeOutputs,
		theirKey:           multiSigKey,
	}

	return <-errChan
}

// OurSigs...
func (r *ChannelReservation) OurSigs() [][]byte {
	r.RLock()
	defer r.Unlock()
	return r.ourSigs
}

// TheirFunds...
func (r *ChannelReservation) TheirFunds() ([]*wire.TxIn, []*wire.TxOut, *btcec.PublicKey) {
	r.RLock()
	defer r.Unlock()
	return r.theirInputs, r.theirChange, r.theirKey
}

// CompleteFundingReservation...
func (r *ChannelReservation) CompleteReservation(theirSigs [][]byte) error {
	errChan := make(chan error, 1)

	r.wallet.msgChan <- &addCounterPartySigsMsg{
		pendingFundingID: r.reservationID,
		theirSigs:        theirSigs,
		err:              errChan,
	}

	return <-errChan
}

// FinalFundingTransaction...
func (r *ChannelReservation) FinalFundingTx() *btcutil.Tx {
	r.RLock()
	defer r.Unlock()
	return r.completedFundingTx
}

// RequestFundingReserveCancellation...
// TODO(roasbeef): also return mutated state?
func (r *ChannelReservation) Cancel() {
	doneChan := make(chan struct{}, 1)
	r.wallet.msgChan <- &fundingReserveCancelMsg{
		pendingFundingID: r.reservationID,
		done:             doneChan,
	}

	<-doneChan
}
<commit_msg>lnwallet/reservation: s/Unlock/RUnlock, fix panic caused by double unlock<commit_after>package wallet

import (
	"sync"

	"github.com/btcsuite/btcd/btcec"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil"
)

// ChannelReservation...
type ChannelReservation struct {
	FundingType FundingType

	FundingAmount btcutil.Amount
	ReserveAmount btcutil.Amount
	MinFeePerKb   btcutil.Amount

	sync.RWMutex // All fields below owned by the lnwallet.

	theirInputs []*wire.TxIn
	ourInputs   []*wire.TxIn

	theirChange []*wire.TxOut
	ourChange   []*wire.TxOut

	ourKey   *btcec.PrivateKey
	theirKey *btcec.PublicKey

	// In order of sorted inputs. Sorting is done in accordance
	// to BIP-69: https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki.
	theirSigs [][]byte
	ourSigs   [][]byte

	normalizedTxID wire.ShaHash

	fundingTx *wire.MsgTx
	// TODO(roasbef): time locks, who pays fee etc.
	// TODO(roasbeef): record Bob's ln-ID?

	completedFundingTx *btcutil.Tx

	reservationID uint64
	wallet        *LightningWallet
}

// newChannelReservation...
func newChannelReservation(t FundingType, fundingAmt btcutil.Amount,
	minFeeRate btcutil.Amount, wallet *LightningWallet, id uint64) *ChannelReservation {
	return &ChannelReservation{
		FundingType:   t,
		FundingAmount: fundingAmt,
		MinFeePerKb:   minFeeRate,
		wallet:        wallet,
		reservationID: id,
	}
}

// OurFunds...
func (r *ChannelReservation) OurFunds() ([]*wire.TxIn, []*wire.TxOut, *btcec.PublicKey) {
	r.RLock()
	defer r.RUnlock()
	return r.ourInputs, r.ourChange, r.ourKey.PubKey()
}

// AddCounterPartyFunds...
func (r *ChannelReservation) AddFunds(theirInputs []*wire.TxIn, theirChangeOutputs []*wire.TxOut, multiSigKey *btcec.PublicKey) error {
	errChan := make(chan error, 1)

	r.wallet.msgChan <- &addCounterPartyFundsMsg{
		pendingFundingID:   r.reservationID,
		theirInputs:        theirInputs,
		theirChangeOutputs: theirChangeOutputs,
		theirKey:           multiSigKey,
	}

	return <-errChan
}

// OurSigs...
func (r *ChannelReservation) OurSigs() [][]byte {
	r.RLock()
	defer r.RUnlock()
	return r.ourSigs
}

// TheirFunds...
// TODO(roasbeef): return error if accessors not yet populated?
func (r *ChannelReservation) TheirFunds() ([]*wire.TxIn, []*wire.TxOut, *btcec.PublicKey) {
	r.RLock()
	defer r.RUnlock()
	return r.theirInputs, r.theirChange, r.theirKey
}

// CompleteFundingReservation...
func (r *ChannelReservation) CompleteReservation(theirSigs [][]byte) error {
	errChan := make(chan error, 1)

	r.wallet.msgChan <- &addCounterPartySigsMsg{
		pendingFundingID: r.reservationID,
		theirSigs:        theirSigs,
		err:              errChan,
	}

	return <-errChan
}

// FinalFundingTransaction...
func (r *ChannelReservation) FinalFundingTx() *btcutil.Tx {
	r.RLock()
	defer r.RUnlock()
	return r.completedFundingTx
}

// RequestFundingReserveCancellation...
// TODO(roasbeef): also return mutated state?
func (r *ChannelReservation) Cancel() {
	doneChan := make(chan struct{}, 1)
	r.wallet.msgChan <- &fundingReserveCancelMsg{
		pendingFundingID: r.reservationID,
		done:             doneChan,
	}

	<-doneChan
}
<|endoftext|>


<commit_before>package sourcegraph

import (
	"database/sql/driver"
	"errors"
	"fmt"
	"net/url"
	"regexp"
	"strings"
	"time"

	"sourcegraph.com/sourcegraph/go-nnz/nnz"
)

// Repo is a code repository returned by the Sourcegraph API.
type Repo struct {
	// RID is the numeric primary key for a repository.
	RID int

	// URI is a normalized identifier for this repository based on its primary
	// clone URL. E.g., "github.com/user/repo".
	URI string

	// URIAlias is another URI that, if accessed, will redirect to
	// this repository's primary URI. It's used, for example, to
	// redirect from GitHub repos to their canonical URI (such as Go
	// repos on gopkg.in).
	URIAlias nnz.String `db:"uri_alias"`

	// Name is the base name (the final path component) of the repository,
	// typically the name of the directory that the repository would be cloned
	// into. (For example, for git://example.com/foo.git, the name is "foo".)
	Name string

	// OwnerUserID is the account that owns this repository.
	OwnerUserID int `db:"owner_user_id"`

	// OwnerGitHubUserID is the GitHub user ID of this repository's owner, if this
	// is a GitHub repository.
	OwnerGitHubUserID nnz.Int `db:"owner_github_user_id" json:",omitempty"`

	// Description is a brief description of the repository.
	Description string `json:",omitempty"`

	// VCS is the short name of the VCS system that this repository uses: "git"
	// or "hg".
	VCS string `db:"vcs"`

	// HTTPCloneURL is the HTTPS clone URL of the repository (or the
	// HTTP clone URL, if no HTTPS clone URL is available).
	HTTPCloneURL string `db:"http_clone_url"`

	// SSHCloneURL is the SSH clone URL if the repository, if any.
	SSHCloneURL nnz.String `db:"ssh_clone_url"`

	// HomepageURL is the URL to the repository's homepage, if any.
	HomepageURL nnz.String `db:"homepage_url"`

	// DefaultBranch is the default VCS branch used (typically "master" for git
	// repositories and "default" for hg repositories).
	DefaultBranch string `db:"default_branch"`

	// Language is the primary programming language used in this repository.
	Language string

	// GitHubStars is the number of stargazers this repository has on GitHub (or
	// 0 if it is not a GitHub repository).
	GitHubStars int `db:"github_stars"`

	// GitHubID is the GitHub ID of this repository. If a GitHub repository is
	// renamed, the ID remains the same and should be used to resolve across the
	// name change.
	GitHubID nnz.Int `db:"github_id" json:",omitempty"`

	// Disabled is whether this repo should not be downloaded and processed by the worker.
	Disabled bool `json:",omitempty"`

	// Deprecated repositories are labeled as such and hidden from global search results.
	Deprecated bool

	// Fork is whether this repository is a fork.
	Fork bool

	// Mirror is whether this repository is a mirror.
	Mirror bool

	// Private is whether this repository is private.
	Private bool

	// CreatedAt is when this repository was created. If it represents
	// an externally hosted (e.g., GitHub) repository, the creation
	// date is when it was created at that origin.
	CreatedAt time.Time `db:"created_at"`

	// UpdatedAt is when this repository's metadata was last updated
	// (on its origin if it's an externally hosted repository).
	UpdatedAt time.Time `db:"updated_at"`

	// PushedAt is when this repository's was last (VCS-)pushed to.
	PushedAt time.Time `db:"pushed_at"`

	// Stat holds repository statistics. It's only filled in if Repo{Get,List}Options has Stats == true.
	Stat RepoStats `db:"-" json:",omitempty"`
}

// IsGitHubRepo returns true iff this repository is hosted on GitHub.
func (r *Repo) IsGitHubRepo() bool { return r.GitHubID != 0 }

// GitHubHTMLURL returns URL to the GitHub HTML page (e.g.,
// https://github.com/foo/bar, not a clone URL) for this repo, if it's
// a GitHub repo. Otherwise it returns the empty string.
func (r *Repo) GitHubHTMLURL() string {
	var ghuri string
	if IsGitHubRepoURI(r.URI) {
		ghuri = r.URI
	} else if IsGitHubRepoURI(string(r.URIAlias)) {
		ghuri = string(r.URIAlias)
	}
	if ghuri == "" {
		return ""
	}
	return (&url.URL{Scheme: "https", Host: "github.com", Path: "/" + strings.TrimPrefix(ghuri, githubRepoURIPrefix)}).String()
}

const (
	Git = "git"
	Hg  = "hg"
)

func MapByURI(repos []*Repo) map[string]*Repo {
	repoMap := make(map[string]*Repo, len(repos))
	for _, repo := range repos {
		repoMap[repo.URI] = repo
	}
	return repoMap
}

type Repos []*Repo

func (rs Repos) URIs() (uris []string) {
	uris = make([]string, len(rs))
	for i, r := range rs {
		uris[i] = r.URI
	}
	return
}

const githubRepoURIPrefix = "github.com/"

// IsGitHubRepoURI returns true iff this repository is hosted on GitHub.
func IsGitHubRepoURI(repoURI string) bool {
	return strings.HasPrefix(strings.ToLower(repoURI), githubRepoURIPrefix)
}

// IsGoogleCodeRepoURI returns true iff this repository is hosted on Google
// Code (code.google.com).
func IsGoogleCodeRepoURI(repoURI string) bool {
	return strings.HasPrefix(strings.ToLower(repoURI), "code.google.com/p/")
}

// RepoSpec returns the RepoSpec that specifies r.
func (r *Repo) RepoSpec() RepoSpec {
	return RepoSpec{URI: r.URI, RID: r.RID}
}

// ErrRenamed is an error type that indicates that a repository was renamed from
// OldURI to NewURI.
type ErrRenamed struct {
	// OldURI is the previous repository URI.
	OldURI string

	// NewURI is the new URI that the repository was renamed to.
	NewURI string
}

func (e ErrRenamed) Error() string {
	return fmt.Sprintf("repository URI %q was renamed to %q; use the new name", e.OldURI, e.NewURI)
}

// ErrNotExist is an error definitively indicating that no such repository
// exists.
var ErrNotExist = errors.New("repository does not exist on external host")

// ErrForbidden is an error indicating that the repository can no longer be
// accessed due to server's refusal to serve it (possibly DMCA takedowns on
// github etc)
var ErrForbidden = errors.New("repository is unavailable")

// ErrNotPersisted is an error indicating that no such repository is persisted
// locally. The repository might exist on a remote host, but it must be
// explicitly added (it will not be implicitly added via a Get call).
var ErrNotPersisted = errors.New("repository is not persisted locally, but it might exist remotely (explicitly add it to check)")

// ErrNotPersisted is an error indicating that repository cannot be created
// without an explicit clone URL, because it has a non-standard URI. It implies
// ErrNotPersisted.
var ErrNonStandardURI = errors.New("cannot infer repository clone URL because repository host is not standard; try adding it explicitly")

type ErrRedirect struct {
	RedirectURI string
}

func (e ErrRedirect) Error() string {
	return fmt.Sprintf("the repository requested exists at another URI (%s)", e.RedirectURI)
}

var errRedirectMsgPattern = regexp.MustCompile(`the repository requested exists at another URI \(([^\(\)]*)\)`)

func ErrRedirectFromString(msg string) *ErrRedirect {
	if match := errRedirectMsgPattern.FindStringSubmatch(msg); len(match) == 2 {
		return &ErrRedirect{match[1]}
	}
	return nil
}

// IsNotPresent returns whether err is one of ErrNotExist, ErrNotPersisted, or
// ErrRedirected.
func IsNotPresent(err error) bool {
	return err == ErrNotExist || err == ErrNotPersisted
}

func IsForbidden(err error) bool {
	return err == ErrForbidden
}

// ErrNoScheme is an error indicating that a clone URL contained no scheme
// component (e.g., "http://").
var ErrNoScheme = errors.New("clone URL has no scheme")

// ExternalHostTimeout is the timeout for HTTP requests to external repository
// hosts.
var ExternalHostTimeout = time.Second * 7

// StatType is the name of a repository statistic (see below for a listing).
type RepoStatType string

// Stats holds statistics for a repository.
type RepoStats map[RepoStatType]int

const (
	// StatXRefs is the number of external references to any def defined in a
	// repository (i.e., references from other repositories). It is only
	// computed per-repository (and not per-repository-commit) because it is
	// not easy to determine which specific commit a ref references.
	RepoStatXRefs = "xrefs"

	// StatAuthors is the number of resolved people who contributed code to any
	// def defined in a repository (i.e., references from other
	// repositories). It is only computed per-repository-commit.
	RepoStatAuthors = "authors"

	// StatClients is the number of resolved people who have committed refs that
	// reference a def defined in the repository. It is only computed
	// per-repository (and not per-repository-commit) because it is not easy to
	// determine which specific commit a ref references.
	RepoStatClients = "clients"

	// StatDependencies is the number of repositories that the repository
	// depends on. It is only computed per-repository-commit.
	RepoStatDependencies = "dependencies"

	// StatDependents is the number of repositories containing refs to a def
	// defined in the repository. It is only computed per-repository (and not
	// per-repository-commit) because it is not easy to determine which specific
	// commit a ref references.
	RepoStatDependents = "dependents"

	// StatDefs is the number of defs defined in a repository commit. It
	// is only computed per-repository-commit (or else it would count 1 def
	// for each revision of the repository that we have processed).
	RepoStatDefs = "defs"

	// StatExportedDefs is the number of exported defs defined in a
	// repository commit. It is only computed per-repository-commit (or else it
	// would count 1 def for each revision of the repository that we have
	// processed).
	RepoStatExportedDefs = "exported-defs"
)

var RepoStatTypes = map[RepoStatType]struct{}{RepoStatXRefs: struct{}{}, RepoStatAuthors: struct{}{}, RepoStatClients: struct{}{}, RepoStatDependencies: struct{}{}, RepoStatDependents: struct{}{}, RepoStatDefs: struct{}{}, RepoStatExportedDefs: struct{}{}}

// Value implements database/sql/driver.Valuer.
func (x RepoStatType) Value() (driver.Value, error) {
	return string(x), nil
}

// Scan implements database/sql.Scanner.
func (x *RepoStatType) Scan(v interface{}) error {
	if data, ok := v.([]byte); ok {
		*x = RepoStatType(data)
		return nil
	}
	return fmt.Errorf("%T.Scan failed: %v", x, v)
}
<commit_msg>move ErrNoRepoBuild into go-sourcegraph<commit_after>package sourcegraph

import (
	"database/sql/driver"
	"errors"
	"fmt"
	"net/url"
	"regexp"
	"strings"
	"time"

	"sourcegraph.com/sourcegraph/go-nnz/nnz"
)

// Repo is a code repository returned by the Sourcegraph API.
type Repo struct {
	// RID is the numeric primary key for a repository.
	RID int

	// URI is a normalized identifier for this repository based on its primary
	// clone URL. E.g., "github.com/user/repo".
	URI string

	// URIAlias is another URI that, if accessed, will redirect to
	// this repository's primary URI. It's used, for example, to
	// redirect from GitHub repos to their canonical URI (such as Go
	// repos on gopkg.in).
	URIAlias nnz.String `db:"uri_alias"`

	// Name is the base name (the final path component) of the repository,
	// typically the name of the directory that the repository would be cloned
	// into. (For example, for git://example.com/foo.git, the name is "foo".)
	Name string

	// OwnerUserID is the account that owns this repository.
	OwnerUserID int `db:"owner_user_id"`

	// OwnerGitHubUserID is the GitHub user ID of this repository's owner, if this
	// is a GitHub repository.
	OwnerGitHubUserID nnz.Int `db:"owner_github_user_id" json:",omitempty"`

	// Description is a brief description of the repository.
	Description string `json:",omitempty"`

	// VCS is the short name of the VCS system that this repository uses: "git"
	// or "hg".
	VCS string `db:"vcs"`

	// HTTPCloneURL is the HTTPS clone URL of the repository (or the
	// HTTP clone URL, if no HTTPS clone URL is available).
	HTTPCloneURL string `db:"http_clone_url"`

	// SSHCloneURL is the SSH clone URL if the repository, if any.
	SSHCloneURL nnz.String `db:"ssh_clone_url"`

	// HomepageURL is the URL to the repository's homepage, if any.
	HomepageURL nnz.String `db:"homepage_url"`

	// DefaultBranch is the default VCS branch used (typically "master" for git
	// repositories and "default" for hg repositories).
	DefaultBranch string `db:"default_branch"`

	// Language is the primary programming language used in this repository.
	Language string

	// GitHubStars is the number of stargazers this repository has on GitHub (or
	// 0 if it is not a GitHub repository).
	GitHubStars int `db:"github_stars"`

	// GitHubID is the GitHub ID of this repository. If a GitHub repository is
	// renamed, the ID remains the same and should be used to resolve across the
	// name change.
	GitHubID nnz.Int `db:"github_id" json:",omitempty"`

	// Disabled is whether this repo should not be downloaded and processed by the worker.
	Disabled bool `json:",omitempty"`

	// Deprecated repositories are labeled as such and hidden from global search results.
	Deprecated bool

	// Fork is whether this repository is a fork.
	Fork bool

	// Mirror is whether this repository is a mirror.
	Mirror bool

	// Private is whether this repository is private.
	Private bool

	// CreatedAt is when this repository was created. If it represents
	// an externally hosted (e.g., GitHub) repository, the creation
	// date is when it was created at that origin.
	CreatedAt time.Time `db:"created_at"`

	// UpdatedAt is when this repository's metadata was last updated
	// (on its origin if it's an externally hosted repository).
	UpdatedAt time.Time `db:"updated_at"`

	// PushedAt is when this repository's was last (VCS-)pushed to.
	PushedAt time.Time `db:"pushed_at"`

	// Stat holds repository statistics. It's only filled in if Repo{Get,List}Options has Stats == true.
	Stat RepoStats `db:"-" json:",omitempty"`
}

// IsGitHubRepo returns true iff this repository is hosted on GitHub.
func (r *Repo) IsGitHubRepo() bool { return r.GitHubID != 0 }

// GitHubHTMLURL returns URL to the GitHub HTML page (e.g.,
// https://github.com/foo/bar, not a clone URL) for this repo, if it's
// a GitHub repo. Otherwise it returns the empty string.
func (r *Repo) GitHubHTMLURL() string {
	var ghuri string
	if IsGitHubRepoURI(r.URI) {
		ghuri = r.URI
	} else if IsGitHubRepoURI(string(r.URIAlias)) {
		ghuri = string(r.URIAlias)
	}
	if ghuri == "" {
		return ""
	}
	return (&url.URL{Scheme: "https", Host: "github.com", Path: "/" + strings.TrimPrefix(ghuri, githubRepoURIPrefix)}).String()
}

const (
	Git = "git"
	Hg  = "hg"
)

func MapByURI(repos []*Repo) map[string]*Repo {
	repoMap := make(map[string]*Repo, len(repos))
	for _, repo := range repos {
		repoMap[repo.URI] = repo
	}
	return repoMap
}

type Repos []*Repo

func (rs Repos) URIs() (uris []string) {
	uris = make([]string, len(rs))
	for i, r := range rs {
		uris[i] = r.URI
	}
	return
}

const githubRepoURIPrefix = "github.com/"

// IsGitHubRepoURI returns true iff this repository is hosted on GitHub.
func IsGitHubRepoURI(repoURI string) bool {
	return strings.HasPrefix(strings.ToLower(repoURI), githubRepoURIPrefix)
}

// IsGoogleCodeRepoURI returns true iff this repository is hosted on Google
// Code (code.google.com).
func IsGoogleCodeRepoURI(repoURI string) bool {
	return strings.HasPrefix(strings.ToLower(repoURI), "code.google.com/p/")
}

// RepoSpec returns the RepoSpec that specifies r.
func (r *Repo) RepoSpec() RepoSpec {
	return RepoSpec{URI: r.URI, RID: r.RID}
}

// ErrRenamed is an error type that indicates that a repository was renamed from
// OldURI to NewURI.
type ErrRenamed struct {
	// OldURI is the previous repository URI.
	OldURI string

	// NewURI is the new URI that the repository was renamed to.
	NewURI string
}

func (e ErrRenamed) Error() string {
	return fmt.Sprintf("repository URI %q was renamed to %q; use the new name", e.OldURI, e.NewURI)
}

// ErrNotExist is an error definitively indicating that no such repository
// exists.
var ErrNotExist = errors.New("repository does not exist on external host")

// ErrForbidden is an error indicating that the repository can no longer be
// accessed due to server's refusal to serve it (possibly DMCA takedowns on
// github etc)
var ErrForbidden = errors.New("repository is unavailable")

// ErrNotPersisted is an error indicating that no such repository is persisted
// locally. The repository might exist on a remote host, but it must be
// explicitly added (it will not be implicitly added via a Get call).
var ErrNotPersisted = errors.New("repository is not persisted locally, but it might exist remotely (explicitly add it to check)")

// ErrNotPersisted is an error indicating that repository cannot be created
// without an explicit clone URL, because it has a non-standard URI. It implies
// ErrNotPersisted.
var ErrNonStandardURI = errors.New("cannot infer repository clone URL because repository host is not standard; try adding it explicitly")

// ErrNoRepoBuild indicates that no build could be found for a repo
// revspec.
var ErrNoRepoBuild = errors.New("no suitable repo build found for revspec")

type ErrRedirect struct {
	RedirectURI string
}

func (e ErrRedirect) Error() string {
	return fmt.Sprintf("the repository requested exists at another URI (%s)", e.RedirectURI)
}

var errRedirectMsgPattern = regexp.MustCompile(`the repository requested exists at another URI \(([^\(\)]*)\)`)

func ErrRedirectFromString(msg string) *ErrRedirect {
	if match := errRedirectMsgPattern.FindStringSubmatch(msg); len(match) == 2 {
		return &ErrRedirect{match[1]}
	}
	return nil
}

// IsNotPresent returns whether err is one of ErrNotExist, ErrNotPersisted, or
// ErrRedirected.
func IsNotPresent(err error) bool {
	return err == ErrNotExist || err == ErrNotPersisted
}

func IsForbidden(err error) bool {
	return err == ErrForbidden
}

// ErrNoScheme is an error indicating that a clone URL contained no scheme
// component (e.g., "http://").
var ErrNoScheme = errors.New("clone URL has no scheme")

// ExternalHostTimeout is the timeout for HTTP requests to external repository
// hosts.
var ExternalHostTimeout = time.Second * 7

// StatType is the name of a repository statistic (see below for a listing).
type RepoStatType string

// Stats holds statistics for a repository.
type RepoStats map[RepoStatType]int

const (
	// StatXRefs is the number of external references to any def defined in a
	// repository (i.e., references from other repositories). It is only
	// computed per-repository (and not per-repository-commit) because it is
	// not easy to determine which specific commit a ref references.
	RepoStatXRefs = "xrefs"

	// StatAuthors is the number of resolved people who contributed code to any
	// def defined in a repository (i.e., references from other
	// repositories). It is only computed per-repository-commit.
	RepoStatAuthors = "authors"

	// StatClients is the number of resolved people who have committed refs that
	// reference a def defined in the repository. It is only computed
	// per-repository (and not per-repository-commit) because it is not easy to
	// determine which specific commit a ref references.
	RepoStatClients = "clients"

	// StatDependencies is the number of repositories that the repository
	// depends on. It is only computed per-repository-commit.
	RepoStatDependencies = "dependencies"

	// StatDependents is the number of repositories containing refs to a def
	// defined in the repository. It is only computed per-repository (and not
	// per-repository-commit) because it is not easy to determine which specific
	// commit a ref references.
	RepoStatDependents = "dependents"

	// StatDefs is the number of defs defined in a repository commit. It
	// is only computed per-repository-commit (or else it would count 1 def
	// for each revision of the repository that we have processed).
	RepoStatDefs = "defs"

	// StatExportedDefs is the number of exported defs defined in a
	// repository commit. It is only computed per-repository-commit (or else it
	// would count 1 def for each revision of the repository that we have
	// processed).
	RepoStatExportedDefs = "exported-defs"
)

var RepoStatTypes = map[RepoStatType]struct{}{RepoStatXRefs: struct{}{}, RepoStatAuthors: struct{}{}, RepoStatClients: struct{}{}, RepoStatDependencies: struct{}{}, RepoStatDependents: struct{}{}, RepoStatDefs: struct{}{}, RepoStatExportedDefs: struct{}{}}

// Value implements database/sql/driver.Valuer.
func (x RepoStatType) Value() (driver.Value, error) {
	return string(x), nil
}

// Scan implements database/sql.Scanner.
func (x *RepoStatType) Scan(v interface{}) error {
	if data, ok := v.([]byte); ok {
		*x = RepoStatType(data)
		return nil
	}
	return fmt.Errorf("%T.Scan failed: %v", x, v)
}
<|endoftext|>


<commit_before>// Copyright 2013 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// http://studygolang.com
// Author：polaris	studygolang@gmail.com

package service

import (
	"fmt"
	"html/template"
	"logger"
	"model"
	"net/url"
	"regexp"
	"strconv"
	"time"
	"util"
)

// 获得某个对象的所有评论
// owner: 被评论对象属主
// TODO:分页暂不做
func FindObjComments(objid, objtype string, owner, lastCommentUid int /*, page, pageNum int*/) (comments []map[string]interface{}, ownerUser, lastReplyUser *model.User) {
	commentList, err := model.NewComment().Where("objid=" + objid + " and objtype=" + objtype).FindAll()
	if err != nil {
		logger.Errorln("comment service FindObjComments Error:", err)
		return
	}

	uids := util.Models2Intslice(commentList, "Uid")

	// 避免某些情况下最后回复人没在回复列表中
	uids = append(uids, owner, lastCommentUid)

	// 获得用户信息
	userMap := GetUserInfos(uids)
	ownerUser = userMap[owner]
	if lastCommentUid != 0 {
		lastReplyUser = userMap[lastCommentUid]
	}
	comments = make([]map[string]interface{}, 0, len(commentList))
	for _, comment := range commentList {
		tmpMap := make(map[string]interface{})
		util.Struct2Map(tmpMap, comment)
		tmpMap["content"] = template.HTML(decodeCmtContent(comment))
		tmpMap["user"] = userMap[comment.Uid]
		comments = append(comments, tmpMap)
	}
	return
}

func decodeCmtContent(comment *model.Comment) string {
	// 安全过滤
	content := template.HTMLEscapeString(comment.Content)
	// @别人
	reg := regexp.MustCompile(`@([^\s@]{4,20})`)
	content = reg.ReplaceAllString(content, `<a href="/user/$1" title="@$1">@$1</a>`)

	// 回复某一楼层
	reg = regexp.MustCompile(`#(\d+)楼`)
	url := fmt.Sprintf("%s%d#comment", model.PathUrlMap[comment.Objtype], comment.Objid)
	content = reg.ReplaceAllString(content, `<a href="`+url+`$1" title="$1">#$1<span>楼</span></a>`)

	comment.Content = content

	return content
}

// 获得最近的评论
// 如果 uid!=0，表示获取某人的评论；
// 如果 objtype!=-1，表示获取某类型的评论；
func FindRecentComments(uid, objtype int, limit string) []*model.Comment {
	cond := ""
	if uid != 0 {
		cond = "uid=" + strconv.Itoa(uid)
	}
	if objtype != -1 {
		cond = "objtype=" + strconv.Itoa(objtype)
	}

	comments, err := model.NewComment().Where(cond).Order("cid DESC").Limit(limit).FindAll()
	if err != nil {
		logger.Errorln("comment service FindRecentComments error:", err)
		return nil
	}

	cmtMap := make(map[int][]*model.Comment, len(model.PathUrlMap))
	for _, comment := range comments {
		decodeCmtContent(comment)

		if _, ok := cmtMap[comment.Objtype]; !ok {
			cmtMap[comment.Objtype] = make([]*model.Comment, 0, 10)
		}

		cmtMap[comment.Objtype] = append(cmtMap[comment.Objtype], comment)
	}

	for cmtType, cmts := range cmtMap {
		switch cmtType {
		case model.TYPE_TOPIC:
			FillCommentObjs(cmts, TopicComment{})
		case model.TYPE_ARTICLE:
			FillCommentObjs(cmts, ArticleComment{})
		case model.TYPE_RESOURCE:
			FillCommentObjs(cmts, ResourceComment{})
		}
	}

	return comments
}

// 评论总数(objtype != -1 时，取某一类型的评论总数)
func CommentsTotal(objtype int) (total int) {
	var cond string
	if objtype != -1 {
		cond = "objtype=" + strconv.Itoa(objtype)
	}

	total, err := model.NewComment().Where(cond).Count()
	if err != nil {
		logger.Errorln("comment service CommentsTotal error:", err)
		return
	}
	return
}

// 获取多个评论信息
func FindCommentsByIds(cids []int) []*model.Comment {
	if len(cids) == 0 {
		return nil
	}
	inCids := util.Join(cids, ",")
	comments, err := model.NewComment().Where("cid in(" + inCids + ")").FindAll()
	if err != nil {
		logger.Errorln("comment service FindCommentsByIds error:", err)
		return nil
	}
	return comments
}

// 填充评论对应的主体信息
func FillCommentObjs(comments []*model.Comment, cmtObj CommentObjecter) {
	if len(comments) == 0 {
		return
	}
	count := len(comments)
	commentMap := make(map[int][]*model.Comment, count)
	idMap := make(map[int]int, count)
	for _, comment := range comments {
		if _, ok := commentMap[comment.Objid]; !ok {
			commentMap[comment.Objid] = make([]*model.Comment, 0, count)
		}
		commentMap[comment.Objid] = append(commentMap[comment.Objid], comment)
		idMap[comment.Objid] = 1
	}
	ids := util.MapIntKeys(idMap)
	cmtObj.SetObjinfo(ids, commentMap)
}

// 填充 Comment 对象的 Objinfo 成员接口
// 评论属主应该实现该接口（以便填充 Objinfo 成员）
type CommentObjecter interface {
	// ids 是属主的主键 slice （comment 中的 objid）
	// commentMap 中的 key 是属主 id
	SetObjinfo(ids []int, commentMap map[int][]*model.Comment)
}

// 提供给其他service调用（包内）
func getComments(cids map[int]int) map[int]*model.Comment {
	comments := FindCommentsByIds(util.MapIntKeys(cids))
	commentMap := make(map[int]*model.Comment, len(comments))
	for _, comment := range comments {
		commentMap[comment.Cid] = comment
	}
	return commentMap
}

var commenters = make(map[string]Commenter)

// 评论接口
type Commenter interface {
	// 评论回调接口，用于更新对象自身需要更新的数据
	UpdateComment(int, int, int, string)
}

// 注册评论对象，使得某种类型（帖子、博客等）可以被评论
func RegisterCommentObject(objname string, commenter Commenter) {
	if commenter == nil {
		panic("service: Register commenter is nil")
	}
	if _, dup := commenters[objname]; dup {
		panic("service: Register called twice for commenter " + objname)
	}
	commenters[objname] = commenter
}

// 发表评论（或回复）。
// objname 注册的评论对象名
// uid 评论人
func PostComment(uid, objid int, form url.Values) error {
	comment := model.NewComment()
	comment.Objid = objid
	objtype := util.MustInt(form.Get("objtype"))
	comment.Objtype = objtype
	comment.Uid = uid
	comment.Content = form.Get("content")

	// TODO:评论楼层怎么处理，避免冲突？最后的楼层信息保存在内存中？

	// 暂时只是从数据库中取出最后的评论楼层
	stringBuilder := util.NewBuffer()
	stringBuilder.Append("objid=").AppendInt(objid).Append(" AND objtype=").AppendInt(objtype)
	tmpCmt, err := model.NewComment().Where(stringBuilder.String()).Order("ctime DESC").Find()
	if err != nil {
		logger.Errorln("post comment service error:", err)
		return err
	} else {
		comment.Floor = tmpCmt.Floor + 1
	}
	// 入评论库
	cid, err := comment.Insert()
	if err != nil {
		logger.Errorln("post comment service error:", err)
		return err
	}
	// 回调，不关心处理结果（有些对象可能不需要回调）
	if commenter, ok := commenters[form.Get("objname")]; ok {
		logger.Debugf("评论[objid:%d] [objtype:%d] [uid:%d] 成功，通知被评论者更新", objid, objtype, uid)
		go commenter.UpdateComment(cid, objid, uid, time.Now().Format("2006-01-02 15:04:05"))
	}

	// 发评论，活跃度+5
	go IncUserWeight("uid="+strconv.Itoa(uid), 5)

	// 给被评论对象所有者发系统消息
	ext := map[string]interface{}{
		"objid":   objid,
		"objtype": objtype,
		"cid":     cid,
		"uid":     uid,
	}
	go SendSystemMsgTo(0, objtype, ext)

	// @某人 发系统消息
	go SendSysMsgAtUids(form.Get("uid"), ext)

	return nil
}

func ModifyComment(cid, content string) (errMsg string, err error) {
	err = model.NewComment().Set("content=?", content).Where("cid=" + cid).Update()
	if err != nil {
		logger.Errorf("更新评论内容 【%s】 失败：%s\n", cid, err)
		errMsg = "对不起，服务器内部错误，请稍后再试！"
		return
	}

	return
}
<commit_msg>个人首页最近回复bugfix<commit_after>// Copyright 2013 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// http://studygolang.com
// Author：polaris	studygolang@gmail.com

package service

import (
	"fmt"
	"html/template"
	"logger"
	"model"
	"net/url"
	"regexp"
	"strconv"
	"time"
	"util"
)

// 获得某个对象的所有评论
// owner: 被评论对象属主
// TODO:分页暂不做
func FindObjComments(objid, objtype string, owner, lastCommentUid int /*, page, pageNum int*/) (comments []map[string]interface{}, ownerUser, lastReplyUser *model.User) {
	commentList, err := model.NewComment().Where("objid=" + objid + " and objtype=" + objtype).FindAll()
	if err != nil {
		logger.Errorln("comment service FindObjComments Error:", err)
		return
	}

	uids := util.Models2Intslice(commentList, "Uid")

	// 避免某些情况下最后回复人没在回复列表中
	uids = append(uids, owner, lastCommentUid)

	// 获得用户信息
	userMap := GetUserInfos(uids)
	ownerUser = userMap[owner]
	if lastCommentUid != 0 {
		lastReplyUser = userMap[lastCommentUid]
	}
	comments = make([]map[string]interface{}, 0, len(commentList))
	for _, comment := range commentList {
		tmpMap := make(map[string]interface{})
		util.Struct2Map(tmpMap, comment)
		tmpMap["content"] = template.HTML(decodeCmtContent(comment))
		tmpMap["user"] = userMap[comment.Uid]
		comments = append(comments, tmpMap)
	}
	return
}

func decodeCmtContent(comment *model.Comment) string {
	// 安全过滤
	content := template.HTMLEscapeString(comment.Content)
	// @别人
	reg := regexp.MustCompile(`@([^\s@]{4,20})`)
	content = reg.ReplaceAllString(content, `<a href="/user/$1" title="@$1">@$1</a>`)

	// 回复某一楼层
	reg = regexp.MustCompile(`#(\d+)楼`)
	url := fmt.Sprintf("%s%d#comment", model.PathUrlMap[comment.Objtype], comment.Objid)
	content = reg.ReplaceAllString(content, `<a href="`+url+`$1" title="$1">#$1<span>楼</span></a>`)

	comment.Content = content

	return content
}

// 获得最近的评论
// 如果 uid!=0，表示获取某人的评论；
// 如果 objtype!=-1，表示获取某类型的评论；
func FindRecentComments(uid, objtype int, limit string) []*model.Comment {
	cond := ""
	if uid != 0 {
		cond = "uid=" + strconv.Itoa(uid)
	}
	if objtype != -1 {
		if cond != "" {
			cond += " AND "
		}
		cond += "objtype=" + strconv.Itoa(objtype)
	}

	comments, err := model.NewComment().Where(cond).Order("cid DESC").Limit(limit).FindAll()
	if err != nil {
		logger.Errorln("comment service FindRecentComments error:", err)
		return nil
	}

	cmtMap := make(map[int][]*model.Comment, len(model.PathUrlMap))
	for _, comment := range comments {
		decodeCmtContent(comment)

		if _, ok := cmtMap[comment.Objtype]; !ok {
			cmtMap[comment.Objtype] = make([]*model.Comment, 0, 10)
		}

		cmtMap[comment.Objtype] = append(cmtMap[comment.Objtype], comment)
	}

	for cmtType, cmts := range cmtMap {
		switch cmtType {
		case model.TYPE_TOPIC:
			FillCommentObjs(cmts, TopicComment{})
		case model.TYPE_ARTICLE:
			FillCommentObjs(cmts, ArticleComment{})
		case model.TYPE_RESOURCE:
			FillCommentObjs(cmts, ResourceComment{})
		}
	}

	return comments
}

// 评论总数(objtype != -1 时，取某一类型的评论总数)
func CommentsTotal(objtype int) (total int) {
	var cond string
	if objtype != -1 {
		cond = "objtype=" + strconv.Itoa(objtype)
	}

	total, err := model.NewComment().Where(cond).Count()
	if err != nil {
		logger.Errorln("comment service CommentsTotal error:", err)
		return
	}
	return
}

// 获取多个评论信息
func FindCommentsByIds(cids []int) []*model.Comment {
	if len(cids) == 0 {
		return nil
	}
	inCids := util.Join(cids, ",")
	comments, err := model.NewComment().Where("cid in(" + inCids + ")").FindAll()
	if err != nil {
		logger.Errorln("comment service FindCommentsByIds error:", err)
		return nil
	}
	return comments
}

// 填充评论对应的主体信息
func FillCommentObjs(comments []*model.Comment, cmtObj CommentObjecter) {
	if len(comments) == 0 {
		return
	}
	count := len(comments)
	commentMap := make(map[int][]*model.Comment, count)
	idMap := make(map[int]int, count)
	for _, comment := range comments {
		if _, ok := commentMap[comment.Objid]; !ok {
			commentMap[comment.Objid] = make([]*model.Comment, 0, count)
		}
		commentMap[comment.Objid] = append(commentMap[comment.Objid], comment)
		idMap[comment.Objid] = 1
	}
	ids := util.MapIntKeys(idMap)
	cmtObj.SetObjinfo(ids, commentMap)
}

// 填充 Comment 对象的 Objinfo 成员接口
// 评论属主应该实现该接口（以便填充 Objinfo 成员）
type CommentObjecter interface {
	// ids 是属主的主键 slice （comment 中的 objid）
	// commentMap 中的 key 是属主 id
	SetObjinfo(ids []int, commentMap map[int][]*model.Comment)
}

// 提供给其他service调用（包内）
func getComments(cids map[int]int) map[int]*model.Comment {
	comments := FindCommentsByIds(util.MapIntKeys(cids))
	commentMap := make(map[int]*model.Comment, len(comments))
	for _, comment := range comments {
		commentMap[comment.Cid] = comment
	}
	return commentMap
}

var commenters = make(map[string]Commenter)

// 评论接口
type Commenter interface {
	// 评论回调接口，用于更新对象自身需要更新的数据
	UpdateComment(int, int, int, string)
}

// 注册评论对象，使得某种类型（帖子、博客等）可以被评论
func RegisterCommentObject(objname string, commenter Commenter) {
	if commenter == nil {
		panic("service: Register commenter is nil")
	}
	if _, dup := commenters[objname]; dup {
		panic("service: Register called twice for commenter " + objname)
	}
	commenters[objname] = commenter
}

// 发表评论（或回复）。
// objname 注册的评论对象名
// uid 评论人
func PostComment(uid, objid int, form url.Values) error {
	comment := model.NewComment()
	comment.Objid = objid
	objtype := util.MustInt(form.Get("objtype"))
	comment.Objtype = objtype
	comment.Uid = uid
	comment.Content = form.Get("content")

	// TODO:评论楼层怎么处理，避免冲突？最后的楼层信息保存在内存中？

	// 暂时只是从数据库中取出最后的评论楼层
	stringBuilder := util.NewBuffer()
	stringBuilder.Append("objid=").AppendInt(objid).Append(" AND objtype=").AppendInt(objtype)
	tmpCmt, err := model.NewComment().Where(stringBuilder.String()).Order("ctime DESC").Find()
	if err != nil {
		logger.Errorln("post comment service error:", err)
		return err
	} else {
		comment.Floor = tmpCmt.Floor + 1
	}
	// 入评论库
	cid, err := comment.Insert()
	if err != nil {
		logger.Errorln("post comment service error:", err)
		return err
	}
	// 回调，不关心处理结果（有些对象可能不需要回调）
	if commenter, ok := commenters[form.Get("objname")]; ok {
		logger.Debugf("评论[objid:%d] [objtype:%d] [uid:%d] 成功，通知被评论者更新", objid, objtype, uid)
		go commenter.UpdateComment(cid, objid, uid, time.Now().Format("2006-01-02 15:04:05"))
	}

	// 发评论，活跃度+5
	go IncUserWeight("uid="+strconv.Itoa(uid), 5)

	// 给被评论对象所有者发系统消息
	ext := map[string]interface{}{
		"objid":   objid,
		"objtype": objtype,
		"cid":     cid,
		"uid":     uid,
	}
	go SendSystemMsgTo(0, objtype, ext)

	// @某人 发系统消息
	go SendSysMsgAtUids(form.Get("uid"), ext)

	return nil
}

func ModifyComment(cid, content string) (errMsg string, err error) {
	err = model.NewComment().Set("content=?", content).Where("cid=" + cid).Update()
	if err != nil {
		logger.Errorf("更新评论内容 【%s】 失败：%s\n", cid, err)
		errMsg = "对不起，服务器内部错误，请稍后再试！"
		return
	}

	return
}
<|endoftext|>


<commit_before>package mandelbrot

import (
	"fractal"
	"image"
	"image/color"
	"math"
	"math/cmplx"
)

type Mandelbrot struct {
	*image.Paletted
	maxIterations int
	fractal.DefaultNavigator
}

func HSVToRGBA(h, s, v float64) color.RGBA {
	hi := int(math.Mod(math.Floor(h / 60), 6))
	f := (h / 60) - math.Floor(h / 60)
	p := v * (1 - s)
	q := v * (1 - (f*s))
	t := v * (1 - ((1 - f) * s))

	RGB := func(r, g, b float64) color.RGBA {
		return color.RGBA{uint8(r * 255), uint8(g * 255), uint8(b * 255), 255}
	}

	var c color.RGBA
	switch hi {
		case 0: c = RGB(v, t, p)
		case 1: c = RGB(q, v, p)
		case 2: c = RGB(p, v, t)
		case 3: c = RGB(p, q, v)
		case 4: c = RGB(t, p, v)
		case 5: c = RGB(v, p, q)
	}
	return c
}

func NewFractal(o fractal.Options) (fractal.Fractal, error) {
	w := o.GetIntDefault("w", 256)
	h := o.GetIntDefault("h", 256)
	x := o.GetIntDefault("x", 0)
	y := o.GetIntDefault("y", 0)
	it := o.GetIntDefault("i", 256)
	z := o.GetIntDefault("z", 0)

	p := color.Palette{color.Black}
	numColors := float64(12)
	for i := float64(0); i<numColors; i += 1 {
		degree := i/numColors * 360
		p = append(p, HSVToRGBA(degree, 1, 1))
	}

	// Center the image by considering the fractal range of (-2.5, 1), (-1, 1)
	// Explanation of magic numbers 1 and 7 being added to z
	//   +1: Google maps JS sends us zero based zoom so  we add one here to
	//       work with our math
	//   +7: The zoom factor is converted to 2^z, so having a zoom factor of 7
	//       (128x) makes the fractal range comfortably visible in pixel space
	nav := fractal.NewDefaultNavigator(float64(z+1+6), x, y)
	//nav := fractal.NewDefaultNavigator(float64(z+1)*200, x + int(-float64(w)/1.75), y - h/2)
	return &Mandelbrot{image.NewPaletted(image.Rect(0, 0, w, h), p), it, nav}, nil
}

//func (m *Mandelbrot) At(x, y int) color.Color {
func (m *Mandelbrot) ColorIndexAt(x, y int) uint8 {
	/*
	For every point (x,y) in your view rectangle
	  Let z=x+yi
	  Set n=0
	  Set w=0
	  While(n less than limit and |w|<2)
		Let w=w*w+z
		Increment n
	  End While
	  if(|w|<2) then z is a member of the approximate
		Mandelbrot set, plot (x,y) in the Mandelbrot set color
	  otherwise z is outside the Mandelbrot set,
		plot (x,y) in the outside color.
	End for
	*/
	r, i := m.Transform(image.Pt(x, y))
	z := complex(r, i)
	w := complex(0, 0)
	it := 0
	for (cmplx.Abs(w) < 2) && (it < m.maxIterations) {
		w = w * w + z

		it++
		// Black stored at m.Palette[0], so skip it
		if it % len(m.Palette) == 0 {
			it++
		}
	}

	if cmplx.Abs(w) < 2 {
		// Pixel in mandelbrot set, return black
		return 0
	}

	return uint8(it % len(m.Palette))
}

func (m *Mandelbrot) Transform(p image.Point) (float64, float64) {
	b := m.Bounds()
	t := m.GetTranslate()
	x := p.X + (t.X * b.Dx())
	y := p.Y + (t.Y * b.Dy())
	z := math.Pow(2, m.GetZoom())
	return float64(x)/z, float64(y)/z
}
<commit_msg>Clean up documentation<commit_after>// Implementes Mandelbrot set, see:
//   http://eldar.mathstat.uoguelph.ca/dashlock/ftax/Mandel.html
package mandelbrot

import (
	"fractal"
	"image"
	"image/color"
	"math"
	"math/cmplx"
)

type Mandelbrot struct {
	*image.Paletted
	maxIterations int
	fractal.DefaultNavigator
}

func HSVToRGBA(h, s, v float64) color.RGBA {
	hi := int(math.Mod(math.Floor(h / 60), 6))
	f := (h / 60) - math.Floor(h / 60)
	p := v * (1 - s)
	q := v * (1 - (f*s))
	t := v * (1 - ((1 - f) * s))

	RGB := func(r, g, b float64) color.RGBA {
		return color.RGBA{uint8(r * 255), uint8(g * 255), uint8(b * 255), 255}
	}

	var c color.RGBA
	switch hi {
		case 0: c = RGB(v, t, p)
		case 1: c = RGB(q, v, p)
		case 2: c = RGB(p, v, t)
		case 3: c = RGB(p, q, v)
		case 4: c = RGB(t, p, v)
		case 5: c = RGB(v, p, q)
	}
	return c
}

func NewFractal(o fractal.Options) (fractal.Fractal, error) {
	w := o.GetIntDefault("w", 256)
	h := o.GetIntDefault("h", 256)
	x := o.GetIntDefault("x", 0)
	y := o.GetIntDefault("y", 0)
	it := o.GetIntDefault("i", 256)
	z := o.GetIntDefault("z", 0)

	p := color.Palette{color.Black}
	numColors := float64(12)
	for i := float64(0); i<numColors; i += 1 {
		degree := i/numColors * 360
		p = append(p, HSVToRGBA(degree, 1, 1))
	}

	// Center the image by considering the fractal range of (-2.5, 1), (-1, 1)
	// Explanation of magic numbers 1 and 7 being added to z
	//   +1: Google maps JS sends us zero based zoom so  we add one here to
	//       work with our math
	//   +7: The zoom factor is converted to 2^z, so having a zoom factor of 7
	//       (128x) makes the fractal range comfortably visible in pixel space
	nav := fractal.NewDefaultNavigator(float64(z+1+6), x, y)
	//nav := fractal.NewDefaultNavigator(float64(z+1)*200, x + int(-float64(w)/1.75), y - h/2)
	return &Mandelbrot{image.NewPaletted(image.Rect(0, 0, w, h), p), it, nav}, nil
}

//func (m *Mandelbrot) At(x, y int) color.Color {
func (m *Mandelbrot) ColorIndexAt(x, y int) uint8 {
	r, i := m.Transform(image.Pt(x, y))
	z := complex(r, i)
	w := complex(0, 0)
	it := 0
	for (cmplx.Abs(w) < 2) && (it < m.maxIterations) {
		w = w * w + z

		it++
		// Black stored at m.Palette[0], so skip it
		if it % len(m.Palette) == 0 {
			it++
		}
	}

	if cmplx.Abs(w) < 2 {
		// Pixel in mandelbrot set, return black
		return 0
	}

	return uint8(it % len(m.Palette))
}

func (m *Mandelbrot) Transform(p image.Point) (float64, float64) {
	b := m.Bounds()
	t := m.GetTranslate()
	x := p.X + (t.X * b.Dx())
	y := p.Y + (t.Y * b.Dy())
	z := math.Pow(2, m.GetZoom())
	return float64(x)/z, float64(y)/z
}
<|endoftext|>


<commit_before>package queues
/*
 *  Filename:    priority.go
 *  Package:     queues
 *  Author:      Bryan Matsuo <bmatsuo@soe.ucsc.edu>
 *  Created:     Wed Jul  6 22:18:57 PDT 2011
 *  Description:
 */
import (
    "sort"
    "fmt"
    "container/heap"
    "container/vector"
)

//  A PrioritizedTask is a Task that also has key (float64). Generally,
//  a lower key means higher priority.
type PrioritizedTask interface {
    Task
    Key() float64
    SetKey(float64)
}
//  A structure that satisfies the PrioritizedTask interface (and thus
//  Task aswell).
type PTask struct {
    F func(int64)
    P float64
}
func (pt *PTask) Type() string {
    return "PTask"
}
func (pt *PTask) SetFunc(f func(int64)) {
    pt.F = f
}
func (pt *PTask) Func() func(int64) {
    return pt.F
}
func (pt *PTask) Key() float64 {
    return pt.P
}
func (pt *PTask) SetKey(k float64) {
    pt.P = k
}

type pQueue struct {
    elements []RegisteredTask
}
func newPQueue() *pQueue {
    var h = new(pQueue)
    h.elements = make([]RegisteredTask, 0, 5)
    return h
}
func (h *pQueue) GetPTask(i int) PrioritizedTask {
    if n := len(h.elements) ; i < 0 || i >= n {
        panic("badindex")
    }
    return h.elements[i].Task().(PrioritizedTask)
}
func (h *pQueue) Len() int {
    return len(h.elements)
}
func (h *pQueue) Less(i, j int) bool {
    return h.GetPTask(i).Key() < h.GetPTask(j).Key()
}
func (h *pQueue) Swap(i, j int) {
    if n := len(h.elements) ; i < 0 || i >=n || j < 0 || j >= n {
        panic("badindex")
    }
    var tmp = h.elements[i]
    h.elements[i] = h.elements[j]
    h.elements[j] = tmp
}
func (h *pQueue) Push(x interface{}) {
    switch x.(RegisteredTask).Task().(type) {
    case PrioritizedTask:
        h.elements = append(h.elements, x.(RegisteredTask))
    default:
        panic("badtype")
    }
}
func (h *pQueue) Pop() interface{} {
    if len(h.elements) <= 0 {
        panic("empty")
    }
    var head = h.elements[0]
    h.elements = h.elements[1:]
    return head
}
func (h *pQueue) FindId(id int64) (int, RegisteredTask) {
    for i, elm := range h.elements {
        if elm.Id() == id {
            return i, elm
        }
    }
    return -1, nil
}

//  A heap-based priority queue. This implementation of a priority queue
//  is ideal for many situations involving a priority queue. However, other
//  priority queue implementations exist, each with their strengths and
//  weaknesses. See ArrayPriorityQueue and VectorPriorityQueue.
type PriorityQueue struct {
    h  *pQueue
}

//  Create a new heap-based priority queue.
func NewPriorityQueue() *PriorityQueue {
    var pq = new(PriorityQueue)
    pq.h = newPQueue()
    // No need to call heap.Init(pq.h) on an empty heap.
    return pq
}

//  The number of items in the queue.
func (pq *PriorityQueue) Len() int {
    return pq.h.Len()
}

//  Remove a task from the queue with runtime O(log(n)).
func (pq *PriorityQueue) Dequeue() RegisteredTask {
    if pq.Len() <= 0 {
        panic("empty")
    }
    return heap.Pop(pq.h).(RegisteredTask)
}

//  Add a task to the queue with runtime O(log(n)). The Task() method
//  of task must satisfy the PrioritizedTask interface, or a runtime
//  panic is thrown.
func (pq *PriorityQueue) Enqueue(task RegisteredTask) {
    switch task.Task().(type) {
    case PrioritizedTask:
        heap.Push(pq.h, task)
    default:
        panic(fmt.Sprintf("nokey %s", task.Task().Type()))
    }
}

//  Set a task's key with runtime O(n).
func (pq *PriorityQueue) SetKey(id int64, k float64) {
    var i, task = pq.h.FindId(id)
    if i < 0 {
        return
    }
    heap.Remove(pq.h, i)
    task.Task().(PrioritizedTask).SetKey(k)
    heap.Push(pq.h, task)
}

//  A priority queue based on the "container/vector" package. This priority
//  queue implementation has fast dequeues and slow enqueues.
type VectorPriorityQueue struct {
    head   int
    hmax   int
    v *vector.Vector
}

// Create a new VectorPriorityQueue.
func NewVectorPriorityQueue() *VectorPriorityQueue {
    var vpq = new(VectorPriorityQueue)
    vpq.v = new(vector.Vector)
    vpq.hmax = 1
    return vpq
}

func (vpq *VectorPriorityQueue) Len() int {
    return vpq.v.Len() - vpq.head
}

//  Add a task to the priority queue in O(n) time. This is done with a
//  O(log(n)) binary search and an insert operation.
func (vpq *VectorPriorityQueue) Enqueue(task RegisteredTask) {
    switch task.Task().(type) {
    case PrioritizedTask:
        break
    default:
        panic(fmt.Sprintf("nokey %s", task.Task().Type()))
    }
    var key = task.Task().(PrioritizedTask).Key()
    var insertoffset = sort.Search(vpq.Len(), func(i int) bool {
            if vpq.v.At(vpq.head+i).(RegisteredTask).Task().(PrioritizedTask).Key() >= key {
                return true
            }
            return false })
    vpq.v.Insert(vpq.head+insertoffset, task)
}

//  Remove the task with the smallest key in O(1) amortized time.
func (vpq *VectorPriorityQueue) Dequeue() RegisteredTask {
    var front = vpq.v.At(vpq.head).(RegisteredTask)
    vpq.head++
    if vpq.head >= vpq.hmax {
        vpq.v.Cut(0, vpq.head)
        vpq.hmax *= 2
        vpq.head = 0
    }
    return front
}

//  Change the value of a task's key in O(n) time. This performs search,
//  delete, and enqueue operations. Hence, this is not a fast method.
func (vpq *VectorPriorityQueue) SetKey(id int64, k float64) {
    var (
        n    = vpq.Len()
        i    int
        task RegisteredTask
    )
    for i = vpq.head ; i < n ; i++ {
        task = vpq.v.At(i).(RegisteredTask)
        if task.Id() == id {
            break
        }
    }
    if i < n {
        vpq.v.Delete(i)
        task.Task().(PrioritizedTask).SetKey(k)
        vpq.Enqueue(task)
    }
}

//  An array-based priority queue with a constant time dequeue and a
//  linear time equeue. It should slightly outperform a
//  VectorPriorityQueue, but will likely be removed from the library
//  because it is requires more maintenance.
type ArrayPriorityQueue struct {
    v          []RegisteredTask
    head, tail int
}

//  Create a new array-based priority queue.
func NewArrayPriorityQueue() *ArrayPriorityQueue {
    var apq = new(ArrayPriorityQueue)
    apq.v = make([]RegisteredTask, 10)
    return apq
}

//  The number of items in the queue.
func (apq *ArrayPriorityQueue) Len() int {
    return apq.tail - apq.head
}

//  Add a task to the queue with runtime O(n) (on average n/2 + log_2(n))
func (apq *ArrayPriorityQueue) Enqueue(task RegisteredTask) {
    var key = task.Task().(PrioritizedTask).Key()
    var n = apq.Len()
    var insertoffset = sort.Search(
            n,
            func(i int)bool{
                return apq.v[apq.head+i].Task().(PrioritizedTask).Key() >= key } )
    if apq.tail != len(apq.v) {
        for j := apq.tail ; j > apq.head+insertoffset ; j-- {
            apq.v[j] = apq.v[j-1]
        }
        apq.v[apq.head+insertoffset] = task
        apq.tail++
        return
    }
    var newv = apq.v
    if apq.head <= len(apq.v)/2 {
        newv = make([]RegisteredTask, 2* len(apq.v))
    }
    copy(newv, apq.v[apq.head:apq.head+insertoffset])
    newv[insertoffset] = task
    copy(newv[insertoffset+1:], apq.v[apq.head+insertoffset:apq.tail])
    for i := apq.head ; i < apq.tail ; i++ {
        apq.v[i] = nil
    }
    apq.v = newv
    apq.head = 0
    apq.tail = n+1
}

//  Remove the next task with a runtime O(1).
func (apq *ArrayPriorityQueue) Dequeue() RegisteredTask {
    if apq.Len() == 0 {
        panic("empty")
    }
    var task = apq.v[apq.head]
    apq.v[apq.head] = nil
    apq.head++
    return task
}

//  Add a task to the queue with runtime O(n).
func (apq *ArrayPriorityQueue) SetKey(id int64, k float64) {
}
<commit_msg>Clean up slice copy operations.<commit_after>package queues
/*
 *  Filename:    priority.go
 *  Package:     queues
 *  Author:      Bryan Matsuo <bmatsuo@soe.ucsc.edu>
 *  Created:     Wed Jul  6 22:18:57 PDT 2011
 *  Description:
 */
import (
    "sort"
    "fmt"
    "container/heap"
    "container/vector"
)

//  A PrioritizedTask is a Task that also has key (float64). Generally,
//  a lower key means higher priority.
type PrioritizedTask interface {
    Task
    Key() float64
    SetKey(float64)
}
//  A structure that satisfies the PrioritizedTask interface (and thus
//  Task aswell).
type PTask struct {
    F func(int64)
    P float64
}
func (pt *PTask) Type() string {
    return "PTask"
}
func (pt *PTask) SetFunc(f func(int64)) {
    pt.F = f
}
func (pt *PTask) Func() func(int64) {
    return pt.F
}
func (pt *PTask) Key() float64 {
    return pt.P
}
func (pt *PTask) SetKey(k float64) {
    pt.P = k
}

type pQueue struct {
    elements []RegisteredTask
}
func newPQueue() *pQueue {
    var h = new(pQueue)
    h.elements = make([]RegisteredTask, 0, 5)
    return h
}
func (h *pQueue) GetPTask(i int) PrioritizedTask {
    if n := len(h.elements) ; i < 0 || i >= n {
        panic("badindex")
    }
    return h.elements[i].Task().(PrioritizedTask)
}
func (h *pQueue) Len() int {
    return len(h.elements)
}
func (h *pQueue) Less(i, j int) bool {
    return h.GetPTask(i).Key() < h.GetPTask(j).Key()
}
func (h *pQueue) Swap(i, j int) {
    if n := len(h.elements) ; i < 0 || i >=n || j < 0 || j >= n {
        panic("badindex")
    }
    var tmp = h.elements[i]
    h.elements[i] = h.elements[j]
    h.elements[j] = tmp
}
func (h *pQueue) Push(x interface{}) {
    switch x.(RegisteredTask).Task().(type) {
    case PrioritizedTask:
        h.elements = append(h.elements, x.(RegisteredTask))
    default:
        panic("badtype")
    }
}
func (h *pQueue) Pop() interface{} {
    if len(h.elements) <= 0 {
        panic("empty")
    }
    var head = h.elements[0]
    h.elements = h.elements[1:]
    return head
}
func (h *pQueue) FindId(id int64) (int, RegisteredTask) {
    for i, elm := range h.elements {
        if elm.Id() == id {
            return i, elm
        }
    }
    return -1, nil
}

//  A heap-based priority queue. This implementation of a priority queue
//  is ideal for many situations involving a priority queue. However, other
//  priority queue implementations exist, each with their strengths and
//  weaknesses. See ArrayPriorityQueue and VectorPriorityQueue.
type PriorityQueue struct {
    h  *pQueue
}

//  Create a new heap-based priority queue.
func NewPriorityQueue() *PriorityQueue {
    var pq = new(PriorityQueue)
    pq.h = newPQueue()
    // No need to call heap.Init(pq.h) on an empty heap.
    return pq
}

//  The number of items in the queue.
func (pq *PriorityQueue) Len() int {
    return pq.h.Len()
}

//  Remove a task from the queue with runtime O(log(n)).
func (pq *PriorityQueue) Dequeue() RegisteredTask {
    if pq.Len() <= 0 {
        panic("empty")
    }
    return heap.Pop(pq.h).(RegisteredTask)
}

//  Add a task to the queue with runtime O(log(n)). The Task() method
//  of task must satisfy the PrioritizedTask interface, or a runtime
//  panic is thrown.
func (pq *PriorityQueue) Enqueue(task RegisteredTask) {
    switch task.Task().(type) {
    case PrioritizedTask:
        heap.Push(pq.h, task)
    default:
        panic(fmt.Sprintf("nokey %s", task.Task().Type()))
    }
}

//  Set a task's key with runtime O(n).
func (pq *PriorityQueue) SetKey(id int64, k float64) {
    var i, task = pq.h.FindId(id)
    if i < 0 {
        return
    }
    heap.Remove(pq.h, i)
    task.Task().(PrioritizedTask).SetKey(k)
    heap.Push(pq.h, task)
}

//  A priority queue based on the "container/vector" package. This priority
//  queue implementation has fast dequeues and slow enqueues.
type VectorPriorityQueue struct {
    head   int
    hmax   int
    v *vector.Vector
}

// Create a new VectorPriorityQueue.
func NewVectorPriorityQueue() *VectorPriorityQueue {
    var vpq = new(VectorPriorityQueue)
    vpq.v = new(vector.Vector)
    vpq.hmax = 1
    return vpq
}

func (vpq *VectorPriorityQueue) Len() int {
    return vpq.v.Len() - vpq.head
}

//  Add a task to the priority queue in O(n) time. This is done with a
//  O(log(n)) binary search and an insert operation.
func (vpq *VectorPriorityQueue) Enqueue(task RegisteredTask) {
    switch task.Task().(type) {
    case PrioritizedTask:
        break
    default:
        panic(fmt.Sprintf("nokey %s", task.Task().Type()))
    }
    var key = task.Task().(PrioritizedTask).Key()
    var insertoffset = sort.Search(vpq.Len(), func(i int) bool {
            if vpq.v.At(vpq.head+i).(RegisteredTask).Task().(PrioritizedTask).Key() >= key {
                return true
            }
            return false })
    vpq.v.Insert(vpq.head+insertoffset, task)
}

//  Remove the task with the smallest key in O(1) amortized time.
func (vpq *VectorPriorityQueue) Dequeue() RegisteredTask {
    var front = vpq.v.At(vpq.head).(RegisteredTask)
    vpq.head++
    if vpq.head >= vpq.hmax {
        vpq.v.Cut(0, vpq.head)
        vpq.hmax *= 2
        vpq.head = 0
    }
    return front
}

//  Change the value of a task's key in O(n) time. This performs search,
//  delete, and enqueue operations. Hence, this is not a fast method.
func (vpq *VectorPriorityQueue) SetKey(id int64, k float64) {
    var (
        n    = vpq.Len()
        i    int
        task RegisteredTask
    )
    for i = vpq.head ; i < n ; i++ {
        task = vpq.v.At(i).(RegisteredTask)
        if task.Id() == id {
            break
        }
    }
    if i < n {
        vpq.v.Delete(i)
        task.Task().(PrioritizedTask).SetKey(k)
        vpq.Enqueue(task)
    }
}

//  An array-based priority queue with a constant time dequeue and a
//  linear time equeue. It should slightly outperform a
//  VectorPriorityQueue, but will likely be removed from the library
//  because it is requires more maintenance.
type ArrayPriorityQueue struct {
    v          []RegisteredTask
    head, tail int
}

//  Create a new array-based priority queue.
func NewArrayPriorityQueue() *ArrayPriorityQueue {
    var apq = new(ArrayPriorityQueue)
    apq.v = make([]RegisteredTask, 10)
    return apq
}

//  The number of items in the queue.
func (apq *ArrayPriorityQueue) Len() int {
    return apq.tail - apq.head
}

//  Add a task to the queue with runtime O(n) (on average n/2 + log_2(n))
func (apq *ArrayPriorityQueue) Enqueue(task RegisteredTask) {
    key := task.Task().(PrioritizedTask).Key()
    n := apq.Len()
    // Perform a lg(n) time search for the proper insert index.
    geq := func(i int)bool{
        return apq.v[apq.head+i].Task().(PrioritizedTask).Key() >= key
    }
    var insertoffset = sort.Search(n, geq)

    // Shift elements to make room for the insertion, if possible.
    if apq.tail != len(apq.v) {
        copy(apq.v[apq.head+insertoffset+1:],
            apq.v[apq.head+insertoffset:apq.tail])
        apq.v[apq.head+insertoffset] = task
        apq.tail++
        return
    }

    // Create a larger slice and insert the task during the copy.
    var newv = apq.v
    if apq.head <= len(apq.v)/2 {
        newv = make([]RegisteredTask, 2* len(apq.v))
    }
    copy(newv, apq.v[apq.head:apq.head+insertoffset])
    newv[insertoffset] = task
    copy(newv[insertoffset+1:], apq.v[apq.head+insertoffset:apq.tail])
    var zero RegisteredTask
    for i := apq.head ; i < apq.tail ; i++ {
        apq.v[i] = zero
    }
    apq.v = newv
    apq.head = 0
    apq.tail = n+1
}

//  Remove the next task with a runtime O(1).
func (apq *ArrayPriorityQueue) Dequeue() RegisteredTask {
    if apq.Len() == 0 {
        panic("empty")
    }
    var task = apq.v[apq.head]
    apq.v[apq.head] = nil
    apq.head++
    return task
}

//  Add a task to the queue with runtime O(n).
func (apq *ArrayPriorityQueue) SetKey(id int64, k float64) {
}
<|endoftext|>


<commit_before>package bot

import (
	"encoding/json"
	"fmt"
	"log"
	"regexp"
	"sync"
	"time"
)

// Maximum amount of time that a plugin can hold a lock on a datum.
// If the plugin tries to UpdateDatum() after the timeout expires, it'll
// get an error. Even an external plugin should be able to manage
// 200 ms.
const lockTimeout = 200 * time.Millisecond

// Map of registered brains
var brains = make(map[string]func(Handler, *log.Logger) SimpleBrain)

// Global lock protecting the map of datum locks, individual datam,
// and datumLock.checkouts.
var dataLock sync.Mutex

// lock struct that protects a checked out datum
type datumLock struct {
	checkouts  int // the number of threads checking out this datum
	sync.Mutex     // the lock that protects the datum
}

// A global map of all synchronized items - we'll add the "glist"
var data = make(map[string]*datumLock)

// The lock is good as long a the token exists; token gets deleted
// on expiration.
var lockTokens = make(map[string]bool)
var ltLock sync.Mutex

const keyRegex = `[\w:]+` // keys can ony be word chars + separator (:)
var keyRe = regexp.MustCompile(keyRegex)

// checkout returns the []byte from the brain, with a lock that expires
// after lockTimeout. It returns a lock token, a pointer to the raw []byte
// data, true if the key exists, and a RetVal.
func checkout(d string, rw bool) (string, *[]byte, bool, RetVal) {
	if !keyRe.MatchString(d) {
		err := fmt.Errorf("Invalid key supplied to checkout: %s", d)
		Log(Error, err)
		return "", nil, false, InvalidDatumKey
	}
	if b.brain == nil {
		Log(Error, "Brain function called with no brain configured")
		return "", nil, false, BrainFailed
	}
	var dl *datumLock
	dataLock.Lock() // wait for access to the global list
	dl, ok := data[d]
	if ok {
		dl.checkouts++
	} else {
		dl = &datumLock{checkouts: 1}
		data[d] = dl
	}
	dataLock.Unlock()
	var lt string
	if rw { // checked out read/write, generate a lock token
		ltb := make([]byte, 8)
		random.Read(ltb)
		lt = fmt.Sprintf("%x", ltb)
		ltLock.Lock()
		lockTokens[lt] = true
		ltLock.Unlock()
	} else {
		lt = ""
	}
	dl.Lock() // block until we get the lock for the datum
	// Retrieve the datum from the brain before starting the timer
	datum, exists, err := b.brain.Retrieve(d)
	if err != nil {
		dl.Unlock()
		return "", nil, false, BrainFailed
	}
	if rw {
		// once rw lock is acquired, spin off lock expiration thread
		go func(lt string, dl *datumLock) {
			time.Sleep(lockTimeout)
			ltLock.Lock()
			if _, ok := lockTokens[lt]; ok {
				Log(Error, fmt.Sprintf("Lock token %s expired, releasing lock", lt))
				delete(lockTokens, lt)
				dataLock.Lock()
				dl.Unlock()
				dl.checkouts--
				if dl.checkouts == 0 { // nobody was waiting
					delete(data, d)
				}
				dataLock.Unlock()
			}
			ltLock.Unlock()
		}(lt, dl)
	} else { // read-only copy checked out
		dataLock.Lock()
		dl.Unlock()
		dl.checkouts--
		if dl.checkouts == 0 {
			delete(data, d)
		}
		dataLock.Unlock()
	}
	return lt, &datum, exists, Ok
}

// update sends updated []byte to the brain while holding the lock, or discards
// the data and returns an error.
func update(d, lt string, datum *[]byte) (ret RetVal) {
	if b.brain == nil {
		Log(Error, "Brain function called with no brain configured")
		return BrainFailed
	}
	dataLock.Lock() // acquire the global lock
	dl, ok := data[d]
	if !ok {
		Log(Error, fmt.Sprintf("Update called on non-existent datum: %s", d))
		return DatumNotFound
	}
	ltLock.Lock() // we hope to get this lock before the timeout thread does
	if _, ok := lockTokens[lt]; ok {
		err := b.brain.Store(d, *datum)
		dl.Unlock() // unlock after we've updated, successful or not
		if err != nil {
			Log(Error, fmt.Sprintf("Storing datum %s: %v", d, err))
			ret = BrainFailed
		}
		delete(lockTokens, lt)
		dl.checkouts--
		if dl.checkouts == 0 {
			delete(data, d)
		}
		// when !ok, the lock token is expired and the dl is already unlocked
	} else {
		ret = DatumLockExpired
	}
	ltLock.Unlock()
	// Up to now has been 'instant' (no blocking) since the global lock was acquired
	dataLock.Unlock()
	return
}

// checkoutDatum is the robot internal version of CheckoutDatum that uses
// the provided key as-is.
func checkoutDatum(key string, datum interface{}, rw bool) (locktoken string, exists bool, ret RetVal) {
	var dbytes *[]byte
	locktoken, dbytes, exists, ret = checkout(key, rw)
	if exists { // exists = true implies no error
		err := json.Unmarshal(*dbytes, datum)
		if err != nil {
			Log(Error, fmt.Errorf("Unmarshalling datum %s: %v", key, err))
			exists = false
			ret = DataFormatError
		}
	}
	return
}

// checkinDatum is the internal version of CheckinDatum that uses the key as-is
func checkinDatum(key, locktoken string) {
	if locktoken == "" {
		return
	}
	ltLock.Lock()
	if _, ok := lockTokens[locktoken]; ok { // see if the lock was even held
		delete(lockTokens, locktoken)
		dataLock.Lock()
		dl, ok := data[key]
		if ok {
			dl.Unlock()
			dl.checkouts--
			if dl.checkouts == 0 {
				delete(data, key)
			}
		}
		dataLock.Unlock()
	}
	ltLock.Unlock()
}

// updateDatum is the internal version of UpdateDatum that uses the key as-is
func updateDatum(key, locktoken string, datum interface{}) (ret RetVal) {
	dbytes, err := json.Marshal(datum)
	if err != nil {
		Log(Error, fmt.Sprintf("Unmarshalling datum %s: %v", key, err))
		return DataFormatError
	}
	return update(key, locktoken, &dbytes)
}

// CheckoutDatum gets a datum from the robot's brain and unmarshals it into
// a struct. If rw is set, the datum is checked out read-write and a non-empty
// lock token is returned that expires after lockTimeout (250ms). The bool
// return indicates whether the datum exists.
func (r *Robot) CheckoutDatum(key string, datum interface{}, rw bool) (locktoken string, exists bool, ret RetVal) {
	b.lock.RLock()
	pluginName := plugins[plugIDmap[r.pluginID]].name
	b.lock.RUnlock()
	key = pluginName + ":" + key
	return checkoutDatum(key, datum, rw)
}

// CheckinDatum unlocks a datum without updating it, it always succeeds
func (r *Robot) CheckinDatum(key, locktoken string) {
	if locktoken == "" {
		return
	}
	b.lock.RLock()
	pluginName := plugins[plugIDmap[r.pluginID]].name
	b.lock.RUnlock()
	key = pluginName + ":" + key
	checkinDatum(key, locktoken)
}

// UpdateDatum tries to update a piece of data in the robot's brain, providing
// a struct to marshall and a (hopefully good) lock token. If err != nil, the
// update failed.
func (r *Robot) UpdateDatum(key, locktoken string, datum interface{}) (ret RetVal) {
	b.lock.RLock()
	pluginName := plugins[plugIDmap[r.pluginID]].name
	b.lock.RUnlock()
	key = pluginName + ":" + key
	return updateDatum(key, locktoken, datum)
}

// RegisterSimpleBrain allows brain implementations to register a function with a named
// brain type that returns an SimpleBrain interface.
// This can only be called from a brain provider's init() function(s). Pass in a Logger
// so the brain can log it's own error messages if needed.
func RegisterSimpleBrain(name string, provider func(Handler, *log.Logger) SimpleBrain) {
	if stopRegistrations {
		return
	}
	if brains[name] != nil {
		log.Fatal("Attempted registration of duplicate brain provider name:", name)
	}
	brains[name] = provider
}
<commit_msg>Start redesigned brain core (unfinished)<commit_after>package bot

import (
	"encoding/json"
	"fmt"
	"log"
	"regexp"
	"sync"
	"time"
)

// Map of registered brains
var brains = make(map[string]func(Handler, *log.Logger) SimpleBrain)

type brainOpType int

const (
	checkOutBytes brainOpType = iota
	checkInBytes
	updateBytes
	quit
)

type brainOp struct {
	opType brainOpType
	opData interface{}
}

type checkOutRequest struct {
	key   string
	rw    bool
	reply chan checkOutReply
}

type checkInRequest struct {
	key   string
	token string
}

type updateRequest struct {
	key   string
	token string
	datum *[]byte
	reply chan RetVal
}

type checkOutReply struct {
	token  string
	bytes  *[]byte
	exists bool
	retval RetVal
}

type memState int

const (
	newMemory memState = iota
	seen
	available
)

type memstatus struct {
	state   memState
	token   string // whoever has this token owns the lock for this memory
	waiters []checkOutRequest
}

var brainChanEvents = make(chan brainOp)

// how often does the robot cycle through memories and update state?
// a value of time.Second means a lock will last between 1 and 2 seconds
const memCycle = time.Second

func replyToWaiter(m *memstatus) {
	cr := m.waiters[0]
	m.waiters = m.waiters[1:]
	lt, d, e, r := getDatum(cr.key, true)
	m.state = newMemory
	m.token = lt
	cr.reply <- checkOutReply{lt, d, e, r}
}

func getDatum(dkey string, rw bool) (token string, databytes *[]byte, exists bool, ret RetVal) {
	if !keyRe.MatchString(dkey) {
		err := fmt.Errorf("Invalid key supplied to checkout: %s", dkey)
		Log(Error, err)
		return "", nil, false, InvalidDatumKey
	}
	b.lock.RLock()
	brain := b.brain
	b.lock.RUnlock()
	if brain == nil {
		Log(Error, "Brain function called with no brain configured")
		return "", nil, false, BrainFailed
	}
	if rw { // checked out read/write, generate a lock token
		ltb := make([]byte, 8)
		random.Read(ltb)
		token = fmt.Sprintf("%x", ltb)
	} else {
		token = ""
	}
	var err error
	var db []byte
	db, exists, err = b.brain.Retrieve(dkey)
	if err != nil {
		return "", nil, false, BrainFailed
	}
	return token, &db, exists, Ok
}

func storeDatum(key string, datum *[]byte) (ret RetVal) {
	b.lock.RLock()
	brain := b.brain
	b.lock.RUnlock()
	if brain == nil {
		Log(Error, "Brain function called with no brain configured")
		return BrainFailed
	}
	err := b.brain.Store(key, *datum)
	if err != nil {
		Log(Error, fmt.Sprintf("Storing datum %s: %v", key, err))
		ret = BrainFailed
	}
	return Ok
}

var brainRunning bool
var brLock sync.RWMutex

// runBrain is the select loop that serializes access to brain
// functions and insures consistency.
func runBrain() {
	brainRunning = true
	// map key to status
	memories := make(map[string]*memstatus)
	processMemories := time.Tick(memCycle)
loop:
	for {
		select {
		case evt := <-brainChanEvents:
			switch evt.opType {
			case checkOutBytes:
				cr := evt.opData.(checkOutRequest)
				memStat, exists := memories[cr.key]
				if !exists {
					lt, d, e, r := getDatum(cr.key, cr.rw)
					if r != Ok {
						cr.reply <- checkOutReply{lt, d, e, r}
						break
					}
					if cr.rw {
						m := &memstatus{
							newMemory,
							lt,
							make([]checkOutRequest, 0, 2),
						}
						memories[cr.key] = m
					}
					cr.reply <- checkOutReply{lt, d, e, r}
					break
				}
				if !cr.rw {
					lt, d, e, r := getDatum(cr.key, cr.rw)
					cr.reply <- checkOutReply{lt, d, e, r}
					break
				} // read-write request below
				// if state is available, there are no waiters
				if memStat.state == available {
					lt, d, e, r := getDatum(cr.key, cr.rw)
					memStat.state = newMemory
					memStat.token = lt // this memory has a new owner now
					memories[cr.key] = memStat
					cr.reply <- checkOutReply{lt, d, e, r}
				} else {
					memStat.waiters = append(memStat.waiters, cr)
					memories[cr.key] = memStat
				}
			case checkInBytes:
				ci := evt.opData.(checkInRequest)
				m := memories[ci.key]
				// memory expired and somebody else owns it
				if ci.token != m.token {
					break
				}
				if len(m.waiters) > 0 {
					replyToWaiter(m)
					break
				}
				delete(memories, ci.key)
			case updateBytes:
				ur := evt.opData.(updateRequest)
				m := memories[ur.key]
				if ur.token != m.token {
					ur.reply <- DatumLockExpired
					break
				}
				ur.reply <- storeDatum(ur.key, ur.datum)
				if len(m.waiters) > 0 {
					replyToWaiter(m)
					break
				}
				delete(memories, ur.key)
			case quit:
				break loop
			}
		case <-processMemories:
			for _, m := range memories {
				switch m.state {
				case newMemory:
					m.state = seen
				case seen:
					if len(m.waiters) > 0 {
						replyToWaiter(m)
						break
					}
					m.state = available
				}
			}
		}
	}
	brLock.Lock()
	brainRunning = false
	brLock.Unlock()
}

const keyRegex = `[\w:]+` // keys can ony be word chars + separator (:)
var keyRe = regexp.MustCompile(keyRegex)

// checkout returns the []byte from the brain, with a lock token granting
// ownership for a limited time
func checkout(d string, rw bool) (string, *[]byte, bool, RetVal) {
	if !keyRe.MatchString(d) {
		err := fmt.Errorf("Invalid key supplied to checkout: %s", d)
		Log(Error, err)
		return "", nil, false, InvalidDatumKey
	}
	reply := make(chan checkOutReply)
	cr := checkOutRequest{d, rw, reply}
	brainChanEvents <- brainOp{checkOutBytes, cr}
	r := <-reply
	return r.token, r.bytes, r.exists, r.retval
}

// update sends updated []byte to the brain while holding the lock, or discards
// the data and returns an error.
func update(d, lt string, datum *[]byte) (ret RetVal) {
	if lt == "" {
		return Ok
	}
	reply := make(chan RetVal)
	ur := updateRequest{d, lt, datum, reply}
	brainChanEvents <- brainOp{updateBytes, ur}
	return <-reply
}

// checkinDatum is the internal version of CheckinDatum that uses the key as-is
func checkinDatum(key, locktoken string) {
	if locktoken == "" {
		return
	}
	ci := checkInRequest{key, locktoken}
	brainChanEvents <- brainOp{checkInBytes, ci}
}

// checkoutDatum is the robot internal version of CheckoutDatum that uses
// the provided key as-is.
func checkoutDatum(key string, datum interface{}, rw bool) (locktoken string, exists bool, ret RetVal) {
	var dbytes *[]byte
	locktoken, dbytes, exists, ret = checkout(key, rw)
	if exists { // exists = true implies no error
		err := json.Unmarshal(*dbytes, datum)
		if err != nil {
			Log(Error, fmt.Errorf("Unmarshalling datum %s: %v", key, err))
			exists = false
			ret = DataFormatError
		}
	}
	return
}

// updateDatum is the internal version of UpdateDatum that uses the key as-is
func updateDatum(key, locktoken string, datum interface{}) (ret RetVal) {
	dbytes, err := json.Marshal(datum)
	if err != nil {
		Log(Error, fmt.Sprintf("Unmarshalling datum %s: %v", key, err))
		return DataFormatError
	}
	return update(key, locktoken, &dbytes)
}

// CheckoutDatum gets a datum from the robot's brain and unmarshals it into
// a struct. If rw is set, the datum is checked out read-write and a non-empty
// lock token is returned that expires after lockTimeout (250ms). The bool
// return indicates whether the datum exists.
func (r *Robot) CheckoutDatum(key string, datum interface{}, rw bool) (locktoken string, exists bool, ret RetVal) {
	b.lock.RLock()
	pluginName := plugins[plugIDmap[r.pluginID]].name
	b.lock.RUnlock()
	key = pluginName + ":" + key
	return checkoutDatum(key, datum, rw)
}

// CheckinDatum unlocks a datum without updating it, it always succeeds
func (r *Robot) CheckinDatum(key, locktoken string) {
	if locktoken == "" {
		return
	}
	b.lock.RLock()
	pluginName := plugins[plugIDmap[r.pluginID]].name
	b.lock.RUnlock()
	key = pluginName + ":" + key
	checkinDatum(key, locktoken)
}

// UpdateDatum tries to update a piece of data in the robot's brain, providing
// a struct to marshall and a (hopefully good) lock token. If err != nil, the
// update failed.
func (r *Robot) UpdateDatum(key, locktoken string, datum interface{}) (ret RetVal) {
	b.lock.RLock()
	pluginName := plugins[plugIDmap[r.pluginID]].name
	b.lock.RUnlock()
	key = pluginName + ":" + key
	return updateDatum(key, locktoken, datum)
}

// RegisterSimpleBrain allows brain implementations to register a function with a named
// brain type that returns an SimpleBrain interface.
// This can only be called from a brain provider's init() function(s). Pass in a Logger
// so the brain can log it's own error messages if needed.
func RegisterSimpleBrain(name string, provider func(Handler, *log.Logger) SimpleBrain) {
	if stopRegistrations {
		return
	}
	if brains[name] != nil {
		log.Fatal("Attempted registration of duplicate brain provider name:", name)
	}
	brains[name] = provider
}
<|endoftext|>


<commit_before>package class

import (
    //"fmt"
    //"log"
    . "jvmgo/any"
    "jvmgo/classfile"
    "jvmgo/classpath"
)

const (
    jlObjectName = "java/lang/Object"
    jlClassName = "java/lang/Class"
    jlThreadName = "java/lang/Thread"
    jlStringName = "java/lang/String"
)

// the bootstrap class loader
type ClassLoader struct {
    classPath   *classpath.ClassPath
    classMap    map[string]*Class
}

func NewClassLoader(cp *classpath.ClassPath) (*ClassLoader) {
    classMap := map[string]*Class{}
    return &ClassLoader{cp, classMap}
}

func (self *ClassLoader) Init() {
    self.LoadClass(jlObjectName)
    self.LoadClass(jlClassName)
    jlClassClass := self.classMap[jlClassName]
    for _, class := range self.classMap {
        if class.jClass == nil {
            class.jClass = jlClassClass.NewObj()
            class.jClass.extra = class
        }
    }
    self.loadPrimitiveClasses()
    self.loadPrimitiveArrayClasses()
}

func (self *ClassLoader) loadPrimitiveClasses() {
    for _, primitiveType := range primitiveTypes {
        self.loadPrimitiveClass(primitiveType)
    }
}
func (self *ClassLoader) loadPrimitiveClass(className string) {
    jlClassClass := self.classMap[jlClassName]
    class := &Class{name: className}
    class.jClass = jlClassClass.NewObj()
    class.jClass.extra = class
    class.MarkInitialized()
    self.classMap[className] = class
}

func (self *ClassLoader) loadPrimitiveArrayClasses() {
    arrayTypes := []string{"[Z", "[B", "[C", "[S", "[I", "[J", "[F", "[D"}
    for _, arrayType := range arrayTypes {
        self.loadArrayClass(arrayType)
    }
}
func (self *ClassLoader) loadArrayClass(className string) {
    jlClassClass := self.classMap[jlClassName]
    jlObjecClass := self.classMap[jlObjectName]

    class := &Class{name: className}
    class.superClass = jlObjecClass
    class.jClass = jlClassClass.NewObj()
    class.jClass.extra = class
    class.MarkInitialized()
    self.classMap[className] = class
}

func (self *ClassLoader) getRefArrayClass(componentClass *Class) (*Class) {
    arrClassName := "[" + componentClass.Name() + ";"
    return self._getRefArrayClass(arrClassName)
}
func (self *ClassLoader) _getRefArrayClass(arrClassName string) (*Class) {
    arrClass := self.classMap[arrClassName]
    if arrClass == nil {
        self.loadArrayClass(arrClassName)
    }
    return self.classMap[arrClassName]
}

func (self *ClassLoader) StringClass() (*Class) {
    return self.getClass(jlStringName)
}
func (self *ClassLoader) ThreadClass() (*Class) {
    return self.getClass(jlThreadName)
}

// todo
func (self *ClassLoader) GetPrimitiveClass(name string) (*Class) {
    return self.getClass(name)
}

// todo dangerous
func (self *ClassLoader) getClass(name string) (*Class) {
    class := self.classMap[name]
    if class == nil {
        panic("class not loaded! " + name)
    }
    return class
}

func (self *ClassLoader) LoadClass(name string) (*Class) {
    class := self.classMap[name]
    if class != nil {
        // already loaded
        return class
    } else {
        if name[0] == '[' {
            return self._getRefArrayClass(name)
        } else {
            return self.reallyLoadClass(name)
        }
    }
}

func (self *ClassLoader) reallyLoadClass(name string) (*Class) {
    //log.Printf("load: %v", name)
    class := self.parseClassFile(name)
    self.resolveSuperClass(class)
    self.resolveInterfaces(class)
    calcStaticFieldSlots(class)
    calcInstanceFieldSlots(class)
    prepare(class)
    // todo
    class.classLoader = self
    self.classMap[name] = class

    jlClassClass := self.classMap[jlClassName]
    if jlClassClass != nil {
        class.jClass = jlClassClass.NewObj()
        class.jClass.extra = class
    }

    return class
}

func (self *ClassLoader) parseClassFile(name string) (class *Class) {
    classData, err := self.classPath.ReadClassData(name)
    if err != nil {
        // todo
        panic("class not found: " + name + "!")
    }

    cf, err := classfile.ParseClassFile(classData)
    if err != nil {
        // todo
        panic("failed to parse class file: " + name + "!" + err.Error())
    }

    return cf2class(cf)
}

// todo
func (self *ClassLoader) resolveSuperClass(class *Class) {
    if class.superClassName != "" {
        class.superClass = self.LoadClass(class.superClassName)
    }
}
func (self *ClassLoader) resolveInterfaces(class *Class) {
    for _, interfaceName := range class.interfaceNames {
        self.LoadClass(interfaceName)
    }
}

func calcStaticFieldSlots(class *Class) {
    slotId := uint(0)
    for _, field := range class.fields {
        if field.IsStatic() {
            field.slot = slotId
            slotId++
        }
    }
    class.staticFieldCount = slotId
}

func calcInstanceFieldSlots(class *Class) {
    slotId := uint(0)
    if class.superClassName != "" {
        slotId = class.superClass.instanceFieldCount
    }
    for _, field := range class.fields {
        if !field.IsStatic() {
            field.slot = slotId
            slotId++
        }
    }
    class.instanceFieldCount = slotId
}

func prepare(class *Class) {
    class.staticFieldValues = make([]Any, class.staticFieldCount)
    for _, field := range class.fields {
        if field.IsStatic() {
            class.staticFieldValues[field.slot] = field.defaultValue()
        }
    }
}
<commit_msg>code refactor<commit_after>package class

import (
    //"fmt"
    //"log"
    . "jvmgo/any"
    "jvmgo/classfile"
    "jvmgo/classpath"
)

const (
    jlObjectName = "java/lang/Object"
    jlClassName = "java/lang/Class"
    jlThreadName = "java/lang/Thread"
    jlStringName = "java/lang/String"
)

// the bootstrap class loader
type ClassLoader struct {
    classPath   *classpath.ClassPath
    classMap    map[string]*Class
}

func NewClassLoader(cp *classpath.ClassPath) (*ClassLoader) {
    classMap := map[string]*Class{}
    return &ClassLoader{cp, classMap}
}

func (self *ClassLoader) Init() {
    self.LoadClass(jlObjectName)
    self.LoadClass(jlClassName)
    jlClassClass := self.classMap[jlClassName]
    for _, class := range self.classMap {
        if class.jClass == nil {
            class.jClass = jlClassClass.NewObj()
            class.jClass.extra = class
        }
    }
    self.loadPrimitiveClasses()
    self.loadPrimitiveArrayClasses()
}

func (self *ClassLoader) loadPrimitiveClasses() {
    for _, primitiveType := range primitiveTypes {
        self.loadPrimitiveClass(primitiveType)
    }
}
func (self *ClassLoader) loadPrimitiveClass(className string) {
    jlClassClass := self.classMap[jlClassName]
    class := &Class{name: className}
    class.jClass = jlClassClass.NewObj()
    class.jClass.extra = class
    class.MarkInitialized()
    self.classMap[className] = class
}

func (self *ClassLoader) loadPrimitiveArrayClasses() {
    arrayTypes := []string{"[Z", "[B", "[C", "[S", "[I", "[J", "[F", "[D"}
    for _, arrayType := range arrayTypes {
        self.loadArrayClass(arrayType)
    }
}
func (self *ClassLoader) loadArrayClass(className string) {
    jlClassClass := self.classMap[jlClassName]
    jlObjecClass := self.classMap[jlObjectName]

    class := &Class{name: className}
    class.superClass = jlObjecClass
    class.jClass = jlClassClass.NewObj()
    class.jClass.extra = class
    class.MarkInitialized()
    self.classMap[className] = class
}

func (self *ClassLoader) getRefArrayClass(componentClass *Class) (*Class) {
    arrClassName := "[" + componentClass.Name() + ";"
    return self._getRefArrayClass(arrClassName)
}
func (self *ClassLoader) _getRefArrayClass(arrClassName string) (*Class) {
    arrClass := self.classMap[arrClassName]
    if arrClass == nil {
        self.loadArrayClass(arrClassName)
    }
    return self.classMap[arrClassName]
}

func (self *ClassLoader) StringClass() (*Class) {
    return self.getClass(jlStringName)
}
func (self *ClassLoader) ThreadClass() (*Class) {
    return self.getClass(jlThreadName)
}

// todo
func (self *ClassLoader) GetPrimitiveClass(name string) (*Class) {
    return self.getClass(name)
}

// todo dangerous
func (self *ClassLoader) getClass(name string) (*Class) {
    class := self.classMap[name]
    if class == nil {
        panic("class not loaded! " + name)
    }
    return class
}

func (self *ClassLoader) LoadClass(name string) (*Class) {
    class := self.classMap[name]
    if class != nil {
        // already loaded
        return class
    } else if name[0] == '[' {
        // array class
        return self._getRefArrayClass(name)
    } else {
        return self.reallyLoadClass(name)
    }
}

func (self *ClassLoader) reallyLoadClass(name string) (*Class) {
    //log.Printf("load: %v", name)
    class := self.parseClassFile(name)
    self.resolveSuperClass(class)
    self.resolveInterfaces(class)
    calcStaticFieldSlots(class)
    calcInstanceFieldSlots(class)
    prepare(class)
    // todo
    class.classLoader = self
    self.classMap[name] = class

    jlClassClass := self.classMap[jlClassName]
    if jlClassClass != nil {
        class.jClass = jlClassClass.NewObj()
        class.jClass.extra = class
    }

    return class
}

func (self *ClassLoader) parseClassFile(name string) (class *Class) {
    classData, err := self.classPath.ReadClassData(name)
    if err != nil {
        // todo
        panic("class not found: " + name + "!")
    }

    cf, err := classfile.ParseClassFile(classData)
    if err != nil {
        // todo
        panic("failed to parse class file: " + name + "!" + err.Error())
    }

    return cf2class(cf)
}

// todo
func (self *ClassLoader) resolveSuperClass(class *Class) {
    if class.superClassName != "" {
        class.superClass = self.LoadClass(class.superClassName)
    }
}
func (self *ClassLoader) resolveInterfaces(class *Class) {
    for _, interfaceName := range class.interfaceNames {
        self.LoadClass(interfaceName)
    }
}

func calcStaticFieldSlots(class *Class) {
    slotId := uint(0)
    for _, field := range class.fields {
        if field.IsStatic() {
            field.slot = slotId
            slotId++
        }
    }
    class.staticFieldCount = slotId
}

func calcInstanceFieldSlots(class *Class) {
    slotId := uint(0)
    if class.superClassName != "" {
        slotId = class.superClass.instanceFieldCount
    }
    for _, field := range class.fields {
        if !field.IsStatic() {
            field.slot = slotId
            slotId++
        }
    }
    class.instanceFieldCount = slotId
}

func prepare(class *Class) {
    class.staticFieldValues = make([]Any, class.staticFieldCount)
    for _, field := range class.fields {
        if field.IsStatic() {
            class.staticFieldValues[field.slot] = field.defaultValue()
        }
    }
}
<|endoftext|>


<commit_before>package slackboard

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"strings"
)

type UnixDial struct {
	sockPath string
}

func (u UnixDial) Dial(proto, addr string) (conn net.Conn, err error) {
	return net.Dial("unix", u.sockPath)
}

func sendNotification2Slackboard(server, api, body string) error {
	var client *http.Client
	var url string

	if strings.HasPrefix(server, "unix:/") {
		// UNIX Socket
		client = &http.Client{
			Transport: &http.Transport{
				Dial: (&UnixDial{sockPath: server[5:]}).Dial,
			},
		}
		url = fmt.Sprintf("http://localhost/%s", api)
	} else {
		// TCP
		client = &http.Client{}

		url = fmt.Sprintf("http://%s/%s", server, api)
		if strings.HasPrefix(server, "http://") || strings.HasPrefix(server, "https://") {
			url = fmt.Sprintf("%s/%s", server, api)
		}
	}

	resp, err := client.Post(
		url,
		"application/json",
		strings.NewReader(string(body)))
	if err != nil {
		return err
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		content, _ := ioutil.ReadAll(resp.Body)
		return errors.New(string(content))
	}

	return nil
}

func SendNotification2SlackboardDirectly(server string, payload *SlackboardDirectPayload) error {
	if strings.Index(payload.Payload.Channel, "#") != 0 &&
		strings.Index(payload.Payload.Channel, "@") != 0 {
		payload.Payload.Channel = "#" + payload.Payload.Channel
	}

	body, err := json.Marshal(payload)
	if err != nil {
		return err
	}

	return sendNotification2Slackboard(server, "notify-directly", string(body))
}

func SendNotification2Slackboard(server string, payload *SlackboardPayload) error {
	body, err := json.Marshal(payload)
	if err != nil {
		return err
	}

	return sendNotification2Slackboard(server, "notify", string(body))
}
<commit_msg>cli: set timeout for http.Client.<commit_after>package slackboard

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"strings"
	"time"
)

type UnixDial struct {
	sockPath string
}

func (u UnixDial) Dial(proto, addr string) (conn net.Conn, err error) {
	return net.Dial("unix", u.sockPath)
}

func sendNotification2Slackboard(server, api, body string) error {
	var client *http.Client
	var url string

	if strings.HasPrefix(server, "unix:/") {
		// UNIX Socket
		client = &http.Client{
			Transport: &http.Transport{
				Dial: (&UnixDial{sockPath: server[5:]}).Dial,
			},
			Timeout: 30 * time.Second,
		}
		url = fmt.Sprintf("http://localhost/%s", api)
	} else {
		// TCP
		client = &http.Client{
			Timeout: 30 * time.Second,
		}

		url = fmt.Sprintf("http://%s/%s", server, api)
		if strings.HasPrefix(server, "http://") || strings.HasPrefix(server, "https://") {
			url = fmt.Sprintf("%s/%s", server, api)
		}
	}

	resp, err := client.Post(
		url,
		"application/json",
		strings.NewReader(string(body)))
	if err != nil {
		return err
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		content, _ := ioutil.ReadAll(resp.Body)
		return errors.New(string(content))
	}

	return nil
}

func SendNotification2SlackboardDirectly(server string, payload *SlackboardDirectPayload) error {
	if strings.Index(payload.Payload.Channel, "#") != 0 &&
		strings.Index(payload.Payload.Channel, "@") != 0 {
		payload.Payload.Channel = "#" + payload.Payload.Channel
	}

	body, err := json.Marshal(payload)
	if err != nil {
		return err
	}

	return sendNotification2Slackboard(server, "notify-directly", string(body))
}

func SendNotification2Slackboard(server string, payload *SlackboardPayload) error {
	body, err := json.Marshal(payload)
	if err != nil {
		return err
	}

	return sendNotification2Slackboard(server, "notify", string(body))
}
<|endoftext|>