<commit_before>package firebase

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strconv"
	"time"

	"cloud.google.com/go/compute/metadata"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"

	"github.com/knq/jwt/bearer"
	"github.com/knq/jwt/gserviceaccount"
)

const (
	// DefaultTokenExpiration is the default expiration for generated OAuth2 tokens.
	DefaultTokenExpiration = 1 * time.Hour
)

// requiredScopes are the oauth2 scopes required for Google's service accounts
// when using with firebase.
var requiredScopes = []string{
	"https://www.googleapis.com/auth/userinfo.email",
	"https://www.googleapis.com/auth/firebase.database",
}

// Option is an option to modify a Firebase ref.
type Option func(r *Ref) error

// URL is an option to set Firebase base URL.
func URL(urlstr string) Option {
	return func(r *Ref) error {
		u, err := url.Parse(urlstr)
		if err != nil {
			return fmt.Errorf("could not parse url: %v", err)
		}

		r.url = u

		return nil
	}
}

// ProjectID is an option that sets the Firebase base URL as
// https://<projectID>.firebaseio.com/.
func ProjectID(projectID string) Option {
	return func(r *Ref) error {
		if projectID == "" {
			return errors.New("project id cannot be empty")
		}

		// set url
		err := URL("https://" + projectID + ".firebaseio.com/")(r)
		if err != nil {
			return errors.New("invalid project id")
		}

		return nil
	}
}

// Transport is an option to set the underlying HTTP transport used when making
// requests against a Firebase ref.
func Transport(roundTripper http.RoundTripper) Option {
	return func(r *Ref) error {
		r.transport = roundTripper
		return nil
	}
}

// WatchBufferLen is an option that sets the channel buffer size for the
// returned event channels from Watch and Listen.
func WatchBufferLen(len int) Option {
	return func(r *Ref) error {
		r.watchBufLen = len
		return nil
	}
}

// GoogleServiceAccountCredentialsJSON is an option that loads Google Service
// Account credentials for use with the Firebase ref from a JSON encoded buf.
//
// Google Service Account credentials can be downloaded from the Google Cloud
// console: https://console.cloud.google.com/iam-admin/serviceaccounts/
func GoogleServiceAccountCredentialsJSON(buf []byte) Option {
	return func(r *Ref) error {
		var err error

		// load service account credentials
		gsa, err := gserviceaccount.FromJSON(buf)
		if err != nil {
			return err
		}

		// simple check
		if gsa.ProjectID == "" || gsa.ClientEmail == "" || gsa.PrivateKey == "" {
			return errors.New("google service account credentials missing project_id, client_email or private_key")
		}

		// set ref url
		err = ProjectID(gsa.ProjectID)(r)
		if err != nil {
			return err
		}

		// create token source
		ts, err := gsa.TokenSource(nil, requiredScopes...)
		if err != nil {
			return err
		}

		// add subject
		err = bearer.Claim("sub", gsa.ClientEmail)(ts)
		if err != nil {
			return err
		}

		// wrap with a reusable token source
		r.source = oauth2.ReuseTokenSource(nil, ts)

		return nil
	}
}

// GoogleServiceAccountCredentialsFile is an option that loads Google Service
// Account credentials for use with the Firebase ref from the specified file.
//
// Google Service Account credentials can be downloaded from the Google Cloud
// console: https://console.cloud.google.com/iam-admin/serviceaccounts/
func GoogleServiceAccountCredentialsFile(path string) Option {
	return func(r *Ref) error {
		buf, err := ioutil.ReadFile(path)
		if err != nil {
			return fmt.Errorf("could not read google service account credentials file: %v", err)
		}

		return GoogleServiceAccountCredentialsJSON(buf)(r)
	}
}

// GoogleComputeCredentials is an option that loads the Google Service Account
// credentials from the GCE metadata associated with the GCE compute instance.
// If serviceAccount is empty, then the default service account credentials
// associated with the GCE instance will be used.
func GoogleComputeCredentials(serviceAccount string) Option {
	return func(r *Ref) error {
		var err error

		// get compute metadata scopes associated with the service account
		scopes, err := metadata.Scopes(serviceAccount)
		if err != nil {
			return err
		}

		// check if all the necessary scopes are provided
		for _, s := range requiredScopes {
			if !sliceContains(scopes, s) {
				// NOTE: if you are seeing this error, you probably need to
				// recreate your compute instance with the correct scope
				//
				// as of August 2016, there is not a way to add a scope to an
				// existing compute instance
				return fmt.Errorf("missing required scope %s in compute metadata", s)
			}
		}

		// get compute metadata project id
		projectID, err := metadata.ProjectID()
		if err != nil {
			return err
		}
		if projectID == "" {
			return errors.New("could not retrieve project id from compute metadata service")
		}

		// set ref url
		err = ProjectID(projectID)(r)
		if err != nil {
			return err
		}

		// set transport as the oauth2.Transport
		return Transport(&oauth2.Transport{
			Source: google.ComputeTokenSource(serviceAccount),
			Base:   r.transport,
		})(r)
	}
}

// DefaultQueryOptions is an option that sets the default query options on the
// ref.
func DefaultQueryOptions(opts ...QueryOption) Option {
	return func(r *Ref) error {
		r.SetQueryOptions(opts...)
		return nil
	}
}

// DefaultAuthOverride is an  option that sets the default
// auth_variable_override variable.
func DefaultAuthOverride(val interface{}) Option {
	return func(r *Ref) error {
		return DefaultQueryOptions(AuthOverride(val))(r)
	}
}

// DefaultAuthUID is an option that sets the default auth user id ("uid") via
// the auth_variable_override on the ref.
func DefaultAuthUID(uid string) Option {
	return func(r *Ref) error {
		return DefaultQueryOptions(AuthUID(uid))(r)
	}
}

// httpLogger handles logging http requests and responses.
type httpLogger struct {
	transport                 http.RoundTripper
	requestLogf, responseLogf Logf
}

// RoundTrip satisifies the http.RoundTripper interface.
func (hl *httpLogger) RoundTrip(req *http.Request) (*http.Response, error) {
	trans := hl.transport
	if trans == nil {
		trans = http.DefaultTransport
	}

	reqBody, _ := httputil.DumpRequestOut(req, true)
	res, err := trans.RoundTrip(req)
	resBody, _ := httputil.DumpResponse(res, true)

	hl.requestLogf("%s", reqBody)
	hl.responseLogf("%s", resBody)

	return res, err
}

// Logf is a logging func.
type Logf func(string, ...interface{})

// Log is an option that writes all HTTP request and response data to the
// respective logger.
//
// NOTE: this Option will not work with Watch/Listen.
func Log(requestLogf, responseLogf Logf) Option {
	return func(r *Ref) error {
		return Transport(&httpLogger{
			transport:    r.transport,
			requestLogf:  requestLogf,
			responseLogf: responseLogf,
		})(r)
	}
}

// QueryOption is an option used to modify the underlying http.Request for
// Firebase.
type QueryOption func(url.Values) error

// Shallow is a query option that toggles a query to return shallow result (ie, the keys only).
func Shallow(v url.Values) error {
	v.Add("shallow", "true")
	return nil
}

// PrintPretty is a query option that toggles pretty formatting for query
// results.
func PrintPretty(v url.Values) error {
	v.Add("print", "pretty")
	return nil
}

// jsonQuery returns a QueryOption for a field and json encodes the val.
func jsonQuery(field string, val interface{}) QueryOption {
	// json encode
	buf, err := json.Marshal(val)
	if err != nil {
		err = fmt.Errorf("could not marshal query option: %v", err)
	}

	return func(v url.Values) error {
		if err != nil {
			return err
		}

		v.Add(field, string(buf))
		return nil
	}
}

// uintQuery returns a QueryOption for a field that converts n into a string.
func uintQuery(field string, n uint) QueryOption {
	val := strconv.FormatUint(uint64(n), 10)
	return func(v url.Values) error {
		v.Add(field, val)
		return nil
	}
}

// OrderBy is a query option that sets Firebase's returned result order.
func OrderBy(field string) QueryOption {
	return jsonQuery("orderBy", field)
}

// EqualTo is a query option that sets the order by filter to equalTo val.
func EqualTo(val interface{}) QueryOption {
	return jsonQuery("equalTo", val)
}

// StartAt is a query option that sets the order by filter to startAt val.
func StartAt(val interface{}) QueryOption {
	return jsonQuery("startAt", val)
}

// EndAt is a query option that sets the order by filter to endAt val.
func EndAt(val interface{}) QueryOption {
	return jsonQuery("endAt", val)
}

// AuthOverride is a query option that sets the auth_variable_override.
func AuthOverride(val interface{}) QueryOption {
	return jsonQuery("auth_variable_override", val)
}

// AuthUID is a query option that sets the auth user id ("uid") via the
// auth_variable_override for a single query.
func AuthUID(uid string) QueryOption {
	return AuthOverride(map[string]interface{}{
		"uid": uid,
	})
}

// LimitToFirst is a query option that limit's Firebase's returned results to
// the first n items.
func LimitToFirst(n uint) QueryOption {
	return uintQuery("limitToFirst", n)
}

// LimitToLast is a query option that limit's Firebase's returned results to
// the last n items.
func LimitToLast(n uint) QueryOption {
	return uintQuery("limitToLast", n)
}

// sliceContains returns true if haystack contains needle.
func sliceContains(haystack []string, needle string) bool {
	for _, s := range haystack {
		if s == needle {
			return true
		}
	}

	return false
}
<commit_msg>Fixing oauth2 token issues for Firebase v4 compability<commit_after>package firebase

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strconv"
	"time"

	"cloud.google.com/go/compute/metadata"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"

	"github.com/knq/jwt/gserviceaccount"
)

const (
	// DefaultTokenExpiration is the default expiration for generated OAuth2 tokens.
	DefaultTokenExpiration = 1 * time.Hour
)

// requiredScopes are the oauth2 scopes required for Google's service accounts
// when using with firebase.
var requiredScopes = []string{
	"https://www.googleapis.com/auth/userinfo.email",
	"https://www.googleapis.com/auth/firebase.database",
	//"https://www.googleapis.com/auth/identitytoolkit", // will this be required in the future?
}

// Option is an option to modify a Firebase ref.
type Option func(r *Ref) error

// URL is an option to set Firebase base URL.
func URL(urlstr string) Option {
	return func(r *Ref) error {
		u, err := url.Parse(urlstr)
		if err != nil {
			return fmt.Errorf("could not parse url: %v", err)
		}

		r.url = u

		return nil
	}
}

// ProjectID is an option that sets the Firebase base URL as
// https://<projectID>.firebaseio.com/.
func ProjectID(projectID string) Option {
	return func(r *Ref) error {
		if projectID == "" {
			return errors.New("project id cannot be empty")
		}

		// set url
		err := URL("https://" + projectID + ".firebaseio.com/")(r)
		if err != nil {
			return errors.New("invalid project id")
		}

		return nil
	}
}

// Transport is an option to set the underlying HTTP transport used when making
// requests against a Firebase ref.
func Transport(roundTripper http.RoundTripper) Option {
	return func(r *Ref) error {
		r.transport = roundTripper
		return nil
	}
}

// WatchBufferLen is an option that sets the channel buffer size for the
// returned event channels from Watch and Listen.
func WatchBufferLen(len int) Option {
	return func(r *Ref) error {
		r.watchBufLen = len
		return nil
	}
}

// GoogleServiceAccountCredentialsJSON is an option that loads Google Service
// Account credentials for use with the Firebase ref from a JSON encoded buf.
//
// Google Service Account credentials can be downloaded from the Google Cloud
// console: https://console.cloud.google.com/iam-admin/serviceaccounts/
func GoogleServiceAccountCredentialsJSON(buf []byte) Option {
	return func(r *Ref) error {
		var err error

		// load service account credentials
		gsa, err := gserviceaccount.FromJSON(buf)
		if err != nil {
			return err
		}

		// simple check
		if gsa.ProjectID == "" || gsa.ClientEmail == "" || gsa.PrivateKey == "" {
			return errors.New("google service account credentials missing project_id, client_email or private_key")
		}

		// set ref url
		err = ProjectID(gsa.ProjectID)(r)
		if err != nil {
			return err
		}

		// create token source
		ts, err := gsa.TokenSource(nil, requiredScopes...)
		if err != nil {
			return err
		}

		// as of v4 it appears that including the subject with the token is
		// longer necessary, and will cause a 401 unauthorized error with newly
		// created firebase databases.
		//
		// add subject
		/*err = bearer.Claim("sub", gsa.ClientEmail)(ts)
		if err != nil {
			return err
		}*/

		// wrap with a reusable token source
		r.source = oauth2.ReuseTokenSource(nil, ts)

		return nil
	}
}

// GoogleServiceAccountCredentialsFile is an option that loads Google Service
// Account credentials for use with the Firebase ref from the specified file.
//
// Google Service Account credentials can be downloaded from the Google Cloud
// console: https://console.cloud.google.com/iam-admin/serviceaccounts/
func GoogleServiceAccountCredentialsFile(path string) Option {
	return func(r *Ref) error {
		buf, err := ioutil.ReadFile(path)
		if err != nil {
			return fmt.Errorf("could not read google service account credentials file: %v", err)
		}

		return GoogleServiceAccountCredentialsJSON(buf)(r)
	}
}

// GoogleComputeCredentials is an option that loads the Google Service Account
// credentials from the GCE metadata associated with the GCE compute instance.
// If serviceAccount is empty, then the default service account credentials
// associated with the GCE instance will be used.
func GoogleComputeCredentials(serviceAccount string) Option {
	return func(r *Ref) error {
		var err error

		// get compute metadata scopes associated with the service account
		scopes, err := metadata.Scopes(serviceAccount)
		if err != nil {
			return err
		}

		// check if all the necessary scopes are provided
		for _, s := range requiredScopes {
			if !sliceContains(scopes, s) {
				// NOTE: if you are seeing this error, you probably need to
				// recreate your compute instance with the correct scope
				//
				// as of August 2016, there is not a way to add a scope to an
				// existing compute instance
				return fmt.Errorf("missing required scope %s in compute metadata", s)
			}
		}

		// get compute metadata project id
		projectID, err := metadata.ProjectID()
		if err != nil {
			return err
		}
		if projectID == "" {
			return errors.New("could not retrieve project id from compute metadata service")
		}

		// set ref url
		err = ProjectID(projectID)(r)
		if err != nil {
			return err
		}

		// set transport as the oauth2.Transport
		return Transport(&oauth2.Transport{
			Source: google.ComputeTokenSource(serviceAccount),
			Base:   r.transport,
		})(r)
	}
}

// DefaultQueryOptions is an option that sets the default query options on the
// ref.
func DefaultQueryOptions(opts ...QueryOption) Option {
	return func(r *Ref) error {
		r.SetQueryOptions(opts...)
		return nil
	}
}

// DefaultAuthOverride is an  option that sets the default
// auth_variable_override variable.
func DefaultAuthOverride(val interface{}) Option {
	return func(r *Ref) error {
		return DefaultQueryOptions(AuthOverride(val))(r)
	}
}

// DefaultAuthUID is an option that sets the default auth user id ("uid") via
// the auth_variable_override on the ref.
func DefaultAuthUID(uid string) Option {
	return func(r *Ref) error {
		return DefaultQueryOptions(AuthUID(uid))(r)
	}
}

// httpLogger handles logging http requests and responses.
type httpLogger struct {
	transport                 http.RoundTripper
	requestLogf, responseLogf Logf
}

// RoundTrip satisifies the http.RoundTripper interface.
func (hl *httpLogger) RoundTrip(req *http.Request) (*http.Response, error) {
	trans := hl.transport
	if trans == nil {
		trans = http.DefaultTransport
	}

	reqBody, _ := httputil.DumpRequestOut(req, true)
	res, err := trans.RoundTrip(req)
	resBody, _ := httputil.DumpResponse(res, true)

	hl.requestLogf("%s", reqBody)
	hl.responseLogf("%s", resBody)

	return res, err
}

// Logf is a logging func.
type Logf func(string, ...interface{})

// Log is an option that writes all HTTP request and response data to the
// respective logger.
//
// NOTE: this Option will not work with Watch/Listen.
func Log(requestLogf, responseLogf Logf) Option {
	return func(r *Ref) error {
		return Transport(&httpLogger{
			transport:    r.transport,
			requestLogf:  requestLogf,
			responseLogf: responseLogf,
		})(r)
	}
}

// QueryOption is an option used to modify the underlying http.Request for
// Firebase.
type QueryOption func(url.Values) error

// Shallow is a query option that toggles a query to return shallow result (ie, the keys only).
func Shallow(v url.Values) error {
	v.Add("shallow", "true")
	return nil
}

// PrintPretty is a query option that toggles pretty formatting for query
// results.
func PrintPretty(v url.Values) error {
	v.Add("print", "pretty")
	return nil
}

// jsonQuery returns a QueryOption for a field and json encodes the val.
func jsonQuery(field string, val interface{}) QueryOption {
	// json encode
	buf, err := json.Marshal(val)
	if err != nil {
		err = fmt.Errorf("could not marshal query option: %v", err)
	}

	return func(v url.Values) error {
		if err != nil {
			return err
		}

		v.Add(field, string(buf))
		return nil
	}
}

// uintQuery returns a QueryOption for a field that converts n into a string.
func uintQuery(field string, n uint) QueryOption {
	val := strconv.FormatUint(uint64(n), 10)
	return func(v url.Values) error {
		v.Add(field, val)
		return nil
	}
}

// OrderBy is a query option that sets Firebase's returned result order.
func OrderBy(field string) QueryOption {
	return jsonQuery("orderBy", field)
}

// EqualTo is a query option that sets the order by filter to equalTo val.
func EqualTo(val interface{}) QueryOption {
	return jsonQuery("equalTo", val)
}

// StartAt is a query option that sets the order by filter to startAt val.
func StartAt(val interface{}) QueryOption {
	return jsonQuery("startAt", val)
}

// EndAt is a query option that sets the order by filter to endAt val.
func EndAt(val interface{}) QueryOption {
	return jsonQuery("endAt", val)
}

// AuthOverride is a query option that sets the auth_variable_override.
func AuthOverride(val interface{}) QueryOption {
	return jsonQuery("auth_variable_override", val)
}

// AuthUID is a query option that sets the auth user id ("uid") via the
// auth_variable_override for a single query.
func AuthUID(uid string) QueryOption {
	return AuthOverride(map[string]interface{}{
		"uid": uid,
	})
}

// LimitToFirst is a query option that limit's Firebase's returned results to
// the first n items.
func LimitToFirst(n uint) QueryOption {
	return uintQuery("limitToFirst", n)
}

// LimitToLast is a query option that limit's Firebase's returned results to
// the last n items.
func LimitToLast(n uint) QueryOption {
	return uintQuery("limitToLast", n)
}

// sliceContains returns true if haystack contains needle.
func sliceContains(haystack []string, needle string) bool {
	for _, s := range haystack {
		if s == needle {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package main

import (
	"debug/elf"
	"encoding/binary"
	"errors"
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"io"
	"os"
	"path/filepath"
	"strings"
)

type redirect struct {
	src string
	dst string

	srcVMA uint64
	dstVMA uint64
}

func exit(err error) {
	fmt.Fprintf(os.Stderr, "[redirects] error: %s\n", err.Error())
	os.Exit(1)
}

func pkgPrefix() (string, error) {
	goPath := os.Getenv("GOPATH") + "/src/"
	cwd, err := os.Getwd()
	if err != nil {
		return "", err
	}

	return strings.TrimPrefix(cwd, goPath), nil
}

func collectGoFiles(root string) ([]string, error) {
	var goFiles []string
	err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return err
		}

		if filepath.Ext(path) == ".go" && !strings.Contains(path, "_test") {
			goFiles = append(goFiles, path)
		}

		return err
	})
	if err != nil {
		return nil, err
	}

	return goFiles, nil
}

func findRedirects(goFiles []string) ([]*redirect, error) {
	var redirects []*redirect

	prefix, err := pkgPrefix()
	if err != nil {
		return nil, err
	}

	for _, goFile := range goFiles {
		fset := token.NewFileSet()

		f, err := parser.ParseFile(fset, goFile, nil, parser.ParseComments)
		if err != nil {
			return nil, fmt.Errorf("%s: %s", goFile, err)
		}

		cmap := ast.NewCommentMap(fset, f, f.Comments)
		cmap.Filter(f)
		for astNode, commentGroups := range cmap {
			fnDecl, ok := astNode.(*ast.FuncDecl)
			if !ok {
				continue
			}

			for _, commentGroup := range commentGroups {
				for _, comment := range commentGroup.List {
					if !strings.Contains(comment.Text, "go:redirect-from") {
						continue
					}

					// build qualified name to fn
					fqName := fmt.Sprintf("%s/%s.%s",
						prefix,
						goFile[:strings.LastIndexByte(goFile, '/')],
						fnDecl.Name,
					)

					fields := strings.Fields(comment.Text)
					if len(fields) != 2 || fields[0] != "//go:redirect-from" {
						return nil, fmt.Errorf("malformed go:redirect-from syntax for %q", fqName)
					}

					redirects = append(redirects, &redirect{
						src: fields[1],
						dst: fqName,
					})
				}
			}
		}
	}

	return redirects, nil
}

func elfRedirectTableOffset(imgFile string) (uint64, error) {
	f, err := elf.Open(imgFile)
	if err != nil {
		return 0, err
	}
	defer f.Close()

	redirectsSection := f.Section(".goredirectstbl")
	if redirectsSection == nil {
		return 0, fmt.Errorf("%s: missing .goredirectstbl section", imgFile)
	}

	return redirectsSection.Offset, nil
}

func elfWriteRedirectTable(redirects []*redirect, imgFile string) error {
	redirectTableOffset, err := elfRedirectTableOffset(imgFile)
	if err != nil {
		return err
	}

	// Open kernel image file and seek to table offset
	f, err := os.OpenFile(imgFile, os.O_WRONLY, os.ModeType)
	if err != nil {
		return err
	}
	defer f.Close()

	if _, err = f.Seek(int64(redirectTableOffset), io.SeekStart); err != nil {
		return err
	}

	for _, redirect := range redirects {
		binary.Write(f, binary.LittleEndian, redirect.srcVMA)
		binary.Write(f, binary.LittleEndian, redirect.dstVMA)
	}

	return nil
}

func elfResolveRedirectSymbols(redirects []*redirect, imgFile string) error {
	f, err := elf.Open(imgFile)
	if err != nil {
		return err
	}
	defer f.Close()

	symbols, err := f.Symbols()
	if err != nil {
		return err
	}

	for _, redirect := range redirects {
		for _, symbol := range symbols {
			if symbol.Name == redirect.src {
				redirect.srcVMA = symbol.Value
			}
			if symbol.Name == redirect.dst {
				redirect.dstVMA = symbol.Value
			}
		}

		switch {
		case redirect.srcVMA == 0:
			return fmt.Errorf("%s: could not locate address of %q", imgFile, redirect.src)
		case redirect.dstVMA == 0:
			return fmt.Errorf("%s: could not locate address of %q", imgFile, redirect.dst)
		}
	}

	return nil
}

func main() {
	flag.Parse()
	if matches, _ := filepath.Glob("kernel/"); len(matches) != 1 {
		exit(errors.New("this tool must be run from the kernel root folder"))
	}

	if len(flag.Args()) == 0 {
		exit(errors.New("missing command"))
	}

	cmd := flag.Arg(0)
	var imgFile string
	switch cmd {
	case "count":
	case "populate-table":
		if len(flag.Args()) != 2 {
			exit(errors.New("populate-table requires the path to the kernel image as an argument"))
		}
		imgFile = flag.Arg(1)
	default:
		exit(fmt.Errorf("unknown command %q", cmd))
	}

	goFiles, err := collectGoFiles("kernel/")
	if err != nil {
		exit(err)
	}

	redirects, err := findRedirects(goFiles)
	if err != nil {
		exit(err)
	}

	if cmd == "count" {
		fmt.Printf("%d", len(redirects))
		return
	}

	if err = elfResolveRedirectSymbols(redirects, imgFile); err != nil {
		exit(err)
	}

	if err = elfWriteRedirectTable(redirects, imgFile); err != nil {
		exit(err)
	}
}
<commit_msg>Redirects tool should only consider comments attached to functions<commit_after>package main

import (
	"debug/elf"
	"encoding/binary"
	"errors"
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"io"
	"os"
	"path/filepath"
	"strings"
)

type redirect struct {
	src string
	dst string

	srcVMA uint64
	dstVMA uint64
}

func exit(err error) {
	fmt.Fprintf(os.Stderr, "[redirects] error: %s\n", err.Error())
	os.Exit(1)
}

func pkgPrefix() (string, error) {
	goPath := os.Getenv("GOPATH") + "/src/"
	cwd, err := os.Getwd()
	if err != nil {
		return "", err
	}

	return strings.TrimPrefix(cwd, goPath), nil
}

func collectGoFiles(root string) ([]string, error) {
	var goFiles []string
	err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return err
		}

		if filepath.Ext(path) == ".go" && !strings.Contains(path, "_test") {
			goFiles = append(goFiles, path)
		}

		return err
	})
	if err != nil {
		return nil, err
	}

	return goFiles, nil
}

func findRedirects(goFiles []string) ([]*redirect, error) {
	var redirects []*redirect

	prefix, err := pkgPrefix()
	if err != nil {
		return nil, err
	}

	for _, goFile := range goFiles {
		fset := token.NewFileSet()

		f, err := parser.ParseFile(fset, goFile, nil, parser.ParseComments)
		if err != nil {
			return nil, fmt.Errorf("%s: %s", goFile, err)
		}

		cmap := ast.NewCommentMap(fset, f, f.Comments)
		cmap.Filter(f)
		for astNode := range cmap {
			fnDecl, ok := astNode.(*ast.FuncDecl)
			if !ok || fnDecl.Doc == nil {
				continue
			}

			for _, comment := range fnDecl.Doc.List {
				if !strings.Contains(comment.Text, "go:redirect-from") {
					continue
				}

				// build qualified name to fn
				fqName := fmt.Sprintf("%s/%s.%s",
					prefix,
					goFile[:strings.LastIndexByte(goFile, '/')],
					fnDecl.Name,
				)

				fields := strings.Fields(comment.Text)
				if len(fields) != 2 || fields[0] != "//go:redirect-from" {
					return nil, fmt.Errorf("malformed go:redirect-from syntax for %q\n-> %q", fqName, comment.Text)
				}

				redirects = append(redirects, &redirect{
					src: fields[1],
					dst: fqName,
				})
			}
		}
	}

	return redirects, nil
}

func elfRedirectTableOffset(imgFile string) (uint64, error) {
	f, err := elf.Open(imgFile)
	if err != nil {
		return 0, err
	}
	defer f.Close()

	redirectsSection := f.Section(".goredirectstbl")
	if redirectsSection == nil {
		return 0, fmt.Errorf("%s: missing .goredirectstbl section", imgFile)
	}

	return redirectsSection.Offset, nil
}

func elfWriteRedirectTable(redirects []*redirect, imgFile string) error {
	redirectTableOffset, err := elfRedirectTableOffset(imgFile)
	if err != nil {
		return err
	}

	// Open kernel image file and seek to table offset
	f, err := os.OpenFile(imgFile, os.O_WRONLY, os.ModeType)
	if err != nil {
		return err
	}
	defer f.Close()

	if _, err = f.Seek(int64(redirectTableOffset), io.SeekStart); err != nil {
		return err
	}

	for _, redirect := range redirects {
		binary.Write(f, binary.LittleEndian, redirect.srcVMA)
		binary.Write(f, binary.LittleEndian, redirect.dstVMA)
	}

	return nil
}

func elfResolveRedirectSymbols(redirects []*redirect, imgFile string) error {
	f, err := elf.Open(imgFile)
	if err != nil {
		return err
	}
	defer f.Close()

	symbols, err := f.Symbols()
	if err != nil {
		return err
	}

	for _, redirect := range redirects {
		for _, symbol := range symbols {
			if symbol.Name == redirect.src {
				redirect.srcVMA = symbol.Value
			}
			if symbol.Name == redirect.dst {
				redirect.dstVMA = symbol.Value
			}
		}

		switch {
		case redirect.srcVMA == 0:
			return fmt.Errorf("%s: could not locate address of %q", imgFile, redirect.src)
		case redirect.dstVMA == 0:
			return fmt.Errorf("%s: could not locate address of %q", imgFile, redirect.dst)
		}
	}

	return nil
}

func main() {
	flag.Parse()
	if matches, _ := filepath.Glob("kernel/"); len(matches) != 1 {
		exit(errors.New("this tool must be run from the kernel root folder"))
	}

	if len(flag.Args()) == 0 {
		exit(errors.New("missing command"))
	}

	cmd := flag.Arg(0)
	var imgFile string
	switch cmd {
	case "count":
	case "populate-table":
		if len(flag.Args()) != 2 {
			exit(errors.New("populate-table requires the path to the kernel image as an argument"))
		}
		imgFile = flag.Arg(1)
	default:
		exit(fmt.Errorf("unknown command %q", cmd))
	}

	goFiles, err := collectGoFiles("kernel/")
	if err != nil {
		exit(err)
	}

	redirects, err := findRedirects(goFiles)
	if err != nil {
		exit(err)
	}

	if cmd == "count" {
		fmt.Printf("%d", len(redirects))
		return
	}

	if err = elfResolveRedirectSymbols(redirects, imgFile); err != nil {
		exit(err)
	}

	if err = elfWriteRedirectTable(redirects, imgFile); err != nil {
		exit(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"net/url"
	"os"
	"sort"
	"strings"
)

const packageTemplateString = `<!DOCTYPE html>

		<meta charset="utf-8">
		<title>{{.Repo.PackageName}}.{{.Repo.MajorVersion}}{{.Repo.SubPath}} - {{.Repo.GopkgPath}}</title>
		<link href='//fonts.googleapis.com/css?family=Ubuntu+Mono|Ubuntu' rel='stylesheet' >
		<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet" >
		<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet" >

{{.Repo.GopkgPath}}

							{{.Synopsis}}

						 Source Code
						 API Documentation

Getting started

To get the package, execute:

go get {{.Repo.GopkgPath}}

To import this package, add the following line to your code:

import "{{.Repo.GopkgPath}}"

								{{if .CleanPackageName}}
Refer to it as {{.CleanPackageName}}.{{end}}

For more details, see the API documentation.

Versions

						{{ if .LatestVersions }}
							{{ range .LatestVersions }}

									v{{.Major}}
									→
									{{.}}

							{{ end }}
						{{ else }}

								v0
								→
								master

						{{ end }}

gopkg.in

`

var packageTemplate *template.Template

func gopkgVersionRoot(repo *Repo, version Version) string {
	return repo.GopkgVersionRoot(version)
}

var packageFuncs = template.FuncMap{
	"gopkgVersionRoot": gopkgVersionRoot,
}

func init() {
	var err error
	packageTemplate, err = template.New("page").Funcs(packageFuncs).Parse(packageTemplateString)
	if err != nil {
		fmt.Fprintf(os.Stderr, "fatal: parsing package template failed: %s\n", err)
		os.Exit(1)
	}
}

type packageData struct {
	Repo             *Repo
	LatestVersions   VersionList // Contains only the latest version for each major
	FullVersion      Version     // Version that the major requested resolves to
	CleanPackageName string
	Synopsis         string
}

// SearchResults is used with the GDDO (godoc.org) search API
type SearchResults struct {
	Results []struct {
		Path     string `json:"path"`
		Synopsis string `json:"synopsis"`
	} `json:"results"`
}

func renderPackagePage(resp http.ResponseWriter, req *http.Request, repo *Repo) {
	data := &packageData{
		Repo: repo,
	}

	// calculate version mapping
	latestVersionsMap := make(map[int]Version)
	for _, v := range repo.AllVersions {
		v2, exists := latestVersionsMap[v.Major]
		if !exists || v2.Less(v) {
			latestVersionsMap[v.Major] = v
		}
	}
	data.FullVersion = latestVersionsMap[repo.MajorVersion.Major]
	data.LatestVersions = make(VersionList, 0, len(latestVersionsMap))
	for _, v := range latestVersionsMap {
		data.LatestVersions = append(data.LatestVersions, v)
	}
	sort.Sort(sort.Reverse(data.LatestVersions))

	// find clean package name
	data.CleanPackageName = repo.PackageName
	if strings.HasPrefix(data.CleanPackageName, "go-") {
		data.CleanPackageName = data.CleanPackageName[3:]
	}
	if strings.HasSuffix(data.CleanPackageName, "-go") {
		data.CleanPackageName = data.CleanPackageName[:len(data.CleanPackageName)-3]
	}
	for i, c := range data.CleanPackageName {
		if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' {
			continue
		}
		if i > 0 && (c == '_' || c >= '0' && c <= '9') {
			continue
		}
		data.CleanPackageName = ""
		break
	}

	// retrieve synopsis
	str := `http://api.godoc.org/search?q=` + url.QueryEscape(repo.GopkgPath())
	fmt.Println(str)
	searchResp, err := http.Get(str)
	if err == nil {
		searchResults := &SearchResults{}
		err = json.NewDecoder(searchResp.Body).Decode(&searchResults)
		searchResp.Body.Close()
		if err == nil {
			gopkgPath := repo.GopkgPath()
			for _, result := range searchResults.Results {
				if result.Path == gopkgPath {
					data.Synopsis = result.Synopsis
					break
				}
			}
		}
	}

	err = packageTemplate.Execute(resp, data)
	if err != nil {
		log.Printf("error executing package page template: %v", err)
	}
}
<commit_msg>Drop debugging left over.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"net/url"
	"os"
	"sort"
	"strings"
)

const packageTemplateString = `<!DOCTYPE html>

		<meta charset="utf-8">
		<title>{{.Repo.PackageName}}.{{.Repo.MajorVersion}}{{.Repo.SubPath}} - {{.Repo.GopkgPath}}</title>
		<link href='//fonts.googleapis.com/css?family=Ubuntu+Mono|Ubuntu' rel='stylesheet' >
		<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet" >
		<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet" >

{{.Repo.GopkgPath}}

							{{.Synopsis}}

						 Source Code
						 API Documentation

Getting started

To get the package, execute:

go get {{.Repo.GopkgPath}}

To import this package, add the following line to your code:

import "{{.Repo.GopkgPath}}"

								{{if .CleanPackageName}}
Refer to it as {{.CleanPackageName}}.{{end}}

For more details, see the API documentation.

Versions

						{{ if .LatestVersions }}
							{{ range .LatestVersions }}

									v{{.Major}}
									→
									{{.}}

							{{ end }}
						{{ else }}

								v0
								→
								master

						{{ end }}

gopkg.in

`

var packageTemplate *template.Template

func gopkgVersionRoot(repo *Repo, version Version) string {
	return repo.GopkgVersionRoot(version)
}

var packageFuncs = template.FuncMap{
	"gopkgVersionRoot": gopkgVersionRoot,
}

func init() {
	var err error
	packageTemplate, err = template.New("page").Funcs(packageFuncs).Parse(packageTemplateString)
	if err != nil {
		fmt.Fprintf(os.Stderr, "fatal: parsing package template failed: %s\n", err)
		os.Exit(1)
	}
}

type packageData struct {
	Repo             *Repo
	LatestVersions   VersionList // Contains only the latest version for each major
	FullVersion      Version     // Version that the major requested resolves to
	CleanPackageName string
	Synopsis         string
}

// SearchResults is used with the godoc.org search API
type SearchResults struct {
	Results []struct {
		Path     string `json:"path"`
		Synopsis string `json:"synopsis"`
	} `json:"results"`
}

func renderPackagePage(resp http.ResponseWriter, req *http.Request, repo *Repo) {
	data := &packageData{
		Repo: repo,
	}

	// calculate version mapping
	latestVersionsMap := make(map[int]Version)
	for _, v := range repo.AllVersions {
		v2, exists := latestVersionsMap[v.Major]
		if !exists || v2.Less(v) {
			latestVersionsMap[v.Major] = v
		}
	}
	data.FullVersion = latestVersionsMap[repo.MajorVersion.Major]
	data.LatestVersions = make(VersionList, 0, len(latestVersionsMap))
	for _, v := range latestVersionsMap {
		data.LatestVersions = append(data.LatestVersions, v)
	}
	sort.Sort(sort.Reverse(data.LatestVersions))

	// find clean package name
	data.CleanPackageName = repo.PackageName
	if strings.HasPrefix(data.CleanPackageName, "go-") {
		data.CleanPackageName = data.CleanPackageName[3:]
	}
	if strings.HasSuffix(data.CleanPackageName, "-go") {
		data.CleanPackageName = data.CleanPackageName[:len(data.CleanPackageName)-3]
	}
	for i, c := range data.CleanPackageName {
		if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' {
			continue
		}
		if i > 0 && (c == '_' || c >= '0' && c <= '9') {
			continue
		}
		data.CleanPackageName = ""
		break
	}

	// retrieve synopsis
	searchResp, err := http.Get("http://api.godoc.org/search?q=" + url.QueryEscape(repo.GopkgPath()))
	if err == nil {
		searchResults := &SearchResults{}
		err = json.NewDecoder(searchResp.Body).Decode(&searchResults)
		searchResp.Body.Close()
		if err == nil {
			gopkgPath := repo.GopkgPath()
			for _, result := range searchResults.Results {
				if result.Path == gopkgPath {
					data.Synopsis = result.Synopsis
					break
				}
			}
		}
	}

	err = packageTemplate.Execute(resp, data)
	if err != nil {
		log.Printf("error executing package page template: %v", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/chrisseto/pty"
	"github.com/chrisseto/sux/pansi"
	"github.com/nsf/termbox-go"
	"io"
	"os"
	"os/exec"
)

type Pane struct {
	*exec.Cmd

	cx, cy        int
	sx, sy        int
	fg, bg        termbox.Attribute
	width, height uint16
	scrollOffset  int

	Prog string
	Args []string

	Pty    *os.File
	output io.Reader
	cells  [][]termbox.Cell
}

func CreatePane(width, height uint16, prog string, args ...string) *Pane {
	return &Pane{
		Cmd: exec.Command(prog, args...),
		cx:  0, cy: 0,
		fg: 0, bg: 0,
		scrollOffset: 0,
		drawOffset:   0,
		Prog:         prog, Args: args,
		width: width, height: height,
		Pty: nil,
	}
}

func (p *Pane) Start() error {
	pterm, err := pty.Start(p.Cmd)
	if err != nil {
		panic(err)
	}
	if err = pty.Setsize(pterm, p.height, p.width); err != nil {
		panic(err)
	}
	p.Pty = pterm
	p.cells = make([][]termbox.Cell, 1, p.height)
	p.cells[0] = make([]termbox.Cell, p.width)
	go p.outputPipe()
	return nil
}

func (p *Pane) Close() error {
	return p.Process.Kill()
}

func (p *Pane) Cells() [][]termbox.Cell {
	return p.cells[p.drawOffset:bound(p.drawOffset+int(p.height), p.drawOffset, len(p.cells))]
}

func (p *Pane) Width() uint16 {
	return p.width
}

func (p *Pane) Height() uint16 {
	return p.height
}

func (p *Pane) Scroll(far int) {
	// p.scrollOffset += far
	p.scrollOffset = bound(p.scrollOffset+far, -len(p.cells), 0)
	Redraw()
}

func (p *Pane) bottomLine() *[]termbox.Cell {
	return &p.cells[len(p.cells)-1]
}

func (p *Pane) newLine() *[]termbox.Cell {
	p.cy++
	p.cells = append(p.cells, make([]termbox.Cell, p.width))
	if len(p.cells)-p.drawOffset > int(p.height) {
		p.drawOffset++
	}
	return p.bottomLine()
}

func (p *Pane) Redraw() {
	for y, line := range p.Cells() {
		for x, cell := range line {
			termbox.SetCell(x, y, cell.Ch, cell.Fg, cell.Bg)
		}
	}
	termbox.SetCursor(p.Cursor())
}

func bound(val, min, max int) int {
	if val < min {
		return min
	}
	if val > max {
		return max
	}
	return val
}

func (p *Pane) Cursor() (int, int) {
	p.cx = bound(p.cx, 0, int(p.width)-1)
	p.cy = bound(p.cy, 0, int(p.height)-1)
	return p.cx, p.cy
}

func (p *Pane) outputPipe() {
	lexer := pansi.NewLexer()
	buf := make([]byte, 32*1024)
	// f, _ := os.Create("output.log")
	for {
		nr, err := p.Pty.Read(buf)
		if nr > 0 {
			f.Write(buf[:nr])

			for _, char := range buf[:nr] {
				lexer.Feed(char)
				if res := lexer.Result(); res != nil {
					p.handleEscapeCode(res)
					lexer.Clear()
					continue
				}
				if lexer.State() != pansi.Ground {
					continue
				}

				switch char {
				case 0x7: //Terminal Bell. Skip for the moment
				case 0xA:
					row = p.newLine()
				case 0xD:
					x, p.cx = 0, 0
				case 0x8:
					if x != 0 {
						x--
						p.cx--
					}
					(*row)[x] = termbox.Cell{' ', p.fg, p.bg}
				default:
					if x >= int(p.width) {
						row = p.newLine()
						x = 0
					}
					(*row)[x] = termbox.Cell{rune(char), p.fg, p.bg}
					x++
					p.cx++
				}
			}

			Redraw()
		}
		if err != nil {
			if err == io.EOF {
				break
			}
			panic(err)
		}

	}
}

func (p *Pane) handleEscapeCode(c *pansi.AnsiEscapeCode) {
	switch c.Type {
	case pansi.SetGraphicMode:
		p.SetGraphicMode(c.Values)
	case pansi.CursorPosition:
		if len(c.Values) == 0 {
			p.cx, p.cy = 0, 0
		} else {
			p.cx, p.cy = c.Values[0], c.Values[1]
		}
	case pansi.CursorUp:
		p.cy--
	case pansi.CursorDown:
		p.cy++
	case pansi.CursorBackward:
		p.cx--
	case pansi.CursorForward:
		p.cx++
	case pansi.EraseLine:
		row := p.bottomLine()
		for i := p.cx; i < len(*row); i++ {
			(*row)[i] = termbox.Cell{' ', p.fg, p.bg}
		}
	case pansi.EraseDisplay:
		p.Clear()
	}
}

func (p *Pane) SetGraphicMode(vals []int) {
	for i := 0; i < len(vals); i++ {
		switch vals[i] {
		case 0:
			p.fg, p.bg = 0, 0
		case 1:
			p.fg |= termbox.AttrBold
		case 7:
			p.fg, p.bg = p.bg, p.fg
		case 38:
			i++
			switch vals[i] {
			case 5:
				i++
				p.fg = termbox.Attribute(vals[i] + 1)
			case 2:
				i += 3 //TODO
			}
		case 48:
			i++
			switch vals[i] {
			case 5:
				i++
				p.bg = termbox.Attribute(vals[i] + 1)
			case 2:
				i += 3 //TODO
			}
		}
	}
}

func (p *Pane) Clear() {
	p.drawOffset = len(p.cells) - 1
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
	Redraw()
}
<commit_msg>Use White text and a black background for default colors<commit_after>package main

import (
	"github.com/chrisseto/pty"
	"github.com/chrisseto/sux/pansi"
	"github.com/nsf/termbox-go"
	"io"
	"os"
	"os/exec"
)

type Pane struct {
	*exec.Cmd

	cx, cy        int
	sx, sy        int
	fg, bg        termbox.Attribute
	width, height uint16
	scrollOffset  int

	Prog string
	Args []string

	Pty    *os.File
	output io.Reader
	cells  [][]termbox.Cell
}

func CreatePane(width, height uint16, prog string, args ...string) *Pane {
	return &Pane{
		Cmd: exec.Command(prog, args...),
		cx:  0, cy: 0,
		fg: 0, bg: 0,
		scrollOffset: 0,
		drawOffset:   0,
		Prog:         prog, Args: args,
		width: width, height: height,
		Pty: nil,
	}
}

func (p *Pane) Start() error {
	pterm, err := pty.Start(p.Cmd)
	if err != nil {
		panic(err)
	}
	if err = pty.Setsize(pterm, p.height, p.width); err != nil {
		panic(err)
	}
	p.Pty = pterm
	p.cells = make([][]termbox.Cell, 1, p.height)
	p.cells[0] = make([]termbox.Cell, p.width)
	go p.outputPipe()
	return nil
}

func (p *Pane) Close() error {
	return p.Process.Kill()
}

func (p *Pane) Cells() [][]termbox.Cell {
	return p.cells[p.drawOffset:bound(p.drawOffset+int(p.height), p.drawOffset, len(p.cells))]
}

func (p *Pane) Width() uint16 {
	return p.width
}

func (p *Pane) Height() uint16 {
	return p.height
}

func (p *Pane) Scroll(far int) {
	// p.scrollOffset += far
	p.scrollOffset = bound(p.scrollOffset+far, -len(p.cells), 0)
	Redraw()
}

func (p *Pane) bottomLine() *[]termbox.Cell {
	return &p.cells[len(p.cells)-1]
}

func (p *Pane) newLine() *[]termbox.Cell {
	p.cy++
	p.cells = append(p.cells, make([]termbox.Cell, p.width))
	if len(p.cells)-p.drawOffset > int(p.height) {
		p.drawOffset++
	}
	return p.bottomLine()
}

func (p *Pane) Redraw() {
	for y, line := range p.Cells() {
		for x, cell := range line {
			termbox.SetCell(x, y, cell.Ch, cell.Fg, cell.Bg)
		}
	}
	termbox.SetCursor(p.Cursor())
}

func bound(val, min, max int) int {
	if val < min {
		return min
	}
	if val > max {
		return max
	}
	return val
}

func (p *Pane) Cursor() (int, int) {
	p.cx = bound(p.cx, 0, int(p.width)-1)
	p.cy = bound(p.cy, 0, int(p.height)-1)
	return p.cx, p.cy
}

func (p *Pane) outputPipe() {
	lexer := pansi.NewLexer()
	buf := make([]byte, 32*1024)
	// f, _ := os.Create("output.log")
	for {
		nr, err := p.Pty.Read(buf)
		if nr > 0 {
			f.Write(buf[:nr])

			for _, char := range buf[:nr] {
				lexer.Feed(char)
				if res := lexer.Result(); res != nil {
					p.handleEscapeCode(res)
					lexer.Clear()
					continue
				}
				if lexer.State() != pansi.Ground {
					continue
				}

				switch char {
				case 0x7: //Terminal Bell. Skip for the moment
				case 0xA:
					row = p.newLine()
				case 0xD:
					x, p.cx = 0, 0
				case 0x8:
					if x != 0 {
						x--
						p.cx--
					}
					(*row)[x] = termbox.Cell{' ', p.fg, p.bg}
				default:
					if x >= int(p.width) {
						row = p.newLine()
						x = 0
					}
					(*row)[x] = termbox.Cell{rune(char), p.fg, p.bg}
					x++
					p.cx++
				}
			}

			Redraw()
		}
		if err != nil {
			if err == io.EOF {
				break
			}
			panic(err)
		}

	}
}

func (p *Pane) handleEscapeCode(c *pansi.AnsiEscapeCode) {
	switch c.Type {
	case pansi.SetGraphicMode:
		p.SetGraphicMode(c.Values)
	case pansi.CursorPosition:
		if len(c.Values) == 0 {
			p.cx, p.cy = 0, 0
		} else {
			p.cx, p.cy = c.Values[0], c.Values[1]
		}
	case pansi.CursorUp:
		p.cy--
	case pansi.CursorDown:
		p.cy++
	case pansi.CursorBackward:
		p.cx--
	case pansi.CursorForward:
		p.cx++
	case pansi.EraseLine:
		row := p.bottomLine()
		for i := p.cx; i < len(*row); i++ {
			(*row)[i] = termbox.Cell{' ', p.fg, p.bg}
		}
	case pansi.EraseDisplay:
		p.Clear()
	}
}

func (p *Pane) SetGraphicMode(vals []int) {
	for i := 0; i < len(vals); i++ {
		switch vals[i] {
		case 0:
			p.fg, p.bg = 8, 1
		case 1:
			p.fg |= termbox.AttrBold
		case 7:
			p.fg, p.bg = p.bg, p.fg
		case 38:
			i++
			switch vals[i] {
			case 5:
				i++
				p.fg = termbox.Attribute(vals[i] + 1)
			case 2:
				i += 3 //TODO
			}
		case 39:
			p.fg = termbox.ColorWhite
		case 48:
			i++
			switch vals[i] {
			case 5:
				i++
				p.bg = termbox.Attribute(vals[i] + 1)
			case 2:
				i += 3 //TODO
			}
		case 49:
			p.bg = termbox.ColorBlack
		}
	}
}

func (p *Pane) Clear() {
	p.drawOffset = len(p.cells) - 1
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
	Redraw()
}
<|endoftext|>


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

package runtime_test

import (
	"runtime"
	"syscall"
	"testing"
	"unsafe"
)

type DLL struct {
	*syscall.DLL
	t *testing.T
}

func GetDLL(t *testing.T, name string) *DLL {
	d, e := syscall.LoadDLL(name)
	if e != nil {
		t.Fatal(e)
	}
	return &DLL{DLL: d, t: t}
}

func (d *DLL) Proc(name string) *syscall.Proc {
	p, e := d.FindProc(name)
	if e != nil {
		d.t.Fatal(e)
	}
	return p
}

func TestStdCall(t *testing.T) {
	type Rect struct {
		left, top, right, bottom int32
	}
	res := Rect{}
	expected := Rect{1, 1, 40, 60}
	a, _, _ := GetDLL(t, "user32.dll").Proc("UnionRect").Call(
		uintptr(unsafe.Pointer(&res)),
		uintptr(unsafe.Pointer(&Rect{10, 1, 14, 60})),
		uintptr(unsafe.Pointer(&Rect{1, 2, 40, 50})))
	if a != 1 || res.left != expected.left ||
		res.top != expected.top ||
		res.right != expected.right ||
		res.bottom != expected.bottom {
		t.Error("stdcall USER32.UnionRect returns", a, "res=", res)
	}
}

func Test64BitReturnStdCall(t *testing.T) {

	const (
		VER_BUILDNUMBER      = 0x0000004
		VER_MAJORVERSION     = 0x0000002
		VER_MINORVERSION     = 0x0000001
		VER_PLATFORMID       = 0x0000008
		VER_PRODUCT_TYPE     = 0x0000080
		VER_SERVICEPACKMAJOR = 0x0000020
		VER_SERVICEPACKMINOR = 0x0000010
		VER_SUITENAME        = 0x0000040

		VER_EQUAL         = 1
		VER_GREATER       = 2
		VER_GREATER_EQUAL = 3
		VER_LESS          = 4
		VER_LESS_EQUAL    = 5

		ERROR_OLD_WIN_VERSION = 1150
	)

	type OSVersionInfoEx struct {
		OSVersionInfoSize uint32
		MajorVersion      uint32
		MinorVersion      uint32
		BuildNumber       uint32
		PlatformId        uint32
		CSDVersion        [128]uint16
		ServicePackMajor  uint16
		ServicePackMinor  uint16
		SuiteMask         uint16
		ProductType       byte
		Reserve           byte
	}

	d := GetDLL(t, "kernel32.dll")

	var m1, m2 uintptr
	VerSetConditionMask := d.Proc("VerSetConditionMask")
	m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_MAJORVERSION, VER_GREATER_EQUAL)
	m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_MINORVERSION, VER_GREATER_EQUAL)
	m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL)
	m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL)

	vi := OSVersionInfoEx{
		MajorVersion:     5,
		MinorVersion:     1,
		ServicePackMajor: 2,
		ServicePackMinor: 0,
	}
	vi.OSVersionInfoSize = uint32(unsafe.Sizeof(vi))
	r, _, e2 := d.Proc("VerifyVersionInfoW").Call(
		uintptr(unsafe.Pointer(&vi)),
		VER_MAJORVERSION|VER_MINORVERSION|VER_SERVICEPACKMAJOR|VER_SERVICEPACKMINOR,
		m1, m2)
	if r == 0 && e2 != ERROR_OLD_WIN_VERSION {
		t.Errorf("VerifyVersionInfo failed: (%d) %s", e2, syscall.Errstr(int(e2)))
	}
}

func TestCDecl(t *testing.T) {
	var buf [50]byte
	a, _, _ := GetDLL(t, "user32.dll").Proc("wsprintfA").Call(
		uintptr(unsafe.Pointer(&buf[0])),
		uintptr(unsafe.Pointer(syscall.StringBytePtr("%d %d %d"))),
		1000, 2000, 3000)
	if string(buf[:a]) != "1000 2000 3000" {
		t.Error("cdecl USER32.wsprintfA returns", a, "buf=", buf[:a])
	}
}

func TestEnumWindows(t *testing.T) {
	d := GetDLL(t, "user32.dll")
	isWindows := d.Proc("IsWindow")
	counter := 0
	cb := syscall.NewCallback(func(hwnd syscall.Handle, lparam uintptr) uintptr {
		if lparam != 888 {
			t.Error("lparam was not passed to callback")
		}
		b, _, _ := isWindows.Call(uintptr(hwnd))
		if b == 0 {
			t.Error("USER32.IsWindow returns FALSE")
		}
		counter++
		return 1 // continue enumeration
	})
	a, _, _ := d.Proc("EnumWindows").Call(cb, 888)
	if a == 0 {
		t.Error("USER32.EnumWindows returns FALSE")
	}
	if counter == 0 {
		t.Error("Callback has been never called or your have no windows")
	}
}

func callback(hwnd syscall.Handle, lparam uintptr) uintptr {
	(*(*func())(unsafe.Pointer(&lparam)))()
	return 0 // stop enumeration
}

// nestedCall calls into Windows, back into Go, and finally to f.
func nestedCall(t *testing.T, f func()) {
	c := syscall.NewCallback(callback)
	d := GetDLL(t, "user32.dll")
	defer d.Release()
	d.Proc("EnumWindows").Call(c, uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&f))))
}

func TestCallback(t *testing.T) {
	var x = false
	nestedCall(t, func() { x = true })
	if !x {
		t.Fatal("nestedCall did not call func")
	}
}

func TestCallbackGC(t *testing.T) {
	nestedCall(t, runtime.GC)
}

func TestCallbackPanic(t *testing.T) {
	// Make sure panic during callback unwinds properly.
	if runtime.LockedOSThread() {
		t.Fatal("locked OS thread on entry to TestCallbackPanic")
	}
	defer func() {
		s := recover()
		if s == nil {
			t.Fatal("did not panic")
		}
		if s.(string) != "callback panic" {
			t.Fatal("wrong panic:", s)
		}
		if runtime.LockedOSThread() {
			t.Fatal("locked OS thread on exit from TestCallbackPanic")
		}
	}()
	nestedCall(t, func() { panic("callback panic") })
	panic("nestedCall returned")
}

func TestCallbackPanicLoop(t *testing.T) {
	// Make sure we don't blow out m->g0 stack.
	for i := 0; i < 100000; i++ {
		TestCallbackPanic(t)
	}
}

func TestCallbackPanicLocked(t *testing.T) {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	if !runtime.LockedOSThread() {
		t.Fatal("runtime.LockOSThread didn't")
	}
	defer func() {
		s := recover()
		if s == nil {
			t.Fatal("did not panic")
		}
		if s.(string) != "callback panic" {
			t.Fatal("wrong panic:", s)
		}
		if !runtime.LockedOSThread() {
			t.Fatal("lost lock on OS thread after panic")
		}
	}()
	nestedCall(t, func() { panic("callback panic") })
	panic("nestedCall returned")
}

func TestBlockingCallback(t *testing.T) {
	c := make(chan int)
	go func() {
		for i := 0; i < 10; i++ {
			c <- <-c
		}
	}()
	nestedCall(t, func() {
		for i := 0; i < 10; i++ {
			c <- i
			if j := <-c; j != i {
				t.Errorf("out of sync %d != %d", j, i)
			}
		}
	})
}

func TestCallbackInAnotherThread(t *testing.T) {
	// TODO: test a function which calls back in another thread: QueueUserAPC() or CreateThread()
}
<commit_msg>runtime: fix syscall test to satisfy new error<commit_after>// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package runtime_test

import (
	"runtime"
	"syscall"
	"testing"
	"unsafe"
)

type DLL struct {
	*syscall.DLL
	t *testing.T
}

func GetDLL(t *testing.T, name string) *DLL {
	d, e := syscall.LoadDLL(name)
	if e != nil {
		t.Fatal(e)
	}
	return &DLL{DLL: d, t: t}
}

func (d *DLL) Proc(name string) *syscall.Proc {
	p, e := d.FindProc(name)
	if e != nil {
		d.t.Fatal(e)
	}
	return p
}

func TestStdCall(t *testing.T) {
	type Rect struct {
		left, top, right, bottom int32
	}
	res := Rect{}
	expected := Rect{1, 1, 40, 60}
	a, _, _ := GetDLL(t, "user32.dll").Proc("UnionRect").Call(
		uintptr(unsafe.Pointer(&res)),
		uintptr(unsafe.Pointer(&Rect{10, 1, 14, 60})),
		uintptr(unsafe.Pointer(&Rect{1, 2, 40, 50})))
	if a != 1 || res.left != expected.left ||
		res.top != expected.top ||
		res.right != expected.right ||
		res.bottom != expected.bottom {
		t.Error("stdcall USER32.UnionRect returns", a, "res=", res)
	}
}

func Test64BitReturnStdCall(t *testing.T) {

	const (
		VER_BUILDNUMBER      = 0x0000004
		VER_MAJORVERSION     = 0x0000002
		VER_MINORVERSION     = 0x0000001
		VER_PLATFORMID       = 0x0000008
		VER_PRODUCT_TYPE     = 0x0000080
		VER_SERVICEPACKMAJOR = 0x0000020
		VER_SERVICEPACKMINOR = 0x0000010
		VER_SUITENAME        = 0x0000040

		VER_EQUAL         = 1
		VER_GREATER       = 2
		VER_GREATER_EQUAL = 3
		VER_LESS          = 4
		VER_LESS_EQUAL    = 5

		ERROR_OLD_WIN_VERSION syscall.Errno = 1150
	)

	type OSVersionInfoEx struct {
		OSVersionInfoSize uint32
		MajorVersion      uint32
		MinorVersion      uint32
		BuildNumber       uint32
		PlatformId        uint32
		CSDVersion        [128]uint16
		ServicePackMajor  uint16
		ServicePackMinor  uint16
		SuiteMask         uint16
		ProductType       byte
		Reserve           byte
	}

	d := GetDLL(t, "kernel32.dll")

	var m1, m2 uintptr
	VerSetConditionMask := d.Proc("VerSetConditionMask")
	m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_MAJORVERSION, VER_GREATER_EQUAL)
	m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_MINORVERSION, VER_GREATER_EQUAL)
	m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL)
	m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL)

	vi := OSVersionInfoEx{
		MajorVersion:     5,
		MinorVersion:     1,
		ServicePackMajor: 2,
		ServicePackMinor: 0,
	}
	vi.OSVersionInfoSize = uint32(unsafe.Sizeof(vi))
	r, _, e2 := d.Proc("VerifyVersionInfoW").Call(
		uintptr(unsafe.Pointer(&vi)),
		VER_MAJORVERSION|VER_MINORVERSION|VER_SERVICEPACKMAJOR|VER_SERVICEPACKMINOR,
		m1, m2)
	if r == 0 && e2 != ERROR_OLD_WIN_VERSION {
		t.Errorf("VerifyVersionInfo failed: %s", e2)
	}
}

func TestCDecl(t *testing.T) {
	var buf [50]byte
	a, _, _ := GetDLL(t, "user32.dll").Proc("wsprintfA").Call(
		uintptr(unsafe.Pointer(&buf[0])),
		uintptr(unsafe.Pointer(syscall.StringBytePtr("%d %d %d"))),
		1000, 2000, 3000)
	if string(buf[:a]) != "1000 2000 3000" {
		t.Error("cdecl USER32.wsprintfA returns", a, "buf=", buf[:a])
	}
}

func TestEnumWindows(t *testing.T) {
	d := GetDLL(t, "user32.dll")
	isWindows := d.Proc("IsWindow")
	counter := 0
	cb := syscall.NewCallback(func(hwnd syscall.Handle, lparam uintptr) uintptr {
		if lparam != 888 {
			t.Error("lparam was not passed to callback")
		}
		b, _, _ := isWindows.Call(uintptr(hwnd))
		if b == 0 {
			t.Error("USER32.IsWindow returns FALSE")
		}
		counter++
		return 1 // continue enumeration
	})
	a, _, _ := d.Proc("EnumWindows").Call(cb, 888)
	if a == 0 {
		t.Error("USER32.EnumWindows returns FALSE")
	}
	if counter == 0 {
		t.Error("Callback has been never called or your have no windows")
	}
}

func callback(hwnd syscall.Handle, lparam uintptr) uintptr {
	(*(*func())(unsafe.Pointer(&lparam)))()
	return 0 // stop enumeration
}

// nestedCall calls into Windows, back into Go, and finally to f.
func nestedCall(t *testing.T, f func()) {
	c := syscall.NewCallback(callback)
	d := GetDLL(t, "user32.dll")
	defer d.Release()
	d.Proc("EnumWindows").Call(c, uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&f))))
}

func TestCallback(t *testing.T) {
	var x = false
	nestedCall(t, func() { x = true })
	if !x {
		t.Fatal("nestedCall did not call func")
	}
}

func TestCallbackGC(t *testing.T) {
	nestedCall(t, runtime.GC)
}

func TestCallbackPanic(t *testing.T) {
	// Make sure panic during callback unwinds properly.
	if runtime.LockedOSThread() {
		t.Fatal("locked OS thread on entry to TestCallbackPanic")
	}
	defer func() {
		s := recover()
		if s == nil {
			t.Fatal("did not panic")
		}
		if s.(string) != "callback panic" {
			t.Fatal("wrong panic:", s)
		}
		if runtime.LockedOSThread() {
			t.Fatal("locked OS thread on exit from TestCallbackPanic")
		}
	}()
	nestedCall(t, func() { panic("callback panic") })
	panic("nestedCall returned")
}

func TestCallbackPanicLoop(t *testing.T) {
	// Make sure we don't blow out m->g0 stack.
	for i := 0; i < 100000; i++ {
		TestCallbackPanic(t)
	}
}

func TestCallbackPanicLocked(t *testing.T) {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	if !runtime.LockedOSThread() {
		t.Fatal("runtime.LockOSThread didn't")
	}
	defer func() {
		s := recover()
		if s == nil {
			t.Fatal("did not panic")
		}
		if s.(string) != "callback panic" {
			t.Fatal("wrong panic:", s)
		}
		if !runtime.LockedOSThread() {
			t.Fatal("lost lock on OS thread after panic")
		}
	}()
	nestedCall(t, func() { panic("callback panic") })
	panic("nestedCall returned")
}

func TestBlockingCallback(t *testing.T) {
	c := make(chan int)
	go func() {
		for i := 0; i < 10; i++ {
			c <- <-c
		}
	}()
	nestedCall(t, func() {
		for i := 0; i < 10; i++ {
			c <- i
			if j := <-c; j != i {
				t.Errorf("out of sync %d != %d", j, i)
			}
		}
	})
}

func TestCallbackInAnotherThread(t *testing.T) {
	// TODO: test a function which calls back in another thread: QueueUserAPC() or CreateThread()
}
<|endoftext|>


<commit_before>package raft

import (
	"sync"
	"time"
)

//------------------------------------------------------------------------------
//
// Typedefs
//
//------------------------------------------------------------------------------

// A peer is a reference to another server involved in the consensus protocol.
type Peer struct {
	server           *server
	Name             string `json:"name"`
	ConnectionString string `json:"connectionString"`
	prevLogIndex     uint64
	mutex            sync.RWMutex
	stopChan         chan bool
	heartbeatTimeout time.Duration
}

//------------------------------------------------------------------------------
//
// Constructor
//
//------------------------------------------------------------------------------

// Creates a new peer.
func newPeer(server *server, name string, connectionString string, heartbeatTimeout time.Duration) *Peer {
	return &Peer{
		server:           server,
		Name:             name,
		ConnectionString: connectionString,
		heartbeatTimeout: heartbeatTimeout,
	}
}

//------------------------------------------------------------------------------
//
// Accessors
//
//------------------------------------------------------------------------------

// Sets the heartbeat timeout.
func (p *Peer) setHeartbeatTimeout(duration time.Duration) {
	p.heartbeatTimeout = duration
}

//--------------------------------------
// Prev log index
//--------------------------------------

// Retrieves the previous log index.
func (p *Peer) getPrevLogIndex() uint64 {
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	return p.prevLogIndex
}

// Sets the previous log index.
func (p *Peer) setPrevLogIndex(value uint64) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	p.prevLogIndex = value
}

//------------------------------------------------------------------------------
//
// Methods
//
//------------------------------------------------------------------------------

//--------------------------------------
// Heartbeat
//--------------------------------------

// Starts the peer heartbeat.
func (p *Peer) startHeartbeat() {
	p.stopChan = make(chan bool)
	c := make(chan bool)
	go p.heartbeat(c)
	<-c
}

// Stops the peer heartbeat.
func (p *Peer) stopHeartbeat(flush bool) {
	p.stopChan <- flush
}

//--------------------------------------
// Copying
//--------------------------------------

// Clones the state of the peer. The clone is not attached to a server and
// the heartbeat timer will not exist.
func (p *Peer) clone() *Peer {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	return &Peer{
		Name:             p.Name,
		ConnectionString: p.ConnectionString,
		prevLogIndex:     p.prevLogIndex,
	}
}

//--------------------------------------
// Heartbeat
//--------------------------------------

// Listens to the heartbeat timeout and flushes an AppendEntries RPC.
func (p *Peer) heartbeat(c chan bool) {
	stopChan := p.stopChan

	c <- true

	ticker := time.Tick(p.heartbeatTimeout)

	debugln("peer.heartbeat: ", p.Name, p.heartbeatTimeout)

	for {
		select {
		case flush := <-stopChan:
			if !flush {
				debugln("peer.heartbeat.stop: ", p.Name)
				return
			} else {
				// before we can safely remove a node
				// we must flush the remove command to the node first
				p.flush()
				debugln("peer.heartbeat.stop.with.flush: ", p.Name)
				return
			}

		case <-ticker:
			start := time.Now()
			p.flush()
			duration := time.Now().Sub(start)
			p.server.DispatchEvent(newEvent(HeartbeatEventType, duration, nil))
		}
	}
}

func (p *Peer) flush() {
	debugln("peer.heartbeat.flush: ", p.Name)
	prevLogIndex := p.getPrevLogIndex()
	entries, prevLogTerm := p.server.log.getEntriesAfter(prevLogIndex, p.server.maxLogEntriesPerRequest)

	if p.server.State() != Leader {
		return
	}

	if entries != nil {
		p.sendAppendEntriesRequest(newAppendEntriesRequest(p.server.currentTerm, prevLogIndex, prevLogTerm, p.server.log.CommitIndex(), p.server.name, entries))
	} else {
		p.sendSnapshotRequest(newSnapshotRequest(p.server.name, p.server.lastSnapshot))
	}
}

//--------------------------------------
// Append Entries
//--------------------------------------

// Sends an AppendEntries request to the peer through the transport.
func (p *Peer) sendAppendEntriesRequest(req *AppendEntriesRequest) {
	tracef("peer.append.send: %s->%s [prevLog:%v length: %v]\n",
		p.server.Name(), p.Name, req.PrevLogIndex, len(req.Entries))

	resp := p.server.Transporter().SendAppendEntriesRequest(p.server, p, req)
	if resp == nil {
		p.server.DispatchEvent(newEvent(HeartbeatTimeoutEventType, p, nil))
		debugln("peer.append.timeout: ", p.server.Name(), "->", p.Name)
		return
	}
	traceln("peer.append.resp: ", p.server.Name(), "<-", p.Name)

	// If successful then update the previous log index.
	p.mutex.Lock()
	if resp.Success() {
		if len(req.Entries) > 0 {
			p.prevLogIndex = req.Entries[len(req.Entries)-1].GetIndex()

			// if peer append a log entry from the current term
			// we set append to true
			if req.Entries[len(req.Entries)-1].GetTerm() == p.server.currentTerm {
				resp.append = true
			}
		}
		traceln("peer.append.resp.success: ", p.Name, "; idx =", p.prevLogIndex)
		// If it was unsuccessful then decrement the previous log index and
		// we'll try again next time.
	} else {
		if resp.Term() > server.Term() {
			// this happens when there is a new leader comes up that this leader does not
			// know yet.
			// this server can know until the new leader send a ae with higher term
			debugln("peer.append.resp.not.update: new.leader.found")
		} else if resp.CommitIndex() >= p.prevLogIndex {
			// we may miss a response from peer
			// so maybe the peer has committed the logs we just sent
			// but we did not receive the successful reply and did not increase
			// the prevLogIndex

			// peer failed to truncate the log and sent a fail reply at this time
			// we just need to update peer's prevLog index to commitIndex

			p.prevLogIndex = resp.CommitIndex()
			debugln("peer.append.resp.update: ", p.Name, "; idx =", p.prevLogIndex)

		} else if p.prevLogIndex > 0 {
			// Decrement the previous log index down until we find a match. Don't
			// let it go below where the peer's commit index is though. That's a
			// problem.
			p.prevLogIndex--
			// if it not enough, we directly decrease to the index of the
			if p.prevLogIndex > resp.Index() {
				p.prevLogIndex = resp.Index()
			}

			debugln("peer.append.resp.decrement: ", p.Name, "; idx =", p.prevLogIndex)
		}
	}
	p.mutex.Unlock()

	// Attach the peer to resp, thus server can know where it comes from
	resp.peer = p.Name
	// Send response to server for processing.
	p.server.sendAsync(resp)
}

// Sends an Snapshot request to the peer through the transport.
func (p *Peer) sendSnapshotRequest(req *SnapshotRequest) {
	debugln("peer.snap.send: ", p.Name)

	resp := p.server.Transporter().SendSnapshotRequest(p.server, p, req)
	if resp == nil {
		debugln("peer.snap.timeout: ", p.Name)
		return
	}

	debugln("peer.snap.recv: ", p.Name)

	// If successful, the peer should have been to snapshot state
	// Send it the snapshot!
	if resp.Success {
		p.sendSnapshotRecoveryRequest()
	} else {
		debugln("peer.snap.failed: ", p.Name)
		return
	}

}

// Sends an Snapshot Recovery request to the peer through the transport.
func (p *Peer) sendSnapshotRecoveryRequest() {
	req := newSnapshotRecoveryRequest(p.server.name, p.server.lastSnapshot)
	debugln("peer.snap.recovery.send: ", p.Name)
	resp := p.server.Transporter().SendSnapshotRecoveryRequest(p.server, p, req)

	if resp == nil {
		debugln("peer.snap.recovery.timeout: ", p.Name)
		return
	}

	if resp.Success {
		p.prevLogIndex = req.LastIndex
	} else {
		debugln("peer.snap.recovery.failed: ", p.Name)
		return
	}

	p.server.sendAsync(resp)
}

//--------------------------------------
// Vote Requests
//--------------------------------------

// send VoteRequest Request
func (p *Peer) sendVoteRequest(req *RequestVoteRequest, c chan *RequestVoteResponse) {
	debugln("peer.vote: ", p.server.Name(), "->", p.Name)
	req.peer = p
	if resp := p.server.Transporter().SendVoteRequest(p.server, p, req); resp != nil {
		debugln("peer.vote.recv: ", p.server.Name(), "<-", p.Name)
		resp.peer = p
		c <- resp
	} else {
		debugln("peer.vote.failed: ", p.server.Name(), "<-", p.Name)
	}
}
<commit_msg>more docs<commit_after>package raft

import (
	"sync"
	"time"
)

//------------------------------------------------------------------------------
//
// Typedefs
//
//------------------------------------------------------------------------------

// A peer is a reference to another server involved in the consensus protocol.
type Peer struct {
	server           *server
	Name             string `json:"name"`
	ConnectionString string `json:"connectionString"`
	prevLogIndex     uint64
	mutex            sync.RWMutex
	stopChan         chan bool
	heartbeatTimeout time.Duration
}

//------------------------------------------------------------------------------
//
// Constructor
//
//------------------------------------------------------------------------------

// Creates a new peer.
func newPeer(server *server, name string, connectionString string, heartbeatTimeout time.Duration) *Peer {
	return &Peer{
		server:           server,
		Name:             name,
		ConnectionString: connectionString,
		heartbeatTimeout: heartbeatTimeout,
	}
}

//------------------------------------------------------------------------------
//
// Accessors
//
//------------------------------------------------------------------------------

// Sets the heartbeat timeout.
func (p *Peer) setHeartbeatTimeout(duration time.Duration) {
	p.heartbeatTimeout = duration
}

//--------------------------------------
// Prev log index
//--------------------------------------

// Retrieves the previous log index.
func (p *Peer) getPrevLogIndex() uint64 {
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	return p.prevLogIndex
}

// Sets the previous log index.
func (p *Peer) setPrevLogIndex(value uint64) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	p.prevLogIndex = value
}

//------------------------------------------------------------------------------
//
// Methods
//
//------------------------------------------------------------------------------

//--------------------------------------
// Heartbeat
//--------------------------------------

// Starts the peer heartbeat.
func (p *Peer) startHeartbeat() {
	p.stopChan = make(chan bool)
	c := make(chan bool)
	go p.heartbeat(c)
	<-c
}

// Stops the peer heartbeat.
func (p *Peer) stopHeartbeat(flush bool) {
	p.stopChan <- flush
}

//--------------------------------------
// Copying
//--------------------------------------

// Clones the state of the peer. The clone is not attached to a server and
// the heartbeat timer will not exist.
func (p *Peer) clone() *Peer {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	return &Peer{
		Name:             p.Name,
		ConnectionString: p.ConnectionString,
		prevLogIndex:     p.prevLogIndex,
	}
}

//--------------------------------------
// Heartbeat
//--------------------------------------

// Listens to the heartbeat timeout and flushes an AppendEntries RPC.
func (p *Peer) heartbeat(c chan bool) {
	stopChan := p.stopChan

	c <- true

	ticker := time.Tick(p.heartbeatTimeout)

	debugln("peer.heartbeat: ", p.Name, p.heartbeatTimeout)

	for {
		select {
		case flush := <-stopChan:
			if !flush {
				debugln("peer.heartbeat.stop: ", p.Name)
				return
			} else {
				// before we can safely remove a node
				// we must flush the remove command to the node first
				p.flush()
				debugln("peer.heartbeat.stop.with.flush: ", p.Name)
				return
			}

		case <-ticker:
			start := time.Now()
			p.flush()
			duration := time.Now().Sub(start)
			p.server.DispatchEvent(newEvent(HeartbeatEventType, duration, nil))
		}
	}
}

func (p *Peer) flush() {
	debugln("peer.heartbeat.flush: ", p.Name)
	prevLogIndex := p.getPrevLogIndex()
	entries, prevLogTerm := p.server.log.getEntriesAfter(prevLogIndex, p.server.maxLogEntriesPerRequest)

	if p.server.State() != Leader {
		return
	}

	if entries != nil {
		p.sendAppendEntriesRequest(newAppendEntriesRequest(p.server.currentTerm, prevLogIndex, prevLogTerm, p.server.log.CommitIndex(), p.server.name, entries))
	} else {
		p.sendSnapshotRequest(newSnapshotRequest(p.server.name, p.server.lastSnapshot))
	}
}

//--------------------------------------
// Append Entries
//--------------------------------------

// Sends an AppendEntries request to the peer through the transport.
func (p *Peer) sendAppendEntriesRequest(req *AppendEntriesRequest) {
	tracef("peer.append.send: %s->%s [prevLog:%v length: %v]\n",
		p.server.Name(), p.Name, req.PrevLogIndex, len(req.Entries))

	resp := p.server.Transporter().SendAppendEntriesRequest(p.server, p, req)
	if resp == nil {
		p.server.DispatchEvent(newEvent(HeartbeatTimeoutEventType, p, nil))
		debugln("peer.append.timeout: ", p.server.Name(), "->", p.Name)
		return
	}
	traceln("peer.append.resp: ", p.server.Name(), "<-", p.Name)

	// If successful then update the previous log index.
	p.mutex.Lock()
	if resp.Success() {
		if len(req.Entries) > 0 {
			p.prevLogIndex = req.Entries[len(req.Entries)-1].GetIndex()

			// if peer append a log entry from the current term
			// we set append to true
			if req.Entries[len(req.Entries)-1].GetTerm() == p.server.currentTerm {
				resp.append = true
			}
		}
		traceln("peer.append.resp.success: ", p.Name, "; idx =", p.prevLogIndex)
		// If it was unsuccessful then decrement the previous log index and
		// we'll try again next time.
	} else {
		if resp.Term() > server.Term() {
			// this happens when there is a new leader comes up that this *leader* has not
			// known yet.
			// this server can know until the new leader send a ae with higher term
			// or this server finish processing this response.
			debugln("peer.append.resp.not.update: new.leader.found")
		} else if resp.CommitIndex() >= p.prevLogIndex {
			// we may miss a response from peer
			// so maybe the peer has committed the logs we just sent
			// but we did not receive the successful reply and did not increase
			// the prevLogIndex

			// peer failed to truncate the log and sent a fail reply at this time
			// we just need to update peer's prevLog index to commitIndex

			p.prevLogIndex = resp.CommitIndex()
			debugln("peer.append.resp.update: ", p.Name, "; idx =", p.prevLogIndex)

		} else if p.prevLogIndex > 0 {
			// Decrement the previous log index down until we find a match. Don't
			// let it go below where the peer's commit index is though. That's a
			// problem.
			p.prevLogIndex--
			// if it not enough, we directly decrease to the index of the
			if p.prevLogIndex > resp.Index() {
				p.prevLogIndex = resp.Index()
			}

			debugln("peer.append.resp.decrement: ", p.Name, "; idx =", p.prevLogIndex)
		}
	}
	p.mutex.Unlock()

	// Attach the peer to resp, thus server can know where it comes from
	resp.peer = p.Name
	// Send response to server for processing.
	p.server.sendAsync(resp)
}

// Sends an Snapshot request to the peer through the transport.
func (p *Peer) sendSnapshotRequest(req *SnapshotRequest) {
	debugln("peer.snap.send: ", p.Name)

	resp := p.server.Transporter().SendSnapshotRequest(p.server, p, req)
	if resp == nil {
		debugln("peer.snap.timeout: ", p.Name)
		return
	}

	debugln("peer.snap.recv: ", p.Name)

	// If successful, the peer should have been to snapshot state
	// Send it the snapshot!
	if resp.Success {
		p.sendSnapshotRecoveryRequest()
	} else {
		debugln("peer.snap.failed: ", p.Name)
		return
	}

}

// Sends an Snapshot Recovery request to the peer through the transport.
func (p *Peer) sendSnapshotRecoveryRequest() {
	req := newSnapshotRecoveryRequest(p.server.name, p.server.lastSnapshot)
	debugln("peer.snap.recovery.send: ", p.Name)
	resp := p.server.Transporter().SendSnapshotRecoveryRequest(p.server, p, req)

	if resp == nil {
		debugln("peer.snap.recovery.timeout: ", p.Name)
		return
	}

	if resp.Success {
		p.prevLogIndex = req.LastIndex
	} else {
		debugln("peer.snap.recovery.failed: ", p.Name)
		return
	}

	p.server.sendAsync(resp)
}

//--------------------------------------
// Vote Requests
//--------------------------------------

// send VoteRequest Request
func (p *Peer) sendVoteRequest(req *RequestVoteRequest, c chan *RequestVoteResponse) {
	debugln("peer.vote: ", p.server.Name(), "->", p.Name)
	req.peer = p
	if resp := p.server.Transporter().SendVoteRequest(p.server, p, req); resp != nil {
		debugln("peer.vote.recv: ", p.server.Name(), "<-", p.Name)
		resp.peer = p
		c <- resp
	} else {
		debugln("peer.vote.failed: ", p.server.Name(), "<-", p.Name)
	}
}
<|endoftext|>


<commit_before>package okapi

import (
	"math/big"
)

// KeyConstructor creates a PrivateKey for given algorithm and purpose.
// The parameters contain the required constituents of the key
// which are algorithm and key type specific.
// If parameters contain only public key constituents the constructor returns
// a partially initialized PrivateKey that can only be used to obtain a PublicKey from it.
// The parameters may also contain key generation parameters
// in which case a full PrivateKey will be generated
type KeyConstructor func(parameters interface{}) (*PrivateKey, error)

// Predefined key constructors for known algorithms and purposes, implementations are provided by subpackages. Note that different implementations can support different set of algorithms/purposes. If given algorithm/purpose combination is not supported by the imported implementations, the value of the corresponding variable will be nil.
var (
	// encryption
	RSA
	// signing
	RSA_MD5, RSA_SHA1, RSA_SHA224, RSA_SHA256, RSA_384, RSA_SHA512
	DSA_SHA1, DSA_224, DSA_SHA256, DSA_384, DSA_SHA512
	ECDSA_SHA1, ECDSA_224, ECDSA_SHA256, ECDSA_384, ECDSA_SHA512
	// key agreement
	DH, ECDH KeyConstructor
)

// PrivateKey provides private key operations for given public key algorithm and purpose.
// The purpose determins which operations are available:
// * encryption: Decrypt
// * signing: Sign
// * key agreement: Derive
type PrivateKey interface {
	// Decrypt decrypts provided input.
	Decrypt(encrypted []byte) (decrypted []byte, err error)
	// Sign generates a signature for the provided input digest.
	// The digest must match the configured key type.
	// The signature format is algorithm specific
	Sign(digest []byte) (signature []byte, err error)
	// Derive generates a shared secret from the public key
	// provided by the other participant of the key agreement
	Derive(pub PublicKey) (secret []byte, err error)
	// Extract a PublicKey from the PrivateKey
	PublicKey() *PublicKey
	// Close MUST be called before discarding a key instance to securely discard and release any associated resources.
	Close()
}

// PublicKey provides public key operations for given public key algorithm and purpose.
// The purpose determins which operations are available:
// * encryption: Encrypt
// * signing: Verify
type PublicKey interface {
	// Encrypt encrypts provided input.
	// Note that the size of input is constrained by the size of the PrivateKey
	Encrypt(plain []byte) (encrypted []byte, err error)
	// Verify checks whether provided signature matches the provided digest.
	// The digest and signature type must match the configured key type.
	Verify(signature []byte, digest []byte) (valid bool, err error)
	// Close MUST be called before discarding a key instance to securely discard and release any associated resources.
	Close()
}
<commit_msg>add PKCS1 1.5/2.0 constructors<commit_after>package okapi

import (
	"math/big"
)

// KeyConstructor creates a PrivateKey for given algorithm and purpose.
// The parameters contain the required constituents of the key
// which are algorithm and key type specific.
// If parameters contain only public key constituents the constructor returns
// a partially initialized PrivateKey that can only be used to obtain a PublicKey from it.
// The parameters may also contain key generation parameters
// in which case a full PrivateKey will be generated
type KeyConstructor func(parameters interface{}) (*PrivateKey, error)

// Predefined key constructors for known algorithms and purposes, implementations are provided by subpackages. Note that different implementations can support different set of algorithms/purposes. If given algorithm/purpose combination is not supported by the imported implementations, the value of the corresponding variable will be nil.
var (
	// encryption
	RSA_PKCS1, RSA_OAEP,
	// signing PKCS1v1.5
	RSA_PKCS1_MD5, RSA_PKCS1_SHA1, RSA_PKCS1_SHA224, RSA_PKCS1_SHA256, RSA_PKCS1_SHA384, RSA_PKCS1_SHA512,
	// signing PKCS1v2.0
	RSA_PSS_MD5, RSA_PSS_SHA1, RSA_PSS_SHA224, RSA_PSS_SHA256, RSA_PSS_SHA384, RSA_PSS_SHA512,
	// signing DSS
	DSA_SHA1, DSA_SHA224, DSA_SHA256, DSA_SHA384, DSA_SHA512,
	ECDSA_SHA1, ECDSA_224, ECDSA_SHA256, ECDSA_384, ECDSA_SHA512,
	// key agreement
	DH, ECDH KeyConstructor
)

// PrivateKey provides private key operations for given public key algorithm and purpose.
// The purpose determins which operations are available:
// * encryption: Decrypt
// * signing: Sign
// * key agreement: Derive
type PrivateKey interface {
	// Decrypt decrypts provided input.
	Decrypt(encrypted []byte) (decrypted []byte, err error)
	// Sign generates a signature for the provided input digest.
	// The digest must match the configured key type.
	// The signature format is algorithm specific
	Sign(digest []byte) (signature []byte, err error)
	// Derive generates a shared secret from the public key
	// provided by the other participant of the key agreement
	Derive(pub PublicKey) (secret []byte, err error)
	// Extract a PublicKey from the PrivateKey
	PublicKey() *PublicKey
	// Close MUST be called before discarding a key instance to securely discard and release any associated resources.
	Close()
}

// PublicKey provides public key operations for given public key algorithm and purpose.
// The purpose determins which operations are available:
// * encryption: Encrypt
// * signing: Verify
type PublicKey interface {
	// Encrypt encrypts provided input.
	// Note that the size of input is constrained by the size of the PrivateKey
	Encrypt(plain []byte) (encrypted []byte, err error)
	// Verify checks whether provided signature matches the provided digest.
	// The digest and signature type must match the configured key type.
	Verify(signature []byte, digest []byte) (valid bool, err error)
	// Close MUST be called before discarding a key instance to securely discard and release any associated resources.
	Close()
}
<|endoftext|>


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

package texttest

import (
	"encoding"
	"testing"

	"github.com/issue9/assert"
)

var (
	_ encoding.TextMarshaler   = &TextObject{}
	_ encoding.TextUnmarshaler = &TextObject{}
)

func TestTextObject(t *testing.T) {
	a := assert.New(t)

	obj := &TextObject{Name: "name", Age: 1}
	data, err := obj.MarshalText()
	a.NotError(err).
		NotNil(data).
		Equal(string(data), "name,1")

	a.NotError(obj.UnmarshalText([]byte("unmarshal,22")))
	a.Equal(obj.Name, "unmarshal").Equal(obj.Age, 22)
}
<commit_msg>添加测试内容<commit_after>// Copyright 2018 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package texttest

import (
	"encoding"
	"testing"

	"github.com/issue9/assert"
)

var (
	_ encoding.TextMarshaler   = &TextObject{}
	_ encoding.TextUnmarshaler = &TextObject{}
)

func TestTextObject(t *testing.T) {
	a := assert.New(t)

	obj := &TextObject{Name: "name", Age: 1}
	data, err := obj.MarshalText()
	a.NotError(err).
		NotNil(data).
		Equal(string(data), "name,1")

	a.NotError(obj.UnmarshalText([]byte("unmarshal,22")))
	a.Equal(obj.Name, "unmarshal").Equal(obj.Age, 22)

	a.Error(obj.UnmarshalText([]byte("unmarshal,22,33")))

	a.Error(obj.UnmarshalText([]byte("22,unmarshal")))
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform/helper/schema"
)

func dataSourceAwsSecurityGroup() *schema.Resource {
	return &schema.Resource{
		Read: dataSourceAwsSecurityGroupRead,

		Schema: map[string]*schema.Schema{
			"vpc_id": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"name": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"filter": ec2CustomFiltersSchema(),

			"id": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"tags": tagsSchemaComputed(),
		},
	}
}

func dataSourceAwsSecurityGroupRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn
	req := &ec2.DescribeSecurityGroupsInput{}

	if id, idExists := d.GetOk("id"); idExists {
		req.GroupIds = []*string{aws.String(id.(string))}
	}

	req.Filters = buildEC2AttributeFilterList(
		map[string]string{
			"group-name": d.Get("name").(string),
			"vpc-id":     d.Get("vpc_id").(string),
		},
	)
	req.Filters = append(req.Filters, buildEC2TagFilterList(
		tagsFromMap(d.Get("tags").(map[string]interface{})),
	)...)
	req.Filters = append(req.Filters, buildEC2CustomFilterList(
		d.Get("filter").(*schema.Set),
	)...)
	if len(req.Filters) == 0 {
		// Don't send an empty filters list; the EC2 API won't accept it.
		req.Filters = nil
	}

	log.Printf("[DEBUG] Describe Security Groups %v\n", req)
	resp, err := conn.DescribeSecurityGroups(req)
	if err != nil {
		return err
	}
	if resp == nil || len(resp.SecurityGroups) == 0 {
		return fmt.Errorf("no matching SecurityGroup found")
	}
	if len(resp.SecurityGroups) > 1 {
		return fmt.Errorf("multiple Security Groups matched; use additional constraints to reduce matches to a single Security Group")
	}

	sg := resp.SecurityGroups[0]

	d.SetId(*sg.GroupId)
	d.Set("id", sg.VpcId)
	d.Set("name", sg.GroupName)
	d.Set("description", sg.Description)
	d.Set("vpc_id", sg.VpcId)
	d.Set("tags", tagsToMap(sg.Tags))
	d.Set("arn", fmt.Sprintf("arn:%s:ec2:%s:%s/security-group/%s",
		meta.(*AWSClient).partition, meta.(*AWSClient).region, *sg.OwnerId, *sg.GroupId))

	return nil
}
<commit_msg>fixing malformed arn attribute for security group data source (#910)<commit_after>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform/helper/schema"
)

func dataSourceAwsSecurityGroup() *schema.Resource {
	return &schema.Resource{
		Read: dataSourceAwsSecurityGroupRead,

		Schema: map[string]*schema.Schema{
			"vpc_id": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"name": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"filter": ec2CustomFiltersSchema(),

			"id": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"tags": tagsSchemaComputed(),
		},
	}
}

func dataSourceAwsSecurityGroupRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn
	req := &ec2.DescribeSecurityGroupsInput{}

	if id, idExists := d.GetOk("id"); idExists {
		req.GroupIds = []*string{aws.String(id.(string))}
	}

	req.Filters = buildEC2AttributeFilterList(
		map[string]string{
			"group-name": d.Get("name").(string),
			"vpc-id":     d.Get("vpc_id").(string),
		},
	)
	req.Filters = append(req.Filters, buildEC2TagFilterList(
		tagsFromMap(d.Get("tags").(map[string]interface{})),
	)...)
	req.Filters = append(req.Filters, buildEC2CustomFilterList(
		d.Get("filter").(*schema.Set),
	)...)
	if len(req.Filters) == 0 {
		// Don't send an empty filters list; the EC2 API won't accept it.
		req.Filters = nil
	}

	log.Printf("[DEBUG] Describe Security Groups %v\n", req)
	resp, err := conn.DescribeSecurityGroups(req)
	if err != nil {
		return err
	}
	if resp == nil || len(resp.SecurityGroups) == 0 {
		return fmt.Errorf("no matching SecurityGroup found")
	}
	if len(resp.SecurityGroups) > 1 {
		return fmt.Errorf("multiple Security Groups matched; use additional constraints to reduce matches to a single Security Group")
	}

	sg := resp.SecurityGroups[0]

	d.SetId(*sg.GroupId)
	d.Set("id", sg.VpcId)
	d.Set("name", sg.GroupName)
	d.Set("description", sg.Description)
	d.Set("vpc_id", sg.VpcId)
	d.Set("tags", tagsToMap(sg.Tags))
	d.Set("arn", fmt.Sprintf("arn:%s:ec2:%s:%s:security-group/%s",
		meta.(*AWSClient).partition, meta.(*AWSClient).region, *sg.OwnerId, *sg.GroupId))

	return nil
}
<|endoftext|>


<commit_before>// IronCache (cloud k/v store) client library
package cache

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

	"github.com/iron-io/iron_go/api"
	"github.com/iron-io/iron_go/config"
)

var (
	JSON = Codec{Marshal: json.Marshal, Unmarshal: json.Unmarshal}
	Gob  = Codec{Marshal: gobMarshal, Unmarshal: gobUnmarshal}
)

type Cache struct {
	Settings config.Settings
	Name     string
}

type Item struct {
	// Value is the Item's value
	Value interface{}
	// Object is the Item's value for use with a Codec.
	Object interface{}
	// Number of seconds until expiration. The zero value defaults to 7 days,
	// maximum is 30 days.
	Expiration time.Duration
	// Caches item only if the key is currently cached.
	Replace bool
	// Caches item only if the key isn't currently cached.
	Add bool
}

// New returns a struct ready to make requests with.
// The cacheName argument is used as namespace.
func New(cacheName string) *Cache {
	return &Cache{Settings: config.Config("iron_cache"), Name: cacheName}
}

func (c *Cache) caches(suffix ...string) *api.URL {
	return api.Action(c.Settings, "caches", suffix...)
}

func (c *Cache) ListCaches(page, perPage int) (caches []*Cache, err error) {
	out := []struct {
		Project_id string
		Name       string
	}{}

	err = c.caches().
		QueryAdd("page", "%d", page).
		QueryAdd("per_page", "%d", perPage).
		Req("GET", nil, &out)
	if err != nil {
		return
	}

	caches = make([]*Cache, 0, len(out))
	for _, item := range out {
		caches = append(caches, &Cache{
			Settings: c.Settings,
			Name:     item.Name,
		})
	}

	return
}

func (c *Cache) ServerVersion() (version string, err error) {
	out := map[string]string{}
	err = api.VersionAction(c.Settings).Req("GET", nil, &out)
	if err != nil {
		return
	}
	return out["version"], nil
}

func (c *Cache) Clear() (err error) {
	return c.caches(c.Name, "clear").Req("POST", nil, nil)
}

// Put adds an Item to the cache, overwriting any existing key of the same name.
func (c *Cache) Put(key string, item *Item) (err error) {
	in := struct {
		Body      interface{} `json:"body"`
		ExpiresIn int         `json:"expires_in,omitempty"`
		Replace   bool        `json:"replace,omitempty"`
		Add       bool        `json:"add,omitempty"`
	}{
		Body:      item.Value,
		ExpiresIn: int(item.Expiration.Seconds()),
		Replace:   item.Replace,
		Add:       item.Add,
	}

	return c.caches(c.Name, "items", key).Req("PUT", &in, nil)
}

func anyToString(value interface{}) (str interface{}, err error) {
	switch v := value.(type) {
	case string:
		str = v
	case uint, uint8, uint16, uint32, uint64, int, int8, int16, int32, int64:
		str = v
	case float32, float64:
		str = v
	case bool:
		str = v
	case fmt.Stringer:
		str = v.String()
	default:
		var bytes []byte
		if bytes, err = json.Marshal(value); err == nil {
			str = string(bytes)
		}
	}

	return
}

func (c *Cache) Set(key string, value interface{}, ttl ...int) (err error) {
	str, err := anyToString(value)
	if err == nil {
		if len(ttl) > 0 {
			err = c.Put(key, &Item{Value: str, Expiration: time.Duration(ttl[0]) * time.Second})
		} else {
			err = c.Put(key, &Item{Value: str})
		}
	}
	return
}
func (c *Cache) Add(key string, value ...interface{}) (err error) {
	str, err := anyToString(value)
	if err == nil {
		err = c.Put(key, &Item{
			Value: str, Expiration: time.Duration(123) * time.Second, Add: true,
		})
	}
	return
}
func (c *Cache) Replace(key string, value ...interface{}) (err error) {
	str, err := anyToString(value)
	if err == nil {
		err = c.Put(key, &Item{
			Value: str, Expiration: time.Duration(123) * time.Second, Replace: true,
		})
	}
	return
}

// Increment increments the corresponding item's value.
func (c *Cache) Increment(key string, amount int64) (value interface{}, err error) {
	in := map[string]int64{"amount": amount}

	out := struct {
		Message string      `json:"msg"`
		Value   interface{} `json:"value"`
	}{}
	if err = c.caches(c.Name, "items", key, "increment").Req("POST", &in, &out); err == nil {
		value = out.Value
	}
	return
}

// Get gets an item from the cache.
func (c *Cache) Get(key string) (value interface{}, err error) {
	out := struct {
		Cache string      `json:"cache"`
		Key   string      `json:"key"`
		Value interface{} `json:"value"`
	}{}
	if err = c.caches(c.Name, "items", key).Req("GET", nil, &out); err == nil {
		value = out.Value
	}
	return
}

func (c *Cache) GetMeta(key string) (value map[string]interface{}, err error) {
	value = map[string]interface{}{}
	err = c.caches(c.Name, "items", key).Req("GET", nil, &value)
	return
}

// Delete removes an item from the cache.
func (c *Cache) Delete(key string) (err error) {
	return c.caches(c.Name, "items", key).Req("DELETE", nil, nil)
}

type Codec struct {
	Marshal   func(interface{}) ([]byte, error)
	Unmarshal func([]byte, interface{}) error
}

func (cd Codec) Put(c *Cache, key string, item *Item) (err error) {
	bytes, err := cd.Marshal(item.Object)
	if err != nil {
		return
	}

	item.Value = string(bytes)

	return c.Put(key, item)
}

func (cd Codec) Get(c *Cache, key string, object interface{}) (err error) {
	value, err := c.Get(key)
	if err != nil {
		return
	}

	err = cd.Unmarshal([]byte(value.(string)), object)
	if err != nil {
		return
	}

	return
}

func gobMarshal(v interface{}) ([]byte, error) {
	writer := bytes.Buffer{}
	enc := gob.NewEncoder(&writer)
	err := enc.Encode(v)
	return writer.Bytes(), err
}

func gobUnmarshal(marshalled []byte, v interface{}) error {
	reader := bytes.NewBuffer(marshalled)
	dec := gob.NewDecoder(reader)
	return dec.Decode(v)
}
<commit_msg>Fix bug that made cache unusable<commit_after>// IronCache (cloud k/v store) client library
package cache

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

	"github.com/iron-io/iron_go/api"
	"github.com/iron-io/iron_go/config"
)

var (
	JSON = Codec{Marshal: json.Marshal, Unmarshal: json.Unmarshal}
	Gob  = Codec{Marshal: gobMarshal, Unmarshal: gobUnmarshal}
)

type Cache struct {
	Settings config.Settings
	Name     string
}

type Item struct {
	// Value is the Item's value
	Value interface{}
	// Object is the Item's value for use with a Codec.
	Object interface{}
	// Number of seconds until expiration. The zero value defaults to 7 days,
	// maximum is 30 days.
	Expiration time.Duration
	// Caches item only if the key is currently cached.
	Replace bool
	// Caches item only if the key isn't currently cached.
	Add bool
}

// New returns a struct ready to make requests with.
// The cacheName argument is used as namespace.
func New(cacheName string) *Cache {
	return &Cache{Settings: config.Config("iron_cache"), Name: cacheName}
}

func (c *Cache) caches(suffix ...string) *api.URL {
	return api.Action(c.Settings, "caches", suffix...)
}

func (c *Cache) ListCaches(page, perPage int) (caches []*Cache, err error) {
	out := []struct {
		Project_id string
		Name       string
	}{}

	err = c.caches().
		QueryAdd("page", "%d", page).
		QueryAdd("per_page", "%d", perPage).
		Req("GET", nil, &out)
	if err != nil {
		return
	}

	caches = make([]*Cache, 0, len(out))
	for _, item := range out {
		caches = append(caches, &Cache{
			Settings: c.Settings,
			Name:     item.Name,
		})
	}

	return
}

func (c *Cache) ServerVersion() (version string, err error) {
	out := map[string]string{}
	err = api.VersionAction(c.Settings).Req("GET", nil, &out)
	if err != nil {
		return
	}
	return out["version"], nil
}

func (c *Cache) Clear() (err error) {
	return c.caches(c.Name, "clear").Req("POST", nil, nil)
}

// Put adds an Item to the cache, overwriting any existing key of the same name.
func (c *Cache) Put(key string, item *Item) (err error) {
	in := struct {
		Value     interface{} `json:"value"`
		ExpiresIn int         `json:"expires_in,omitempty"`
		Replace   bool        `json:"replace,omitempty"`
		Add       bool        `json:"add,omitempty"`
	}{
		Value:     item.Value,
		ExpiresIn: int(item.Expiration.Seconds()),
		Replace:   item.Replace,
		Add:       item.Add,
	}

	return c.caches(c.Name, "items", key).Req("PUT", &in, nil)
}

func anyToString(value interface{}) (str interface{}, err error) {
	switch v := value.(type) {
	case string:
		str = v
	case uint, uint8, uint16, uint32, uint64, int, int8, int16, int32, int64:
		str = v
	case float32, float64:
		str = v
	case bool:
		str = v
	case fmt.Stringer:
		str = v.String()
	default:
		var bytes []byte
		if bytes, err = json.Marshal(value); err == nil {
			str = string(bytes)
		}
	}

	return
}

func (c *Cache) Set(key string, value interface{}, ttl ...int) (err error) {
	str, err := anyToString(value)
	if err == nil {
		if len(ttl) > 0 {
			err = c.Put(key, &Item{Value: str, Expiration: time.Duration(ttl[0]) * time.Second})
		} else {
			err = c.Put(key, &Item{Value: str})
		}
	}
	return
}
func (c *Cache) Add(key string, value ...interface{}) (err error) {
	str, err := anyToString(value)
	if err == nil {
		err = c.Put(key, &Item{
			Value: str, Expiration: time.Duration(123) * time.Second, Add: true,
		})
	}
	return
}
func (c *Cache) Replace(key string, value ...interface{}) (err error) {
	str, err := anyToString(value)
	if err == nil {
		err = c.Put(key, &Item{
			Value: str, Expiration: time.Duration(123) * time.Second, Replace: true,
		})
	}
	return
}

// Increment increments the corresponding item's value.
func (c *Cache) Increment(key string, amount int64) (value interface{}, err error) {
	in := map[string]int64{"amount": amount}

	out := struct {
		Message string      `json:"msg"`
		Value   interface{} `json:"value"`
	}{}
	if err = c.caches(c.Name, "items", key, "increment").Req("POST", &in, &out); err == nil {
		value = out.Value
	}
	return
}

// Get gets an item from the cache.
func (c *Cache) Get(key string) (value interface{}, err error) {
	out := struct {
		Cache string      `json:"cache"`
		Key   string      `json:"key"`
		Value interface{} `json:"value"`
	}{}
	if err = c.caches(c.Name, "items", key).Req("GET", nil, &out); err == nil {
		value = out.Value
	}
	return
}

func (c *Cache) GetMeta(key string) (value map[string]interface{}, err error) {
	value = map[string]interface{}{}
	err = c.caches(c.Name, "items", key).Req("GET", nil, &value)
	return
}

// Delete removes an item from the cache.
func (c *Cache) Delete(key string) (err error) {
	return c.caches(c.Name, "items", key).Req("DELETE", nil, nil)
}

type Codec struct {
	Marshal   func(interface{}) ([]byte, error)
	Unmarshal func([]byte, interface{}) error
}

func (cd Codec) Put(c *Cache, key string, item *Item) (err error) {
	bytes, err := cd.Marshal(item.Object)
	if err != nil {
		return
	}

	item.Value = string(bytes)

	return c.Put(key, item)
}

func (cd Codec) Get(c *Cache, key string, object interface{}) (err error) {
	value, err := c.Get(key)
	if err != nil {
		return
	}

	err = cd.Unmarshal([]byte(value.(string)), object)
	if err != nil {
		return
	}

	return
}

func gobMarshal(v interface{}) ([]byte, error) {
	writer := bytes.Buffer{}
	enc := gob.NewEncoder(&writer)
	err := enc.Encode(v)
	return writer.Bytes(), err
}

func gobUnmarshal(marshalled []byte, v interface{}) error {
	reader := bytes.NewBuffer(marshalled)
	dec := gob.NewDecoder(reader)
	return dec.Decode(v)
}
<|endoftext|>


<commit_before>// Challenge 32 - Break HMAC-SHA1 with a slightly less artificial timing leak
// http://cryptopals.com/sets/4/challenges/32

package cryptopals

import (
	"crypto/sha1"
	"net/http"
	"time"
)

type challenge32 struct {
}

func (challenge32) ForgeHmacSHA1SignaturePrecise(addr, file string) []byte {
	sig := make([]byte, sha1.Size)
	x := challenge31{}

	for i := 0; i < len(sig); i++ {
		var valBest byte
		var timeBest time.Duration

		for j := 0; j < 256; j++ {
			sig[i] = byte(j)
			url := x.buildURL(addr, file, sig)
			start := time.Now()

			for k := 0; k < 15; k++ {
				resp, _ := http.Get(url)
				resp.Body.Close()
			}

			elapsed := time.Since(start)

			if elapsed > timeBest {
				valBest = byte(j)
				timeBest = elapsed
			}
		}

		sig[i] = valBest
	}

	return sig
}
<commit_msg>Fix solution for challenge 32<commit_after>// Challenge 32 - Break HMAC-SHA1 with a slightly less artificial timing leak
// http://cryptopals.com/sets/4/challenges/32

package cryptopals

import (
	"crypto/sha1"
	"net/http"
	"time"
)

type challenge32 struct {
}

func (challenge32) ForgeHmacSHA1SignaturePrecise(addr, file string) []byte {
	sig := make([]byte, sha1.Size)
	x := challenge31{}

	for i := 0; i < len(sig); i++ {
		var valBest byte
		var timeBest time.Duration

		for j := 0; j < 256; j++ {
			sig[i] = byte(j)
			url := x.buildURL(addr, file, sig)
			fastest := time.Hour

			for k := 0; k < 10; k++ {
				start := time.Now()
				resp, _ := http.Get(url)
				elapsed := time.Since(start)
				resp.Body.Close()

				if elapsed < fastest {
					fastest = elapsed
				}
			}

			if fastest > timeBest {
				valBest = byte(j)
				timeBest = fastest
			}
		}

		sig[i] = valBest
	}

	return sig
}
<|endoftext|>