<commit_before>// Package analytics provides the methods to run an analytics reporting system
// for API requests which may be useful to users for measuring access and
// possibly identifying bad actors abusing requests.
package analytics

import (
	"log"
	"net/http"
	"strings"
	"time"

	"runtime"

	"encoding/json"

	"github.com/boltdb/bolt"
)

type apiRequest struct {
	URL        string `json:"url"`
	Method     string `json:"http_method"`
	Origin     string `json:"origin"`
	Proto      string `json:"http_protocol"`
	RemoteAddr string `json:"ip_address"`
	Timestamp  int64  `json:"timestamp"`
	External   bool   `json:"external"`
}

var (
	store      *bolt.DB
	recordChan chan apiRequest
)

// Record queues an apiRequest for metrics
func Record(req *http.Request) {
	external := strings.Contains(req.URL.Path, "/external/")

	r := apiRequest{
		URL:        req.URL.String(),
		Method:     req.Method,
		Origin:     req.Header.Get("Origin"),
		Proto:      req.Proto,
		RemoteAddr: req.RemoteAddr,
		Timestamp:  time.Now().Unix() * 1000,
		External:   external,
	}

	// put r on buffered recordChan to take advantage of batch insertion in DB
	recordChan <- r
}

// Close exports the abillity to close our db file. Should be called with defer
// after call to Init() from the same place.
func Close() {
	err := store.Close()
	if err != nil {
		log.Println(err)
	}
}

// Init creates a db connection, should run an initial prune of old data, and
// sets up the queue/batching channel
func Init() {
	var err error
	store, err = bolt.Open("analytics.db", 0666, nil)
	if err != nil {
		log.Fatalln(err)
	}

	recordChan = make(chan apiRequest, 1024*64*runtime.NumCPU())

	go serve()

	if err != nil {
		log.Fatalln(err)
	}
}

func serve() {
	// make timer to notify select to batch request insert from recordChan
	// interval: 30 seconds
	apiRequestTimer := time.NewTicker(time.Second * 30)

	// make timer to notify select to remove old analytics
	// interval: 2 weeks
	// TODO: enable analytics backup service to cloud
	pruneDBTimer := time.NewTicker(time.Hour * 24 * 14)

	for {
		select {
		case <-apiRequestTimer.C:
			var reqs []apiRequest
			batchSize := len(recordChan)

			for i := 0; i < batchSize; i++ {
				reqs = append(reqs, <-recordChan)
			}

			err := batchInsert(reqs)
			if err != nil {
				log.Println(err)
			}

		case <-pruneDBTimer.C:

		default:
			time.Sleep(time.Millisecond * 1)
		}
	}
}

// Week returns the map containing decoded javascript needed to chart a week of data by day
func Week() (map[string]interface{}, error) {
	// set thresholds for today and the 6 days preceeding
	times := [7]time.Time{}
	dates := [7]string{}
	now := time.Now()
	today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC)

	for i := range times {
		// subtract 24 * i hours to make days prior
		dur := time.Duration(24 * i * -1)
		day := today.Add(time.Hour * dur)

		// day threshold is [...n-1-i, n-1, n]
		times[len(times)-1-i] = day
		dates[len(times)-1-i] = day.Format("01/02")
	}

	// get api request analytics from db
	var requests = []apiRequest{}
	err := store.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("requests"))

		err := b.ForEach(func(k, v []byte) error {
			var r apiRequest
			err := json.Unmarshal(v, &r)
			if err != nil {
				log.Println("Error decoding json from analytics db:", err)
				return nil
			}

			requests = append(requests, r)

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

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

	ips := [7]map[string]struct{}{}
	for i := range ips {
		ips[i] = make(map[string]struct{})
	}

	total := [7]int{}
	unique := [7]int{}

CHECK_REQUEST:
	for i := range requests {
		ts := time.Unix(requests[i].Timestamp/1000, 0)

		for j := range times {
			// if on today, there will be no next iteration to set values for
			// day prior so all valid requests belong to today
			if j == len(times)-1 {
				if ts.After(times[j]) || ts.Equal(times[j]) {
					// do all record keeping
					total[j]++

					if _, ok := ips[j][requests[i].RemoteAddr]; !ok {
						unique[j]++
						ips[j][requests[i].RemoteAddr] = struct{}{}
					}

					continue CHECK_REQUEST
				}
			}

			if ts.Equal(times[j]) {
				// increment total count for current time threshold (day)
				total[j]++

				// if no IP found for current threshold, increment unique and record IP
				if _, ok := ips[j][requests[i].RemoteAddr]; !ok {
					unique[j]++
					ips[j][requests[i].RemoteAddr] = struct{}{}
				}

				continue CHECK_REQUEST
			}

			if ts.Before(times[j]) {
				// check if older than earliest threshold
				if j == 0 {
					continue CHECK_REQUEST
				}

				// increment total count for previous time threshold (day)
				total[j-1]++

				// if no IP found for day prior, increment unique and record IP
				if _, ok := ips[j-1][requests[i].RemoteAddr]; !ok {
					unique[j-1]++
					ips[j-1][requests[i].RemoteAddr] = struct{}{}
				}
			}
		}
	}

	jsUnique, err := json.Marshal(unique)
	if err != nil {
		return nil, err
	}

	jsTotal, err := json.Marshal(total)
	if err != nil {
		return nil, err
	}

	return map[string]interface{}{
		"dates":  dates,
		"unique": string(jsUnique),
		"total":  string(jsTotal),
	}, nil
}
<commit_msg>testing optimization for cpu usage that is less interruptive<commit_after>// Package analytics provides the methods to run an analytics reporting system
// for API requests which may be useful to users for measuring access and
// possibly identifying bad actors abusing requests.
package analytics

import (
	"log"
	"net/http"
	"strings"
	"time"

	"runtime"

	"encoding/json"

	"github.com/boltdb/bolt"
)

type apiRequest struct {
	URL        string `json:"url"`
	Method     string `json:"http_method"`
	Origin     string `json:"origin"`
	Proto      string `json:"http_protocol"`
	RemoteAddr string `json:"ip_address"`
	Timestamp  int64  `json:"timestamp"`
	External   bool   `json:"external"`
}

var (
	store      *bolt.DB
	recordChan chan apiRequest
)

// Record queues an apiRequest for metrics
func Record(req *http.Request) {
	external := strings.Contains(req.URL.Path, "/external/")

	r := apiRequest{
		URL:        req.URL.String(),
		Method:     req.Method,
		Origin:     req.Header.Get("Origin"),
		Proto:      req.Proto,
		RemoteAddr: req.RemoteAddr,
		Timestamp:  time.Now().Unix() * 1000,
		External:   external,
	}

	// put r on buffered recordChan to take advantage of batch insertion in DB
	recordChan <- r
}

// Close exports the abillity to close our db file. Should be called with defer
// after call to Init() from the same place.
func Close() {
	err := store.Close()
	if err != nil {
		log.Println(err)
	}
}

// Init creates a db connection, should run an initial prune of old data, and
// sets up the queue/batching channel
func Init() {
	var err error
	store, err = bolt.Open("analytics.db", 0666, nil)
	if err != nil {
		log.Fatalln(err)
	}

	recordChan = make(chan apiRequest, 1024*64*runtime.NumCPU())

	go serve()

	if err != nil {
		log.Fatalln(err)
	}
}

func serve() {
	// make timer to notify select to batch request insert from recordChan
	// interval: 30 seconds
	apiRequestTimer := time.NewTicker(time.Second * 30)

	// make timer to notify select to remove old analytics
	// interval: 2 weeks
	// TODO: enable analytics backup service to cloud
	pruneDBTimer := time.NewTicker(time.Hour * 24 * 14)

	for {
		select {
		case <-apiRequestTimer.C:
			var reqs []apiRequest
			batchSize := len(recordChan)

			for i := 0; i < batchSize; i++ {
				reqs = append(reqs, <-recordChan)
			}

			err := batchInsert(reqs)
			if err != nil {
				log.Println(err)
			}

		case <-pruneDBTimer.C:

		case <-time.After(time.Second * 30):
			continue
		}
	}
}

// Week returns the map containing decoded javascript needed to chart a week of data by day
func Week() (map[string]interface{}, error) {
	// set thresholds for today and the 6 days preceeding
	times := [7]time.Time{}
	dates := [7]string{}
	now := time.Now()
	today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC)

	for i := range times {
		// subtract 24 * i hours to make days prior
		dur := time.Duration(24 * i * -1)
		day := today.Add(time.Hour * dur)

		// day threshold is [...n-1-i, n-1, n]
		times[len(times)-1-i] = day
		dates[len(times)-1-i] = day.Format("01/02")
	}

	// get api request analytics from db
	var requests = []apiRequest{}
	err := store.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("requests"))

		err := b.ForEach(func(k, v []byte) error {
			var r apiRequest
			err := json.Unmarshal(v, &r)
			if err != nil {
				log.Println("Error decoding json from analytics db:", err)
				return nil
			}

			requests = append(requests, r)

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

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

	ips := [7]map[string]struct{}{}
	for i := range ips {
		ips[i] = make(map[string]struct{})
	}

	total := [7]int{}
	unique := [7]int{}

CHECK_REQUEST:
	for i := range requests {
		ts := time.Unix(requests[i].Timestamp/1000, 0)

		for j := range times {
			// if on today, there will be no next iteration to set values for
			// day prior so all valid requests belong to today
			if j == len(times)-1 {
				if ts.After(times[j]) || ts.Equal(times[j]) {
					// do all record keeping
					total[j]++

					if _, ok := ips[j][requests[i].RemoteAddr]; !ok {
						unique[j]++
						ips[j][requests[i].RemoteAddr] = struct{}{}
					}

					continue CHECK_REQUEST
				}
			}

			if ts.Equal(times[j]) {
				// increment total count for current time threshold (day)
				total[j]++

				// if no IP found for current threshold, increment unique and record IP
				if _, ok := ips[j][requests[i].RemoteAddr]; !ok {
					unique[j]++
					ips[j][requests[i].RemoteAddr] = struct{}{}
				}

				continue CHECK_REQUEST
			}

			if ts.Before(times[j]) {
				// check if older than earliest threshold
				if j == 0 {
					continue CHECK_REQUEST
				}

				// increment total count for previous time threshold (day)
				total[j-1]++

				// if no IP found for day prior, increment unique and record IP
				if _, ok := ips[j-1][requests[i].RemoteAddr]; !ok {
					unique[j-1]++
					ips[j-1][requests[i].RemoteAddr] = struct{}{}
				}
			}
		}
	}

	jsUnique, err := json.Marshal(unique)
	if err != nil {
		return nil, err
	}

	jsTotal, err := json.Marshal(total)
	if err != nil {
		return nil, err
	}

	return map[string]interface{}{
		"dates":  dates,
		"unique": string(jsUnique),
		"total":  string(jsTotal),
	}, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"errors"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/AlecAivazis/survey"
	surveyCore "github.com/AlecAivazis/survey/core"
	"github.com/cheggaaa/pb"
	"github.com/tarm/serial"
	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	port     = kingpin.Flag("port", "port").String()
	filename = kingpin.Flag("filename", "filename").ExistingFile()
	force    = kingpin.Flag("force", "Force flash.").Short('f').Bool()
	verbose  = kingpin.Flag("verbose", "Verbose mode.").Short('v').Bool()
)

func make_checksum(payload []byte) []byte {
	var checksum uint16 = 0xFFFF
	for i := 0; i < len(payload); i++ {
		checksum -= uint16(payload[i])
	}
	ret := make([]byte, 2)
	binary.LittleEndian.PutUint16(ret, checksum)
	return ret
}

func WriteAll(s *serial.Port, raw []byte) error {
	if *verbose {
		fmt.Println("Write", raw[:2], raw[2:len(raw)-2], raw[len(raw)-2:])
	}
	n, err := s.Write(raw)
	if err != nil {
		return err
	}
	if n != len(raw) {
		return errors.New("Didn't write all bytes.")
	}
	return nil
}

func WriteFrame(s *serial.Port, payload []byte) error {
	length := make([]byte, 2)
	binary.LittleEndian.PutUint16(length, uint16(len(payload)+4))
	frame := append(length, payload...)
	frame = append(frame, make_checksum(frame)...)
	return WriteAll(s, frame)
}

func ReadAll(s *serial.Port, n int) ([]byte, error) {
	buf := make([]byte, n)
	bytes_read := 0
	for bytes_read < n {
		c, err := s.Read(buf[bytes_read:])
		if err != nil {
			return nil, err
		}
		if c == 0 {
			return nil, errors.New("Read timeout")
		}
		bytes_read += c
	}
	return buf, nil
}

func EmptyRx(s *serial.Port) {
	c := 1
	buf := make([]byte, 1024)
	for c > 0 {
		c, _ = s.Read(buf)
	}
}

func ReadFrame(s *serial.Port) ([]byte, error) {
	head, err := ReadAll(s, 3)
	if err != nil {
		return nil, err
	}
	if head[0] != 0x55 {
		return nil, errors.New("Invalid response")
	}
	size := int(binary.LittleEndian.Uint16(head[1:]))
	body, err := ReadAll(s, size-3)
	if err != nil {
		return nil, err
	}
	payload := body[:len(body)-2]
	checksum := body[len(body)-2:]
	if *verbose {
		fmt.Println("Read", head, payload, checksum)
	}
	checksum_cmp := make_checksum(append(head, payload...))
	if !bytes.Equal(checksum, checksum_cmp) {
		return nil, errors.New("Invalid checksum")
	}
	return payload, nil
}

func ping(s *serial.Port) ([]byte, error) {
	err := WriteFrame(s, []byte{0xC0})
	if err != nil {
		return nil, err
	}
	answer, err := ReadFrame(s)
	if err != nil {
		return nil, err
	}
	if answer[0] != 0xC0 {
		return nil, errors.New("Unexpected answer to ping")
	}
	return answer, nil
}

func communicate(s *serial.Port, request []byte, response []byte) error {
	err := WriteFrame(s, request)
	if err != nil {
		return err
	}
	msg, err := ReadFrame(s)
	if err != nil {
		return err
	}
	if !bytes.Equal(msg, response) {
		errors.New("Unexpected response: " + hex.Dump(response))
	}
	return nil
}

func ask_write(s *serial.Port, address int) error {
	ask_permission := []byte{0xc2, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
	binary.LittleEndian.PutUint16(ask_permission[1:3], uint16(address))
	get_permission := []byte{0xc2, 0x80, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
	binary.LittleEndian.PutUint16(get_permission[2:4], uint16(address))

	return communicate(s, ask_permission, get_permission)
}

func write_chunk(s *serial.Port, address int, data []byte) error {
	write_instruction := []byte{0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}
	binary.LittleEndian.PutUint16(write_instruction[1:3], uint16(address))
	write_instruction = append(write_instruction, data...)
	write_confirmation := []byte{0xc3, 0x00, 0x00, 0x00, 0x00}

	return communicate(s, write_instruction, write_confirmation)
}

func update(s *serial.Port, firmware []byte) error {
	start_address := 0x1800

	bar := pb.New(len(firmware)).SetUnits(pb.U_BYTES)
	bar.Start()

	for bytes_written := 0; bytes_written < len(firmware); bytes_written += 1024 {
		tries := 0
	ask:
		err := ask_write(s, start_address+bytes_written)
		if err != nil {
			tries++
			if tries <= 3 {
				EmptyRx(s)
				goto ask
			}
		}
		for chunk := 0; chunk < 1024; chunk += 256 {
			err = write_chunk(s, start_address+bytes_written+chunk, firmware[bytes_written+chunk:bytes_written+chunk+256])
			if err != nil {
				tries++
				if tries <= 3 {
					EmptyRx(s)
					goto ask
				}
			}
			bar.Add(256)
		}
	}

	bar.FinishPrint("Upload completed.")
	return nil
}

func restart(s *serial.Port) error {
	return WriteFrame(s, []byte{0xC1, 0x00})
}

func choose(query string, options []string) (string, error) {
	if len(options) == 1 {
		fmt.Printf("Using %s.\n", options[0])
		return options[0], nil
	}

	if runtime.GOOS == "windows" {
		surveyCore.SelectFocusIcon = ">"
	}

	ret := ""
	message := fmt.Sprintf("%s:", strings.Title(query))
	if len(options) == 0 {
		fmt.Printf("Could not autodetect %s. Please enter manually.\n", query)
		prompt := &survey.Input{Message: message}
		survey.AskOne(prompt, &ret, nil)
	} else {
		prompt := &survey.Select{
			Message: message,
			Options: options,
		}
		survey.AskOne(prompt, &ret, nil)
	}

	if ret == "" {
		return "", errors.New("interrupted")
	}
	return ret, nil
}

func main() {

	kingpin.Parse()
	var err error

	if *filename == "" {
		binFiles, _ := filepath.Glob("*.bin")
		hexFiles, _ := filepath.Glob("*.hex")
		*filename, err = choose("firmware image", append(binFiles, hexFiles...))
		if err != nil {
			fmt.Printf("No firmware image selected: %s.", err)
			return
		}
	}
	if *port == "" {
		var serialPortFmt string
		if runtime.GOOS == "windows" {
			serialPortFmt = "COM%d"
		} else {
			serialPortFmt = "/dev/ttyUSB%d"
		}

		var candidates []string
		for i := 0; i < 255; i += 1 {
			candidate := fmt.Sprintf(serialPortFmt, i)
			c := &serial.Config{Name: candidate, Baud: 115200, ReadTimeout: time.Second * 1}
			s, err := serial.OpenPort(c)
			if s != nil {
				s.Close()
			}
			if err == nil {
				candidates = append(candidates, candidate)
			}
		}

		*port, err = choose("serial port", candidates)
		if err != nil {
			fmt.Printf("No serial port selected: %s.", err)
			return
		}
	}

	data, err := ioutil.ReadFile(*filename)
	if err != nil {
		panic(err)
	}

	if (len(data) < 0x9000 || len(data) > 0xe7ff) && !*force {
		panic(fmt.Sprintf("Unexpected firmare size: %d bytes", len(data)))
	}

	c := &serial.Config{Name: *port, Baud: 115200, ReadTimeout: time.Second * 1}
	s, err := serial.OpenPort(c)
	if err != nil {
		panic(err)
	}

	_, err = ping(s)
	if err != nil {
		panic(err)
	}

	err = update(s, data)
	if err != nil {
		panic(err)
	}

	err = restart(s)
	if err != nil {
		panic(err)
	}

	fmt.Println("Success!")

	s.Close()
}
<commit_msg>make error messages friendlier<commit_after>package main

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"errors"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/AlecAivazis/survey"
	surveyCore "github.com/AlecAivazis/survey/core"
	"github.com/cheggaaa/pb"
	"github.com/tarm/serial"
	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	port     = kingpin.Flag("port", "port").String()
	filename = kingpin.Flag("filename", "filename").ExistingFile()
	force    = kingpin.Flag("force", "Force flash.").Short('f').Bool()
	verbose  = kingpin.Flag("verbose", "Verbose mode.").Short('v').Bool()
)

func make_checksum(payload []byte) []byte {
	var checksum uint16 = 0xFFFF
	for i := 0; i < len(payload); i++ {
		checksum -= uint16(payload[i])
	}
	ret := make([]byte, 2)
	binary.LittleEndian.PutUint16(ret, checksum)
	return ret
}

func WriteAll(s *serial.Port, raw []byte) error {
	if *verbose {
		fmt.Println("Write", raw[:2], raw[2:len(raw)-2], raw[len(raw)-2:])
	}
	n, err := s.Write(raw)
	if err != nil {
		return err
	}
	if n != len(raw) {
		return errors.New("Didn't write all bytes.")
	}
	return nil
}

func WriteFrame(s *serial.Port, payload []byte) error {
	length := make([]byte, 2)
	binary.LittleEndian.PutUint16(length, uint16(len(payload)+4))
	frame := append(length, payload...)
	frame = append(frame, make_checksum(frame)...)
	return WriteAll(s, frame)
}

func ReadAll(s *serial.Port, n int) ([]byte, error) {
	buf := make([]byte, n)
	bytes_read := 0
	for bytes_read < n {
		c, err := s.Read(buf[bytes_read:])
		if err != nil {
			return nil, err
		}
		if c == 0 {
			return nil, errors.New("Read timeout")
		}
		bytes_read += c
	}
	return buf, nil
}

func EmptyRx(s *serial.Port) {
	c := 1
	buf := make([]byte, 1024)
	for c > 0 {
		c, _ = s.Read(buf)
	}
}

func ReadFrame(s *serial.Port) ([]byte, error) {
	head, err := ReadAll(s, 3)
	if err != nil {
		return nil, err
	}
	if head[0] != 0x55 {
		return nil, errors.New("Invalid response")
	}
	size := int(binary.LittleEndian.Uint16(head[1:]))
	body, err := ReadAll(s, size-3)
	if err != nil {
		return nil, err
	}
	payload := body[:len(body)-2]
	checksum := body[len(body)-2:]
	if *verbose {
		fmt.Println("Read", head, payload, checksum)
	}
	checksum_cmp := make_checksum(append(head, payload...))
	if !bytes.Equal(checksum, checksum_cmp) {
		return nil, errors.New("Invalid checksum")
	}
	return payload, nil
}

func ping(s *serial.Port) ([]byte, error) {
	err := WriteFrame(s, []byte{0xC0})
	if err != nil {
		return nil, err
	}
	answer, err := ReadFrame(s)
	if err != nil {
		return nil, err
	}
	if answer[0] != 0xC0 {
		return nil, errors.New("Unexpected answer to ping")
	}
	return answer, nil
}

func communicate(s *serial.Port, request []byte, response []byte) error {
	err := WriteFrame(s, request)
	if err != nil {
		return err
	}
	msg, err := ReadFrame(s)
	if err != nil {
		return err
	}
	if !bytes.Equal(msg, response) {
		errors.New("Unexpected response: " + hex.Dump(response))
	}
	return nil
}

func ask_write(s *serial.Port, address int) error {
	ask_permission := []byte{0xc2, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
	binary.LittleEndian.PutUint16(ask_permission[1:3], uint16(address))
	get_permission := []byte{0xc2, 0x80, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
	binary.LittleEndian.PutUint16(get_permission[2:4], uint16(address))

	return communicate(s, ask_permission, get_permission)
}

func write_chunk(s *serial.Port, address int, data []byte) error {
	write_instruction := []byte{0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}
	binary.LittleEndian.PutUint16(write_instruction[1:3], uint16(address))
	write_instruction = append(write_instruction, data...)
	write_confirmation := []byte{0xc3, 0x00, 0x00, 0x00, 0x00}

	return communicate(s, write_instruction, write_confirmation)
}

func update(s *serial.Port, firmware []byte) error {
	start_address := 0x1800

	bar := pb.New(len(firmware)).SetUnits(pb.U_BYTES)
	bar.Start()

	for bytes_written := 0; bytes_written < len(firmware); bytes_written += 1024 {
		tries := 0
	ask:
		err := ask_write(s, start_address+bytes_written)
		if err != nil {
			tries++
			if tries <= 3 {
				EmptyRx(s)
				goto ask
			}
		}
		for chunk := 0; chunk < 1024; chunk += 256 {
			offset := bytes_written + chunk
			err = write_chunk(s, start_address+offset, firmware[offset:offset+256])
			if err != nil {
				tries++
				if tries <= 3 {
					EmptyRx(s)
					goto ask
				}
			}
			bar.Add(256)
		}
	}

	bar.FinishPrint("Upload completed.")
	return nil
}

func restart(s *serial.Port) error {
	return WriteFrame(s, []byte{0xC1, 0x00})
}

func choose(query string, options []string) (string, error) {
	if len(options) == 1 {
		fmt.Printf("Using %s.\n", options[0])
		return options[0], nil
	}

	if runtime.GOOS == "windows" {
		surveyCore.SelectFocusIcon = ">"
	}

	ret := ""
	message := fmt.Sprintf("%s:", strings.Title(query))
	if len(options) == 0 {
		fmt.Printf("Could not autodetect %s. Please enter manually.\n", query)
		prompt := &survey.Input{Message: message}
		survey.AskOne(prompt, &ret, nil)
	} else {
		prompt := &survey.Select{
			Message: message,
			Options: options,
		}
		survey.AskOne(prompt, &ret, nil)
	}

	if ret == "" {
		return "", errors.New("interrupted")
	}
	return ret, nil
}

func main() {

	kingpin.Parse()
	var err error

	if *filename == "" {
		binFiles, _ := filepath.Glob("*.bin")
		hexFiles, _ := filepath.Glob("*.hex")
		*filename, err = choose("firmware image", append(binFiles, hexFiles...))
		if err != nil {
			fmt.Printf("No firmware image selected: %s.", err)
			return
		}
	}
	if *port == "" {
		var serialPortFmt string
		if runtime.GOOS == "windows" {
			serialPortFmt = "COM%d"
		} else {
			serialPortFmt = "/dev/ttyUSB%d"
		}

		var candidates []string
		for i := 0; i < 255; i += 1 {
			candidate := fmt.Sprintf(serialPortFmt, i)
			c := &serial.Config{Name: candidate, Baud: 115200, ReadTimeout: time.Second * 1}
			s, err := serial.OpenPort(c)
			if s != nil {
				s.Close()
			}
			if err == nil {
				candidates = append(candidates, candidate)
			}
		}

		*port, err = choose("serial port", candidates)
		if err != nil {
			fmt.Printf("No serial port selected: %s.", err)
			return
		}
	}

	data, err := ioutil.ReadFile(*filename)
	if err != nil {
		fmt.Printf("Cannot read firmware image %s: %s.", *filename, err)
		return
	}

	if (len(data) < 0x9000 || len(data) > 0xe7ff) && !*force {
		fmt.Printf("Unexpected firmare size: %d bytes. Use --force to flash anyway.", len(data))
		return
	}

	c := &serial.Config{Name: *port, Baud: 115200, ReadTimeout: time.Second * 1}
	s, err := serial.OpenPort(c)
	if err != nil {
		fmt.Printf("Cannot open serial port %s: %s.", *port, err)
		return
	}

	_, err = ping(s)
	if err != nil {
		fmt.Printf("Cannot ping the remote: %s.", err)
		return
	}

	err = update(s, data)
	if err != nil {
		fmt.Printf("Error flashing firmware: %s.", err)
		return
	}

	err = restart(s)
	if err != nil {
		fmt.Printf("Error restarting the remote: %s.", err)
		return
	}

	fmt.Println("Success!")

	s.Close()
}
<|endoftext|>


<commit_before>// Package ulimit provides structure and helper function to parse and represent
// resource limits (Rlimit and Ulimit, its human friendly version).
package units

import (
	"fmt"
	"strconv"
	"strings"
)

// Ulimit is a human friendly version of Rlimit.
type Ulimit struct {
	Name string
	Hard int64
	Soft int64
}

// Rlimit specifies the resource limits, such as max open files.
type Rlimit struct {
	Type int    `json:"type,omitempty"`
	Hard uint64 `json:"hard,omitempty"`
	Soft uint64 `json:"soft,omitempty"`
}

const (
	// magic numbers for making the syscall
	// some of these are defined in the syscall package, but not all.
	// Also since Windows client doesn't get access to the syscall package, need to
	//	define these here
	rlimitAs         = 9
	rlimitCore       = 4
	rlimitCPU        = 0
	rlimitData       = 2
	rlimitFsize      = 1
	rlimitLocks      = 10
	rlimitMemlock    = 8
	rlimitMsgqueue   = 12
	rlimitNice       = 13
	rlimitNofile     = 7
	rlimitNproc      = 6
	rlimitRss        = 5
	rlimitRtprio     = 14
	rlimitRttime     = 15
	rlimitSigpending = 11
	rlimitStack      = 3
)

var ulimitNameMapping = map[string]int{
	//"as":         rlimitAs, // Disabled since this doesn't seem usable with the way Docker inits a container.
	"core":       rlimitCore,
	"cpu":        rlimitCPU,
	"data":       rlimitData,
	"fsize":      rlimitFsize,
	"locks":      rlimitLocks,
	"memlock":    rlimitMemlock,
	"msgqueue":   rlimitMsgqueue,
	"nice":       rlimitNice,
	"nofile":     rlimitNofile,
	"nproc":      rlimitNproc,
	"rss":        rlimitRss,
	"rtprio":     rlimitRtprio,
	"rttime":     rlimitRttime,
	"sigpending": rlimitSigpending,
	"stack":      rlimitStack,
}

// ParseUlimit parses and returns a Ulimit from the specified string.
func ParseUlimit(val string) (*Ulimit, error) {
	parts := strings.SplitN(val, "=", 2)
	if len(parts) != 2 {
		return nil, fmt.Errorf("invalid ulimit argument: %s", val)
	}

	if _, exists := ulimitNameMapping[parts[0]]; !exists {
		return nil, fmt.Errorf("invalid ulimit type: %s", parts[0])
	}

	limitVals := strings.SplitN(parts[1], ":", 2)
	if len(limitVals) > 2 {
		return nil, fmt.Errorf("too many limit value arguments - %s, can only have up to two, `soft[:hard]`", parts[1])
	}

	soft, err := strconv.ParseInt(limitVals[0], 10, 64)
	if err != nil {
		return nil, err
	}

	hard := soft // in case no hard was set
	if len(limitVals) == 2 {
		hard, err = strconv.ParseInt(limitVals[1], 10, 64)
	}
	if soft > hard {
		return nil, fmt.Errorf("ulimit soft limit must be less than or equal to hard limit: %d > %d", soft, hard)
	}

	return &Ulimit{Name: parts[0], Soft: soft, Hard: hard}, nil
}

// GetRlimit returns the RLimit corresponding to Ulimit.
func (u *Ulimit) GetRlimit() (*Rlimit, error) {
	t, exists := ulimitNameMapping[u.Name]
	if !exists {
		return nil, fmt.Errorf("invalid ulimit name %s", u.Name)
	}

	return &Rlimit{Type: t, Soft: uint64(u.Soft), Hard: uint64(u.Hard)}, nil
}

func (u *Ulimit) String() string {
	return fmt.Sprintf("%s=%d:%d", u.Name, u.Soft, u.Hard)
}
<commit_msg>Remove double package definition.<commit_after>package units

import (
	"fmt"
	"strconv"
	"strings"
)

// Ulimit is a human friendly version of Rlimit.
type Ulimit struct {
	Name string
	Hard int64
	Soft int64
}

// Rlimit specifies the resource limits, such as max open files.
type Rlimit struct {
	Type int    `json:"type,omitempty"`
	Hard uint64 `json:"hard,omitempty"`
	Soft uint64 `json:"soft,omitempty"`
}

const (
	// magic numbers for making the syscall
	// some of these are defined in the syscall package, but not all.
	// Also since Windows client doesn't get access to the syscall package, need to
	//	define these here
	rlimitAs         = 9
	rlimitCore       = 4
	rlimitCPU        = 0
	rlimitData       = 2
	rlimitFsize      = 1
	rlimitLocks      = 10
	rlimitMemlock    = 8
	rlimitMsgqueue   = 12
	rlimitNice       = 13
	rlimitNofile     = 7
	rlimitNproc      = 6
	rlimitRss        = 5
	rlimitRtprio     = 14
	rlimitRttime     = 15
	rlimitSigpending = 11
	rlimitStack      = 3
)

var ulimitNameMapping = map[string]int{
	//"as":         rlimitAs, // Disabled since this doesn't seem usable with the way Docker inits a container.
	"core":       rlimitCore,
	"cpu":        rlimitCPU,
	"data":       rlimitData,
	"fsize":      rlimitFsize,
	"locks":      rlimitLocks,
	"memlock":    rlimitMemlock,
	"msgqueue":   rlimitMsgqueue,
	"nice":       rlimitNice,
	"nofile":     rlimitNofile,
	"nproc":      rlimitNproc,
	"rss":        rlimitRss,
	"rtprio":     rlimitRtprio,
	"rttime":     rlimitRttime,
	"sigpending": rlimitSigpending,
	"stack":      rlimitStack,
}

// ParseUlimit parses and returns a Ulimit from the specified string.
func ParseUlimit(val string) (*Ulimit, error) {
	parts := strings.SplitN(val, "=", 2)
	if len(parts) != 2 {
		return nil, fmt.Errorf("invalid ulimit argument: %s", val)
	}

	if _, exists := ulimitNameMapping[parts[0]]; !exists {
		return nil, fmt.Errorf("invalid ulimit type: %s", parts[0])
	}

	limitVals := strings.SplitN(parts[1], ":", 2)
	if len(limitVals) > 2 {
		return nil, fmt.Errorf("too many limit value arguments - %s, can only have up to two, `soft[:hard]`", parts[1])
	}

	soft, err := strconv.ParseInt(limitVals[0], 10, 64)
	if err != nil {
		return nil, err
	}

	hard := soft // in case no hard was set
	if len(limitVals) == 2 {
		hard, err = strconv.ParseInt(limitVals[1], 10, 64)
	}
	if soft > hard {
		return nil, fmt.Errorf("ulimit soft limit must be less than or equal to hard limit: %d > %d", soft, hard)
	}

	return &Ulimit{Name: parts[0], Soft: soft, Hard: hard}, nil
}

// GetRlimit returns the RLimit corresponding to Ulimit.
func (u *Ulimit) GetRlimit() (*Rlimit, error) {
	t, exists := ulimitNameMapping[u.Name]
	if !exists {
		return nil, fmt.Errorf("invalid ulimit name %s", u.Name)
	}

	return &Rlimit{Type: t, Soft: uint64(u.Soft), Hard: uint64(u.Hard)}, nil
}

func (u *Ulimit) String() string {
	return fmt.Sprintf("%s=%d:%d", u.Name, u.Soft, u.Hard)
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package config

const (
	FetchOnlyKey                            = "fetch-only"
	ConfigFileKey                           = "config-file"
	VersionKey                              = "version"
	GenesisConfigFileKey                    = "genesis"
	NetworkNameKey                          = "network-id"
	TxFeeKey                                = "tx-fee"
	CreationTxFeeKey                        = "creation-tx-fee"
	UptimeRequirementKey                    = "uptime-requirement"
	MinValidatorStakeKey                    = "min-validator-stake"
	MaxValidatorStakeKey                    = "max-validator-stake"
	MinDelegatorStakeKey                    = "min-delegator-stake"
	MinDelegatorFeeKey                      = "min-delegation-fee"
	MinStakeDurationKey                     = "min-stake-duration"
	MaxStakeDurationKey                     = "max-stake-duration"
	StakeMintingPeriodKey                   = "stake-minting-period"
	AssertionsEnabledKey                    = "assertions-enabled"
	SignatureVerificationEnabledKey         = "signature-verification-enabled"
	DBEnabledKey                            = "db-enabled"
	DBPathKey                               = "db-dir"
	PublicIPKey                             = "public-ip"
	DynamicUpdateDurationKey                = "dynamic-update-duration"
	DynamicPublicIPResolverKey              = "dynamic-public-ip"
	ConnMeterResetDurationKey               = "conn-meter-reset-duration"
	ConnMeterMaxConnsKey                    = "conn-meter-max-conns"
	HTTPHostKey                             = "http-host"
	HTTPPortKey                             = "http-port"
	HTTPSEnabledKey                         = "http-tls-enabled"
	HTTPSKeyFileKey                         = "http-tls-key-file"
	HTTPSCertFileKey                        = "http-tls-cert-file"
	HTTPAllowedOrigins                      = "http-allowed-origins"
	APIAuthRequiredKey                      = "api-auth-required"
	APIAuthPasswordFileKey                  = "api-auth-password-file" // #nosec G101
	BootstrapIPsKey                         = "bootstrap-ips"
	BootstrapIDsKey                         = "bootstrap-ids"
	StakingPortKey                          = "staking-port"
	StakingEnabledKey                       = "staking-enabled"
	P2pTLSEnabledKey                        = "p2p-tls-enabled"
	StakingKeyPathKey                       = "staking-tls-key-file"
	StakingCertPathKey                      = "staking-tls-cert-file"
	StakingDisabledWeightKey                = "staking-disabled-weight"
	MaxNonStakerPendingMsgsKey              = "max-non-staker-pending-msgs"
	StakerMsgReservedKey                    = "staker-msg-reserved"
	StakerCPUReservedKey                    = "staker-cpu-reserved"
	MaxPendingMsgsKey                       = "max-pending-msgs"
	NetworkInitialTimeoutKey                = "network-initial-timeout"
	NetworkMinimumTimeoutKey                = "network-minimum-timeout"
	NetworkMaximumTimeoutKey                = "network-maximum-timeout"
	NetworkTimeoutHalflifeKey               = "network-timeout-halflife"
	NetworkTimeoutCoefficientKey            = "network-timeout-coefficient"
	NetworkHealthMinPeersKey                = "network-health-min-conn-peers"
	NetworkHealthMaxTimeSinceMsgReceivedKey = "network-health-max-time-since-msg-received"
	NetworkHealthMaxTimeSinceMsgSentKey     = "network-health-max-time-since-msg-sent"
	NetworkHealthMaxPortionSendQueueFillKey = "network-health-max-portion-send-queue-full"
	NetworkHealthMaxSendFailRateKey         = "network-health-max-send-fail-rate"
	NetworkHealthMaxOutstandingDurationKey  = "network-health-max-outstanding-request-duration"
	NetworkPeerListSizeKey                  = "network-peer-list-size"
	NetworkPeerListGossipSizeKey            = "network-peer-list-gossip-size"
	NetworkPeerListGossipFreqKey            = "network-peer-list-gossip-frequency"
	SendQueueSizeKey                        = "send-queue-size"
	BenchlistFailThresholdKey               = "benchlist-fail-threshold"
	BenchlistPeerSummaryEnabledKey          = "benchlist-peer-summary-enabled"
	BenchlistDurationKey                    = "benchlist-duration"
	BenchlistMinFailingDurationKey          = "benchlist-min-failing-duration"
	PluginDirKey                            = "plugin-dir"
	BuildDirKey                             = "build-dir"
	LogsDirKey                              = "log-dir"
	LogLevelKey                             = "log-level"
	LogDisplayLevelKey                      = "log-display-level"
	LogDisplayHighlightKey                  = "log-display-highlight"
	SnowSampleSizeKey                       = "snow-sample-size"
	SnowQuorumSizeKey                       = "snow-quorum-size"
	SnowVirtuousCommitThresholdKey          = "snow-virtuous-commit-threshold"
	SnowRogueCommitThresholdKey             = "snow-rogue-commit-threshold"
	SnowAvalancheNumParentsKey              = "snow-avalanche-num-parents"
	SnowAvalancheBatchSizeKey               = "snow-avalanche-batch-size"
	SnowConcurrentRepollsKey                = "snow-concurrent-repolls"
	SnowOptimalProcessingKey                = "snow-optimal-processing"
	SnowMaxProcessingKey                    = "snow-max-processing"
	SnowMaxTimeProcessingKey                = "snow-max-time-processing"
	SnowEpochFirstTransition                = "snow-epoch-first-transition"
	SnowEpochDuration                       = "snow-epoch-duration"
	WhitelistedSubnetsKey                   = "whitelisted-subnets"
	AdminAPIEnabledKey                      = "api-admin-enabled"
	InfoAPIEnabledKey                       = "api-info-enabled"
	KeystoreAPIEnabledKey                   = "api-keystore-enabled"
	MetricsAPIEnabledKey                    = "api-metrics-enabled"
	HealthAPIEnabledKey                     = "api-health-enabled"
	IpcAPIEnabledKey                        = "api-ipcs-enabled"
	IpcsChainIDsKey                         = "ipcs-chain-ids"
	IpcsPathKey                             = "ipcs-path"
	ConsensusGossipFrequencyKey             = "consensus-gossip-frequency"
	ConsensusShutdownTimeoutKey             = "consensus-shutdown-timeout"
	FdLimitKey                              = "fd-limit"
	CorethConfigKey                         = "coreth-config"
	IndexEnabledKey                         = "index-enabled"
	IndexAllowIncompleteKey                 = "index-allow-incomplete"
	RouterHealthMaxDropRateKey              = "router-health-max-drop-rate"
	RouterHealthMaxOutstandingRequestsKey   = "router-health-max-outstanding-requests"
	HealthCheckFreqKey                      = "health-check-frequency"
	HealthCheckAveragerHalflifeKey          = "health-check-averager-halflife"
	RetryBootstrapKey                       = "bootstrap-retry-enabled"
	RetryBootstrapMaxAttemptsKey            = "bootstrap-retry-max-attempts"
	PeerAliasTimeoutKey                     = "peer-alias-timeout"
	PluginModeKey                           = "plugin-mode-enabled"
	BootstrapBeaconConnectionTimeoutKey     = "bootstrap-beacon-connection-timeout"
	ChainConfigDirKey                       = "chainconfig-dir"
)
<commit_msg>change chainconfig flag name<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package config

const (
	FetchOnlyKey                            = "fetch-only"
	ConfigFileKey                           = "config-file"
	VersionKey                              = "version"
	GenesisConfigFileKey                    = "genesis"
	NetworkNameKey                          = "network-id"
	TxFeeKey                                = "tx-fee"
	CreationTxFeeKey                        = "creation-tx-fee"
	UptimeRequirementKey                    = "uptime-requirement"
	MinValidatorStakeKey                    = "min-validator-stake"
	MaxValidatorStakeKey                    = "max-validator-stake"
	MinDelegatorStakeKey                    = "min-delegator-stake"
	MinDelegatorFeeKey                      = "min-delegation-fee"
	MinStakeDurationKey                     = "min-stake-duration"
	MaxStakeDurationKey                     = "max-stake-duration"
	StakeMintingPeriodKey                   = "stake-minting-period"
	AssertionsEnabledKey                    = "assertions-enabled"
	SignatureVerificationEnabledKey         = "signature-verification-enabled"
	DBEnabledKey                            = "db-enabled"
	DBPathKey                               = "db-dir"
	PublicIPKey                             = "public-ip"
	DynamicUpdateDurationKey                = "dynamic-update-duration"
	DynamicPublicIPResolverKey              = "dynamic-public-ip"
	ConnMeterResetDurationKey               = "conn-meter-reset-duration"
	ConnMeterMaxConnsKey                    = "conn-meter-max-conns"
	HTTPHostKey                             = "http-host"
	HTTPPortKey                             = "http-port"
	HTTPSEnabledKey                         = "http-tls-enabled"
	HTTPSKeyFileKey                         = "http-tls-key-file"
	HTTPSCertFileKey                        = "http-tls-cert-file"
	HTTPAllowedOrigins                      = "http-allowed-origins"
	APIAuthRequiredKey                      = "api-auth-required"
	APIAuthPasswordFileKey                  = "api-auth-password-file" // #nosec G101
	BootstrapIPsKey                         = "bootstrap-ips"
	BootstrapIDsKey                         = "bootstrap-ids"
	StakingPortKey                          = "staking-port"
	StakingEnabledKey                       = "staking-enabled"
	P2pTLSEnabledKey                        = "p2p-tls-enabled"
	StakingKeyPathKey                       = "staking-tls-key-file"
	StakingCertPathKey                      = "staking-tls-cert-file"
	StakingDisabledWeightKey                = "staking-disabled-weight"
	MaxNonStakerPendingMsgsKey              = "max-non-staker-pending-msgs"
	StakerMsgReservedKey                    = "staker-msg-reserved"
	StakerCPUReservedKey                    = "staker-cpu-reserved"
	MaxPendingMsgsKey                       = "max-pending-msgs"
	NetworkInitialTimeoutKey                = "network-initial-timeout"
	NetworkMinimumTimeoutKey                = "network-minimum-timeout"
	NetworkMaximumTimeoutKey                = "network-maximum-timeout"
	NetworkTimeoutHalflifeKey               = "network-timeout-halflife"
	NetworkTimeoutCoefficientKey            = "network-timeout-coefficient"
	NetworkHealthMinPeersKey                = "network-health-min-conn-peers"
	NetworkHealthMaxTimeSinceMsgReceivedKey = "network-health-max-time-since-msg-received"
	NetworkHealthMaxTimeSinceMsgSentKey     = "network-health-max-time-since-msg-sent"
	NetworkHealthMaxPortionSendQueueFillKey = "network-health-max-portion-send-queue-full"
	NetworkHealthMaxSendFailRateKey         = "network-health-max-send-fail-rate"
	NetworkHealthMaxOutstandingDurationKey  = "network-health-max-outstanding-request-duration"
	NetworkPeerListSizeKey                  = "network-peer-list-size"
	NetworkPeerListGossipSizeKey            = "network-peer-list-gossip-size"
	NetworkPeerListGossipFreqKey            = "network-peer-list-gossip-frequency"
	SendQueueSizeKey                        = "send-queue-size"
	BenchlistFailThresholdKey               = "benchlist-fail-threshold"
	BenchlistPeerSummaryEnabledKey          = "benchlist-peer-summary-enabled"
	BenchlistDurationKey                    = "benchlist-duration"
	BenchlistMinFailingDurationKey          = "benchlist-min-failing-duration"
	PluginDirKey                            = "plugin-dir"
	BuildDirKey                             = "build-dir"
	LogsDirKey                              = "log-dir"
	LogLevelKey                             = "log-level"
	LogDisplayLevelKey                      = "log-display-level"
	LogDisplayHighlightKey                  = "log-display-highlight"
	SnowSampleSizeKey                       = "snow-sample-size"
	SnowQuorumSizeKey                       = "snow-quorum-size"
	SnowVirtuousCommitThresholdKey          = "snow-virtuous-commit-threshold"
	SnowRogueCommitThresholdKey             = "snow-rogue-commit-threshold"
	SnowAvalancheNumParentsKey              = "snow-avalanche-num-parents"
	SnowAvalancheBatchSizeKey               = "snow-avalanche-batch-size"
	SnowConcurrentRepollsKey                = "snow-concurrent-repolls"
	SnowOptimalProcessingKey                = "snow-optimal-processing"
	SnowMaxProcessingKey                    = "snow-max-processing"
	SnowMaxTimeProcessingKey                = "snow-max-time-processing"
	SnowEpochFirstTransition                = "snow-epoch-first-transition"
	SnowEpochDuration                       = "snow-epoch-duration"
	WhitelistedSubnetsKey                   = "whitelisted-subnets"
	AdminAPIEnabledKey                      = "api-admin-enabled"
	InfoAPIEnabledKey                       = "api-info-enabled"
	KeystoreAPIEnabledKey                   = "api-keystore-enabled"
	MetricsAPIEnabledKey                    = "api-metrics-enabled"
	HealthAPIEnabledKey                     = "api-health-enabled"
	IpcAPIEnabledKey                        = "api-ipcs-enabled"
	IpcsChainIDsKey                         = "ipcs-chain-ids"
	IpcsPathKey                             = "ipcs-path"
	ConsensusGossipFrequencyKey             = "consensus-gossip-frequency"
	ConsensusShutdownTimeoutKey             = "consensus-shutdown-timeout"
	FdLimitKey                              = "fd-limit"
	CorethConfigKey                         = "coreth-config"
	IndexEnabledKey                         = "index-enabled"
	IndexAllowIncompleteKey                 = "index-allow-incomplete"
	RouterHealthMaxDropRateKey              = "router-health-max-drop-rate"
	RouterHealthMaxOutstandingRequestsKey   = "router-health-max-outstanding-requests"
	HealthCheckFreqKey                      = "health-check-frequency"
	HealthCheckAveragerHalflifeKey          = "health-check-averager-halflife"
	RetryBootstrapKey                       = "bootstrap-retry-enabled"
	RetryBootstrapMaxAttemptsKey            = "bootstrap-retry-max-attempts"
	PeerAliasTimeoutKey                     = "peer-alias-timeout"
	PluginModeKey                           = "plugin-mode-enabled"
	BootstrapBeaconConnectionTimeoutKey     = "bootstrap-beacon-connection-timeout"
	ChainConfigDirKey                       = "chain-config-dir"
)
<|endoftext|>


<commit_before>package main

import (
	"encoding/csv"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"reflect"
	"strconv"
	"time"

	"github.com/julienschmidt/httprouter"
)

const timeFormat = "2006-01-02"

type rate struct {
	Date  time.Time `json:"date"`
	Value float64   `json:"value"`
}

// MarshalJSON overrides marshal, prints shorter date string
func (r *rate) MarshalJSON() ([]byte, error) {
	type alias rate
	return json.Marshal(&struct {
		Date string `json:"date"`
		*alias
	}{
		Date:  r.Date.Format("2006-01-02"),
		alias: (*alias)(r),
	})
}

var lastRefresh time.Time
var maturities = []string{"1w", "2w", "1m", "2m", "3m", "6m", "9m", "12m"}
var retentions = []string{"week", "month", "three_months", "six_months", "year", "two_years", "six_years"}
var historyCache map[string][]rate
var influxCache map[string]map[string][]rate

// runs in go routine and takes care of refreshing the cache
func refreshCache() {
	historyCache = make(map[string][]rate)
	influxCache = make(map[string]map[string][]rate)

	for {
		if time.Since(lastRefresh) < time.Hour*24 {
			time.Sleep(time.Minute)
			continue
		}

		// refresh
		log.Println("refreshing history cache")
		for _, maturity := range maturities {
			// TODO: make path to files configureable
			file := fmt.Sprintf("../euribor-rates-%s.csv", maturity)
			historyCache[maturity] = parseFile(file)
		}
		log.Println("refreshing influx cache")
		for _, retention := range retentions {
			results := queryInflux(retention)
			cache := make(map[string][]rate)
			// fmt.Println(results.Values)
			// fmt.Println(reflect.TypeOf(results.Values).Kind())
			for _, value := range results.Values {
				// fmt.Println(value, reflect.TypeOf(value).Kind(), reflect.TypeOf(value))
				m, r, err := transformInfluxValueToRate(reflect.ValueOf(value).Interface().([]interface{}))
				if err != nil {
					log.Println("error converting influx value to rate")
					continue
				}
				if rates, ok := cache[m]; ok {
					rates = append(rates, r)
					cache[m] = rates
				} else {
					cache[m] = []rate{r}
				}
			}
			influxCache[retention] = cache
		}
		log.Println("cache refresh completed")

		lastRefresh = time.Now()
	}
}

func parseFile(file string) []rate {
	f, err := os.Open(file)
	if err != nil {
		log.Println(err)
		return []rate{}
	}

	r := csv.NewReader(f)
	r.Comment = '#'
	r.FieldsPerRecord = 2

	records, err := r.ReadAll()
	if err != nil {
		log.Println(err)
		return []rate{}
	}

	rates := []rate{}
	for _, record := range records {
		date, err := time.ParseInLocation(timeFormat, record[0], time.UTC)
		if err != nil {
			log.Println(err)
			continue
		}
		value, err := strconv.ParseFloat(record[1], 64)
		if err != nil {
			log.Println(err)
			continue
		}
		rates = append(rates, rate{date, value})
	}

	return rates
}

func isValidRetention(r string) bool {
	// TODO: fix some kind of mapping
	// last-week
	// last-month
	// last-quater
	// last-six-months
	// last-year
	// last-two-years
	// last-six-years

	return false
}

func isValidMaturity(m string) bool {
	switch m {
	case "1w":
		return true
	case "2w":
		return true
	case "1m":
		return true
	case "2m":
		return true
	case "3m":
		return true
	case "6m":
		return true
	case "9m":
		return true
	case "12m":
		return true
	}
	return false
}

// index handler
func index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	fmt.Fprint(w, "Welcome to the Euribor rates service!\n")
}

// influx handler
func influx(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	retention := params.ByName("retention")
	if isValidRetention(retention) == false {
		//TODO: return http error code
		fmt.Fprintf(w, errorMsg("uknown retention"))
		return
	}

	maturity := params.ByName("maturity")
	if isValidMaturity(maturity) == false {
		// TODO: return http error code
		fmt.Fprintf(w, errorMsg("uknown maturity"))
		return
	}

	rates := []rate{}
	for _, r := range influxCache[retention][maturity] {
		rates = append(rates, r)
	}

	jsonData, err := json.Marshal(rates)
	if err != nil {
		fmt.Fprintf(w, errorMsg(err.Error()))
		return
	}

	fmt.Fprintf(w, string(jsonData))
}

// history handler
func history(w http.ResponseWriter, r *http.Request, params httprouter.Params) {

	year, err := strconv.ParseInt(params.ByName("year"), 10, 32)
	if err != nil {
		// TODO: return http error code
		fmt.Fprint(w, errorMsg(err.Error()))
		return
	}
	if year < 2010 || year > int64(time.Now().Year()) {
		fmt.Fprintf(w, errorMsg("no data"))
		return
	}

	maturity := params.ByName("maturity")
	if isValidMaturity(maturity) == false {
		// TODO: return http error code
		fmt.Fprintf(w, errorMsg("uknown maturity"))
		return
	}

	rates := []rate{}
	for _, r := range historyCache[maturity] {
		if int64(r.Date.Year()) != year {
			continue
		}
		rates = append(rates, r)
	}

	jsonData, err := json.Marshal(rates)
	if err != nil {
		fmt.Fprintf(w, errorMsg(err.Error()))
		return
	}

	fmt.Fprintf(w, string(jsonData))
}

func errorMsg(msg string) string {
	return fmt.Sprintf("{\"error\":\"%s\"}", msg)
}

func main() {
	go refreshCache()

	router := httprouter.New()

	// routes for general info
	// TODO: add routes for list of supported retentions/maturities
	router.GET("/", index)

	// routes to serve the app
	router.GET("/rates/app/:retention/:maturity", influx)

	// routes to serve historical queries
	router.GET("/rates/history/:year/:maturity", history)

	log.Fatal(http.ListenAndServe(":8080", router))
}
<commit_msg>Add flag to specify path to history files<commit_after>package main

import (
	"encoding/csv"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"reflect"
	"strconv"
	"time"

	"github.com/julienschmidt/httprouter"
)

const timeFormat = "2006-01-02"

type rate struct {
	Date  time.Time `json:"date"`
	Value float64   `json:"value"`
}

// MarshalJSON overrides marshal, prints shorter date string
func (r *rate) MarshalJSON() ([]byte, error) {
	type alias rate
	return json.Marshal(&struct {
		Date string `json:"date"`
		*alias
	}{
		Date:  r.Date.Format("2006-01-02"),
		alias: (*alias)(r),
	})
}

var lastRefresh time.Time
var maturities = []string{"1w", "2w", "1m", "2m", "3m", "6m", "9m", "12m"}
var retentions = []string{"week", "month", "three_months", "six_months", "year", "two_years", "six_years"}
var historyPath string
var historyCache map[string][]rate
var influxCache map[string]map[string][]rate

// runs in go routine and takes care of refreshing the cache
func refreshCache() {
	historyCache = make(map[string][]rate)
	influxCache = make(map[string]map[string][]rate)

	for {
		if time.Since(lastRefresh) < time.Hour*24 {
			time.Sleep(time.Minute)
			continue
		}

		// refresh
		log.Println("refreshing history cache")
		for _, maturity := range maturities {
			// TODO: make path to files configureable
			file := fmt.Sprintf("%s/euribor-rates-%s.csv", historyPath, maturity)
			historyCache[maturity] = parseFile(file)
		}
		log.Println("refreshing influx cache")
		for _, retention := range retentions {
			results := queryInflux(retention)
			cache := make(map[string][]rate)
			// fmt.Println(results.Values)
			// fmt.Println(reflect.TypeOf(results.Values).Kind())
			for _, value := range results.Values {
				// fmt.Println(value, reflect.TypeOf(value).Kind(), reflect.TypeOf(value))
				m, r, err := transformInfluxValueToRate(reflect.ValueOf(value).Interface().([]interface{}))
				if err != nil {
					log.Println("error converting influx value to rate")
					continue
				}
				if rates, ok := cache[m]; ok {
					rates = append(rates, r)
					cache[m] = rates
				} else {
					cache[m] = []rate{r}
				}
			}
			influxCache[retention] = cache
		}
		log.Println("cache refresh completed")

		lastRefresh = time.Now()
	}
}

func parseFile(file string) []rate {
	f, err := os.Open(file)
	if err != nil {
		log.Println(err)
		return []rate{}
	}

	r := csv.NewReader(f)
	r.Comment = '#'
	r.FieldsPerRecord = 2

	records, err := r.ReadAll()
	if err != nil {
		log.Println(err)
		return []rate{}
	}

	rates := []rate{}
	for _, record := range records {
		date, err := time.ParseInLocation(timeFormat, record[0], time.UTC)
		if err != nil {
			log.Println(err)
			continue
		}
		value, err := strconv.ParseFloat(record[1], 64)
		if err != nil {
			log.Println(err)
			continue
		}
		rates = append(rates, rate{date, value})
	}

	return rates
}

func isValidRetention(r string) bool {
	// TODO: fix some kind of mapping
	// last-week
	// last-month
	// last-quater
	// last-six-months
	// last-year
	// last-two-years
	// last-six-years

	return false
}

func isValidMaturity(m string) bool {
	switch m {
	case "1w":
		return true
	case "2w":
		return true
	case "1m":
		return true
	case "2m":
		return true
	case "3m":
		return true
	case "6m":
		return true
	case "9m":
		return true
	case "12m":
		return true
	}
	return false
}

// index handler
func index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	fmt.Fprint(w, "Welcome to the Euribor rates service!\n")
}

// influx handler
func influx(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	retention := params.ByName("retention")
	if isValidRetention(retention) == false {
		//TODO: return http error code
		fmt.Fprintf(w, errorMsg("uknown retention"))
		return
	}

	maturity := params.ByName("maturity")
	if isValidMaturity(maturity) == false {
		// TODO: return http error code
		fmt.Fprintf(w, errorMsg("uknown maturity"))
		return
	}

	rates := []rate{}
	for _, r := range influxCache[retention][maturity] {
		rates = append(rates, r)
	}

	jsonData, err := json.Marshal(rates)
	if err != nil {
		fmt.Fprintf(w, errorMsg(err.Error()))
		return
	}

	fmt.Fprintf(w, string(jsonData))
}

// history handler
func history(w http.ResponseWriter, r *http.Request, params httprouter.Params) {

	year, err := strconv.ParseInt(params.ByName("year"), 10, 32)
	if err != nil {
		// TODO: return http error code
		fmt.Fprint(w, errorMsg(err.Error()))
		return
	}
	if year < 2010 || year > int64(time.Now().Year()) {
		fmt.Fprintf(w, errorMsg("no data"))
		return
	}

	maturity := params.ByName("maturity")
	if isValidMaturity(maturity) == false {
		// TODO: return http error code
		fmt.Fprintf(w, errorMsg("uknown maturity"))
		return
	}

	rates := []rate{}
	for _, r := range historyCache[maturity] {
		if int64(r.Date.Year()) != year {
			continue
		}
		rates = append(rates, r)
	}

	jsonData, err := json.Marshal(rates)
	if err != nil {
		fmt.Fprintf(w, errorMsg(err.Error()))
		return
	}

	fmt.Fprintf(w, string(jsonData))
}

func errorMsg(msg string) string {
	return fmt.Sprintf("{\"error\":\"%s\"}", msg)
}

func main() {
	flag.StringVar(&historyPath, "history-path", ".", "path to history rate CSV files")
	flag.Parse()

	go refreshCache()

	router := httprouter.New()

	// routes for general info
	// TODO: add routes for list of supported retentions/maturities
	router.GET("/", index)

	// routes to serve the app
	router.GET("/rates/app/:retention/:maturity", influx)

	// routes to serve historical queries
	router.GET("/rates/history/:year/:maturity", history)

	log.Fatal(http.ListenAndServe(":8080", router))
}
<|endoftext|>


<commit_before>package tagexpressions

import (
	"errors"
	"fmt"
	"regexp"
	"strings"
)

const OPERAND = "operand"
const OPERATOR = "operator"

type Evaluatable interface {
	Evaluate(variables []string) bool
	ToString() string
}

func Parse(infix string) (Evaluatable, error) {
	tokens := tokenize(infix)
	if len(tokens) == 0 {
		return &trueExpr{}, nil
	}
	expressions := &EvaluatableStack{}
	operators := &StringStack{}
	expectedTokenType := OPERAND

	for _, token := range tokens {
		if isUnary(token) {
			if err := check(expectedTokenType, OPERAND); err != nil {
				return nil, err
			}
			operators.Push(token)
			expectedTokenType = OPERAND
		} else if isBinary(token) {
			if err := check(expectedTokenType, OPERATOR); err != nil {
				return nil, err
			}
			for operators.Len() > 0 &&
				isOp(operators.Peek()) &&
				((ASSOC[token] == "left" && PREC[token] <= PREC[operators.Peek()]) ||
					(ASSOC[token] == "right" && PREC[token] < PREC[operators.Peek()])) {
				pushExpr(operators.Pop(), expressions)
			}
			operators.Push(token)
			expectedTokenType = OPERAND
		} else if "(" == token {
			if err := check(expectedTokenType, OPERAND); err != nil {
				return nil, err
			}
			operators.Push(token)
			expectedTokenType = OPERAND
		} else if ")" == token {
			if err := check(expectedTokenType, OPERATOR); err != nil {
				return nil, err
			}
			for operators.Len() > 0 && operators.Peek() != "(" {
				pushExpr(operators.Pop(), expressions)
			}
			if operators.Len() == 0 {
				return nil, errors.New("Syntax error. Unmatched )")
			}
			if operators.Peek() == "(" {
				operators.Pop()
			}
			expectedTokenType = OPERATOR
		} else {
			if err := check(expectedTokenType, OPERAND); err != nil {
				return nil, err
			}
			pushExpr(token, expressions)
			expectedTokenType = OPERATOR
		}
	}

	for operators.Len() > 0 {
		if operators.Peek() == "(" {
			return nil, errors.New("Syntax error. Unmatched (")
		}
		pushExpr(operators.Pop(), expressions)
	}

	return expressions.Pop(), nil
}

var ASSOC = map[string]string{
	"or":  "left",
	"and": "left",
	"not": "right",
}

var PREC = map[string]int{
	"(":   -2,
	")":   -1,
	"or":  0,
	"and": 1,
	"not": 2,
}

var whitespaceRegex = regexp.MustCompile(`\s`)

func tokenize(expr string) []string {
	tokens := []string{}
	isEscaped := false
	token := []rune{}
	for _, c := range expr {
		if '\\' == c {
			isEscaped = true
		} else {
			if whitespaceRegex.MatchString(string(c)) {
				// skip
				if len(token) > 0 {
					// end of token
					tokens = append(tokens, string(token))
					token = []rune{}
				}
			} else {
				switch c {
				case '(', ')':
					if !isEscaped {
						if len(token) > 0 {
							// end of token
							tokens = append(tokens, string(token))
							token = []rune{}
						}
						tokens = append(tokens, string(c))
						break
					}
					fallthrough
				default:
					token = append(token, c)
				}
			}
			isEscaped = false
		}
	}
	if len(token) > 0 {
		tokens = append(tokens, string(token))
	}
	return tokens
}

func isUnary(token string) bool {
	return "not" == token
}

func isBinary(token string) bool {
	return "or" == token || "and" == token
}

func isOp(token string) bool {
	_, ok := ASSOC[token]
	return ok
}

func check(expectedTokenType, tokenType string) error {
	if expectedTokenType != tokenType {
		return fmt.Errorf("Syntax error. Expected %s", expectedTokenType)
	}
	return nil
}

func pushExpr(token string, stack *EvaluatableStack) {
	if token == "and" {
		rightAndExpr := stack.Pop()
		stack.Push(&andExpr{
			leftExpr:  stack.Pop(),
			rightExpr: rightAndExpr,
		})
	} else if token == "or" {
		rightOrExpr := stack.Pop()
		stack.Push(&orExpr{
			leftExpr:  stack.Pop(),
			rightExpr: rightOrExpr,
		})
	} else if token == "not" {
		stack.Push(&notExpr{expr: stack.Pop()})
	} else {
		stack.Push(&literalExpr{value: token})
	}
}

type literalExpr struct {
	value string
}

func (l *literalExpr) Evaluate(variables []string) bool {
	for _, variable := range variables {
		if variable == l.value {
			return true
		}
	}
	return false
}

func (l *literalExpr) ToString() string {
	return strings.Replace(
		strings.Replace(l.value, "(", "\\(", -1),
		")",
		"\\)",
		-1,
	)
}

type orExpr struct {
	leftExpr  Evaluatable
	rightExpr Evaluatable
}

func (o *orExpr) Evaluate(variables []string) bool {
	return o.leftExpr.Evaluate(variables) || o.rightExpr.Evaluate(variables)
}

func (o *orExpr) ToString() string {
	return fmt.Sprintf("( %s or %s )", o.leftExpr.ToString(), o.rightExpr.ToString())
}

type andExpr struct {
	leftExpr  Evaluatable
	rightExpr Evaluatable
}

func (a *andExpr) Evaluate(variables []string) bool {
	return a.leftExpr.Evaluate(variables) && a.rightExpr.Evaluate(variables)
}

func (a *andExpr) ToString() string {
	return fmt.Sprintf("( %s and %s )", a.leftExpr.ToString(), a.rightExpr.ToString())
}

type notExpr struct {
	expr Evaluatable
}

func (n *notExpr) Evaluate(variables []string) bool {
	return !n.expr.Evaluate(variables)
}

func (n *notExpr) ToString() string {
	return fmt.Sprintf("not ( %s )", n.expr.ToString())
}

type trueExpr struct{}

func (t *trueExpr) Evaluate(variables []string) bool {
	return true
}

func (t *trueExpr) ToString() string {
	return "true"
}
<commit_msg>Refactor tokenize method<commit_after>package tagexpressions

import (
	"errors"
	"fmt"
	"strings"
	"unicode"
)

const OPERAND = "operand"
const OPERATOR = "operator"

type Evaluatable interface {
	Evaluate(variables []string) bool
	ToString() string
}

func Parse(infix string) (Evaluatable, error) {
	tokens := tokenize(infix)
	if len(tokens) == 0 {
		return &trueExpr{}, nil
	}
	expressions := &EvaluatableStack{}
	operators := &StringStack{}
	expectedTokenType := OPERAND

	for _, token := range tokens {
		if isUnary(token) {
			if err := check(expectedTokenType, OPERAND); err != nil {
				return nil, err
			}
			operators.Push(token)
			expectedTokenType = OPERAND
		} else if isBinary(token) {
			if err := check(expectedTokenType, OPERATOR); err != nil {
				return nil, err
			}
			for operators.Len() > 0 &&
				isOp(operators.Peek()) &&
				((ASSOC[token] == "left" && PREC[token] <= PREC[operators.Peek()]) ||
					(ASSOC[token] == "right" && PREC[token] < PREC[operators.Peek()])) {
				pushExpr(operators.Pop(), expressions)
			}
			operators.Push(token)
			expectedTokenType = OPERAND
		} else if "(" == token {
			if err := check(expectedTokenType, OPERAND); err != nil {
				return nil, err
			}
			operators.Push(token)
			expectedTokenType = OPERAND
		} else if ")" == token {
			if err := check(expectedTokenType, OPERATOR); err != nil {
				return nil, err
			}
			for operators.Len() > 0 && operators.Peek() != "(" {
				pushExpr(operators.Pop(), expressions)
			}
			if operators.Len() == 0 {
				return nil, errors.New("Syntax error. Unmatched )")
			}
			if operators.Peek() == "(" {
				operators.Pop()
			}
			expectedTokenType = OPERATOR
		} else {
			if err := check(expectedTokenType, OPERAND); err != nil {
				return nil, err
			}
			pushExpr(token, expressions)
			expectedTokenType = OPERATOR
		}
	}

	for operators.Len() > 0 {
		if operators.Peek() == "(" {
			return nil, errors.New("Syntax error. Unmatched (")
		}
		pushExpr(operators.Pop(), expressions)
	}

	return expressions.Pop(), nil
}

var ASSOC = map[string]string{
	"or":  "left",
	"and": "left",
	"not": "right",
}

var PREC = map[string]int{
	"(":   -2,
	")":   -1,
	"or":  0,
	"and": 1,
	"not": 2,
}

func tokenize(expr string) []string {
	var tokens []string
	var token []rune

	collectToken := func() {
		if len(token) > 0 {
			tokens = append(tokens, string(token))
			token = []rune{}
		}
	}

	escaped := false
	for _, c := range expr {
		if unicode.IsSpace(c) {
			collectToken()
			continue
		}

		switch c {
		case '\\':
			escaped = true
		case '(', ')':
			if escaped {
				token = append(token, c)
				escaped = false
			} else {
				collectToken()
				tokens = append(tokens, string(c))
			}
		default:
			token = append(token, c)
		}
	}

	collectToken()
	return tokens
}

func isUnary(token string) bool {
	return "not" == token
}

func isBinary(token string) bool {
	return "or" == token || "and" == token
}

func isOp(token string) bool {
	_, ok := ASSOC[token]
	return ok
}

func check(expectedTokenType, tokenType string) error {
	if expectedTokenType != tokenType {
		return fmt.Errorf("Syntax error. Expected %s", expectedTokenType)
	}
	return nil
}

func pushExpr(token string, stack *EvaluatableStack) {
	if token == "and" {
		rightAndExpr := stack.Pop()
		stack.Push(&andExpr{
			leftExpr:  stack.Pop(),
			rightExpr: rightAndExpr,
		})
	} else if token == "or" {
		rightOrExpr := stack.Pop()
		stack.Push(&orExpr{
			leftExpr:  stack.Pop(),
			rightExpr: rightOrExpr,
		})
	} else if token == "not" {
		stack.Push(&notExpr{expr: stack.Pop()})
	} else {
		stack.Push(&literalExpr{value: token})
	}
}

type literalExpr struct {
	value string
}

func (l *literalExpr) Evaluate(variables []string) bool {
	for _, variable := range variables {
		if variable == l.value {
			return true
		}
	}
	return false
}

func (l *literalExpr) ToString() string {
	return strings.Replace(
		strings.Replace(l.value, "(", "\\(", -1),
		")",
		"\\)",
		-1,
	)
}

type orExpr struct {
	leftExpr  Evaluatable
	rightExpr Evaluatable
}

func (o *orExpr) Evaluate(variables []string) bool {
	return o.leftExpr.Evaluate(variables) || o.rightExpr.Evaluate(variables)
}

func (o *orExpr) ToString() string {
	return fmt.Sprintf("( %s or %s )", o.leftExpr.ToString(), o.rightExpr.ToString())
}

type andExpr struct {
	leftExpr  Evaluatable
	rightExpr Evaluatable
}

func (a *andExpr) Evaluate(variables []string) bool {
	return a.leftExpr.Evaluate(variables) && a.rightExpr.Evaluate(variables)
}

func (a *andExpr) ToString() string {
	return fmt.Sprintf("( %s and %s )", a.leftExpr.ToString(), a.rightExpr.ToString())
}

type notExpr struct {
	expr Evaluatable
}

func (n *notExpr) Evaluate(variables []string) bool {
	return !n.expr.Evaluate(variables)
}

func (n *notExpr) ToString() string {
	return fmt.Sprintf("not ( %s )", n.expr.ToString())
}

type trueExpr struct{}

func (t *trueExpr) Evaluate(variables []string) bool {
	return true
}

func (t *trueExpr) ToString() string {
	return "true"
}
<|endoftext|>


<commit_before>package unilog

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"syscall"
	"text/template"
	"time"

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

	"github.com/DataDog/datadog-go/statsd"
	"github.com/getsentry/raven-go"

	"github.com/stripe/unilog/clevels"
	flag "launchpad.net/gnuflag"
)

// hold the argument passed in with "-statstags"
var statstags string

// A filter to be applied to log lines prior to prefixing them
// with a timestamp and logging them.
type Filter func(string) string

// Unilog represents a unilog process. unilog is intended to be used
// as a standalone application, but is exported as a package to allow
// users to perform compile-time configuration to simplify deployment.
type Unilog struct {
	// Sentry DSN for reporting Unilog errors
	// If this is unset, unilog will not report errors to Sentry
	SentryDSN string
	// StatsdAddress for sending metrics
	// If this is unset, it wlil default to "127.0.0.1:8200" -> TODO: is this what we want?
	StatsdAddress string
	// The email address from which unilog will send mail on
	// errors
	MailTo string
	// The email address to which unilog will email breakages. If
	// either MailTo or MailFrom is unset, unilog will not
	// generate email.
	MailFrom string

	// A series of filters which will be applied to each log line
	// in order
	Filters []Filter

	// The version that unilog will report on the command-line and
	// in error emails. Defaults to the toplevel Version constant.
	Version string
	// The number of log lines to buffer in-memory, in case
	// unilog's disk writer falls behind. Note that when talking
	// to unilog over a pipe, the kernel also maintains an
	// in-kernel pipe buffer, sized 64kb on Linux.
	BufferLines int

	Name    string
	Verbose bool

	lines     <-chan string
	errs      <-chan error
	sigReopen <-chan os.Signal
	sigTerm   <-chan os.Signal
	shutdown  chan struct{}
	file      io.WriteCloser
	target    string

	b struct {
		broken bool
		at     time.Time
		count  int
	}
}

func stringFlag(val *string, longname, shortname, init, help string) {
	flag.StringVar(val, longname, init, help)
	flag.StringVar(val, shortname, init, help)
}

func boolFlag(val *bool, longname, shortname string, init bool, help string) {
	flag.BoolVar(val, longname, init, help)
	flag.BoolVar(val, shortname, init, help)
}

func (u *Unilog) fillDefaults() {
	if u.Version == "" {
		u.Version = Version
	}
	if u.BufferLines == 0 {
		u.BufferLines = DefaultBuffer
	}
}

func (u *Unilog) addFlags() {
	stringFlag(&u.Name, "name", "a", "", "Name of logged program")
	boolFlag(&u.Verbose, "verbose", "v", false, "Echo lines to stdout")
	flag.StringVar(&u.MailFrom, "mailfrom", u.MailFrom, "Address to send error emails from")
	flag.StringVar(&u.MailTo, "mailto", u.MailTo, "Address to send error emails to")
	flag.StringVar(&u.SentryDSN, "sentrydsn", u.SentryDSN, "Sentry DSN to send errors to")
	flag.StringVar(&u.StatsdAddress, "statsdaddress", "127.0.0.1:8200", "Address to send statsd metrics to")
	flag.StringVar(&clevels.AusterityFile, "austerityfile", clevels.AusterityFile, "(optional) Location of file to read austerity level from")
	stringFlag(&statstags, "statstags", "s", "", `(optional) tags to include with all statsd metrics (e.g. "foo:bar,baz:quz")`)
}

var emailTemplate = template.Must(template.New("email").Parse(`From: {{.From}}
To: {{.To}}
Subject: [unilog] {{.Name}} could not {{.Action}}

Hi there,

This is unilog reporting from {{.Hostname}}. I'm sad to report that
{{.Name}} is having some troubles writing to its log. I got caught up
trying to log a line to {{.Target}}.

To avoid spamming you, I'm going to shut up for an hour. Please fix me.

{{.Error}}
--
Sent from unilog {{.Version}}
`))

const (
	// Version is the Unilog version. Reported in emails and in
	// response to --version on the command line. Can be overriden
	// by the Version field in a Unilog object.
	Version = "0.3"
	// DefaultBuffer is the default size (in lines) of the
	// in-process line buffer
	DefaultBuffer = 1 << 12
)

var Stats *statsd.Client

func readlines(in io.Reader, bufsize int, shutdown chan struct{}) (<-chan string, <-chan error) {
	linec := make(chan string, bufsize)
	errc := make(chan error, 1)

	u := NewUnilogReader(in, shutdown)
	r := bufio.NewReader(u)

	go func() {
		var err error
		var s string

		for err == nil {
			s, err = r.ReadString('\n')
			if s != "" {
				s = strings.TrimRight(s, "\n")
				linec <- s
				if Stats != nil {
					Stats.Count("unilog.bytes", int64(len(s)), nil, .1)
				}
			}
		}

		if err != io.EOF {
			errc <- err
		}
		close(linec)
	}()

	return linec, errc
}

func (u *Unilog) reopen() error {
	if u.target == "-" {
		u.file = os.Stdout
		return nil
	}

	if u.file != nil {
		u.file.Close()
		u.file = nil
	}

	var e error
	if u.file, e = os.OpenFile(u.target, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644); e != nil {
		u.file = nil
		return e
	}
	return nil
}

func (u *Unilog) format(line string) string {
	for _, filter := range u.Filters {
		if filter != nil {
			line = filter(line)
		}
	}
	return fmt.Sprintf("[%s] %s\n", time.Now().Format("2006-01-02 15:04:05.000000"), line)
}

func (u *Unilog) logLine(line string) {
	formatted := u.format(line)
	if u.Verbose {
		defer io.WriteString(os.Stdout, formatted)
	}

	var e error
	if u.file == nil {
		e = u.reopen()
	}
	if e != nil {
		u.handleError("reopen_file", e)
		return
	}
	_, e = io.WriteString(u.file, formatted)
	if e != nil {
		u.handleError("write_to_log", e)
	} else {
		u.b.broken = false
	}
}

func (u *Unilog) run() {
	for {
		select {
		case e := <-u.errs:
			if e != nil && e != io.EOF {
				panic(e)
			} else {
				return
			}
		case <-u.sigReopen:
			u.reopen()
		case <-u.sigTerm:
			if u.shutdown != nil {
				close(u.shutdown)
				u.shutdown = nil
			}
		case line, ok := <-u.lines:
			if !ok {
				return
			}
			u.logLine(line)
		}
	}
}

func (u *Unilog) handleError(action string, e error) {
	if !u.b.broken {
		u.b.broken = true
		u.b.at = time.Now()
		u.b.count = 0
	} else if time.Since(u.b.at) > time.Hour {
		u.b.at = time.Now()
		u.b.count = 0
	}

	message := fmt.Sprintf("Could not %s: %s", action, e.Error())

	if terminal.IsTerminal(1) {
		fmt.Printf("%s\n", message)
		return
	}

	if Stats != nil {
		emsg := fmt.Sprintf("err_action:%s", action)
		Stats.Count("unilog.errors.error_total", 1, []string{emsg}, 1)
	}

	if u.b.count == 0 && u.SentryDSN != "" {
		hostname, _ := os.Hostname()
		keys := map[string]string{
			"Hostname": hostname,
			"Action":   action,
			"Name":     u.Name,
			"Target":   u.target,
			"Error":    e.Error(),
			"Version":  Version,
		}
		raven.CaptureError(e, keys)
	}

	if u.b.count == 0 && u.MailFrom != "" && u.MailTo != "" {
		message := new(bytes.Buffer)
		hostname, _ := os.Hostname()
		emailTemplate.Execute(message, map[string]string{
			"Hostname": hostname,
			"From":     u.MailFrom,
			"To":       u.MailTo,
			"Action":   action,
			"Name":     u.Name,
			"Target":   u.target,
			"Error":    e.Error(),
			"Version":  Version,
		})
		cmd := exec.Command("sendmail", "-t")
		cmd.Stdin = message
		cmd.Run()
	}

	u.b.count++
}

func (u *Unilog) Main() {
	u.fillDefaults()

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [options] dstfile\n", os.Args[0])
		flag.PrintDefaults()
	}

	u.addFlags()
	var flagVersion bool
	boolFlag(&flagVersion, "version", "V", false, "Print the version number and exit")

	flag.Parse(true)

	if flagVersion {
		fmt.Printf("This is unilog v%s\n", Version)
		return
	}
	args := flag.Args()
	if len(args) != 1 {
		flag.Usage()
		os.Exit(1)
	}

	reopen := make(chan os.Signal, 2)
	signal.Notify(reopen, syscall.SIGALRM, syscall.SIGHUP)
	u.sigReopen = reopen

	term := make(chan os.Signal, 2)
	signal.Notify(term, syscall.SIGTERM, syscall.SIGINT)
	u.sigTerm = term

	u.shutdown = make(chan struct{})
	u.target = flag.Arg(0)
	u.reopen()

	u.lines, u.errs = readlines(os.Stdin, u.BufferLines, u.shutdown)

	fileName := u.target

	Stats, _ = statsd.New(u.StatsdAddress)

	Stats.Tags = append(Stats.Tags, fmt.Sprintf("file_name:%s", fileName))
	if statstags != "" {
		Stats.Tags = append(Stats.Tags, strings.Split(statstags, ",")...)
	}

	clevels.Stats = Stats

	_ = raven.SetDSN(u.SentryDSN)

	u.run()
}
<commit_msg>Change name of statsd metric<commit_after>package unilog

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"syscall"
	"text/template"
	"time"

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

	"github.com/DataDog/datadog-go/statsd"
	"github.com/getsentry/raven-go"

	"github.com/stripe/unilog/clevels"
	flag "launchpad.net/gnuflag"
)

// hold the argument passed in with "-statstags"
var statstags string

// A filter to be applied to log lines prior to prefixing them
// with a timestamp and logging them.
type Filter func(string) string

// Unilog represents a unilog process. unilog is intended to be used
// as a standalone application, but is exported as a package to allow
// users to perform compile-time configuration to simplify deployment.
type Unilog struct {
	// Sentry DSN for reporting Unilog errors
	// If this is unset, unilog will not report errors to Sentry
	SentryDSN string
	// StatsdAddress for sending metrics
	// If this is unset, it wlil default to "127.0.0.1:8200" -> TODO: is this what we want?
	StatsdAddress string
	// The email address from which unilog will send mail on
	// errors
	MailTo string
	// The email address to which unilog will email breakages. If
	// either MailTo or MailFrom is unset, unilog will not
	// generate email.
	MailFrom string

	// A series of filters which will be applied to each log line
	// in order
	Filters []Filter

	// The version that unilog will report on the command-line and
	// in error emails. Defaults to the toplevel Version constant.
	Version string
	// The number of log lines to buffer in-memory, in case
	// unilog's disk writer falls behind. Note that when talking
	// to unilog over a pipe, the kernel also maintains an
	// in-kernel pipe buffer, sized 64kb on Linux.
	BufferLines int

	Name    string
	Verbose bool

	lines     <-chan string
	errs      <-chan error
	sigReopen <-chan os.Signal
	sigTerm   <-chan os.Signal
	shutdown  chan struct{}
	file      io.WriteCloser
	target    string

	b struct {
		broken bool
		at     time.Time
		count  int
	}
}

func stringFlag(val *string, longname, shortname, init, help string) {
	flag.StringVar(val, longname, init, help)
	flag.StringVar(val, shortname, init, help)
}

func boolFlag(val *bool, longname, shortname string, init bool, help string) {
	flag.BoolVar(val, longname, init, help)
	flag.BoolVar(val, shortname, init, help)
}

func (u *Unilog) fillDefaults() {
	if u.Version == "" {
		u.Version = Version
	}
	if u.BufferLines == 0 {
		u.BufferLines = DefaultBuffer
	}
}

func (u *Unilog) addFlags() {
	stringFlag(&u.Name, "name", "a", "", "Name of logged program")
	boolFlag(&u.Verbose, "verbose", "v", false, "Echo lines to stdout")
	flag.StringVar(&u.MailFrom, "mailfrom", u.MailFrom, "Address to send error emails from")
	flag.StringVar(&u.MailTo, "mailto", u.MailTo, "Address to send error emails to")
	flag.StringVar(&u.SentryDSN, "sentrydsn", u.SentryDSN, "Sentry DSN to send errors to")
	flag.StringVar(&u.StatsdAddress, "statsdaddress", "127.0.0.1:8200", "Address to send statsd metrics to")
	flag.StringVar(&clevels.AusterityFile, "austerityfile", clevels.AusterityFile, "(optional) Location of file to read austerity level from")
	stringFlag(&statstags, "statstags", "s", "", `(optional) tags to include with all statsd metrics (e.g. "foo:bar,baz:quz")`)
}

var emailTemplate = template.Must(template.New("email").Parse(`From: {{.From}}
To: {{.To}}
Subject: [unilog] {{.Name}} could not {{.Action}}

Hi there,

This is unilog reporting from {{.Hostname}}. I'm sad to report that
{{.Name}} is having some troubles writing to its log. I got caught up
trying to log a line to {{.Target}}.

To avoid spamming you, I'm going to shut up for an hour. Please fix me.

{{.Error}}
--
Sent from unilog {{.Version}}
`))

const (
	// Version is the Unilog version. Reported in emails and in
	// response to --version on the command line. Can be overriden
	// by the Version field in a Unilog object.
	Version = "0.3"
	// DefaultBuffer is the default size (in lines) of the
	// in-process line buffer
	DefaultBuffer = 1 << 12
)

var Stats *statsd.Client

func readlines(in io.Reader, bufsize int, shutdown chan struct{}) (<-chan string, <-chan error) {
	linec := make(chan string, bufsize)
	errc := make(chan error, 1)

	u := NewUnilogReader(in, shutdown)
	r := bufio.NewReader(u)

	go func() {
		var err error
		var s string

		for err == nil {
			s, err = r.ReadString('\n')
			if s != "" {
				s = strings.TrimRight(s, "\n")
				linec <- s
				if Stats != nil {
					Stats.Count("unilog.bytes", int64(len(s)), nil, .1)
				}
			}
		}

		if err != io.EOF {
			errc <- err
		}
		close(linec)
	}()

	return linec, errc
}

func (u *Unilog) reopen() error {
	if u.target == "-" {
		u.file = os.Stdout
		return nil
	}

	if u.file != nil {
		u.file.Close()
		u.file = nil
	}

	var e error
	if u.file, e = os.OpenFile(u.target, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644); e != nil {
		u.file = nil
		return e
	}
	return nil
}

func (u *Unilog) format(line string) string {
	for _, filter := range u.Filters {
		if filter != nil {
			line = filter(line)
		}
	}
	return fmt.Sprintf("[%s] %s\n", time.Now().Format("2006-01-02 15:04:05.000000"), line)
}

func (u *Unilog) logLine(line string) {
	formatted := u.format(line)
	if u.Verbose {
		defer io.WriteString(os.Stdout, formatted)
	}

	var e error
	if u.file == nil {
		e = u.reopen()
	}
	if e != nil {
		u.handleError("reopen_file", e)
		return
	}
	_, e = io.WriteString(u.file, formatted)
	if e != nil {
		u.handleError("write_to_log", e)
	} else {
		u.b.broken = false
	}
}

func (u *Unilog) run() {
	for {
		select {
		case e := <-u.errs:
			if e != nil && e != io.EOF {
				panic(e)
			} else {
				return
			}
		case <-u.sigReopen:
			u.reopen()
		case <-u.sigTerm:
			if u.shutdown != nil {
				close(u.shutdown)
				u.shutdown = nil
			}
		case line, ok := <-u.lines:
			if !ok {
				return
			}
			u.logLine(line)
		}
	}
}

func (u *Unilog) handleError(action string, e error) {
	if !u.b.broken {
		u.b.broken = true
		u.b.at = time.Now()
		u.b.count = 0
	} else if time.Since(u.b.at) > time.Hour {
		u.b.at = time.Now()
		u.b.count = 0
	}

	message := fmt.Sprintf("Could not %s: %s", action, e.Error())

	if terminal.IsTerminal(1) {
		fmt.Printf("%s\n", message)
		return
	}

	if Stats != nil {
		emsg := fmt.Sprintf("err_action:%s", action)
		Stats.Count("unilog.errors_total", 1, []string{emsg}, 1)
	}

	if u.b.count == 0 && u.SentryDSN != "" {
		hostname, _ := os.Hostname()
		keys := map[string]string{
			"Hostname": hostname,
			"Action":   action,
			"Name":     u.Name,
			"Target":   u.target,
			"Error":    e.Error(),
			"Version":  Version,
		}
		raven.CaptureError(e, keys)
	}

	if u.b.count == 0 && u.MailFrom != "" && u.MailTo != "" {
		message := new(bytes.Buffer)
		hostname, _ := os.Hostname()
		emailTemplate.Execute(message, map[string]string{
			"Hostname": hostname,
			"From":     u.MailFrom,
			"To":       u.MailTo,
			"Action":   action,
			"Name":     u.Name,
			"Target":   u.target,
			"Error":    e.Error(),
			"Version":  Version,
		})
		cmd := exec.Command("sendmail", "-t")
		cmd.Stdin = message
		cmd.Run()
	}

	u.b.count++
}

func (u *Unilog) Main() {
	u.fillDefaults()

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [options] dstfile\n", os.Args[0])
		flag.PrintDefaults()
	}

	u.addFlags()
	var flagVersion bool
	boolFlag(&flagVersion, "version", "V", false, "Print the version number and exit")

	flag.Parse(true)

	if flagVersion {
		fmt.Printf("This is unilog v%s\n", Version)
		return
	}
	args := flag.Args()
	if len(args) != 1 {
		flag.Usage()
		os.Exit(1)
	}

	reopen := make(chan os.Signal, 2)
	signal.Notify(reopen, syscall.SIGALRM, syscall.SIGHUP)
	u.sigReopen = reopen

	term := make(chan os.Signal, 2)
	signal.Notify(term, syscall.SIGTERM, syscall.SIGINT)
	u.sigTerm = term

	u.shutdown = make(chan struct{})
	u.target = flag.Arg(0)
	u.reopen()

	u.lines, u.errs = readlines(os.Stdin, u.BufferLines, u.shutdown)

	fileName := u.target

	Stats, _ = statsd.New(u.StatsdAddress)

	Stats.Tags = append(Stats.Tags, fmt.Sprintf("file_name:%s", fileName))
	if statstags != "" {
		Stats.Tags = append(Stats.Tags, strings.Split(statstags, ",")...)
	}

	clevels.Stats = Stats

	_ = raven.SetDSN(u.SentryDSN)

	u.run()
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"net/http"
	"os"
	"strings"
)

type TokenType int

const (
	POPEN TokenType = iota
	PCLOSE
	LINE
)

type Token struct {
	Type  TokenType
	Value string
}

func main() {
	ch := make(chan Token)
	out := make(chan string)

	var f *os.File
	f, err := os.Open("slides.txt")
	if err != nil {
		f = os.Stdin
	}

	go parse(ch, out)

	err = scan(f, ch)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	f.Close()

	data := <-out

	http.Handle("/", slides(toHtml(data)))
	err = http.ListenAndServe(":3001", nil)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func slides(data string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, data)
	})
}

func toHtml(data string) string {
	result := `<!doctype html>

<title>Slides</title>
{{style}}

{{data}}

`

	style := ``

	result = strings.Replace(result, "{{style}}", style, -1)
	result = strings.Replace(result, "{{data}}", data, -1)

	return result
}

func scan(f *os.File, ch chan Token) error {
	scanner := bufio.NewScanner(f)
	scanner.Split(bufio.ScanLines)

	ch <- Token{POPEN, ""}

	for scanner.Scan() {
		line := scanner.Text()

		switch line {
		case "":
			ch <- Token{PCLOSE, ""}
			ch <- Token{POPEN, ""}
		default:
			ch <- Token{LINE, line}

		}
	}

	if scanner.Err() == nil {
		ch <- Token{PCLOSE, ""}
	}

	close(ch)

	return nil
}

func parse(ch chan Token, out chan string) {
	s := ""
	for token := range ch {
		switch token.Type {
		case POPEN:
			s += fmt.Sprintf("
\n")
		case PCLOSE:
			s += fmt.Sprintf("
\n")
		case LINE:
			s += fmt.Sprintf("\t%s
\n", token.Value)
		}
	}
	out <- s
}
<commit_msg>Use command line argument for input file.<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"net/http"
	"os"
	"strings"
)

type TokenType int

const (
	POPEN TokenType = iota
	PCLOSE
	LINE
)

type Token struct {
	Type  TokenType
	Value string
}

func main() {
	flag.Parse()

	ch := make(chan Token)
	out := make(chan string)

	var f *os.File
	var err error

	if flag.NArg() < 1 || flag.Arg(0) == "-" {
		f = os.Stdin
	} else {
		path := flag.Arg(0)
		f, err = os.Open(path)
		if err != nil {
			fmt.Printf("%s: %v\n", path, err)
			os.Exit(1)
		}
	}

	go parse(ch, out)

	err = scan(f, ch)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	f.Close()

	data := <-out

	http.Handle("/", slides(toHtml(data)))
	fmt.Println("Slides are available at http://localhost:3001/")
	err = http.ListenAndServe(":3001", nil)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func slides(data string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, data)
	})
}

func toHtml(data string) string {
	result := `<!doctype html>

<title>Slides</title>
{{style}}

{{data}}

`

	style := ``

	result = strings.Replace(result, "{{style}}", style, -1)
	result = strings.Replace(result, "{{data}}", data, -1)

	return result
}

func scan(f *os.File, ch chan Token) error {
	scanner := bufio.NewScanner(f)
	scanner.Split(bufio.ScanLines)

	ch <- Token{POPEN, ""}

	for scanner.Scan() {
		line := scanner.Text()

		switch line {
		case "":
			ch <- Token{PCLOSE, ""}
			ch <- Token{POPEN, ""}
		default:
			ch <- Token{LINE, line}

		}
	}

	if scanner.Err() == nil {
		ch <- Token{PCLOSE, ""}
	}

	close(ch)

	return nil
}

func parse(ch chan Token, out chan string) {
	s := ""
	for token := range ch {
		switch token.Type {
		case POPEN:
			s += fmt.Sprintf("
\n")
		case PCLOSE:
			s += fmt.Sprintf("
\n")
		case LINE:
			s += fmt.Sprintf("\t%s
\n", token.Value)
		}
	}
	out <- s
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"github.com/cptaffe/lang/optim"
	"github.com/cptaffe/lang/parser"
	"io"
	"log"
	"os"
)

type Program struct {
	Str string // string of input
	Len int    // length of tree last time
}

func Compute(s *Program) string {
	t := optim.Eval(parser.Parse(s.Str))
	if t == nil {
		return "error..."
	}
	var str string
	app := ", "
	for i := 0; i < (len(t.Sub) - s.Len); i++ {
		str += t.Sub[s.Len+i].String()
		if i != (len(t.Sub)-s.Len)-1 {
			str += app
		}
	}
	str = fmt.Sprintf("result: {%s}", str)
	s.Len = len(t.Sub) // set new len
	return str
}

// Read input from stdin & output result to stdout
func readFile() string {
	r := bufio.NewReader(os.Stdin)
	var str string
	for {
		b, _, err := r.ReadLine()
		if err != nil {
			if err == io.EOF {
			} else {
				log.Print(err)
			}
		}
		if string(b) == "exit" {
			os.Exit(0)
		} else if string(b) == "exec" {
			return str
		} else {
			str += string(b)
		}
	}
}

func main() {
	p := new(Program)
	p.Str = readFile()
	Compute(p);
}
<commit_msg>Updated file stdin interface<commit_after>package main

import (
	"bufio"
	"fmt"
	"github.com/cptaffe/lang/optim"
	"github.com/cptaffe/lang/parser"
	"io"
	"log"
	"os"
)

type Program struct {
	Str string // string of input
	Len int    // length of tree last time
}

func Compute(s *Program) string {
	t := optim.Eval(parser.Parse(s.Str, "input"))
	if t == nil {
		return "error..."
	}
	var str string
	app := ", "
	for i := 0; i < (len(t.Sub) - s.Len); i++ {
		str += t.Sub[s.Len+i].String()
		if i != (len(t.Sub)-s.Len)-1 {
			str += app
		}
	}
	str = fmt.Sprintf("result: {%s}", str)
	s.Len = len(t.Sub) // set new len
	return str
}

// Read input from stdin & output result to stdout
func readFile(file io.Reader) string {
	r := bufio.NewReader(file)
	var str string
	for {
		b, _, err := r.ReadLine()
		if err != nil {
			if err == io.EOF {
				return str
			} else {
				log.Print(err)
			}
		}
		if string(b) == "exit" {
			os.Exit(0)
		} else if string(b) == "exec" {
			return str
		} else {
			str += string(b)
		}
	}
}

func main() {
	p := new(Program)
	if len(os.Args) > 1 {
		file, err := os.Open(os.Args[1])
		if err != nil {
			log.Fatal(err)
		}
		p.Str = readFile(file)
	} else {
		p.Str = readFile(os.Stdin)
	}
	Compute(p);
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"runtime"
	"testing"

	"github.com/mjibson/go-dsp/fft"
)

var (
	numprocs int
	MAX_N int
)

func main() {
	var r testing.BenchmarkResult

	for MAX_N = 1 << 20; MAX_N > 0; MAX_N >>= 1 {
		for n := 1; n <= runtime.NumCPU(); n++ {
			numprocs = n

			for i := MAX_N; i > 0; i >>= 1 {
				fft.MP_MIN_BLOCKSIZE = i

				fft.MP_METHOD = fft.MP_METHOD_NORMAL
				r = testing.Benchmark(BenchmarkFFT)
				fmt.Printf("%20s %12d %2d %10d %12d\n", "normal", MAX_N, numprocs, i, r.NsPerOp())

				fft.MP_METHOD = fft.MP_METHOD_WAIT_GROUP
				r = testing.Benchmark(BenchmarkFFT)
				fmt.Printf("%20s %12d %2d %10d %12d\n", "waitgroups", MAX_N, numprocs, i, r.NsPerOp())

				fft.MP_METHOD = fft.MP_METHOD_WORKER_POOLS
				for j := 1; j <= n * 2; j++ {
					fft.WORKER_POOLS_COUNT = j
					r = testing.Benchmark(BenchmarkFFT)
					fmt.Printf("%17s-%02d %12d %2d %10d %12d\n", "workerpools", j, MAX_N, numprocs, i, r.NsPerOp())
				}
			}
		}
	}
}

func BenchmarkFFT(b *testing.B) {
	b.StopTimer()

	runtime.GOMAXPROCS(numprocs)

	N := MAX_N
	a := make([]complex128, N)
	for i := 0; i < N; i++ {
		a[i] = complex(float64(i)/float64(N), 0)
	}

	fft.EnsureRadix2Factors(N)

	b.StartTimer()

	for i := 0; i < b.N; i++ {
		fft.FFT(a)
	}
}
<commit_msg>Working test for current version<commit_after>package main

import (
	"fmt"
	"runtime"
	"testing"

	"github.com/mjibson/go-dsp/fft"
)

var (
	numprocs int
	MAX_N int
)

func main() {
	var r testing.BenchmarkResult

	for MAX_N = 1 << 20; MAX_N > 0; MAX_N >>= 1 {
		for n := 1; n <= runtime.NumCPU(); n++ {
			numprocs = n

			for j := 1; j <= n * 2; j++ {
				fft.WORKER_POOL_SIZE = j
				r = testing.Benchmark(BenchmarkFFT)
				fmt.Printf("WPS: %02d N: %12d procs: %2d ns: %12d\n", j, MAX_N, numprocs, r.NsPerOp())
			}
		}
	}
}

func BenchmarkFFT(b *testing.B) {
	b.StopTimer()

	runtime.GOMAXPROCS(numprocs)

	N := MAX_N
	a := make([]complex128, N)
	for i := 0; i < N; i++ {
		a[i] = complex(float64(i)/float64(N), 0)
	}

	fft.EnsureRadix2Factors(N)

	b.StartTimer()

	for i := 0; i < b.N; i++ {
		fft.FFT(a)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"

	"github.com/mitchellh/cli"
	"github.com/slackpad/consul-live/tester"
)

func main() {
	log.SetPrefix("@@@ ==> ")

	c := cli.NewCLI("consul-live", "0.0.1")
	c.Args = os.Args[1:]
	c.Commands = map[string]cli.CommandFactory{
		"load":    tester.LoadCommandFactory,
		"upgrade": tester.UpgradeCommandFactory,
	}

	exitStatus, err := c.Run()
	if err != nil {
		log.Println(err)
	}
	os.Exit(exitStatus)
}
<commit_msg>Adds a comment about logs.<commit_after>package main

import (
	"log"
	"os"

	"github.com/mitchellh/cli"
	"github.com/slackpad/consul-live/tester"
)

func main() {
	// This helps us find our logs vs. those from Consul running under our
	// control.
	log.SetPrefix("@@@ ==> ")

	c := cli.NewCLI("consul-live", "0.0.1")
	c.Args = os.Args[1:]
	c.Commands = map[string]cli.CommandFactory{
		"load":    tester.LoadCommandFactory,
		"upgrade": tester.UpgradeCommandFactory,
	}

	exitStatus, err := c.Run()
	if err != nil {
		log.Println(err)
	}
	os.Exit(exitStatus)
}
<|endoftext|>