<commit_before>// Copyright 2020 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 codec

import (
	"bytes"
	"errors"
	"fmt"
	"go/format"
	"io"
	"reflect"
	"sort"
	"strings"
	"text/template"
)

// Generate write a Go file to w with definitions for encoding values using
// this package. It generates code for the type of each value in vs, as well
// as any types they depend on.
// packageName is the name following the file's package declaration.

func Generate(w io.Writer, packageName string, vs ...interface{}) error {
	g := &generator{
		pkg:  packageName,
		done: map[reflect.Type]bool{},
	}
	funcs := template.FuncMap{
		"funcName":   g.funcName,
		"goName":     g.goName,
		"encodeStmt": g.encodeStatement,
		"decodeStmt": g.decodeStatement,
	}

	newTemplate := func(name, body string) *template.Template {
		return template.Must(template.New(name).Delims("«", "»").Funcs(funcs).Parse(body))
	}

	g.initialTemplate = newTemplate("initial", initial)
	g.sliceTemplate = newTemplate("slice", slice)
	g.mapTemplate = newTemplate("map", mapBody)

	for _, v := range vs {
		g.todo = append(g.todo, reflect.TypeOf(v))
	}

	// Mark the types we already know about as done.
	for t := range typeInfosByType {
		g.done[t] = true
	}
	// The empty interface doesn't need any additional code. It's tricky to get
	// its reflect.Type: we need to dereference the pointer type.
	var iface interface{}
	g.done[reflect.TypeOf(&iface).Elem()] = true

	src, err := g.generate()
	if err != nil {
		return err
	}
	src, err = format.Source(src)
	if err != nil {
		return fmt.Errorf("format.Source: %v", err)
	}
	_, err = w.Write(src)
	return err
}

type generator struct {
	pkg             string
	todo            []reflect.Type
	done            map[reflect.Type]bool
	initialTemplate *template.Template
	sliceTemplate   *template.Template
	mapTemplate     *template.Template
}

func (g *generator) generate() ([]byte, error) {
	importMap := map[string]bool{}
	var pieces [][]byte
	for len(g.todo) > 0 {
		t := g.todo[0]
		g.todo = g.todo[1:]
		if !g.done[t] {
			if t.PkgPath() != "" {
				importMap[t.PkgPath()] = true
			}
			code, err := g.gen(t)
			if err != nil {
				return nil, err
			}
			pieces = append(pieces, code)
			// We use the same code for T and *T, so both are done.
			g.done[t] = true
			g.done[reflect.PtrTo(t)] = true
		}
	}

	var imports []string
	for i := range importMap {
		imports = append(imports, i)
	}
	sort.Strings(imports)
	result, err := execute(g.initialTemplate, struct {
		Package string
		Imports []string
	}{
		Package: g.pkg,
		Imports: imports,
	})
	if err != nil {
		return nil, err
	}
	for _, p := range pieces {
		result = append(result, p...)
	}
	return result, nil
}

func (g *generator) gen(t reflect.Type) ([]byte, error) {
	switch t.Kind() {
	case reflect.Slice:
		return g.genSlice(t)
	case reflect.Map:
		return g.genMap(t)
	default:
		return nil, errors.New("unimplemented")
	}
}

func (g *generator) genSlice(t reflect.Type) ([]byte, error) {
	et := t.Elem()
	g.todo = append(g.todo, et)
	return execute(g.sliceTemplate, struct {
		Type, ElType reflect.Type
	}{
		Type:   t,
		ElType: et,
	})
}

func (g *generator) genMap(t reflect.Type) ([]byte, error) {
	et := t.Elem()
	kt := t.Key()
	g.todo = append(g.todo, kt, et)
	return execute(g.mapTemplate, struct {
		Type, ElType, KeyType reflect.Type
	}{
		Type:    t,
		ElType:  et,
		KeyType: kt,
	})
}

func execute(tmpl *template.Template, data interface{}) ([]byte, error) {
	var buf bytes.Buffer
	if err := tmpl.Execute(&buf, data); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// encodeStatement returns a Go statement that encodes a value denoted by arg, of type t.
func (g *generator) encodeStatement(t reflect.Type, arg string) string {
	bn, native := builtinName(t)
	if bn != "" {
		// t can be handled by an Encoder method.
		if t != native {
			// t is not the Encoder method's argument type, so we must cast.
			arg = fmt.Sprintf("%s(%s)", native, arg)
		}
		return fmt.Sprintf("e.Encode%s(%s)", bn, arg)
	}
	if t.Kind() == reflect.Interface {
		return fmt.Sprintf("e.EncodeAny(%s)", arg)
	}
	return fmt.Sprintf("encode_%s(e, %s)", g.funcName(t), arg)
}

func (g *generator) decodeStatement(t reflect.Type, arg string) string {
	bn, native := builtinName(t)
	if bn != "" {
		// t can be handled by a Decoder method.
		if t != native {
			// t is not the Decoder method's return type, so we must cast.
			return fmt.Sprintf("%s = %s(d.Decode%s())", arg, g.goName(t), bn)
		}
		return fmt.Sprintf("%s = d.Decode%s()", arg, bn)
	}
	if t.Kind() == reflect.Interface {
		// t is an interface, so use DecodeAny, possibly with a type assertion.
		if t.NumMethod() == 0 {
			return fmt.Sprintf("%s = d.DecodeAny()", arg)
		}
		return fmt.Sprintf("%s = d.DecodeAny().(%s)", arg, g.goName(t))
	}
	// Assume we will generate a decode method for t.
	return fmt.Sprintf("decode_%s(d, &%s)", g.funcName(t), arg)
}

// builtinName returns the suffix to append to "encode" or "decode" to get the
// Encoder/Decoder method name for t. If t cannot be encoded by an Encoder
// method, the suffix is "". The second return value is the "native" type of the
// method: the argument to the Encoder method, and the return value of the
// Decoder method.
func builtinName(t reflect.Type) (suffix string, native reflect.Type) {
	switch t.Kind() {
	case reflect.String:
		return "String", reflect.TypeOf("")
	case reflect.Bool:
		return "Bool", reflect.TypeOf(true)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return "Int", reflect.TypeOf(int64(0))
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return "Uint", reflect.TypeOf(uint64(0))
	case reflect.Float32, reflect.Float64:
		return "Float64", reflect.TypeOf(0.0)
	case reflect.Slice:
		if t.Elem() == reflect.TypeOf(byte(0)) {
			return "Bytes", reflect.TypeOf([]byte(nil))
		}
	}
	return "", nil
}

// goName returns the name of t as it should appear in a Go program.
// E.g. "go/ast.File" => ast.File
// It assumes all package paths are represented in the file by their last element.
func (g *generator) goName(t reflect.Type) string {
	switch t.Kind() {
	case reflect.Slice:
		return fmt.Sprintf("[]%s", g.goName(t.Elem()))
	case reflect.Map:
		return fmt.Sprintf("map[%s]%s", g.goName(t.Key()), g.goName(t.Elem()))
	case reflect.Ptr:
		return fmt.Sprintf("*%s", g.goName(t.Elem()))
	default:
		s := t.String()
		if strings.HasPrefix(s, g.pkg+".") {
			s = s[len(g.pkg)+1:]
		}
		return s
	}
}

var funcNameReplacer = strings.NewReplacer("[]", "slice_", "[", "_", "]", "_", ".", "_", "*", "")

// funcName returns the name for t that is used as part of the encode/decode function name.
// E.g. "ast.File" => "ast_File".
func (g *generator) funcName(t reflect.Type) string {
	return funcNameReplacer.Replace(g.goName(t))
}

// Template body for the beginning of the file.
const initial = `
// Copyright 2020 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.

// DO NOT MODIFY. Generated code.

package «.Package»

import (
  "reflect"
  "unsafe"

  «range .Imports»
  «.»
  «end»

  "golang.org/x/pkgsite/internal/godoc/codec"
)

`

// Template body for a slice type.
const slice = `
func encode_«funcName .Type»(e *codec.Encoder, s «goName .Type») {
	if s == nil {
		e.EncodeUint(0)
		return
	}
	e.StartList(len(s))
	for _, x := range s {
		«encodeStmt .ElType "x"»
	}
}

func decode_«funcName .Type»(d *codec.Decoder, p *«goName .Type») {
	n := d.StartList()
	if n < 0 { return }
	s := make([]«goName .ElType», n)
	for i := 0; i < n; i++ {
		«decodeStmt .ElType "s[i]"»
	}
	*p = s
}

func init() {
  codec.Register(«goName .Type»(nil),
    func(e *codec.Encoder, x interface{}) { encode_«funcName .Type»(e, x.(«goName .Type»)) },
    func(d *codec.Decoder) interface{} { var x «goName .Type»; decode_«funcName .Type»(d, &x); return x })
}
`

// Template body for a map type.
// A map of size N is encoded as a list of length 2N, containing alternating
// keys and values.
//
// In the decode function, we declare a variable v to hold the decoded map value
// rather than decoding directly into m[v]. This is necessary for decode
// functions that take pointers: you can't take a pointer to a map element.
const mapBody = `
func encode_«funcName .Type»(e *codec.Encoder, m «goName .Type») {
	if m == nil {
		e.EncodeUint(0)
		return
	}
	e.StartList(2*len(m))
	for k, v := range m {
		«encodeStmt .KeyType "k"»
		«encodeStmt .ElType "v"»
	}
}

func decode_«funcName .Type»(d *codec.Decoder, p *«goName .Type») {
	n2 := d.StartList()
	if n2 < 0 { return }
	n := n2/2
	m := make(«goName .Type», n)
	var k «goName .KeyType»
	var v «goName .ElType»
	for i := 0; i < n; i++ {
		«decodeStmt .KeyType "k"»
		«decodeStmt .ElType "v"»
		m[k] = v
	}
	*p = m
}

func init() {
	codec.Register(«goName .Type»(nil),
	func(e *codec.Encoder, x interface{}) { encode_«funcName .Type»(e, x.(«goName .Type»)) },
	func(d *codec.Decoder) interface{} { var x «goName .Type»; decode_«funcName .Type»(d, &x); return x })
}
`
<commit_msg>internal/godoc/codec: use variables in templates<commit_after>// Copyright 2020 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 codec

import (
	"bytes"
	"errors"
	"fmt"
	"go/format"
	"io"
	"reflect"
	"sort"
	"strings"
	"text/template"
)

// Generate write a Go file to w with definitions for encoding values using
// this package. It generates code for the type of each value in vs, as well
// as any types they depend on.
// packageName is the name following the file's package declaration.

func Generate(w io.Writer, packageName string, vs ...interface{}) error {
	g := &generator{
		pkg:  packageName,
		done: map[reflect.Type]bool{},
	}
	funcs := template.FuncMap{
		"funcName":   g.funcName,
		"goName":     g.goName,
		"encodeStmt": g.encodeStatement,
		"decodeStmt": g.decodeStatement,
	}

	newTemplate := func(name, body string) *template.Template {
		return template.Must(template.New(name).Delims("«", "»").Funcs(funcs).Parse(body))
	}

	g.initialTemplate = newTemplate("initial", initialBody)
	g.sliceTemplate = newTemplate("slice", sliceBody)
	g.mapTemplate = newTemplate("map", mapBody)

	for _, v := range vs {
		g.todo = append(g.todo, reflect.TypeOf(v))
	}

	// Mark the types we already know about as done.
	for t := range typeInfosByType {
		g.done[t] = true
	}
	// The empty interface doesn't need any additional code. It's tricky to get
	// its reflect.Type: we need to dereference the pointer type.
	var iface interface{}
	g.done[reflect.TypeOf(&iface).Elem()] = true

	src, err := g.generate()
	if err != nil {
		return err
	}
	src, err = format.Source(src)
	if err != nil {
		return fmt.Errorf("format.Source: %v", err)
	}
	_, err = w.Write(src)
	return err
}

type generator struct {
	pkg             string
	todo            []reflect.Type
	done            map[reflect.Type]bool
	initialTemplate *template.Template
	sliceTemplate   *template.Template
	mapTemplate     *template.Template
}

func (g *generator) generate() ([]byte, error) {
	importMap := map[string]bool{}
	var pieces [][]byte
	for len(g.todo) > 0 {
		t := g.todo[0]
		g.todo = g.todo[1:]
		if !g.done[t] {
			if t.PkgPath() != "" {
				importMap[t.PkgPath()] = true
			}
			code, err := g.gen(t)
			if err != nil {
				return nil, err
			}
			pieces = append(pieces, code)
			// We use the same code for T and *T, so both are done.
			g.done[t] = true
			g.done[reflect.PtrTo(t)] = true
		}
	}

	var imports []string
	for i := range importMap {
		imports = append(imports, i)
	}
	sort.Strings(imports)
	result, err := execute(g.initialTemplate, struct {
		Package string
		Imports []string
	}{
		Package: g.pkg,
		Imports: imports,
	})
	if err != nil {
		return nil, err
	}
	for _, p := range pieces {
		result = append(result, p...)
	}
	return result, nil
}

func (g *generator) gen(t reflect.Type) ([]byte, error) {
	switch t.Kind() {
	case reflect.Slice:
		return g.genSlice(t)
	case reflect.Map:
		return g.genMap(t)
	default:
		return nil, errors.New("unimplemented")
	}
}

func (g *generator) genSlice(t reflect.Type) ([]byte, error) {
	et := t.Elem()
	g.todo = append(g.todo, et)
	return execute(g.sliceTemplate, struct {
		Type, ElType reflect.Type
	}{
		Type:   t,
		ElType: et,
	})
}

func (g *generator) genMap(t reflect.Type) ([]byte, error) {
	et := t.Elem()
	kt := t.Key()
	g.todo = append(g.todo, kt, et)
	return execute(g.mapTemplate, struct {
		Type, ElType, KeyType reflect.Type
	}{
		Type:    t,
		ElType:  et,
		KeyType: kt,
	})
}

func execute(tmpl *template.Template, data interface{}) ([]byte, error) {
	var buf bytes.Buffer
	if err := tmpl.Execute(&buf, data); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// encodeStatement returns a Go statement that encodes a value denoted by arg, of type t.
func (g *generator) encodeStatement(t reflect.Type, arg string) string {
	bn, native := builtinName(t)
	if bn != "" {
		// t can be handled by an Encoder method.
		if t != native {
			// t is not the Encoder method's argument type, so we must cast.
			arg = fmt.Sprintf("%s(%s)", native, arg)
		}
		return fmt.Sprintf("e.Encode%s(%s)", bn, arg)
	}
	if t.Kind() == reflect.Interface {
		return fmt.Sprintf("e.EncodeAny(%s)", arg)
	}
	return fmt.Sprintf("encode_%s(e, %s)", g.funcName(t), arg)
}

func (g *generator) decodeStatement(t reflect.Type, arg string) string {
	bn, native := builtinName(t)
	if bn != "" {
		// t can be handled by a Decoder method.
		if t != native {
			// t is not the Decoder method's return type, so we must cast.
			return fmt.Sprintf("%s = %s(d.Decode%s())", arg, g.goName(t), bn)
		}
		return fmt.Sprintf("%s = d.Decode%s()", arg, bn)
	}
	if t.Kind() == reflect.Interface {
		// t is an interface, so use DecodeAny, possibly with a type assertion.
		if t.NumMethod() == 0 {
			return fmt.Sprintf("%s = d.DecodeAny()", arg)
		}
		return fmt.Sprintf("%s = d.DecodeAny().(%s)", arg, g.goName(t))
	}
	// Assume we will generate a decode method for t.
	return fmt.Sprintf("decode_%s(d, &%s)", g.funcName(t), arg)
}

// builtinName returns the suffix to append to "encode" or "decode" to get the
// Encoder/Decoder method name for t. If t cannot be encoded by an Encoder
// method, the suffix is "". The second return value is the "native" type of the
// method: the argument to the Encoder method, and the return value of the
// Decoder method.
func builtinName(t reflect.Type) (suffix string, native reflect.Type) {
	switch t.Kind() {
	case reflect.String:
		return "String", reflect.TypeOf("")
	case reflect.Bool:
		return "Bool", reflect.TypeOf(true)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return "Int", reflect.TypeOf(int64(0))
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return "Uint", reflect.TypeOf(uint64(0))
	case reflect.Float32, reflect.Float64:
		return "Float64", reflect.TypeOf(0.0)
	case reflect.Slice:
		if t.Elem() == reflect.TypeOf(byte(0)) {
			return "Bytes", reflect.TypeOf([]byte(nil))
		}
	}
	return "", nil
}

// goName returns the name of t as it should appear in a Go program.
// E.g. "go/ast.File" => ast.File
// It assumes all package paths are represented in the file by their last element.
func (g *generator) goName(t reflect.Type) string {
	switch t.Kind() {
	case reflect.Slice:
		return fmt.Sprintf("[]%s", g.goName(t.Elem()))
	case reflect.Map:
		return fmt.Sprintf("map[%s]%s", g.goName(t.Key()), g.goName(t.Elem()))
	case reflect.Ptr:
		return fmt.Sprintf("*%s", g.goName(t.Elem()))
	default:
		s := t.String()
		if strings.HasPrefix(s, g.pkg+".") {
			s = s[len(g.pkg)+1:]
		}
		return s
	}
}

var funcNameReplacer = strings.NewReplacer("[]", "slice_", "[", "_", "]", "_", ".", "_", "*", "")

// funcName returns the name for t that is used as part of the encode/decode function name.
// E.g. "ast.File" => "ast_File".
func (g *generator) funcName(t reflect.Type) string {
	return funcNameReplacer.Replace(g.goName(t))
}

// Template body for the beginning of the file.
const initialBody = `
// Copyright 2020 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.

// DO NOT MODIFY. Generated code.

package «.Package»

import (
  "reflect"
  "unsafe"

  «range .Imports»
  «.»
  «end»

  "golang.org/x/pkgsite/internal/godoc/codec"
)

`

// Template body for a sliceBody type.
const sliceBody = `
« $funcName := funcName .Type »
« $goName := goName .Type »
func encode_«$funcName»(e *codec.Encoder, s «$goName») {
	if s == nil {
		e.EncodeUint(0)
		return
	}
	e.StartList(len(s))
	for _, x := range s {
		«encodeStmt .ElType "x"»
	}
}

func decode_«$funcName»(d *codec.Decoder, p *«$goName») {
	n := d.StartList()
	if n < 0 { return }
	s := make([]«goName .ElType», n)
	for i := 0; i < n; i++ {
		«decodeStmt .ElType "s[i]"»
	}
	*p = s
}

func init() {
  codec.Register(«$goName»(nil),
    func(e *codec.Encoder, x interface{}) { encode_«$funcName»(e, x.(«$goName»)) },
    func(d *codec.Decoder) interface{} { var x «$goName»; decode_«$funcName»(d, &x); return x })
}
`

// Template body for a map type.
// A map of size N is encoded as a list of length 2N, containing alternating
// keys and values.
//
// In the decode function, we declare a variable v to hold the decoded map value
// rather than decoding directly into m[v]. This is necessary for decode
// functions that take pointers: you can't take a pointer to a map element.
const mapBody = `
« $funcName := funcName .Type »
« $goName := goName .Type »
func encode_«$funcName»(e *codec.Encoder, m «$goName») {
	if m == nil {
		e.EncodeUint(0)
		return
	}
	e.StartList(2*len(m))
	for k, v := range m {
		«encodeStmt .KeyType "k"»
		«encodeStmt .ElType "v"»
	}
}

func decode_«$funcName»(d *codec.Decoder, p *«$goName») {
	n2 := d.StartList()
	if n2 < 0 { return }
	n := n2/2
	m := make(«$goName», n)
	var k «goName .KeyType»
	var v «goName .ElType»
	for i := 0; i < n; i++ {
		«decodeStmt .KeyType "k"»
		«decodeStmt .ElType "v"»
		m[k] = v
	}
	*p = m
}

func init() {
	codec.Register(«$goName»(nil),
	func(e *codec.Encoder, x interface{}) { encode_«$funcName»(e, x.(«$goName»)) },
	func(d *codec.Decoder) interface{} { var x «$goName»; decode_«$funcName»(d, &x); return x })
}
`
<|endoftext|>


<commit_before>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"

	"github.com/FactomProject/factom"
)

var addchain = func() *fctCmd {
	cmd := new(fctCmd)
	cmd.helpMsg = "factom-cli addchain [-e EXTID1 -e EXTID2 -E BINEXTID3 ...] ECADDRESS <STDIN>"
	cmd.description = "Create a new Factom Chain. Read data for the First Entry from stdin. Use the Entry Credits from the specified address."
	cmd.execFunc = func(args []string) {
		var (
			eAcii extidsAscii
			eHex  extidsHex
		)
		os.Args = args
		exidCollector = make([][]byte, 0)
		flag.Var(&eAcii, "e", "external id for the entry in ascii")
		flag.Var(&eHex, "E", "external id for the entry in hex")
		flag.Parse()
		args = flag.Args()

		if len(args) < 1 {
			fmt.Println(cmd.helpMsg)
			return
		}
		ecpub := args[0]

		e := new(factom.Entry)

		e.ExtIDs = exidCollector

		// Entry.Content is read from stdin
		if p, err := ioutil.ReadAll(os.Stdin); err != nil {
			errorln(err)
			return
		} else if size := len(p); size > 10240 {
			errorln("Entry of %d bytes is too large", size)
			return
		} else {
			e.Content = p
		}

		c := factom.NewChain(e)

		if _, err := factom.GetChainHead(c.ChainID); err == nil {
			// no error means the client found the chain
			errorln("Chain", c.ChainID, "already exists")
			return
		}

		// get the ec address from the wallet
		ec, err := factom.FetchECAddress(ecpub)
		if err != nil {
			errorln(err)
			return
		}
		// commit the chain
		if txID, err := factom.CommitChain(c, ec); err != nil {
			errorln(err)
			return
		} else {
			fmt.Println("Commiting Chain Transaction ID:", txID)
		}

		// TODO - get commit acknowledgement

		// reveal chain
		if hash, err := factom.RevealChain(c); err != nil {
			errorln(err)
			return
		} else {
			fmt.Println("ChainID:", c.ChainID)
			fmt.Println("Entryhash:", hash)
		}
		// ? get reveal ack
	}
	help.Add("addchain", cmd)
	return cmd
}()
<commit_msg>#task 3 factor-cli addchain, ec address doesn't have a balance<commit_after>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"

	"github.com/FactomProject/factom"
)

var addchain = func() *fctCmd {
	cmd := new(fctCmd)
	cmd.helpMsg = "factom-cli addchain [-e EXTID1 -e EXTID2 -E BINEXTID3 ...] ECADDRESS <STDIN>"
	cmd.description = "Create a new Factom Chain. Read data for the First Entry from stdin. Use the Entry Credits from the specified address."
	cmd.execFunc = func(args []string) {
		var (
			eAcii extidsAscii
			eHex  extidsHex
		)
		os.Args = args
		exidCollector = make([][]byte, 0)
		flag.Var(&eAcii, "e", "external id for the entry in ascii")
		flag.Var(&eHex, "E", "external id for the entry in hex")
		flag.Parse()
		args = flag.Args()

		if len(args) < 1 {
			fmt.Println(cmd.helpMsg)
			return
		}
		ecpub := args[0]

		e := new(factom.Entry)

		e.ExtIDs = exidCollector

		// Entry.Content is read from stdin
		if p, err := ioutil.ReadAll(os.Stdin); err != nil {
			errorln(err)
			return
		} else if size := len(p); size > 10240 {
			errorln("Entry of %d bytes is too large", size)
			return
		} else {
			e.Content = p
		}

		c := factom.NewChain(e)

		if _, err := factom.GetChainHead(c.ChainID); err == nil {
			// no error means the client found the chain
			errorln("Chain", c.ChainID, "already exists")
			return
		}

		// get the ec address from the wallet
		ec, err := factom.FetchECAddress(ecpub)
		if err != nil {
			errorln(err)
			return
		}
		balance, err := factom.GetECAddress(ecpub)
		if err != nil {
			errorln(err)
			return
		}
		if balance == 0 {
			errorln("Entry Credits balance is zero")
			return
		}
		// commit the chain
		if txID, err := factom.CommitChain(c, ec); err != nil {
			errorln(err)
			return
		} else {
			fmt.Println("Commiting Chain Transaction ID:", txID)
		}

		// TODO - get commit acknowledgement

		// reveal chain
		if hash, err := factom.RevealChain(c); err != nil {
			errorln(err)
			return
		} else {
			fmt.Println("ChainID:", c.ChainID)
			fmt.Println("Entryhash:", hash)
		}
		// ? get reveal ack
	}
	help.Add("addchain", cmd)
	return cmd
}()
<|endoftext|>


<commit_before>package gotetra

import (
	"math"
)

const (
	eps = 1e-6
)

func (h *Header) compressCoords(x, y, z, dx, dy, dz int64) int64 {
	// Can be send up with & tricks or conditionals.
	newX := (x + dx + h.CountWidth) % h.CountWidth
	newY := (y + dy + h.CountWidth) % h.CountWidth
	newZ := (z + dz + h.CountWidth) % h.CountWidth

	return newX + newY * h.CountWidth + newZ * h.CountWidth * h.CountWidth
}

func (h *Header) TetraCorners(idx int64, out []int64) {
	countArea := h.CountWidth * h.CountWidth

	x := idx % h.CountWidth
	y := (idx % countArea) / h.CountWidth
	z := idx / countArea

	out[0] = h.compressCoords(x, y, z, 0, 0, 1)
	out[1] = h.compressCoords(x, y, z, 0, 1, 0)
	out[2] = h.compressCoords(x, y, z, 1, 0, 0)
}

func (h *Header) wrapDist(x1, x2 float64) float64 {
	var low, high float64

	if x1 < x2 {
		low, high = x1, x2
	} else {
		low, high = x2, x1
	}

	d1 := high - low
	d2 := low + h.TotalWidth - high

	if d1 > d2 {
		return d2
	} else {
		return d1
	}
}

func (h *Header) Distance(p1, p2 *Particle) float64 {
	dx := h.wrapDist(float64(p1.Xs[0]), float64(p2.Xs[0]))
	dy := h.wrapDist(float64(p1.Xs[1]), float64(p2.Xs[1]))
	dz := h.wrapDist(float64(p1.Xs[2]), float64(p2.Xs[2]))

	return math.Sqrt(dx * dx + dy * dy + dz * dz)
}

type VolumeBuffer struct {
	buf1, buf2, buf3, bufCross [3]float64
}

func NewVolumeBuffer() *VolumeBuffer {
	return &VolumeBuffer{ }
}

func (h *Header) Volume(ps []*Particle, vb *VolumeBuffer) float64 {
	h.subX(ps[1], ps[0], &vb.buf1)
	h.subX(ps[2], ps[0], &vb.buf2)
	h.subX(ps[3], ps[0], &vb.buf3)

	cross(&vb.buf2, &vb.buf3, &vb.bufCross)
	return math.Abs(dot(&vb.buf1, &vb.bufCross)) / 6.0
}

func (h *Header) WithinTetra(
	p *Particle,
	ps []*Particle,
	vol float64,
	vb *VolumeBuffer,
) bool {

	buf := []*Particle{ps[0], ps[1], ps[2], p}
	sum := 0.0
	for i := 0; i < 4; i++ {
		sum += h.Volume(buf, vb)
		buf[i] = ps[(i + 3) % 4]
	}

	return (math.Abs(sum - vol) / vol) <= eps
}

func (h *Header) subX(p1, p2 *Particle, out *[3]float64) {
	for i := 0; i < 3; i++ {
		(*out)[i] = float64(p1.Xs[i]) - float64(p2.Xs[i])
	}
}

func cross(v1, v2, out *[3]float64) {
	(*out)[0] = v1[1] * v2[2] - v1[2] * v2[1]
	(*out)[1] = v1[2] * v2[0] - v1[0] * v2[2]
	(*out)[2] = v1[0] * v2[1] - v1[1] * v2[0]
}

func dot(v1, v2 *[3]float64) float64 {
	sum := 0.0
	for i := 0; i < 3; i++ {
		sum += (*v1)[i] * (*v2)[i]
	}
	return sum
}
<commit_msg>Added tetrahedron bounding box functions to geom.go<commit_after>package gotetra

import (
	"fmt"
	"math"
)

const (
	eps = 1e-6
)

var (
	dirs = [6][4][3]int64{
		{{1, 0, 0}, {1, 1, 0}},
		{{1, 0, 0}, {1, 0, 1}},
		{{0, 1, 0}, {1, 1, 0}},
		{{0, 0, 1}, {1, 0, 1}},
		{{0, 1, 0}, {0, 1, 1}},
		{{0, 0, 1}, {0, 1, 1}},
	}
)

// Note, should probably make this a [4]Particle
type Tetra []*Particle

func (t Tetra) Valid() bool {
	for i := 0; i < 4; i++ {
		if t[i] == nil { return false }
	}
	return true
}

type Bounds struct {
	MinX, MaxX, MinY, MaxY, MinZ, MaxZ int
}

func (h *Header) compressCoords(x, y, z, dx, dy, dz int64) int64 {
	newX := (x + dx + h.CountWidth) % h.CountWidth
	newY := (y + dy + h.CountWidth) % h.CountWidth
	newZ := (z + dz + h.CountWidth) % h.CountWidth

	return newX + newY * h.CountWidth + newZ * h.CountWidth * h.CountWidth
}

func (h *Header) TetraCorners(idx int64, dir int, out []int64) {
	if dir < 0 || dir >= 6 {
		panic(fmt.Sprintf("Unknown direction %d", dir))
	}

	countArea := h.CountWidth * h.CountWidth

	x := idx % h.CountWidth
	y := (idx % countArea) / h.CountWidth
	z := idx / countArea

	out[0] = h.compressCoords(
		x, y, z, dirs[dir][0][0], dirs[dir][0][1], dirs[dir][0][2],
	)
	out[1] = h.compressCoords(
		x, y, z, dirs[dir][1][0], dirs[dir][1][1], dirs[dir][1][2],
	)
	out[2] = h.compressCoords(x, y, z, 1, 1, 1)
}

func (h *Header) wrapDist(x1, x2 float64) float64 {
	var low, high float64

	if x1 < x2 {
		low, high = x1, x2
	} else {
		low, high = x2, x1
	}

	d1 := high - low
	d2 := low + h.TotalWidth - high

	if d1 > d2 {
		return d2
	} else {
		return d1
	}
}

func (h *Header) Distance(p1, p2 *Particle) float64 {
	dx := h.wrapDist(float64(p1.Xs[0]), float64(p2.Xs[0]))
	dy := h.wrapDist(float64(p1.Xs[1]), float64(p2.Xs[1]))
	dz := h.wrapDist(float64(p1.Xs[2]), float64(p2.Xs[2]))

	return math.Sqrt(dx * dx + dy * dy + dz * dz)
}

type VolumeBuffer struct {
	buf1, buf2, buf3, bufCross [3]float64
}

func NewVolumeBuffer() *VolumeBuffer {
	return &VolumeBuffer{ }
}

func (h *Header) Volume(ps Tetra, vb *VolumeBuffer) float64 {
	h.subX(ps[1], ps[0], &vb.buf1)
	h.subX(ps[2], ps[0], &vb.buf2)
	h.subX(ps[3], ps[0], &vb.buf3)

	cross(&vb.buf2, &vb.buf3, &vb.bufCross)
	return math.Abs(dot(&vb.buf1, &vb.bufCross)) / 6.0
}

func (h *Header) WithinTetra(
	p *Particle,
	ps Tetra,
	vol float64,
	vb *VolumeBuffer,
) bool {

	buf := []*Particle{ps[0], ps[1], ps[2], p}
	sum := 0.0
	for i := 0; i < 4; i++ {
		sum += h.Volume(buf, vb)
		buf[i] = ps[(i + 3) % 4]
	}

	return (math.Abs(sum - vol) / vol) <= eps
}

func (h *Header) subX(p1, p2 *Particle, out *[3]float64) {
	for i := 0; i < 3; i++ {
		(*out)[i] = float64(p1.Xs[i]) - float64(p2.Xs[i])
	}
}

func cross(v1, v2, out *[3]float64) {
	(*out)[0] = v1[1] * v2[2] - v1[2] * v2[1]
	(*out)[1] = v1[2] * v2[0] - v1[0] * v2[2]
	(*out)[2] = v1[0] * v2[1] - v1[1] * v2[0]
}

func dot(v1, v2 *[3]float64) float64 {
	sum := 0.0
	for i := 0; i < 3; i++ {
		sum += (*v1)[i] * (*v2)[i]
	}
	return sum
}

func (h *Header) Bounds(t Tetra, cellWidth float64, ib *Bounds) *Bounds {
	if ib == nil {
		ib = &Bounds{}
	}

	minX := float64(t[0].Xs[0])
	minY := float64(t[0].Xs[1])
	minZ := float64(t[0].Xs[2])
	maxX, maxY, maxZ := minX, minY, minX

	for i := 1; i < 4; i++ {
		minX, maxX = minMax(float64(t[i].Xs[0]), minX, maxX)
		minY, maxY = minMax(float64(t[i].Xs[1]), minY, maxY)
		minZ, maxZ = minMax(float64(t[i].Xs[2]), minZ, maxZ)
	}

	ib.MinX = int(minX / cellWidth)
	ib.MaxX = int(math.Ceil(maxX / cellWidth))
	ib.MinY = int(minY / cellWidth)
	ib.MaxY = int(math.Ceil(maxY / cellWidth))
	ib.MinZ = int(minZ / cellWidth)
	ib.MaxZ = int(math.Ceil(maxZ / cellWidth))

	return ib
}

func (h *Header) minMax(coord int64) (min, max float64) {
	min = float64(coord) * h.Width
	return min, min + h.Width
}

func minMax(x, oldMin, oldMax float64) (min, max float64) {
	if x > oldMax {
		return oldMin, x
	} else if x < oldMin {
		return x, oldMax
	} else {
		return oldMin, oldMax
	}
}
<|endoftext|>


<commit_before>package geom

type T interface {
	Bounds(*Bounds) *Bounds
}

type Geom interface {
	T
}

type GeomZ interface {
	T
}

type GeomM interface {
	T
}

type GeomZM interface {
	T
}
<commit_msg>Added docstring<commit_after>/*
Package geom holds geometry geomotry objects that can be encoded, decoded,
and operated on by the other packages in this repository.
*/
package geom

type T interface {
	Bounds(*Bounds) *Bounds
}

type Geom interface {
	T
}

type GeomZ interface {
	T
}

type GeomM interface {
	T
}

type GeomZM interface {
	T
}
<|endoftext|>


<commit_before>package controllers

import (
	"github.com/astaxie/beego"
)

type MainController struct {
	beego.Controller
}

func (c *MainController) Get() {
	c.TplNames = "public/index.html"
}
<commit_msg>change TplNames to TplName<commit_after>package controllers

import (
	"github.com/astaxie/beego"
)

type MainController struct {
	beego.Controller
}

func (c *MainController) Get() {
	c.TplName = "public/index.html"
}
<|endoftext|>


<commit_before>package controllers

import (
	"log"
	"net/http"

	"github.com/antonve/logger-api/config"
	"github.com/antonve/logger-api/models"

	"runtime/debug"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/labstack/echo"
)

// Return201 helper
func Return201(context echo.Context) error {
	return context.JSONBlob(http.StatusCreated, []byte(`{"success": true}`))
}

// Return200 helper
func Return200(context echo.Context) error {
	return context.JSONBlob(http.StatusOK, []byte(`{"success": true}`))
}

// Return400 helper
func Return400(context echo.Context, err error) error {
	handleError(err)
	return Serve400(context)
}

// Serve400 helper
func Serve400(context echo.Context) error {
	return context.JSONBlob(http.StatusBadRequest, []byte(`{"success": false, "errorCode": 400, "errorMessage": "400 bad request"}`))
}

// Return403 helper
func Return403(context echo.Context, err error) error {
	handleError(err)
	return Serve403(context)
}

// Serve403 helper
func Serve403(context echo.Context) error {
	return context.JSONBlob(http.StatusForbidden, []byte(`{"success": false, "errorCode": 403, "errorMessage": "400 forbidden"}`))
}

// Return404 helper
func Return404(context echo.Context, err error) error {
	handleError(err)
	return Serve404(context)
}

// Serve404 helper
func Serve404(context echo.Context) error {
	return context.JSONBlob(http.StatusNotFound, []byte(`{"success": false, "errorCode": 404, "errorMessage": "404 page not found"}`))
}

// Serve405 helper
func Serve405(context echo.Context) error {
	return context.JSONBlob(http.StatusMethodNotAllowed, []byte(`{"success": false, "errorCode": 405, "errorMessage": "405 method not allowed"}`))
}

// Return500 helper
func Return500(context echo.Context, err error) error {
	handleError(err)
	return Serve500(context)
}

// Serve500 helper
func Serve500(context echo.Context) error {
	return context.JSONBlob(http.StatusInternalServerError, []byte(`{"success": false, "errorCode": 500, "errorMessage": "500 internal server error"}`))
}

// getUser helper
func getUser(context echo.Context) *models.User {
	token := context.Get("user").(*jwt.Token)
	if token == nil {
		return nil
	}

	claims := token.Claims.(*models.JwtClaims)
	if claims == nil {
		return nil
	}

	return claims.User
}

func handleError(err error) {
	log.Println(err.Error())

	if config.GetConfig().Debug {
		debug.PrintStack()
	}
}
<commit_msg>Fix getUser helper<commit_after>package controllers

import (
	"log"
	"net/http"

	"github.com/antonve/logger-api/config"
	"github.com/antonve/logger-api/models"

	"runtime/debug"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/labstack/echo"
)

// Return201 helper
func Return201(context echo.Context) error {
	return context.JSONBlob(http.StatusCreated, []byte(`{"success": true}`))
}

// Return200 helper
func Return200(context echo.Context) error {
	return context.JSONBlob(http.StatusOK, []byte(`{"success": true}`))
}

// Return400 helper
func Return400(context echo.Context, err error) error {
	handleError(err)
	return Serve400(context)
}

// Serve400 helper
func Serve400(context echo.Context) error {
	return context.JSONBlob(http.StatusBadRequest, []byte(`{"success": false, "errorCode": 400, "errorMessage": "400 bad request"}`))
}

// Return403 helper
func Return403(context echo.Context, err error) error {
	handleError(err)
	return Serve403(context)
}

// Serve403 helper
func Serve403(context echo.Context) error {
	return context.JSONBlob(http.StatusForbidden, []byte(`{"success": false, "errorCode": 403, "errorMessage": "400 forbidden"}`))
}

// Return404 helper
func Return404(context echo.Context, err error) error {
	handleError(err)
	return Serve404(context)
}

// Serve404 helper
func Serve404(context echo.Context) error {
	return context.JSONBlob(http.StatusNotFound, []byte(`{"success": false, "errorCode": 404, "errorMessage": "404 page not found"}`))
}

// Serve405 helper
func Serve405(context echo.Context) error {
	return context.JSONBlob(http.StatusMethodNotAllowed, []byte(`{"success": false, "errorCode": 405, "errorMessage": "405 method not allowed"}`))
}

// Return500 helper
func Return500(context echo.Context, err error) error {
	handleError(err)
	return Serve500(context)
}

// Serve500 helper
func Serve500(context echo.Context) error {
	return context.JSONBlob(http.StatusInternalServerError, []byte(`{"success": false, "errorCode": 500, "errorMessage": "500 internal server error"}`))
}

// getUser helper
func getUser(context echo.Context) *models.User {
	token := context.Get("user")
	if token == nil {
		return nil
	}

	claims := token.(*jwt.Token).Claims
	if claims == nil {
		return nil
	}

	return claims.(*models.JwtClaims).User
}

func handleError(err error) {
	log.Println(err.Error())

	if config.GetConfig().Debug {
		debug.PrintStack()
	}
}
<|endoftext|>


<commit_before>package multiwriter

import (
	"io"
	"sync"
)

type MultiWriter struct {
	sync.Mutex
	writers []io.Writer
}

// Append writer
func (self *MultiWriter) Append(writers ...io.Writer) {
	self.Lock()
	defer self.Unlock()
	self.writers = append(self.writers, writers...)
}

// Remove writer
func (self *MultiWriter) Remove(writers ...io.Writer) {
	self.Lock()
	defer self.Unlock()
	for i := len(self.writers) - 1; i > 0; i-- {
		for _, v := range writers {
			if self.writers[i] == v {
				self.writers = append(self.writers[:i], self.writers[i+1:]...)
				break
			}
		}
	}
}

// Write implements io.Writer
func (self *MultiWriter) Write(p []byte) (n int, err error) {
	self.Lock()
	defer self.Unlock()

	type result struct {
		n   int
		err error
	}

	rs := make(chan *result)

	for _, w := range self.writers {
		go func(writer io.Writer) {
			n, err := writer.Write(p)
			rs <- &result{n, err}
		}(w)
	}

	for range self.writers {
		r := <-rs
		if r.err != nil {
			return r.n, r.err
		}
		if r.n != len(p) {
			return r.n, io.ErrShortWrite
		}
	}
	return len(p), nil
}

// New return a MultiWriter
func New(writers ...io.Writer) io.Writer {
	w := make([]io.Writer, len(writers))
	copy(w, writers)
	return &MultiWriter{writers: w}
}
<commit_msg>	modified:   multiwriter.go<commit_after>package multiwriter

import (
	"io"
	"sync"
)

type MultiWriter struct {
	sync.Mutex
	writers []io.Writer
}

// Append writer
func (self *MultiWriter) Append(writers ...io.Writer) {
	self.Lock()
	defer self.Unlock()
	self.writers = append(self.writers, writers...)
}

// Remove writer
func (self *MultiWriter) Remove(writers ...io.Writer) {
	self.Lock()
	defer self.Unlock()
	for i := len(self.writers) - 1; i > 0; i-- {
		for _, v := range writers {
			if self.writers[i] == v {
				self.writers = append(self.writers[:i], self.writers[i+1:]...)
				break
			}
		}
	}
}

// Len of writers
func (self *MultiWriter) Len() int {
	return len(self.writers)
}

// Write implements io.Writer
func (self *MultiWriter) Write(p []byte) (n int, err error) {
	self.Lock()
	defer self.Unlock()

	type result struct {
		n   int
		err error
	}

	rs := make(chan *result)

	for _, w := range self.writers {
		go func(writer io.Writer) {
			n, err := writer.Write(p)
			rs <- &result{n, err}
		}(w)
	}

	for range self.writers {
		r := <-rs
		if r.err != nil {
			return r.n, r.err
		}
		if r.n != len(p) {
			return r.n, io.ErrShortWrite
		}
	}
	return len(p), nil
}

// New return a MultiWriter
func New(writers ...io.Writer) io.Writer {
	w := make([]io.Writer, len(writers))
	copy(w, writers)
	return &MultiWriter{writers: w}
}
<|endoftext|>


<commit_before>package odb

import (
	"bufio"
	"encoding/hex"
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/git-lfs/git-lfs/errors"
)

// Signature represents a commit signature, which can represent either
// committership or authorship of the commit that this signature belongs to. It
// specifies a name, email, and time that the signature was created.
type Signature struct {
	// Name is the first and last name of the individual holding this
	// signature.
	Name string
	// Email is the email address of the individual holding this signature.
	Email string
	// When is the instant in time when the signature was created.
	When time.Time
}

const (
	parseTimeZoneOnly  = "2006 -0700"
	formatTimeZoneOnly = "-0700"
)

// String implements the fmt.Stringer interface and formats a Signature as
// expected in the Git commit internal object format. For instance:
//
//  Taylor Blau <ttaylorr@github.com> 1494258422 -0600
func (s *Signature) String() string {
	at := s.When.Unix()
	zone := s.When.Format(formatTimeZoneOnly)

	return fmt.Sprintf("%s <%s> %d %s", s.Name, s.Email, at, zone)
}

var (
	signatureNameRe  = regexp.MustCompile("^[^<]+")
	signatureEmailRe = regexp.MustCompile("<(.*)>")
	signatureTimeRe  = regexp.MustCompile("[-]?\\d+")
)

// ParseSignature parses a given string into a signature instance, returning any
// error that it encounters along the way.
//
// ParseSignature expects the signature encoded in the given string to be
// formatted correctly, and reproduce-able by the Signature.String() function
// above.
func ParseSignature(str string) (*Signature, error) {
	name := signatureNameRe.FindString(str)
	emailParts := signatureEmailRe.FindStringSubmatch(str)
	timeParts := signatureTimeRe.FindAllStringSubmatch(str, 2)

	if len(emailParts) < 2 {
		return nil, errors.Errorf("git/odb: expected email in signature: %q", str)
	}
	email := emailParts[1]

	if len(timeParts) < 1 {
		return nil, errors.Errorf("git/odb: expected time in signature: %q", str)
	}

	epoch, err := strconv.ParseInt(timeParts[0][0], 10, 64)
	if err != nil {
		return nil, errors.Errorf("git/odb: unable to parse time in signature: %q", str)
	}

	t := time.Unix(epoch, 0).In(time.UTC)
	if len(timeParts) > 1 && timeParts[1][0] != "0000" {
		loc, err := parseTimeZone(timeParts[1][0])
		if err != nil {
			return nil, errors.Wrap(err, "git/odb: unable to coerce timezone")
		}

		t = t.In(loc)
	}

	return &Signature{
		Name:  strings.TrimSpace(name),
		Email: email,
		When:  t,
	}, nil
}

// parseTimeZone returns the *time.Location corresponding to a Git-formatted
// string offset. For instance, the string "-0700" would format into a
// *time.Location that is 7 hours east of UTC.
func parseTimeZone(zone string) (*time.Location, error) {
	loc, err := time.Parse(parseTimeZoneOnly, fmt.Sprintf("1970 %s", zone))
	if err != nil {
		return nil, err
	}
	return loc.Location(), nil
}

// Commit encapsulates a Git commit entry.
type Commit struct {
	// Author is the Author this commit, or the original writer of the
	// contents.
	Author *Signature
	// Committer is the individual or entity that added this commit to the
	// history.
	Committer *Signature
	// ParentIDs are the IDs of all parents for which this commit is a
	// linear child.
	ParentIDs [][]byte
	// TreeID is the root Tree associated with this commit.
	TreeID []byte
	// Message is the commit message, including any signing information
	// associated with this commit.
	Message string
}

var _ Object = (*Commit)(nil)

// Type implements Object.ObjectType by returning the correct object type for
// Commits, CommitObjectType.
func (c *Commit) Type() ObjectType { return CommitObjectType }

// Decode implements Object.Decode and decodes the uncompressed commit being
// read. It returns the number of uncompressed bytes being consumed off of the
// stream, which should be strictly equal to the size given.
//
// If any error was encountered along the way, that will be returned, along with
// the number of bytes read up to that point.
func (c *Commit) Decode(from io.Reader, size int64) (n int, err error) {
	s := bufio.NewScanner(from)
	for s.Scan() {
		text := s.Text()
		if len(s.Text()) == 0 {
			continue
		}

		fields := strings.Fields(text)
		if len(fields) > 0 {
			switch fields[0] {
			case "tree":
				id, err := hex.DecodeString(fields[1])
				if err != nil {
					panic(1)
					return n, err
				}
				c.TreeID = id
			case "parent":
				id, err := hex.DecodeString(fields[1])
				if err != nil {
					return n, err
				}
				c.ParentIDs = append(c.ParentIDs, id)
			case "author":
				author, err := ParseSignature(strings.Join(fields[1:], " "))
				if err != nil {
					return n, err
				}

				c.Author = author
			case "committer":
				committer, err := ParseSignature(strings.Join(fields[1:], " "))
				if err != nil {
					return n, err
				}

				c.Committer = committer
			default:
				if len(c.Message) == 0 {
					c.Message = s.Text()
				} else {
					c.Message = strings.Join([]string{c.Message, s.Text()}, "\n")
				}
			}
		}

		n = n + len(text+"\n")
	}

	if err = s.Err(); err != nil {
		return n, err
	}
	return n, err
}

// Encode encodes the commit's contents to the given io.Writer, "w". If there was
// any error copying the commit's contents, that error will be returned.
//
// Otherwise, the number of bytes written will be returned.
func (c *Commit) Encode(to io.Writer) (n int, err error) {
	n, err = fmt.Fprintf(to, "tree %s\n", hex.EncodeToString(c.TreeID))
	if err != nil {
		return n, err
	}

	for _, pid := range c.ParentIDs {
		n1, err := fmt.Fprintf(to, "parent %s\n", hex.EncodeToString(pid))
		if err != nil {
			return n, err
		}

		n = n + n1
	}

	n2, err := fmt.Fprintf(to, "author %s\ncommitter %s\n\n%s\n",
		c.Author, c.Committer, c.Message)
	if err != nil {
		return n, err
	}

	return n + n2, err
}
<commit_msg>git/odb: rewrite signatureTimeRe to anchor to string end<commit_after>package odb

import (
	"bufio"
	"encoding/hex"
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/git-lfs/git-lfs/errors"
)

// Signature represents a commit signature, which can represent either
// committership or authorship of the commit that this signature belongs to. It
// specifies a name, email, and time that the signature was created.
type Signature struct {
	// Name is the first and last name of the individual holding this
	// signature.
	Name string
	// Email is the email address of the individual holding this signature.
	Email string
	// When is the instant in time when the signature was created.
	When time.Time
}

const (
	parseTimeZoneOnly  = "2006 -0700"
	formatTimeZoneOnly = "-0700"
)

// String implements the fmt.Stringer interface and formats a Signature as
// expected in the Git commit internal object format. For instance:
//
//  Taylor Blau <ttaylorr@github.com> 1494258422 -0600
func (s *Signature) String() string {
	at := s.When.Unix()
	zone := s.When.Format(formatTimeZoneOnly)

	return fmt.Sprintf("%s <%s> %d %s", s.Name, s.Email, at, zone)
}

var (
	signatureNameRe  = regexp.MustCompile("^[^<]+")
	signatureEmailRe = regexp.MustCompile("<(.*)>")
	signatureTimeRe  = regexp.MustCompile("(\\d+)(\\s[-]?\\d+)?$")
)

// ParseSignature parses a given string into a signature instance, returning any
// error that it encounters along the way.
//
// ParseSignature expects the signature encoded in the given string to be
// formatted correctly, and reproduce-able by the Signature.String() function
// above.
func ParseSignature(str string) (*Signature, error) {
	name := signatureNameRe.FindString(str)
	emailParts := signatureEmailRe.FindStringSubmatch(str)
	timeParts := signatureTimeRe.FindStringSubmatch(str)

	if len(emailParts) < 2 {
		return nil, errors.Errorf("git/odb: expected email in signature: %q", str)
	}
	email := emailParts[1]

	if len(timeParts) < 2 {
		return nil, errors.Errorf("git/odb: expected time in signature: %q", str)
	}

	timestamp, timezone := timeParts[1], "0000"
	if len(timeParts) >= 3 {
		timezone = strings.TrimSpace(timeParts[2])
	}

	epoch, err := strconv.ParseInt(timestamp, 10, 64)
	if err != nil {
		return nil, errors.Errorf("git/odb: unable to parse time in signature: %q", str)
	}

	t := time.Unix(epoch, 0).In(time.UTC)

	if timezone != "0000" {
		loc, err := parseTimeZone(timezone)
		if err != nil {
			return nil, errors.Wrap(err, "git/odb: unable to coerce timezone")
		}

		t = t.In(loc)
	}

	return &Signature{
		Name:  strings.TrimSpace(name),
		Email: email,
		When:  t,
	}, nil
}

// parseTimeZone returns the *time.Location corresponding to a Git-formatted
// string offset. For instance, the string "-0700" would format into a
// *time.Location that is 7 hours east of UTC.
func parseTimeZone(zone string) (*time.Location, error) {
	loc, err := time.Parse(parseTimeZoneOnly, fmt.Sprintf("1970 %s", zone))
	if err != nil {
		return nil, err
	}
	return loc.Location(), nil
}

// Commit encapsulates a Git commit entry.
type Commit struct {
	// Author is the Author this commit, or the original writer of the
	// contents.
	Author *Signature
	// Committer is the individual or entity that added this commit to the
	// history.
	Committer *Signature
	// ParentIDs are the IDs of all parents for which this commit is a
	// linear child.
	ParentIDs [][]byte
	// TreeID is the root Tree associated with this commit.
	TreeID []byte
	// Message is the commit message, including any signing information
	// associated with this commit.
	Message string
}

var _ Object = (*Commit)(nil)

// Type implements Object.ObjectType by returning the correct object type for
// Commits, CommitObjectType.
func (c *Commit) Type() ObjectType { return CommitObjectType }

// Decode implements Object.Decode and decodes the uncompressed commit being
// read. It returns the number of uncompressed bytes being consumed off of the
// stream, which should be strictly equal to the size given.
//
// If any error was encountered along the way, that will be returned, along with
// the number of bytes read up to that point.
func (c *Commit) Decode(from io.Reader, size int64) (n int, err error) {
	s := bufio.NewScanner(from)
	for s.Scan() {
		text := s.Text()
		if len(s.Text()) == 0 {
			continue
		}

		fields := strings.Fields(text)
		if len(fields) > 0 {
			switch fields[0] {
			case "tree":
				id, err := hex.DecodeString(fields[1])
				if err != nil {
					panic(1)
					return n, err
				}
				c.TreeID = id
			case "parent":
				id, err := hex.DecodeString(fields[1])
				if err != nil {
					return n, err
				}
				c.ParentIDs = append(c.ParentIDs, id)
			case "author":
				author, err := ParseSignature(strings.Join(fields[1:], " "))
				if err != nil {
					return n, err
				}

				c.Author = author
			case "committer":
				committer, err := ParseSignature(strings.Join(fields[1:], " "))
				if err != nil {
					return n, err
				}

				c.Committer = committer
			default:
				if len(c.Message) == 0 {
					c.Message = s.Text()
				} else {
					c.Message = strings.Join([]string{c.Message, s.Text()}, "\n")
				}
			}
		}

		n = n + len(text+"\n")
	}

	if err = s.Err(); err != nil {
		return n, err
	}
	return n, err
}

// Encode encodes the commit's contents to the given io.Writer, "w". If there was
// any error copying the commit's contents, that error will be returned.
//
// Otherwise, the number of bytes written will be returned.
func (c *Commit) Encode(to io.Writer) (n int, err error) {
	n, err = fmt.Fprintf(to, "tree %s\n", hex.EncodeToString(c.TreeID))
	if err != nil {
		return n, err
	}

	for _, pid := range c.ParentIDs {
		n1, err := fmt.Fprintf(to, "parent %s\n", hex.EncodeToString(pid))
		if err != nil {
			return n, err
		}

		n = n + n1
	}

	n2, err := fmt.Fprintf(to, "author %s\ncommitter %s\n\n%s\n",
		c.Author, c.Committer, c.Message)
	if err != nil {
		return n, err
	}

	return n + n2, err
}
<|endoftext|>


<commit_before>package pstoreds

import (
	"fmt"
	"strconv"
	"sync/atomic"
	"time"

	ds "github.com/ipfs/go-datastore"
	query "github.com/ipfs/go-datastore/query"
	errors "github.com/pkg/errors"

	peer "github.com/libp2p/go-libp2p-peer"
	pb "github.com/libp2p/go-libp2p-peerstore/pb"

	b32 "github.com/whyrusleeping/base32"
)

var (
	// GC lookahead entries are stored in keys with pattern:
	// /peers/gc/addrs/<unix timestamp of next visit>/<peer ID b32> => nil
	gcLookaheadBase = ds.NewKey("/peers/gc/addrs")
	// in GC routines, how many operations do we place in a batch before it's committed.
	gcOpsPerBatch = 20
	// queries
	purgeQuery             = query.Query{Prefix: gcLookaheadBase.String(), KeysOnly: true}
	populateLookaheadQuery = query.Query{Prefix: addrBookBase.String(), KeysOnly: true}
)

// cyclicBatch is similar to go-datastore autobatch, but it's driven by an actual Batch facility offered by the
// datastore. It populates an ongoing batch with operations and automatically flushes it after N pending operations
// have been reached. `N` is currently hardcoded to 20. An explicit `Commit()` closes this cyclic batch, erroring all
// further operations.
type cyclicBatch struct {
	ds.Batch
	ds      ds.Batching
	pending int
}

func newCyclicBatch(ds ds.Batching) (ds.Batch, error) {
	batch, err := ds.Batch()
	if err != nil {
		return nil, err
	}
	return &cyclicBatch{Batch: batch, ds: ds}, nil
}

func (cb *cyclicBatch) cycle() (err error) {
	if cb.Batch == nil {
		return errors.New("cyclic batch is closed")
	}
	if cb.pending < gcOpsPerBatch {
		// we haven't reached the threshold yet.
		return nil
	}
	// commit and renew the batch.
	if err = cb.Batch.Commit(); err != nil {
		return errors.Wrap(err, "failed while committing cyclic batch")
	}
	if cb.Batch, err = cb.ds.Batch(); err != nil {
		return errors.Wrap(err, "failed while renewing cyclic batch")
	}
	return nil
}

func (cb *cyclicBatch) Put(key ds.Key, val []byte) error {
	if err := cb.cycle(); err != nil {
		return err
	}
	cb.pending++
	return cb.Batch.Put(key, val)
}

func (cb *cyclicBatch) Delete(key ds.Key) error {
	if err := cb.cycle(); err != nil {
		return err
	}
	cb.pending++
	return cb.Batch.Delete(key)
}

func (cb *cyclicBatch) Commit() error {
	if cb.Batch == nil {
		return errors.New("cyclic batch is closed")
	}
	if err := cb.Batch.Commit(); err != nil {
		return err
	}
	cb.pending = 0
	cb.Batch = nil
	return nil
}

// purgeCycle runs a single GC cycle, operating within the lookahead window.
//
// It scans the lookahead region for entries that need to be visited, and performs a Clean() on them. An errors trigger
// the removal of the GC entry, in order to prevent unactionable items from accumulating. If the error happened to be
// temporary, the entry will be revisited in the next lookahead window.
func (ab *dsAddrBook) purgeCycle() {
	if atomic.LoadInt32(&ab.gcLookaheadRunning) > 0 {
		// yield if lookahead is running.
		return
	}

	var id peer.ID
	record := &addrsRecord{AddrBookRecord: &pb.AddrBookRecord{}}
	batch, err := newCyclicBatch(ab.ds)
	if err != nil {
		log.Warningf("failed while creating batch to purge GC entries: %v", err)
	}

	// This function drops an unparseable GC entry; this is for safety. It is an escape hatch in case
	// we modify the format of keys going forward. If a user runs a new version against an old DB,
	// if we don't clean up unparseable entries we'll end up accumulating garbage.
	dropInError := func(key ds.Key, err error, msg string) {
		if err != nil {
			log.Warningf("failed while %s with GC key: %v, err: %v", msg, key, err)
		}
		if err = batch.Delete(key); err != nil {
			log.Warningf("failed to delete corrupt GC lookahead entry: %v, err: %v", key, err)
		}
	}

	// This function drops a GC key if the entry is cleaned correctly. It may reschedule another visit
	// if the next earliest expiry falls within the current window again.
	dropOrReschedule := func(key ds.Key, ar *addrsRecord) {
		if err := batch.Delete(key); err != nil {
			log.Warningf("failed to delete lookahead entry: %v, err: %v", key, err)
		}

		// re-add the record if it needs to be visited again in this window.
		if len(ar.Addrs) != 0 && ar.Addrs[0].Expiry <= ab.gcCurrWindowEnd {
			gcKey := gcLookaheadBase.ChildString(fmt.Sprintf("%d/%s", ar.Addrs[0].Expiry, key.Name()))
			if err := batch.Put(gcKey, []byte{}); err != nil {
				log.Warningf("failed to add new GC key: %v, err: %v", gcKey, err)
			}
		}
	}

	results, err := ab.ds.Query(purgeQuery)
	if err != nil {
		log.Warningf("failed while fetching entries to purge: %v", err)
		return
	}
	defer results.Close()

	now := time.Now().Unix()

	// keys: 	/peers/gc/addrs/<unix timestamp of next visit>/<peer ID b32>
	// values: 	nil
	for result := range results.Next() {
		gcKey := ds.RawKey(result.Key)
		ts, err := strconv.ParseInt(gcKey.Parent().Name(), 10, 64)
		if err != nil {
			dropInError(gcKey, err, "parsing timestamp")
			log.Warningf("failed while parsing timestamp from key: %v, err: %v", result.Key, err)
			continue
		} else if ts > now {
			// this is an ordered cursor; when we hit an entry with a timestamp beyond now, we can break.
			break
		}

		idb32, err := b32.RawStdEncoding.DecodeString(gcKey.Name())
		if err != nil {
			dropInError(gcKey, err, "parsing peer ID")
			log.Warningf("failed while parsing b32 peer ID from key: %v, err: %v", result.Key, err)
			continue
		}

		id, err = peer.IDFromBytes(idb32)
		if err != nil {
			dropInError(gcKey, err, "decoding peer ID")
			log.Warningf("failed while decoding peer ID from key: %v, err: %v", result.Key, err)
			continue
		}

		// if the record is in cache, we clean it and flush it if necessary.
		if e, ok := ab.cache.Peek(id); ok {
			cached := e.(*addrsRecord)
			cached.Lock()
			if cached.Clean() {
				if err = cached.Flush(batch); err != nil {
					log.Warningf("failed to flush entry modified by GC for peer: &v, err: %v", id.Pretty(), err)
				}
			}
			dropOrReschedule(gcKey, cached)
			cached.Unlock()
			continue
		}

		record.Reset()

		// otherwise, fetch it from the store, clean it and flush it.
		entryKey := addrBookBase.ChildString(gcKey.Name())
		val, err := ab.ds.Get(entryKey)
		if err != nil {
			// captures all errors, including ErrNotFound.
			dropInError(gcKey, err, "fetching entry")
			continue
		}
		err = record.Unmarshal(val)
		if err != nil {
			dropInError(gcKey, err, "unmarshalling entry")
			continue
		}
		if record.Clean() {
			err = record.Flush(batch)
			if err != nil {
				log.Warningf("failed to flush entry modified by GC for peer: &v, err: %v", id.Pretty(), err)
			}
		}
		dropOrReschedule(gcKey, record)
	}

	if err = batch.Commit(); err != nil {
		log.Warningf("failed to commit GC purge batch: %v", err)
	}
}

// populateLookahead populates the lookahead window by scanning the entire store and picking entries whose earliest
// expiration falls within the new window.
//
// Those entries are stored in the lookahead region in the store, indexed by the timestamp when they need to be
// visited, to facilitate temporal range scans.
func (ab *dsAddrBook) populateLookahead() {
	if !atomic.CompareAndSwapInt32(&ab.gcLookaheadRunning, 0, 1) {
		return
	}

	until := time.Now().Add(ab.opts.GCLookaheadInterval).Unix()

	var id peer.ID
	record := &addrsRecord{AddrBookRecord: &pb.AddrBookRecord{}}
	results, err := ab.ds.Query(populateLookaheadQuery)
	if err != nil {
		log.Warningf("failed while querying to populate lookahead GC window: %v", err)
		return
	}
	defer results.Close()

	batch, err := newCyclicBatch(ab.ds)
	if err != nil {
		log.Warningf("failed while creating batch to populate lookahead GC window: %v", err)
		return
	}

	for result := range results.Next() {
		idb32 := ds.RawKey(result.Key).Name()
		k, err := b32.RawStdEncoding.DecodeString(idb32)
		if err != nil {
			log.Warningf("failed while decoding peer ID from key: %v, err: %v", result.Key, err)
			continue
		}
		if id, err = peer.IDFromBytes(k); err != nil {
			log.Warningf("failed while decoding peer ID from key: %v, err: %v", result.Key, err)
		}

		// if the record is in cache, use the cached version.
		if e, ok := ab.cache.Peek(id); ok {
			cached := e.(*addrsRecord)
			cached.RLock()
			if len(cached.Addrs) == 0 || cached.Addrs[0].Expiry > until {
				cached.RUnlock()
				continue
			}
			gcKey := gcLookaheadBase.ChildString(fmt.Sprintf("%d/%s", cached.Addrs[0].Expiry, idb32))
			if err = batch.Put(gcKey, []byte{}); err != nil {
				log.Warningf("failed while inserting GC entry for peer: %v, err: %v", id.Pretty(), err)
			}
			cached.RUnlock()
			continue
		}

		record.Reset()

		val, err := ab.ds.Get(ds.RawKey(result.Key))
		if err != nil {
			log.Warningf("failed which getting record from store for peer: %v, err: %v", id.Pretty(), err)
			continue
		}
		if err := record.Unmarshal(val); err != nil {
			log.Warningf("failed while unmarshalling record from store for peer: %v, err: %v", id.Pretty(), err)
			continue
		}
		if len(record.Addrs) > 0 && record.Addrs[0].Expiry <= until {
			gcKey := gcLookaheadBase.ChildString(fmt.Sprintf("%d/%s", record.Addrs[0].Expiry, idb32))
			if err = batch.Put(gcKey, []byte{}); err != nil {
				log.Warningf("failed while inserting GC entry for peer: %v, err: %v", id.Pretty(), err)
			}
		}
	}

	if err = batch.Commit(); err != nil {
		log.Warningf("failed to commit GC lookahead batch: %v", err)
	}

	ab.gcCurrWindowEnd = until
	atomic.StoreInt32(&ab.gcLookaheadRunning, 0)
}
<commit_msg>pstore ds: enforce order in gc queries.<commit_after>package pstoreds

import (
	"fmt"
	"strconv"
	"sync/atomic"
	"time"

	ds "github.com/ipfs/go-datastore"
	query "github.com/ipfs/go-datastore/query"
	errors "github.com/pkg/errors"

	peer "github.com/libp2p/go-libp2p-peer"
	pb "github.com/libp2p/go-libp2p-peerstore/pb"

	b32 "github.com/whyrusleeping/base32"
)

var (
	// GC lookahead entries are stored in keys with pattern:
	// /peers/gc/addrs/<unix timestamp of next visit>/<peer ID b32> => nil
	gcLookaheadBase = ds.NewKey("/peers/gc/addrs")
	// in GC routines, how many operations do we place in a batch before it's committed.
	gcOpsPerBatch = 20
	// queries
	purgeQuery = query.Query{
		Prefix:   gcLookaheadBase.String(),
		Orders:   []query.Order{query.OrderByKey{}},
		KeysOnly: true,
	}
	populateLookaheadQuery = query.Query{
		Prefix:   addrBookBase.String(),
		Orders:   []query.Order{query.OrderByKey{}},
		KeysOnly: true,
	}
)

// cyclicBatch is similar to go-datastore autobatch, but it's driven by an actual Batch facility offered by the
// datastore. It populates an ongoing batch with operations and automatically flushes it after N pending operations
// have been reached. `N` is currently hardcoded to 20. An explicit `Commit()` closes this cyclic batch, erroring all
// further operations.
type cyclicBatch struct {
	ds.Batch
	ds      ds.Batching
	pending int
}

func newCyclicBatch(ds ds.Batching) (ds.Batch, error) {
	batch, err := ds.Batch()
	if err != nil {
		return nil, err
	}
	return &cyclicBatch{Batch: batch, ds: ds}, nil
}

func (cb *cyclicBatch) cycle() (err error) {
	if cb.Batch == nil {
		return errors.New("cyclic batch is closed")
	}
	if cb.pending < gcOpsPerBatch {
		// we haven't reached the threshold yet.
		return nil
	}
	// commit and renew the batch.
	if err = cb.Batch.Commit(); err != nil {
		return errors.Wrap(err, "failed while committing cyclic batch")
	}
	if cb.Batch, err = cb.ds.Batch(); err != nil {
		return errors.Wrap(err, "failed while renewing cyclic batch")
	}
	return nil
}

func (cb *cyclicBatch) Put(key ds.Key, val []byte) error {
	if err := cb.cycle(); err != nil {
		return err
	}
	cb.pending++
	return cb.Batch.Put(key, val)
}

func (cb *cyclicBatch) Delete(key ds.Key) error {
	if err := cb.cycle(); err != nil {
		return err
	}
	cb.pending++
	return cb.Batch.Delete(key)
}

func (cb *cyclicBatch) Commit() error {
	if cb.Batch == nil {
		return errors.New("cyclic batch is closed")
	}
	if err := cb.Batch.Commit(); err != nil {
		return err
	}
	cb.pending = 0
	cb.Batch = nil
	return nil
}

// purgeCycle runs a single GC cycle, operating within the lookahead window.
//
// It scans the lookahead region for entries that need to be visited, and performs a Clean() on them. An errors trigger
// the removal of the GC entry, in order to prevent unactionable items from accumulating. If the error happened to be
// temporary, the entry will be revisited in the next lookahead window.
func (ab *dsAddrBook) purgeCycle() {
	if atomic.LoadInt32(&ab.gcLookaheadRunning) > 0 {
		// yield if lookahead is running.
		return
	}

	var id peer.ID
	record := &addrsRecord{AddrBookRecord: &pb.AddrBookRecord{}}
	batch, err := newCyclicBatch(ab.ds)
	if err != nil {
		log.Warningf("failed while creating batch to purge GC entries: %v", err)
	}

	// This function drops an unparseable GC entry; this is for safety. It is an escape hatch in case
	// we modify the format of keys going forward. If a user runs a new version against an old DB,
	// if we don't clean up unparseable entries we'll end up accumulating garbage.
	dropInError := func(key ds.Key, err error, msg string) {
		if err != nil {
			log.Warningf("failed while %s with GC key: %v, err: %v", msg, key, err)
		}
		if err = batch.Delete(key); err != nil {
			log.Warningf("failed to delete corrupt GC lookahead entry: %v, err: %v", key, err)
		}
	}

	// This function drops a GC key if the entry is cleaned correctly. It may reschedule another visit
	// if the next earliest expiry falls within the current window again.
	dropOrReschedule := func(key ds.Key, ar *addrsRecord) {
		if err := batch.Delete(key); err != nil {
			log.Warningf("failed to delete lookahead entry: %v, err: %v", key, err)
		}

		// re-add the record if it needs to be visited again in this window.
		if len(ar.Addrs) != 0 && ar.Addrs[0].Expiry <= ab.gcCurrWindowEnd {
			gcKey := gcLookaheadBase.ChildString(fmt.Sprintf("%d/%s", ar.Addrs[0].Expiry, key.Name()))
			if err := batch.Put(gcKey, []byte{}); err != nil {
				log.Warningf("failed to add new GC key: %v, err: %v", gcKey, err)
			}
		}
	}

	results, err := ab.ds.Query(purgeQuery)
	if err != nil {
		log.Warningf("failed while fetching entries to purge: %v", err)
		return
	}
	defer results.Close()

	now := time.Now().Unix()

	// keys: 	/peers/gc/addrs/<unix timestamp of next visit>/<peer ID b32>
	// values: 	nil
	for result := range results.Next() {
		gcKey := ds.RawKey(result.Key)
		ts, err := strconv.ParseInt(gcKey.Parent().Name(), 10, 64)
		if err != nil {
			dropInError(gcKey, err, "parsing timestamp")
			log.Warningf("failed while parsing timestamp from key: %v, err: %v", result.Key, err)
			continue
		} else if ts > now {
			// this is an ordered cursor; when we hit an entry with a timestamp beyond now, we can break.
			break
		}

		idb32, err := b32.RawStdEncoding.DecodeString(gcKey.Name())
		if err != nil {
			dropInError(gcKey, err, "parsing peer ID")
			log.Warningf("failed while parsing b32 peer ID from key: %v, err: %v", result.Key, err)
			continue
		}

		id, err = peer.IDFromBytes(idb32)
		if err != nil {
			dropInError(gcKey, err, "decoding peer ID")
			log.Warningf("failed while decoding peer ID from key: %v, err: %v", result.Key, err)
			continue
		}

		// if the record is in cache, we clean it and flush it if necessary.
		if e, ok := ab.cache.Peek(id); ok {
			cached := e.(*addrsRecord)
			cached.Lock()
			if cached.Clean() {
				if err = cached.Flush(batch); err != nil {
					log.Warningf("failed to flush entry modified by GC for peer: &v, err: %v", id.Pretty(), err)
				}
			}
			dropOrReschedule(gcKey, cached)
			cached.Unlock()
			continue
		}

		record.Reset()

		// otherwise, fetch it from the store, clean it and flush it.
		entryKey := addrBookBase.ChildString(gcKey.Name())
		val, err := ab.ds.Get(entryKey)
		if err != nil {
			// captures all errors, including ErrNotFound.
			dropInError(gcKey, err, "fetching entry")
			continue
		}
		err = record.Unmarshal(val)
		if err != nil {
			dropInError(gcKey, err, "unmarshalling entry")
			continue
		}
		if record.Clean() {
			err = record.Flush(batch)
			if err != nil {
				log.Warningf("failed to flush entry modified by GC for peer: &v, err: %v", id.Pretty(), err)
			}
		}
		dropOrReschedule(gcKey, record)
	}

	if err = batch.Commit(); err != nil {
		log.Warningf("failed to commit GC purge batch: %v", err)
	}
}

// populateLookahead populates the lookahead window by scanning the entire store and picking entries whose earliest
// expiration falls within the new window.
//
// Those entries are stored in the lookahead region in the store, indexed by the timestamp when they need to be
// visited, to facilitate temporal range scans.
func (ab *dsAddrBook) populateLookahead() {
	if !atomic.CompareAndSwapInt32(&ab.gcLookaheadRunning, 0, 1) {
		return
	}

	until := time.Now().Add(ab.opts.GCLookaheadInterval).Unix()

	var id peer.ID
	record := &addrsRecord{AddrBookRecord: &pb.AddrBookRecord{}}
	results, err := ab.ds.Query(populateLookaheadQuery)
	if err != nil {
		log.Warningf("failed while querying to populate lookahead GC window: %v", err)
		return
	}
	defer results.Close()

	batch, err := newCyclicBatch(ab.ds)
	if err != nil {
		log.Warningf("failed while creating batch to populate lookahead GC window: %v", err)
		return
	}

	for result := range results.Next() {
		idb32 := ds.RawKey(result.Key).Name()
		k, err := b32.RawStdEncoding.DecodeString(idb32)
		if err != nil {
			log.Warningf("failed while decoding peer ID from key: %v, err: %v", result.Key, err)
			continue
		}
		if id, err = peer.IDFromBytes(k); err != nil {
			log.Warningf("failed while decoding peer ID from key: %v, err: %v", result.Key, err)
		}

		// if the record is in cache, use the cached version.
		if e, ok := ab.cache.Peek(id); ok {
			cached := e.(*addrsRecord)
			cached.RLock()
			if len(cached.Addrs) == 0 || cached.Addrs[0].Expiry > until {
				cached.RUnlock()
				continue
			}
			gcKey := gcLookaheadBase.ChildString(fmt.Sprintf("%d/%s", cached.Addrs[0].Expiry, idb32))
			if err = batch.Put(gcKey, []byte{}); err != nil {
				log.Warningf("failed while inserting GC entry for peer: %v, err: %v", id.Pretty(), err)
			}
			cached.RUnlock()
			continue
		}

		record.Reset()

		val, err := ab.ds.Get(ds.RawKey(result.Key))
		if err != nil {
			log.Warningf("failed which getting record from store for peer: %v, err: %v", id.Pretty(), err)
			continue
		}
		if err := record.Unmarshal(val); err != nil {
			log.Warningf("failed while unmarshalling record from store for peer: %v, err: %v", id.Pretty(), err)
			continue
		}
		if len(record.Addrs) > 0 && record.Addrs[0].Expiry <= until {
			gcKey := gcLookaheadBase.ChildString(fmt.Sprintf("%d/%s", record.Addrs[0].Expiry, idb32))
			if err = batch.Put(gcKey, []byte{}); err != nil {
				log.Warningf("failed while inserting GC entry for peer: %v, err: %v", id.Pretty(), err)
			}
		}
	}

	if err = batch.Commit(); err != nil {
		log.Warningf("failed to commit GC lookahead batch: %v", err)
	}

	ab.gcCurrWindowEnd = until
	atomic.StoreInt32(&ab.gcLookaheadRunning, 0)
}
<|endoftext|>


<commit_before>package core

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/mdlayher/wavepipe/api"
	"github.com/mdlayher/wavepipe/data"

	"github.com/codegangsta/negroni"
	"github.com/gorilla/context"
	"github.com/unrolled/render"
)

// Negroni instance to test against
var n = negroni.New()

// Render instance to test against
var r = render.New(render.Options{})

func init() {
	// Set up database connection
	data.DB = new(data.SqliteBackend)
	data.DB.DSN("~/.config/wavepipe/wavepipe.db")
	if err := data.DB.Open(); err != nil {
		os.Exit(1)
	}

	// Set up Negroni with API routes
	n.UseHandler(newRouter())
}

// TestAPIRouter verifies that all API request processing functionality is working properly
func TestAPIRouter(t *testing.T) {
	// Table of tests to run, and their expected HTTP status results
	var tests = []struct {
		code   int
		method string
		url    string
	}{
		// Albums API
		//   - valid request
		{200, "GET", "/api/v0/albums"},
		//   - valid request for 1 item
		{200, "GET", "/api/v0/albums/1"},
		//   - valid limit items request
		{200, "GET", "/api/v0/albums?limit=0,10"},
		//   - invalid API version
		{400, "GET", "/api/v999/albums"},
		//   - invalid integer album ID
		{400, "GET", "/api/v0/albums/foo"},
		//   - missing second integer for limit
		{400, "GET", "/api/v0/albums?limit=0"},
		//   - invalid integer pair for limit
		{400, "GET", "/api/v0/albums?limit=foo,bar"},
		//   - album ID not found
		{404, "GET", "/api/v0/albums/99999999"},

		// Art API - skip valid requests, due to binary output
		//   - invalid API version
		{400, "GET", "/api/v999/art"},
		//   - no integer ID provided
		{400, "GET", "/api/v0/art"},
		//   - invalid art stream ID
		{400, "GET", "/api/v0/art/foo"},
		//   - invalid integer size
		{400, "GET", "/api/v0/art/1?size=foo"},
		//   - negative integer size
		{400, "GET", "/api/v0/art/1?size=-1"},
		//   - art ID not found
		{404, "GET", "/api/v0/art/99999999"},

		// Artists API
		//   - valid request
		{200, "GET", "/api/v0/artists"},
		//   - valid request for 1 item
		{200, "GET", "/api/v0/artists/1"},
		//   - valid limit items request
		{200, "GET", "/api/v0/artists?limit=0,10"},
		//   - invalid API version
		{400, "GET", "/api/v999/artists"},
		//   - invalid integer artist ID
		{400, "GET", "/api/v0/artists/foo"},
		//   - missing second integer for limit
		{400, "GET", "/api/v0/artists?limit=0"},
		//   - invalid integer pair for limit
		{400, "GET", "/api/v0/artists?limit=foo,bar"},
		//   - artist ID not found
		{404, "GET", "/api/v0/artists/99999999"},

		// Folders API
		//   - valid request
		{200, "GET", "/api/v0/folders"},
		//   - valid request for 1 item
		{200, "GET", "/api/v0/folders/1"},
		//   - invalid API version
		{400, "GET", "/api/v999/folders"},
		//   - valid limit items request
		{200, "GET", "/api/v0/folders?limit=0,10"},
		//   - invalid integer folder ID
		{400, "GET", "/api/v0/folders/foo"},
		//   - missing second integer for limit
		{400, "GET", "/api/v0/folders?limit=0"},
		//   - invalid integer pair for limit
		{400, "GET", "/api/v0/folders?limit=foo,bar"},
		//   - folder ID not found
		{404, "GET", "/api/v0/folders/99999999"},

		// LastFM API - skip valid requests, due to need for external service
		//   - invalid API version
		{400, "POST", "/api/v999/lastfm"},
		//   - no string action provided
		{400, "POST", "/api/v0/lastfm"},
		//   - invalid string action provided
		{400, "POST", "/api/v0/lastfm/foo"},
		//   - login: no username provided
		{400, "POST", "/api/v0/lastfm/login"},
		//   - action: user must authenticate to last.fm
		{401, "POST", "/api/v0/lastfm/nowplaying"},
		//   - action: user must authenticate to last.fm
		{401, "POST", "/api/v0/lastfm/scrobble"},
		// Cannot test other calls without a valid Last.fm token

		// Login/Logout API - skip due to need for sessions and users

		// Search API
		//   - valid request
		{200, "GET", "/api/v0/search/foo"},
		//   - invalid API version
		{400, "GET", "/api/v999/search"},
		//   - no search query specified
		{400, "GET", "/api/v0/search"},

		// Songs API
		//   - valid request
		{200, "GET", "/api/v0/songs"},
		//   - valid request for 1 item
		{200, "GET", "/api/v0/songs/1"},
		//   - valid limit items request
		{200, "GET", "/api/v0/songs?limit=0,10"},
		//   - valid random items request
		{200, "GET", "/api/v0/songs?random=10"},
		//   - invalid API version
		{400, "GET", "/api/v999/songs"},
		//   - invalid integer song ID
		{400, "GET", "/api/v0/songs/foo"},
		//   - missing second integer for limit
		{400, "GET", "/api/v0/songs?limit=0"},
		//   - invalid integer pair for limit
		{400, "GET", "/api/v0/songs?limit=foo,bar"},
		//   - invalid integer for random
		{400, "GET", "/api/v0/songs?random=foo"},
		//   - song ID not found
		{404, "GET", "/api/v0/songs/99999999"},

		// Status API
		//   - valid request
		{200, "GET", "/api/v0/status"},
		//   - valid request with metrics
		{200, "GET", "/api/v0/status?metrics=true"},
		//   - invalid API version
		{400, "GET", "/api/v999/status"},

		// Stream API - skip valid requests, due to binary output
		//   - invalid API version
		{400, "GET", "/api/v999/stream"},
		//   - no integer stream ID provided
		{400, "GET", "/api/v0/stream"},
		//   - invalid stream stream ID
		{400, "GET", "/api/v0/stream/foo"},
		//   - song ID not found
		{404, "GET", "/api/v0/stream/99999999"},

		// Transcode API - skip valid requests, due to binary output
		//   - invalid API version
		{400, "GET", "/api/v999/transcode"},
		//   - no integer transcode ID provided
		{400, "GET", "/api/v0/transcode"},
		//   - invalid transcode transcode ID
		{400, "GET", "/api/v0/transcode/foo"},
		//   - song ID not found
		{404, "GET", "/api/v0/transcode/99999999"},
		//   - ffmpeg not found, transcoding disabled
		{503, "GET", "/api/v0/transcode/1"},

		// Users API
		//   - valid request
		{200, "GET", "/api/v0/users"},
		//   - valid request for 1 item
		{200, "GET", "/api/v0/users/1"},
		//   - invalid integer user ID
		{400, "GET", "/api/v0/users/foo"},
		//   - user ID not found
		{404, "GET", "/api/v0/users/99999999"},
	}

	// Iterate all tests
	for _, test := range tests {
		// Generate a new HTTP request
		req, err := http.NewRequest(test.method, "http://localhost:8080"+test.url, nil)
		if err != nil {
			t.Fatalf("Failed to create HTTP request")
		}

		// Map context for request
		context.Set(req, api.CtxRender, r)
		context.Set(req, api.CtxUser, &data.User{RoleID: 1})
		context.Set(req, api.CtxSession, new(data.Session))

		// Capture HTTP response via recorder
		w := httptest.NewRecorder()

		// Perform request
		n.ServeHTTP(w, req)

		// Validate results
		if w.Code != test.code {
			t.Fatalf("HTTP [%v != %v] %s %s", w.Code, test.code, test.method, test.url)
		}

		// Check result body as well
		body, err := ioutil.ReadAll(w.Body)
		if err != nil {
			t.Fatal(err)
		}

		// Unmarshal error response
		var errRes api.ErrorResponse
		if err := json.Unmarshal(body, &errRes); err != nil {
			log.Println(string(body))
			t.Fatal(err)
		}

		// If not HTTP 200, check to ensure error code matches
		if errRes.Error != nil && errRes.Error.Code != test.code {
			t.Fatalf("Body [%v != %v] %s %s", errRes.Error.Code, test.code, test.method, test.url)
		}

		log.Printf("OK: [%d] %s %s", test.code, test.method, test.url)
	}
}
<commit_msg>core/apiRouter_test: remove a couple art tests which require an actual file<commit_after>package core

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/mdlayher/wavepipe/api"
	"github.com/mdlayher/wavepipe/data"

	"github.com/codegangsta/negroni"
	"github.com/gorilla/context"
	"github.com/unrolled/render"
)

// Negroni instance to test against
var n = negroni.New()

// Render instance to test against
var r = render.New(render.Options{})

func init() {
	// Set up database connection
	data.DB = new(data.SqliteBackend)
	data.DB.DSN("~/.config/wavepipe/wavepipe.db")
	if err := data.DB.Open(); err != nil {
		os.Exit(1)
	}

	// Set up Negroni with API routes
	n.UseHandler(newRouter())
}

// TestAPIRouter verifies that all API request processing functionality is working properly
func TestAPIRouter(t *testing.T) {
	// Table of tests to run, and their expected HTTP status results
	var tests = []struct {
		code   int
		method string
		url    string
	}{
		// Albums API
		//   - valid request
		{200, "GET", "/api/v0/albums"},
		//   - valid request for 1 item
		{200, "GET", "/api/v0/albums/1"},
		//   - valid limit items request
		{200, "GET", "/api/v0/albums?limit=0,10"},
		//   - invalid API version
		{400, "GET", "/api/v999/albums"},
		//   - invalid integer album ID
		{400, "GET", "/api/v0/albums/foo"},
		//   - missing second integer for limit
		{400, "GET", "/api/v0/albums?limit=0"},
		//   - invalid integer pair for limit
		{400, "GET", "/api/v0/albums?limit=foo,bar"},
		//   - album ID not found
		{404, "GET", "/api/v0/albums/99999999"},

		// Art API - skip valid requests, due to binary output
		//   - invalid API version
		{400, "GET", "/api/v999/art"},
		//   - no integer ID provided
		{400, "GET", "/api/v0/art"},
		//   - invalid art stream ID
		{400, "GET", "/api/v0/art/foo"},
		//   - art ID not found
		{404, "GET", "/api/v0/art/99999999"},

		// Artists API
		//   - valid request
		{200, "GET", "/api/v0/artists"},
		//   - valid request for 1 item
		{200, "GET", "/api/v0/artists/1"},
		//   - valid limit items request
		{200, "GET", "/api/v0/artists?limit=0,10"},
		//   - invalid API version
		{400, "GET", "/api/v999/artists"},
		//   - invalid integer artist ID
		{400, "GET", "/api/v0/artists/foo"},
		//   - missing second integer for limit
		{400, "GET", "/api/v0/artists?limit=0"},
		//   - invalid integer pair for limit
		{400, "GET", "/api/v0/artists?limit=foo,bar"},
		//   - artist ID not found
		{404, "GET", "/api/v0/artists/99999999"},

		// Folders API
		//   - valid request
		{200, "GET", "/api/v0/folders"},
		//   - valid request for 1 item
		{200, "GET", "/api/v0/folders/1"},
		//   - invalid API version
		{400, "GET", "/api/v999/folders"},
		//   - valid limit items request
		{200, "GET", "/api/v0/folders?limit=0,10"},
		//   - invalid integer folder ID
		{400, "GET", "/api/v0/folders/foo"},
		//   - missing second integer for limit
		{400, "GET", "/api/v0/folders?limit=0"},
		//   - invalid integer pair for limit
		{400, "GET", "/api/v0/folders?limit=foo,bar"},
		//   - folder ID not found
		{404, "GET", "/api/v0/folders/99999999"},

		// LastFM API - skip valid requests, due to need for external service
		//   - invalid API version
		{400, "POST", "/api/v999/lastfm"},
		//   - no string action provided
		{400, "POST", "/api/v0/lastfm"},
		//   - invalid string action provided
		{400, "POST", "/api/v0/lastfm/foo"},
		//   - login: no username provided
		{400, "POST", "/api/v0/lastfm/login"},
		//   - action: user must authenticate to last.fm
		{401, "POST", "/api/v0/lastfm/nowplaying"},
		//   - action: user must authenticate to last.fm
		{401, "POST", "/api/v0/lastfm/scrobble"},
		// Cannot test other calls without a valid Last.fm token

		// Login/Logout API - skip due to need for sessions and users

		// Search API
		//   - valid request
		{200, "GET", "/api/v0/search/foo"},
		//   - invalid API version
		{400, "GET", "/api/v999/search"},
		//   - no search query specified
		{400, "GET", "/api/v0/search"},

		// Songs API
		//   - valid request
		{200, "GET", "/api/v0/songs"},
		//   - valid request for 1 item
		{200, "GET", "/api/v0/songs/1"},
		//   - valid limit items request
		{200, "GET", "/api/v0/songs?limit=0,10"},
		//   - valid random items request
		{200, "GET", "/api/v0/songs?random=10"},
		//   - invalid API version
		{400, "GET", "/api/v999/songs"},
		//   - invalid integer song ID
		{400, "GET", "/api/v0/songs/foo"},
		//   - missing second integer for limit
		{400, "GET", "/api/v0/songs?limit=0"},
		//   - invalid integer pair for limit
		{400, "GET", "/api/v0/songs?limit=foo,bar"},
		//   - invalid integer for random
		{400, "GET", "/api/v0/songs?random=foo"},
		//   - song ID not found
		{404, "GET", "/api/v0/songs/99999999"},

		// Status API
		//   - valid request
		{200, "GET", "/api/v0/status"},
		//   - valid request with metrics
		{200, "GET", "/api/v0/status?metrics=true"},
		//   - invalid API version
		{400, "GET", "/api/v999/status"},

		// Stream API - skip valid requests, due to binary output
		//   - invalid API version
		{400, "GET", "/api/v999/stream"},
		//   - no integer stream ID provided
		{400, "GET", "/api/v0/stream"},
		//   - invalid stream stream ID
		{400, "GET", "/api/v0/stream/foo"},
		//   - song ID not found
		{404, "GET", "/api/v0/stream/99999999"},

		// Transcode API - skip valid requests, due to binary output
		//   - invalid API version
		{400, "GET", "/api/v999/transcode"},
		//   - no integer transcode ID provided
		{400, "GET", "/api/v0/transcode"},
		//   - invalid transcode transcode ID
		{400, "GET", "/api/v0/transcode/foo"},
		//   - song ID not found
		{404, "GET", "/api/v0/transcode/99999999"},
		//   - ffmpeg not found, transcoding disabled
		{503, "GET", "/api/v0/transcode/1"},

		// Users API
		//   - valid request
		{200, "GET", "/api/v0/users"},
		//   - valid request for 1 item
		{200, "GET", "/api/v0/users/1"},
		//   - invalid integer user ID
		{400, "GET", "/api/v0/users/foo"},
		//   - user ID not found
		{404, "GET", "/api/v0/users/99999999"},
	}

	// Iterate all tests
	for _, test := range tests {
		// Generate a new HTTP request
		req, err := http.NewRequest(test.method, "http://localhost:8080"+test.url, nil)
		if err != nil {
			t.Fatalf("Failed to create HTTP request")
		}

		// Map context for request
		context.Set(req, api.CtxRender, r)
		context.Set(req, api.CtxUser, &data.User{RoleID: 1})
		context.Set(req, api.CtxSession, new(data.Session))

		// Capture HTTP response via recorder
		w := httptest.NewRecorder()

		// Perform request
		n.ServeHTTP(w, req)

		// Validate results
		if w.Code != test.code {
			t.Fatalf("HTTP [%v != %v] %s %s", w.Code, test.code, test.method, test.url)
		}

		// Check result body as well
		body, err := ioutil.ReadAll(w.Body)
		if err != nil {
			t.Fatal(err)
		}

		// Unmarshal error response
		var errRes api.ErrorResponse
		if err := json.Unmarshal(body, &errRes); err != nil {
			log.Println(string(body))
			t.Fatal(err)
		}

		// If not HTTP 200, check to ensure error code matches
		if errRes.Error != nil && errRes.Error.Code != test.code {
			t.Fatalf("Body [%v != %v] %s %s", errRes.Error.Code, test.code, test.method, test.url)
		}

		log.Printf("OK: [%d] %s %s", test.code, test.method, test.url)
	}
}
<|endoftext|>


<commit_before>package myqlib

import (
	"bytes"
	"fmt"
)

// All Views must implement the following
type View interface {
	// Column help
	Help() chan string
	ShortHelp() chan string

	// Header/Data functions return a channel of strings
	Header(state *MyqState) chan string
	Data(state *MyqState) chan string

	// Use this timecol in the output
	SetTimeCol(timecol *Col)

	// All the cols (including time col)
	all_cols() []Col
}

// NormalView
type NormalView struct {
	DefaultCol       // Views are columns too
	cols       []Col // slice of columns in this view
	timecol    *Col  // timecol to use
}

func NewNormalView(help string, cols ...Col) *NormalView {
	return &NormalView{DefaultCol: DefaultCol{help: help}, cols: cols}
}

func (v *NormalView) Help() chan string {
	ch := make(chan string)

	go func() {
		defer close(ch)
		for shortst := range v.ShortHelp() {
			ch <- shortst
		}

		for _, col := range v.cols {
			for colst := range col.Help() {
				ch <- fmt.Sprint("\t", colst)
			}
		}
	}()

	return ch
}

func (v *NormalView) ShortHelp() chan string {
	ch := make(chan string, 1)
	defer close(ch)
	ch <- fmt.Sprint(v.help)
	return ch
}

func (v *NormalView) SetTimeCol(timecol *Col) {
	v.timecol = timecol
}

func (v *NormalView) Header(state *MyqState) chan string {
	return v.ordered_col_output(func(c Col) chan string {
		return c.Header(state)
	})
}

func (v *NormalView) Data(state *MyqState) chan string {
	return v.ordered_col_output(func(c Col) chan string {
		return c.Data(state)
	})
}

func (v *NormalView) ordered_col_output(get_col_chan func(c Col) chan string) chan string {
	var column_channels []chan string
	for _, col := range v.all_cols() {
		column_channels = append(column_channels, get_col_chan(col))
	}

	ch := make(chan string)
	go func() {
		defer close(ch)
		for {
			var hdrline bytes.Buffer
			got_something := false
			space := false
			for i, col := range v.all_cols() {
				if space {
					hdrline.WriteString(" ")
				} else {
					space = true
				}
				if str, more := <-column_channels[i]; more {
					hdrline.WriteString(str)
					got_something = true
				} else {
					hdrline.WriteString(column_blank(col))
				}
			}
			if got_something {
				ch <- hdrline.String()
			} else {
				break
			}
		}
	}()
	return ch
}

// All columns preceeded by the time column
func (v *NormalView) all_cols() []Col {
	if v.timecol == nil {
		return v.cols
	} else {
		return append([]Col{*v.timecol}, v.cols...)
	}
}

func (v *NormalView) Width() (w int64) {
	for _, col := range v.all_cols() {
		w += col.Width() + 1
	}
	w -= 1
	return
}

// ExtraHeaderView
type ExtraHeaderView struct {
	NormalView
	extra_header func(state *MyqState) chan string
}

func NewExtraHeaderView(help string, extra_header func(state *MyqState) chan string, cols ...Col) *ExtraHeaderView {
	return &ExtraHeaderView{NormalView{DefaultCol: DefaultCol{help: help}, cols: cols}, extra_header}
}

func (v *ExtraHeaderView) Header(state *MyqState) chan string {
	ch := make(chan string)

	go func() {
		defer close(ch)
		extrach := v.extra_header(state)
		for extrastr := range extrach {
			ch <- extrastr
		}
		normalch := v.NormalView.Header(state)
		for normalstr := range normalch {
			ch <- normalstr
		}
	}()

	return ch
}

// ExtraHeaderView
type GroupCol struct {
	NormalView
	title string
}

func NewGroupCol(title, help string, cols ...Col) *GroupCol {
	return &GroupCol{NormalView{DefaultCol: DefaultCol{help: help}, cols: cols}, title}
}

// All columns preceeded by the time column
func (v *GroupCol) all_cols() []Col {
	return v.cols
}

func (v *GroupCol) Header(state *MyqState) chan string {
	ch := make(chan string)

	go func() {
		defer close(ch)

		// Output the columns first
		viewch := v.NormalView.Header(state)
		for viewstr := range viewch {
			ch <- viewstr
		}

		// Then our title (reverse order)
		str := v.title
		if len(str) > int(v.Width()) {
			str = v.title[0:v.Width()]
		}
		ch <- fmt.Sprintf(fmt.Sprint(`%-`, v.Width(), `s`), str)
	}()

	return ch
}
<commit_msg>Fixes #2<commit_after>package myqlib

import (
	"bytes"
	"fmt"
)

// All Views must implement the following
type View interface {
	// Column help
	Help() chan string
	ShortHelp() chan string

	// Header/Data functions return a channel of strings
	Header(state *MyqState) chan string
	Data(state *MyqState) chan string

	// Use this timecol in the output
	SetTimeCol(timecol *Col)

	// All the cols (including time col)
	all_cols() []Col
}

// NormalView
type NormalView struct {
	DefaultCol       // Views are columns too
	cols       []Col // slice of columns in this view
	timecol    *Col  // timecol to use
}

func NewNormalView(help string, cols ...Col) *NormalView {
	return &NormalView{DefaultCol: DefaultCol{help: help}, cols: cols}
}

func (v *NormalView) Help() chan string {
	ch := make(chan string)

	go func() {
		defer close(ch)
		for shortst := range v.ShortHelp() {
			ch <- shortst
		}

		for _, col := range v.cols {
			for colst := range col.Help() {
				ch <- fmt.Sprint("\t", colst)
			}
		}
	}()

	return ch
}

func (v *NormalView) ShortHelp() chan string {
	ch := make(chan string, 1)
	defer close(ch)
	ch <- fmt.Sprint(v.help)
	return ch
}

func (v *NormalView) SetTimeCol(timecol *Col) {
	v.timecol = timecol
}

func (v *NormalView) Header(state *MyqState) chan string {
	return v.ordered_col_output(func(c Col) chan string {
		return c.Header(state)
	})
}

func (v *NormalView) Data(state *MyqState) chan string {
	return v.ordered_col_output(func(c Col) chan string {
		return c.Data(state)
	})
}

func (v *NormalView) ordered_col_output(get_col_chan func(c Col) chan string) chan string {
	var column_channels []chan string
	for _, col := range v.all_cols() {
		column_channels = append(column_channels, get_col_chan(col))
	}

	ch := make(chan string)
	go func() {
		defer close(ch)
		for {
			var hdrline bytes.Buffer
			got_something := false
			space := false
			for i, col := range v.all_cols() {
				if space {
					hdrline.WriteString(" ")
				} else {
					space = true
				}
				if str, more := <-column_channels[i]; more {
					hdrline.WriteString(str)
					got_something = true
				} else {
					hdrline.WriteString(column_blank(col))
				}
			}
			if got_something {
				ch <- hdrline.String()
			} else {
				break
			}
		}
	}()
	return ch
}

// All columns preceeded by the time column
func (v *NormalView) all_cols() []Col {
	if v.timecol == nil {
		return v.cols
	} else {
		return append([]Col{*v.timecol}, v.cols...)
	}
}

func (v *NormalView) Width() (w int64) {
	for _, col := range v.all_cols() {
		w += col.Width() + 1
	}
	w -= 1
	return
}

// ExtraHeaderView
type ExtraHeaderView struct {
	NormalView
	extra_header func(state *MyqState) chan string
}

func NewExtraHeaderView(help string, extra_header func(state *MyqState) chan string, cols ...Col) *ExtraHeaderView {
	return &ExtraHeaderView{NormalView{DefaultCol: DefaultCol{help: help}, cols: cols}, extra_header}
}

func (v *ExtraHeaderView) Header(state *MyqState) chan string {
	ch := make(chan string)

	go func() {
		defer close(ch)
		normalch := v.NormalView.Header(state)
		for normalstr := range normalch {
			ch <- normalstr
		}
    // Extra headers come out above normal headers, which means we send them later
		extrach := v.extra_header(state)
		for extrastr := range extrach {
			ch <- extrastr
		}
	}()

	return ch
}

// ExtraHeaderView
type GroupCol struct {
	NormalView
	title string
}

func NewGroupCol(title, help string, cols ...Col) *GroupCol {
	return &GroupCol{NormalView{DefaultCol: DefaultCol{help: help}, cols: cols}, title}
}

// All columns preceeded by the time column
func (v *GroupCol) all_cols() []Col {
	return v.cols
}

func (v *GroupCol) Header(state *MyqState) chan string {
	ch := make(chan string)

	go func() {
		defer close(ch)

		// Output the columns first
		viewch := v.NormalView.Header(state)
		for viewstr := range viewch {
			ch <- viewstr
		}

		// Then our title (reverse order)
		str := v.title
		if len(str) > int(v.Width()) {
			str = v.title[0:v.Width()]
		}
		ch <- fmt.Sprintf(fmt.Sprint(`%-`, v.Width(), `s`), str)
	}()

	return ch
}
<|endoftext|>


<commit_before>package goad

import (
	"errors"
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/gophergala2016/goad/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws"
	"github.com/gophergala2016/goad/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/session"
	"github.com/gophergala2016/goad/Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/lambda"
	"github.com/gophergala2016/goad/infrastructure"
	"github.com/gophergala2016/goad/queue"
)

// TestConfig type
type TestConfig struct {
	URL            string
	Concurrency    uint
	TotalRequests  uint
	RequestTimeout time.Duration
	Region         string
}

const nano = 1000000000

func (c *TestConfig) cmd(sqsURL string) string {
	return fmt.Sprintf("./goad-lambda %s %d %d %s %s", c.URL, c.Concurrency, c.TotalRequests, sqsURL, c.Region)
}

// Test type
type Test struct {
	config *TestConfig
}

// NewTest returns a configured Test
func NewTest(config *TestConfig) (*Test, error) {
	err := config.check()
	if err != nil {
		return nil, err
	}
	return &Test{config}, nil
}

// Start a test
func (t *Test) Start() <-chan queue.RegionsAggData {
	awsConfig := aws.NewConfig().WithRegion(t.config.Region)
	infra, err := infrastructure.New(awsConfig)
	if err != nil {
		log.Fatal(err)
	}

	t.invokeLambda(awsConfig, infra.QueueURL())

	results := make(chan queue.RegionsAggData)

	go func() {
		for result := range queue.Aggregate(awsConfig, infra.QueueURL(), t.config.TotalRequests) {
			results <- result
		}
		infra.Clean()
		close(results)
	}()

	return results
}

func (t *Test) invokeLambda(awsConfig *aws.Config, sqsURL string) {
	svc := lambda.New(session.New(), awsConfig)

	resp, err := svc.InvokeAsync(&lambda.InvokeAsyncInput{
		FunctionName: aws.String("goad"),
		InvokeArgs:   strings.NewReader(`{"cmd":"` + t.config.cmd(sqsURL) + `"}`),
	})
	fmt.Println(resp, err)
}

func (c TestConfig) check() error {
	if c.Concurrency < 1 || c.Concurrency > 100000 {
		return errors.New("Invalid concurrency (use 1 - 100000)")
	}
	if c.TotalRequests < 1 || c.TotalRequests > 1000000 {
		return errors.New("Invalid total requests (use 1 - 1000000)")
	}
	if c.RequestTimeout.Nanoseconds() < nano || c.RequestTimeout.Nanoseconds() > nano*100 {
		return errors.New("Invalid timeout (1s - 100s)")
	}
	return nil
}
<commit_msg>Invoke multiple lambdas based on concurrency settings<commit_after>package goad

import (
	"errors"
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/gophergala2016/goad/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws"
	"github.com/gophergala2016/goad/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/session"
	"github.com/gophergala2016/goad/Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/lambda"
	"github.com/gophergala2016/goad/infrastructure"
	"github.com/gophergala2016/goad/queue"
)

// TestConfig type
type TestConfig struct {
	URL                string
	Concurrency        uint
	TotalRequests      uint
	RequestTimeout     time.Duration
	Region             string
	ReportingFrequency time.Duration
}

const nano = 1000000000

func (c *TestConfig) cmd(sqsURL string) string {
	return fmt.Sprintf("./goad-lambda %s %d %d %s %s %s %s", c.URL, c.Concurrency, c.TotalRequests, sqsURL, c.Region, c.RequestTimeout, c.ReportingFrequency)
}

// Test type
type Test struct {
	config *TestConfig
}

// NewTest returns a configured Test
func NewTest(config *TestConfig) (*Test, error) {
	err := config.check()
	if err != nil {
		return nil, err
	}
	return &Test{config}, nil
}

// Start a test
func (t *Test) Start() <-chan queue.RegionsAggData {
	awsConfig := aws.NewConfig().WithRegion(t.config.Region)
	infra, err := infrastructure.New(awsConfig)
	if err != nil {
		log.Fatal(err)
	}

	t.invokeLambdas(awsConfig, infra.QueueURL())

	results := make(chan queue.RegionsAggData)

	go func() {
		for result := range queue.Aggregate(awsConfig, infra.QueueURL(), t.config.TotalRequests) {
			results <- result
		}
		infra.Clean()
		close(results)
	}()

	return results
}

func (t *Test) invokeLambdas(awsConfig *aws.Config, sqsURL string) {
	lambdas := numberOfLambdas(t.config.Concurrency)

	for i := 0; i < lambdas; i++ {
		requests, requestsRemainder := divide(t.config.TotalRequests, lambdas)
		concurrency, _ := divide(t.config.Concurrency, lambdas)

		if requestsRemainder > 0 && i == lambdas-1 {
			requests += requestsRemainder
		}

		c := t.config
		cmd := fmt.Sprintf("./goad-lambda %s %d %d %s %s %s %s", c.URL, concurrency, requests, sqsURL, c.Region, c.RequestTimeout, c.ReportingFrequency)

		go t.invokeLambda(awsConfig, cmd)
	}
}

func (t *Test) invokeLambda(awsConfig *aws.Config, cmd string) {
	svc := lambda.New(session.New(), awsConfig)

	svc.InvokeAsync(&lambda.InvokeAsyncInput{
		FunctionName: aws.String("goad"),
		InvokeArgs:   strings.NewReader(`{"cmd":"` + cmd + `"}`),
	})
}

func numberOfLambdas(concurrency uint) int {
	if concurrency/10 > 100 {
		return 100
	}
	return int(concurrency-1)/10 + 1
}

func divide(dividend uint, divisor int) (quotient, remainder uint) {
	return dividend / uint(divisor), dividend % uint(divisor)
}

func (c TestConfig) check() error {
	if c.Concurrency < 1 || c.Concurrency > 100000 {
		return errors.New("Invalid concurrency (use 1 - 100000)")
	}
	if c.TotalRequests < 1 || c.TotalRequests > 1000000 {
		return errors.New("Invalid total requests (use 1 - 1000000)")
	}
	if c.RequestTimeout.Nanoseconds() < nano || c.RequestTimeout.Nanoseconds() > nano*100 {
		return errors.New("Invalid timeout (1s - 100s)")
	}
	return nil
}
<|endoftext|>