<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"sync"
	"sync/atomic"

	"github.com/caelifer/dups/balancer"
	"github.com/caelifer/dups/mapreduce"
)

// Dup type describes found duplicat file
type Dup struct {
	Count int    // Number of identical copies for the hash
	Size  int64  // File size
	Hash  string // Crypto signature
	Path  string // Paths with matching signatures
}

// Value implements mapreduce.Value interface
func (d Dup) Value() interface{} {
	return d
}

func (d Dup) String() string {
	return fmt.Sprintf("%d:%s:%q:%d", d.Count, d.Hash, d.Path, d.Size)
}

// Global stats for activity report
var stats struct {
	TotlalNodes   uint64
	TotalCopies   uint64
	TotalFreeSize uint64
}

// Global pool manager
var WorkQueue = make(chan balancer.Request)

// Start balancer on the background
var _ = balancer.New(WorkQueue)

func main() {
	// Use all available CPU cores
	runtime.GOMAXPROCS(runtime.NumCPU())

	// Process command line params
	paths := os.Args[1:]

	if len(paths) == 0 {
		// Default is current directory
		paths = []string{"."}
	}

	// Channel interfaces between Map() and Reduce() functions
	var keyValChan <-chan mapreduce.KeyValue
	var valChan <-chan mapreduce.Value

	// Start map-reduce
	keyValChan = mapreduce.Map(makeFileSizeMapFnWithPaths(paths))
	valChan = mapreduce.Reduce(keyValChan, reduceByFileSize)

	keyValChan = mapreduce.Map(makeFileHashMapFnFrom(valChan, true))
	valChan = mapreduce.Reduce(keyValChan, reduceByHash)

	keyValChan = mapreduce.Map(makeFileHashMapFnFrom(valChan, false))
	valChan = mapreduce.Reduce(keyValChan, reduceByHash)

	// Final reduce before reporting
	dups := make(chan Dup)
	go func(out chan<- Dup) {
		byHash := make(map[string][]Node)

		for x := range valChan {
			n := x.Value().(Node) // Type assert

			// Aggregate
			if v, ok := byHash[n.Hash]; ok {
				// Found node with the same file size
				byHash[n.Hash] = append(v, n)
			} else {
				byHash[n.Hash] = []Node{n}
			}
		}

		// Reduce
		for hash, nodes := range byHash {
			count := len(nodes)
			if count > 1 {
				// Update free size stats
				atomic.AddUint64(&stats.TotalFreeSize, uint64(nodes[0].Size*int64(count-1)))

				for _, node := range nodes {
					// Update dups number stats
					atomic.AddUint64(&stats.TotalCopies, uint64(1))
					out <- Dup{Count: count, Size: node.Size, Hash: hash, Path: node.Path}
				}
			}
		}
		close(out)
	}(dups)

	// Report
	for d := range dups {
		fmt.Println(d)
	}
	// Stats report
	fmt.Printf("Stats: examined %d files, found %d dups, total free size: %.2fGB\n",
		stats.TotlalNodes, stats.TotalCopies, float64(stats.TotalFreeSize)/(1024*1024*1024))
}

// makeFileSizeMapFnWithPaths
func makeFileSizeMapFnWithPaths(paths []string) mapreduce.MapFn {
	return func(out chan<- mapreduce.KeyValue) {

		// Process all command line paths
		for _, p := range paths {
			err := filepath.Walk(p, func(path string, info os.FileInfo, err error) error {
				// Handle passthrough error
				if err != nil {
					log.Println("WARN", err)
					return nil
				}

				// Only process simple files
				if IsFile(info) {
					// Increase seen files counter
					atomic.AddUint64(&stats.TotlalNodes, 1)
					size := info.Size()
					out <- mapreduce.NewKVType(
						mapreduce.KeyTypeFromInt64(size),
						Node{Path: path, Size: size},
					)
				}
				return nil
			})

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

// reduceByFileSize custom function to filter files by size
func reduceByFileSize(out chan<- mapreduce.Value, in <-chan mapreduce.KeyValue) {
	bySize := make(map[mapreduce.KeyType][]mapreduce.Value)

	for x := range in {
		size := x.Key()          // Get key
		node := x.Value().(Node) // Assert type

		// Add values to the map for aggregation
		if v, ok := bySize[size]; ok {
			// Found node with the same file size
			bySize[size] = append(v, node)
		} else {
			bySize[size] = []mapreduce.Value{node}
		}
	}

	// Send potential duplicates one-by-one to the downstream processing
	for _, nodes := range bySize {
		// Found cluster
		if len(nodes) > 1 {
			for _, n := range nodes {
				out <- n
			}
		}
	}
}

func makeFileHashMapFnFrom(in <-chan mapreduce.Value, fast bool) mapreduce.MapFn {
	return func(out chan<- mapreduce.KeyValue) {
		var wg sync.WaitGroup
		for x := range in {
			node := x.Value().(Node) // Assert type

			// Add to wait group
			wg.Add(1)

			// Calculate hash using balancer
			go func(n Node) {
				WorkQueue <- func() {
					defer wg.Done() // Signal done

					// Default hash value for files < 1024 if fast is true
					hash := "0h"

					// Little optimization to avoid calculating fast hash on small files
					if !fast || node.Size > blockSize {
						// Always calculate hash if fast == false or file size > blockSize
						hash = n.calculateHash(fast) // Fast hash calculation - SHA1 of first 1024 bytes
					}

					// Don't process files for which we failed to calculate SHA1 hash
					if hash == "" {
						log.Printf("WARN Unable calculate SHA1 hash for %q\n", node.Path)
						return
					}

					out <- mapreduce.NewKVType(
						mapreduce.KeyTypeFromString(hash),
						n,
					)
				}
			}(node)
		}
		// Wait for all results be submitted
		wg.Wait()
	}
}

func reduceByHash(out chan<- mapreduce.Value, in <-chan mapreduce.KeyValue) {
	byHash := make(map[mapreduce.KeyType][]Node)

	for x := range in {
		hash := x.Key()
		node := x.Value().(Node) // Assert type

		if v, ok := byHash[hash]; ok {
			// Found node with the same file size
			byHash[hash] = append(v, node)
		} else {
			byHash[hash] = []Node{node}
		}
	}

	// Send out aggregeted results
	for hash, nodes := range byHash {
		if len(nodes) > 1 {
			// Send output for potential duplicates
			for _, node := range nodes {
				node.Hash = hash.String()
				out <- mapreduce.Value(node)
			}
		}
	}
}

func IsFile(fi os.FileInfo) bool {
	return fi.Mode()&os.ModeType == 0
}
<commit_msg>- Avoid processing multiple nodes with the same path<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"sync"
	"sync/atomic"

	"github.com/caelifer/dups/balancer"
	"github.com/caelifer/dups/mapreduce"
)

// Dup type describes found duplicat file
type Dup struct {
	Count int    // Number of identical copies for the hash
	Size  int64  // File size
	Hash  string // Crypto signature
	Path  string // Paths with matching signatures
}

// Value implements mapreduce.Value interface
func (d Dup) Value() interface{} {
	return d
}

func (d Dup) String() string {
	return fmt.Sprintf("%d:%s:%q:%d", d.Count, d.Hash, d.Path, d.Size)
}

// Global stats for activity report
var stats struct {
	TotlalNodes   uint64
	TotalCopies   uint64
	TotalFreeSize uint64
}

// Global pool manager
var WorkQueue = make(chan balancer.Request)

// Start balancer on the background
var _ = balancer.New(WorkQueue)

func main() {
	// Use all available CPU cores
	runtime.GOMAXPROCS(runtime.NumCPU())

	// Process command line params
	paths := os.Args[1:]

	if len(paths) == 0 {
		// Default is current directory
		paths = []string{"."}
	}

	// Channel interfaces between Map() and Reduce() functions
	var keyValChan <-chan mapreduce.KeyValue
	var valChan <-chan mapreduce.Value

	// Start map-reduce and remove duplicate path nodes
	keyValChan = mapreduce.Map(makeNodeMapFnWithPaths(paths))
	valChan = mapreduce.Reduce(keyValChan, reduceByFileName)

	// Map by filesize
	keyValChan = mapreduce.Map(makeFileSizeMapFnFrom(valChan))
	valChan = mapreduce.Reduce(keyValChan, reduceByFileSize)

	// Map by fast SHA1 hash (first 1024 bytes)
	keyValChan = mapreduce.Map(makeFileHashMapFnFrom(valChan, true))
	valChan = mapreduce.Reduce(keyValChan, reduceByHash)

	// Map by SHA1 hash (full file hashing)
	keyValChan = mapreduce.Map(makeFileHashMapFnFrom(valChan, false))
	valChan = mapreduce.Reduce(keyValChan, reduceByHash)

	// Final reduce before reporting
	dups := make(chan Dup)
	go func(out chan<- Dup) {
		byHash := make(map[string][]Node)

		for x := range valChan {
			n := x.Value().(Node) // Type assert

			// Aggregate
			if v, ok := byHash[n.Hash]; ok {
				// Found node with the same file size
				byHash[n.Hash] = append(v, n)
			} else {
				byHash[n.Hash] = []Node{n}
			}
		}

		// Reduce
		for hash, nodes := range byHash {
			count := len(nodes)
			if count > 1 {
				// Update free size stats
				atomic.AddUint64(&stats.TotalFreeSize, uint64(nodes[0].Size*int64(count-1)))

				for _, node := range nodes {
					// Update dups number stats
					atomic.AddUint64(&stats.TotalCopies, uint64(1))
					out <- Dup{Count: count, Size: node.Size, Hash: hash, Path: node.Path}
				}
			}
		}
		close(out)
	}(dups)

	// Report
	for d := range dups {
		fmt.Println(d)
	}
	// Stats report
	fmt.Printf("Stats: examined %d files, found %d dups, total free size: %.2fGB\n",
		stats.TotlalNodes, stats.TotalCopies, float64(stats.TotalFreeSize)/(1024*1024*1024))
}

// makeNodeMapFnWithPaths
func makeNodeMapFnWithPaths(paths []string) mapreduce.MapFn {
	return func(out chan<- mapreduce.KeyValue) {

		// Process all command line paths
		for _, p := range paths {
			err := filepath.Walk(p, func(path string, info os.FileInfo, err error) error {
				// Handle passthrough error
				if err != nil {
					log.Println("WARN", err)
					return nil
				}

				// Only process simple files
				if IsFile(info) {
					size := info.Size()
					out <- mapreduce.NewKVType(
						mapreduce.KeyTypeFromString(path),
						Node{Path: path, Size: size},
					)
				}
				return nil
			})

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

// reduceByFileName custom function remove nodes with duplicate paths
func reduceByFileName(out chan<- mapreduce.Value, in <-chan mapreduce.KeyValue) {
	bySize := make(map[mapreduce.KeyType]mapreduce.Value)

	for x := range in {
		path := x.Key()          // Get key
		node := x.Value().(Node) // Assert type

		// Add values to the map for aggregation, skip nodes with the same path
		if _, ok := bySize[path]; !ok {
			bySize[path] = node
		}
	}

	// Send potential duplicates one-by-one to the downstream processing
	for _, node := range bySize {
		// Increase seen files counter
		atomic.AddUint64(&stats.TotlalNodes, 1)

		out <- node
	}
}

// Very simple function to map nodes by size
func makeFileSizeMapFnFrom(in <-chan mapreduce.Value) mapreduce.MapFn {
	return func(out chan<- mapreduce.KeyValue) {
		for x := range in {
			node := x.Value().(Node) // Assert type

			out <- mapreduce.NewKVType(
				mapreduce.KeyTypeFromInt64(node.Size),
				node,
			)
		}
	}
}

// reduceByFileSize custom function to filter files by size
func reduceByFileSize(out chan<- mapreduce.Value, in <-chan mapreduce.KeyValue) {
	bySize := make(map[mapreduce.KeyType][]mapreduce.Value)

	for x := range in {
		size := x.Key()          // Get key
		node := x.Value().(Node) // Assert type

		// Add values to the map for aggregation
		if v, ok := bySize[size]; ok {
			// Found node with the same file size
			bySize[size] = append(v, node)
		} else {
			bySize[size] = []mapreduce.Value{node}
		}
	}

	// Send potential duplicates one-by-one to the downstream processing
	for _, nodes := range bySize {
		// Found cluster
		if len(nodes) > 1 {
			for _, n := range nodes {
				out <- n
			}
		}
	}
}

func makeFileHashMapFnFrom(in <-chan mapreduce.Value, fast bool) mapreduce.MapFn {
	return func(out chan<- mapreduce.KeyValue) {
		var wg sync.WaitGroup
		for x := range in {
			node := x.Value().(Node) // Assert type

			// Add to wait group
			wg.Add(1)

			// Calculate hash using balancer
			go func(n Node) {
				WorkQueue <- func() {
					defer wg.Done() // Signal done

					// Default hash value for files < 1024 if fast is true
					hash := "0h"

					// Little optimization to avoid calculating fast hash on small files
					if !fast || node.Size > blockSize {
						// Always calculate hash if fast == false or file size > blockSize
						hash = n.calculateHash(fast) // Fast hash calculation - SHA1 of first 1024 bytes
					}

					// Don't process files for which we failed to calculate SHA1 hash
					if hash == "" {
						log.Printf("WARN Unable calculate SHA1 hash for %q\n", node.Path)
						return
					}

					out <- mapreduce.NewKVType(
						mapreduce.KeyTypeFromString(hash),
						n,
					)
				}
			}(node)
		}
		// Wait for all results be submitted
		wg.Wait()
	}
}

func reduceByHash(out chan<- mapreduce.Value, in <-chan mapreduce.KeyValue) {
	byHash := make(map[mapreduce.KeyType][]Node)

	for x := range in {
		hash := x.Key()
		node := x.Value().(Node) // Assert type

		if v, ok := byHash[hash]; ok {
			// Found node with the same file size
			byHash[hash] = append(v, node)
		} else {
			byHash[hash] = []Node{node}
		}
	}

	// Send out aggregeted results
	for hash, nodes := range byHash {
		if len(nodes) > 1 {
			// Send output for potential duplicates
			for _, node := range nodes {
				node.Hash = hash.String()
				out <- mapreduce.Value(node)
			}
		}
	}
}

func IsFile(fi os.FileInfo) bool {
	return fi.Mode()&os.ModeType == 0
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"runtime"
	"unicode/utf8"

	"github.com/vheon/picker/terminal"
	"github.com/vheon/picker/tty"
)

func appendChar(s string, b rune) string {
	return s + string(b)
}

func backspace(s string) string {
	if l := len(s); l > 0 {
		_, size := utf8.DecodeLastRuneInString(s)
		return s[:l-size]
	}
	return s
}

func readAllCandidates(r io.Reader) []Candidate {
	scanner := bufio.NewScanner(r)
	var lines []Candidate
	for scanner.Scan() {
		lines = append(lines, NewCandidate(scanner.Text()))
	}
	return lines
}

func handle_input(picker *Picker, view *View, key rune) *View {
	switch key {
	case terminal.Ctrl_N:
		view.Down()
	case terminal.Ctrl_P:
		view.Up()
	case terminal.Backspace:
		view = picker.Answer(backspace(view.Query))
	case terminal.LF:
		view.Done = true
		view.ClearPrompt()
	case terminal.Ctrl_U, terminal.Ctrl_W:
		view = picker.Answer("")
	default:
		view = picker.Answer(appendChar(view.Query, key))
	}
	return view
}

const visibleRows = 20

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	tty := tty.New()
	tty.Stty("-echo", "-icanon")
	defer tty.Restore()

	terminal := terminal.NewTerminal(tty)

	picker := NewPicker(readAllCandidates(os.Stdin), visibleRows)
	view := picker.Answer("")
	terminal.MakeRoom(view.Height)
	view.DrawOnTerminal(terminal)
	for !view.Done {
		view = handle_input(picker, view, tty.ReadRune())
		view.DrawOnTerminal(terminal)
	}
	fmt.Println(view.Selected())
}
<commit_msg>Specify height by flag<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"os"
	"runtime"
	"unicode/utf8"

	"github.com/vheon/picker/terminal"
	"github.com/vheon/picker/tty"
)

func appendChar(s string, b rune) string {
	return s + string(b)
}

func backspace(s string) string {
	if l := len(s); l > 0 {
		_, size := utf8.DecodeLastRuneInString(s)
		return s[:l-size]
	}
	return s
}

func readAllCandidates(r io.Reader) []Candidate {
	scanner := bufio.NewScanner(r)
	var lines []Candidate
	for scanner.Scan() {
		lines = append(lines, NewCandidate(scanner.Text()))
	}
	return lines
}

func handle_input(picker *Picker, view *View, key rune) *View {
	switch key {
	case terminal.Ctrl_N:
		view.Down()
	case terminal.Ctrl_P:
		view.Up()
	case terminal.Backspace:
		view = picker.Answer(backspace(view.Query))
	case terminal.LF:
		view.Done = true
		view.ClearPrompt()
	case terminal.Ctrl_U, terminal.Ctrl_W:
		view = picker.Answer("")
	default:
		view = picker.Answer(appendChar(view.Query, key))
	}
	return view
}

var visibleRows = flag.Int("h", 20, "Number of visible candidates")

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	flag.Parse()

	tty := tty.New()
	tty.Stty("-echo", "-icanon")
	defer tty.Restore()

	terminal := terminal.NewTerminal(tty)

	if *visibleRows > terminal.Height {
		*visibleRows = terminal.Height - 1
	}
	if *visibleRows < 1 {
		*visibleRows = 1
	}

	picker := NewPicker(readAllCandidates(os.Stdin), *visibleRows)
	view := picker.Answer("")
	terminal.MakeRoom(view.Height)
	view.DrawOnTerminal(terminal)
	for !view.Done {
		view = handle_input(picker, view, tty.ReadRune())
		view.DrawOnTerminal(terminal)
	}
	fmt.Println(view.Selected())
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/nsf/termbox-go"
	"time"
)

func draw_guy(g guy) {
	termbox.SetCell(g.x, g.y, '☺', termbox.Attribute(15), termbox.Attribute(7))
	termbox.SetCell(g.x, g.y+1, '✝', termbox.Attribute(15), termbox.Attribute(7))
	termbox.SetCell(g.x, g.y+2, '∩', termbox.Attribute(15), termbox.Attribute(7))
}

func draw_world() {
	world := [30]string{}

	world[0] = "                                                                                "
	world[1] = "                                                                                "
	world[2] = "                                                                                "
	world[3] = "                                                                               !"
	world[4] = "                                                                      ----------"
	world[5] = "                                                                     |          "
	world[6] = "                                                                     |          "
	world[7] = "                                                                     |          "
	world[8] = "                                                               -----            "
	world[9] = "                                                             |                  "
	world[10] = "                                                            |                   "
	world[11] = "                                                            |                   "
	world[12] = "                                                       -----                    "
	world[13] = "                                                      |                         "
	world[14] = "                                                      |                         "
	world[15] = "                                                      |                         "
	world[16] = "                                                -----                           "
	world[17] = "                                              |                                 "
	world[18] = "                                              |                                 "
	world[19] = "                                              |                                 "
	world[20] = "                                              |                                 "
	world[21] = "                                           ---                                  "
	world[22] = "                           |              |                                     "
	world[23] = "                           |              |                                     "
	world[24] = "                           |              |                                     "
	world[25] = "            ------------------------------                                      "
	world[26] = "                                                                                "
	world[27] = "                                                                                "
	world[28] = "                                                                                "
	world[29] = "--------------------------------------------------------------------------------"

	colour := func(x int, y int, world [30]string) int {
		switch world[y][x] {
		case ' ':
			return 7
		case '-':
			return 3
		case '|':
			return 3
		case '!':
			return 2
		}
		return 0
	}
	colour(0, 0, world)
	width, height := 80, 30
	for y := 0; y < height; y++ {
		for x := 0; x < width; x++ {
			termbox.SetCell(x, y, rune(world[y][x]), termbox.Attribute(colour(x, y, world)), termbox.Attribute(colour(x, y, world)))
		}
	}
}

func draw_all(g guy) {
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
	draw_world()
	draw_guy(g)

	termbox.Flush()
}

type guy struct {
	x          int
	y          int
	jumpCycles int
}

func main() {
	err := termbox.Init()
	if err != nil {
		panic(err)
	}

	ticker := time.NewTicker(100 * time.Millisecond)
	quit := make(chan string)

	event_queue := make(chan termbox.Event)
	go func() {
		for {
			event_queue <- termbox.PollEvent()
		}
	}()

	redrawRoutine := make(chan guy)
	go func(r chan guy) {
		for {
			select {
			case g := <-r:
				draw_all(g)
			}
		}

	}(redrawRoutine)

	go func(t *time.Ticker, gs chan guy) {
		g := guy{x: 10, y: 0, jumpCycles: 0}

		for {
			select {
			case <-t.C:
				if g.jumpCycles == 0 {
					w, h := termbox.Size()
					if g.x+(g.y+3)*w < w*h {
						c := termbox.CellBuffer()[g.x+(g.y+3)*w]
						if c.Ch == 32 {
							g.y++
							gs <- g
						}
					}
				} else if g.jumpCycles > 0 {
					w, _ := termbox.Size()
					if g.x+(g.y-1)*w > 0 {
						c := termbox.CellBuffer()[g.x+(g.y-1)*w]
						if c.Ch == 32 {
							g.y--
							gs <- g
						}
					}
					g.jumpCycles--
				}
			case ev := <-event_queue:
				if ev.Type == termbox.EventKey {
					switch ev.Key {
					case termbox.KeyEsc:
						quit <- "Goodbye!"
					case termbox.KeyArrowLeft:
						w, h := 80, 30
						if g.x-1+(g.y+2)*g.x < w*h {
							c1 := termbox.CellBuffer()[g.x-1+g.y*w]
							c2 := termbox.CellBuffer()[g.x-1+(g.y+1)*w]
							c3 := termbox.CellBuffer()[g.x-1+(g.y+2)*w]

							if g.x > 0 && c1.Ch == 32 && c2.Ch == 32 && c3.Ch == 32 {
								g.x--
								gs <- g
							}
						}
					case termbox.KeyArrowRight:
						w, h := termbox.Size()
						if g.x-1+(g.y+2)*g.x < w*h {
							c1 := termbox.CellBuffer()[g.x+1+g.y*w]
							c2 := termbox.CellBuffer()[g.x+1+(g.y+1)*w]
							c3 := termbox.CellBuffer()[g.x+1+(g.y+2)*w]

							if g.x < 79 && c1.Ch == 32 && c2.Ch == 32 && (c3.Ch == 32 || c3.Ch == 33) {
								g.x++
								gs <- g
							}
						}
					case termbox.KeyArrowUp:
						w, h := termbox.Size()
						if g.x+(g.y+3)*w < w*h {
							c := termbox.CellBuffer()[g.x+(g.y+3)*w]
							if c.Ch != 32 && g.jumpCycles == 0 {
								g.jumpCycles = 5
							}
						}
					}
				}
			}

			if g.y == 1 && g.x == 79 {
				termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
				t.Stop()
				quit <- "You WIN!"
			}
		}
	}(ticker, redrawRoutine)

	m := <-quit
	termbox.Close()
	fmt.Println(m)
}
<commit_msg>Bug on moving left<commit_after>package main

import (
	"fmt"
	"github.com/nsf/termbox-go"
	"time"
)

func draw_guy(g guy) {
	termbox.SetCell(g.x, g.y, '☺', termbox.Attribute(15), termbox.Attribute(7))
	termbox.SetCell(g.x, g.y+1, '✝', termbox.Attribute(15), termbox.Attribute(7))
	termbox.SetCell(g.x, g.y+2, '∩', termbox.Attribute(15), termbox.Attribute(7))
}

func draw_world() {
	world := [30]string{}

	world[0] = "                                                                                "
	world[1] = "                                                                                "
	world[2] = "                                                                                "
	world[3] = "                                                                               !"
	world[4] = "                                                                      ----------"
	world[5] = "                                                                     |          "
	world[6] = "                                                                     |          "
	world[7] = "                                                                     |          "
	world[8] = "                                                               -----            "
	world[9] = "                                                             |                  "
	world[10] = "                                                            |                   "
	world[11] = "                                                            |                   "
	world[12] = "                                                       -----                    "
	world[13] = "                                                      |                         "
	world[14] = "                                                      |                         "
	world[15] = "                                                      |                         "
	world[16] = "                                                -----                           "
	world[17] = "                                              |                                 "
	world[18] = "                                              |                                 "
	world[19] = "                                              |                                 "
	world[20] = "                                              |                                 "
	world[21] = "                                           ---                                  "
	world[22] = "                           |              |                                     "
	world[23] = "                           |              |                                     "
	world[24] = "                           |              |                                     "
	world[25] = "            ------------------------------                                      "
	world[26] = "                                                                                "
	world[27] = "                                                                                "
	world[28] = "                                                                                "
	world[29] = "--------------------------------------------------------------------------------"

	colour := func(x int, y int, world [30]string) int {
		switch world[y][x] {
		case ' ':
			return 7
		case '-':
			return 3
		case '|':
			return 3
		case '!':
			return 2
		}
		return 0
	}
	colour(0, 0, world)
	width, height := 80, 30
	for y := 0; y < height; y++ {
		for x := 0; x < width; x++ {
			termbox.SetCell(x, y, rune(world[y][x]), termbox.Attribute(colour(x, y, world)), termbox.Attribute(colour(x, y, world)))
		}
	}
}

func draw_all(g guy) {
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
	draw_world()
	draw_guy(g)

	termbox.Flush()
}

type guy struct {
	x          int
	y          int
	jumpCycles int
}

func main() {
	err := termbox.Init()
	if err != nil {
		panic(err)
	}

	ticker := time.NewTicker(50 * time.Millisecond)
	quit := make(chan string)

	event_queue := make(chan termbox.Event)
	go func() {
		for {
			event_queue <- termbox.PollEvent()
		}
	}()

	redrawRoutine := make(chan guy)
	go func(r chan guy) {
		for {
			select {
			case g := <-r:
				draw_all(g)
			}
		}

	}(redrawRoutine)

	go func(t *time.Ticker, gs chan guy) {
		g := guy{x: 10, y: 0, jumpCycles: 0}

		for {
			select {
			case <-t.C:
				if g.jumpCycles == 0 {
					w, h := termbox.Size()
					if g.x+(g.y+3)*w < w*h {
						c := termbox.CellBuffer()[g.x+(g.y+3)*w]
						if c.Ch == 32 {
							g.y++
							gs <- g
						}
					}
				} else if g.jumpCycles > 0 {
					w, _ := termbox.Size()
					if g.x+(g.y-1)*w > 0 {
						c := termbox.CellBuffer()[g.x+(g.y-1)*w]
						if c.Ch == 32 {
							g.y--
							gs <- g
						}
					}
					g.jumpCycles--
				}
			case ev := <-event_queue:
				if ev.Type == termbox.EventKey {
					switch ev.Key {
					case termbox.KeyEsc:
						quit <- "Goodbye!"
					case termbox.KeyArrowLeft:
						w, h := termbox.Size()
						if g.x-1+(g.y+2)*g.x < w*h {
							c1 := termbox.CellBuffer()[g.x-1+g.y*w]
							c2 := termbox.CellBuffer()[g.x-1+(g.y+1)*w]
							c3 := termbox.CellBuffer()[g.x-1+(g.y+2)*w]

							if g.x > 0 && c1.Ch == 32 && c2.Ch == 32 && c3.Ch == 32 {
								g.x--
								gs <- g
							}
						}
					case termbox.KeyArrowRight:
						w, h := termbox.Size()
						if g.x-1+(g.y+2)*g.x < w*h {
							c1 := termbox.CellBuffer()[g.x+1+g.y*w]
							c2 := termbox.CellBuffer()[g.x+1+(g.y+1)*w]
							c3 := termbox.CellBuffer()[g.x+1+(g.y+2)*w]

							if g.x < 79 && c1.Ch == 32 && c2.Ch == 32 && (c3.Ch == 32 || c3.Ch == 33) {
								g.x++
								gs <- g
							}
						}
					case termbox.KeyArrowUp:
						w, h := termbox.Size()
						if g.x+(g.y+3)*w < w*h {
							c := termbox.CellBuffer()[g.x+(g.y+3)*w]
							if c.Ch != 32 && g.jumpCycles == 0 {
								g.jumpCycles = 5
							}
						}
					}
				}
			}

			if g.y == 1 && g.x == 79 {
				termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
				t.Stop()
				quit <- "You WIN!"
			}
		}
	}(ticker, redrawRoutine)

	m := <-quit
	termbox.Close()
	fmt.Println(m)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"regexp"
	"strconv"

	"github.com/breml/appcloud/Godeps/_workspace/src/gopkg.in/mgo.v2"
	"github.com/breml/appcloud/Godeps/_workspace/src/gopkg.in/mgo.v2/bson"

	"github.com/cloudfoundry-community/go-cfenv"
)

const prefixDelim = ": "

type Temperature struct {
	ID          bson.ObjectId `bson:"_id,omitempty"`
	Temperature string        `bson:"temperature"`
}

// Initialize global service variables with default values for local
// deployment. If executed in App Cloud, these variables will be replaced
// with the values from ENV.
var (
	name = "appcloud"
	port = "3000"

	mongouri = "mongodb://localhost:27017/weatherDB"
	mongodb  = "weatherDB"
)

func helloHandler(w http.ResponseWriter, r *http.Request) {
	session, err := mgo.Dial(mongouri)
	if err != nil {
		log.Printf("MongoDB dial err %v\n", err)
		return
	}
	defer session.Close()

	c := session.DB(mongodb).C("weatherCOLL")

	var result Temperature
	curTemp := "undef"

	err = c.Find(nil).One(&result)
	if err != nil {
		log.Printf("MongoDB find err %v\n", err)
	} else {
		curTemp = result.Temperature
	}

	w.Header().Set("Content-Type", "text/html; charset=utf-8")

	fmt.Fprintf(w, "
How is the weather ?
")
	fmt.Fprintf(w, "Hello world!

 The weather is %s today.", curTemp)
	fmt.Fprintf(w, "

<form action='/temp'>
Change weather

<input type=text name=temp value=hot>
<input type=submit value='Change the weather...'></form>")
}

func tempHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")

	queryValues := r.URL.Query()

	if len(queryValues["temp"]) > 0 && queryValues["temp"][0] != "" {
		re := regexp.MustCompile("[^a-zA-Z0-9\\ ]")
		temperature := re.ReplaceAllString(queryValues["temp"][0], "")

		session, err := mgo.Dial(mongouri)
		if err != nil {
			log.Printf("MongoDB dial err %v\n", err)
			return
		}
		defer session.Close()

		c := session.DB(mongodb).C("weatherCOLL")

		err = c.DropCollection()
		if err != nil {
			log.Printf("MongoDB drop collection err %v\n", err)
		}

		err = c.Insert(Temperature{Temperature: temperature})
		if err != nil {
			log.Printf("MongoDB insert err %v\n", err)
		} else {
			fmt.Fprintf(w, "The weather is now: %s\n", temperature)
		}
	} else {
		fmt.Fprintf(w, "error: no new temp set
 back")
	}
	fmt.Fprintf(w, "
 back")
}

func main() {
	// Setup logging
	log.SetFlags(0)
	log.SetOutput(os.Stdout)
	log.SetPrefix(name + prefixDelim)

	// Get settings from ENV if present (e. g. if in App Cloud)
	appEnv, err := cfenv.Current()
	if err == nil {
		// Port to bind web app
		port = strconv.Itoa(appEnv.Port)

		name = appEnv.Name
		log.SetPrefix(name + prefixDelim)

		// MongoDB Service
		mgoService, err := appEnv.Services.WithName("mongodb")
		if err == nil {
			var ok bool
			mongouri, ok = mgoService.Credentials["uri"].(string)
			if !ok {
				log.Fatalf("No valid MongoDB uri\n")
			}
			mongodb, ok = mgoService.Credentials["database"].(string)
			if !ok {
				log.Fatalf("No valid MongoDB database\n")
			}
		}
	}

	log.Printf("Use port: %s\n", port)

	http.HandleFunc("/", helloHandler)
	http.HandleFunc("/temp", tempHandler)
	err = http.ListenAndServe(":"+port, nil)
	if err != nil {
		log.Fatalf("ListenAndServe error: %v\n", err)
	}
}
<commit_msg>Remove redundant output<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"regexp"
	"strconv"

	"github.com/breml/appcloud/Godeps/_workspace/src/gopkg.in/mgo.v2"
	"github.com/breml/appcloud/Godeps/_workspace/src/gopkg.in/mgo.v2/bson"

	"github.com/cloudfoundry-community/go-cfenv"
)

const prefixDelim = ": "

type Temperature struct {
	ID          bson.ObjectId `bson:"_id,omitempty"`
	Temperature string        `bson:"temperature"`
}

// Initialize global service variables with default values for local
// deployment. If executed in App Cloud, these variables will be replaced
// with the values from ENV.
var (
	name = "appcloud"
	port = "3000"

	mongouri = "mongodb://localhost:27017/weatherDB"
	mongodb  = "weatherDB"
)

func helloHandler(w http.ResponseWriter, r *http.Request) {
	session, err := mgo.Dial(mongouri)
	if err != nil {
		log.Printf("MongoDB dial err %v\n", err)
		return
	}
	defer session.Close()

	c := session.DB(mongodb).C("weatherCOLL")

	var result Temperature
	curTemp := "undef"

	err = c.Find(nil).One(&result)
	if err != nil {
		log.Printf("MongoDB find err %v\n", err)
	} else {
		curTemp = result.Temperature
	}

	w.Header().Set("Content-Type", "text/html; charset=utf-8")

	fmt.Fprintf(w, "
How is the weather ?
")
	fmt.Fprintf(w, "Hello world!

 The weather is %s today.", curTemp)
	fmt.Fprintf(w, "

<form action='/temp'>
Change weather

<input type=text name=temp value=hot>
<input type=submit value='Change the weather...'></form>")
}

func tempHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")

	queryValues := r.URL.Query()

	if len(queryValues["temp"]) > 0 && queryValues["temp"][0] != "" {
		re := regexp.MustCompile("[^a-zA-Z0-9\\ ]")
		temperature := re.ReplaceAllString(queryValues["temp"][0], "")

		session, err := mgo.Dial(mongouri)
		if err != nil {
			log.Printf("MongoDB dial err %v\n", err)
			return
		}
		defer session.Close()

		c := session.DB(mongodb).C("weatherCOLL")

		err = c.DropCollection()
		if err != nil {
			log.Printf("MongoDB drop collection err %v\n", err)
		}

		err = c.Insert(Temperature{Temperature: temperature})
		if err != nil {
			log.Printf("MongoDB insert err %v\n", err)
		} else {
			fmt.Fprintf(w, "The weather is now: %s\n", temperature)
		}
	} else {
		fmt.Fprintf(w, "error: no new temp set")
	}
	fmt.Fprintf(w, "
 back")
}

func main() {
	// Setup logging
	log.SetFlags(0)
	log.SetOutput(os.Stdout)
	log.SetPrefix(name + prefixDelim)

	// Get settings from ENV if present (e. g. if in App Cloud)
	appEnv, err := cfenv.Current()
	if err == nil {
		// Port to bind web app
		port = strconv.Itoa(appEnv.Port)

		name = appEnv.Name
		log.SetPrefix(name + prefixDelim)

		// MongoDB Service
		mgoService, err := appEnv.Services.WithName("mongodb")
		if err == nil {
			var ok bool
			mongouri, ok = mgoService.Credentials["uri"].(string)
			if !ok {
				log.Fatalf("No valid MongoDB uri\n")
			}
			mongodb, ok = mgoService.Credentials["database"].(string)
			if !ok {
				log.Fatalf("No valid MongoDB database\n")
			}
		}
	}

	log.Printf("Use port: %s\n", port)

	http.HandleFunc("/", helloHandler)
	http.HandleFunc("/temp", tempHandler)
	err = http.ListenAndServe(":"+port, nil)
	if err != nil {
		log.Fatalf("ListenAndServe error: %v\n", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"runtime"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/kardianos/osext"
	sdees "github.com/schollz/sdees/src"
	"github.com/urfave/cli"
)

var (
	Version, BuildTime, Build, OS, LastCommit string
	Debug                                     bool
	DontEncrypt, Clean                        bool
	ResetConfig                               bool
	ImportOldFile, ImportFile                 bool
)

func main() {
	// Delete temp files upon exit
	defer sdees.CleanUp()

	// Handle Ctl+C for cleanUp
	// from http://stackoverflow.com/questions/11268943/golang-is-it-possible-to-capture-a-ctrlc-signal-and-run-a-cleanup-function-in
	c := make(chan os.Signal, 2)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-c
		sdees.CleanUp()
		os.Exit(1)
	}()

	// App information
	setBuild()
	app := cli.NewApp()
	app.Name = "sdees"
	app.Version = Version + " " + Build + " " + BuildTime + " " + OS
	app.Usage = `SDEES Does Editing, Encryption, and Synchronization

	 https://github.com/schollz/sdees

FOLDERS:
	'` + sdees.CachePath + `' stores all encrypted files and repositories
	'` + sdees.ConfigPath + `' stores all configuration files

EXAMPLE USAGE:
   sdees new.txt # edit a new document, new.txt
	 sdees UglyDuckling # edit a entry in a document
   sdees --summary -n 5 # list a summary of last five entries
   sdees --search "dogs cats" # find entries that mention 'dogs' or 'cats'`

	app.Action = func(c *cli.Context) error {
		// Set the log level
		if Debug {
			sdees.DebugMode()
		}

		CheckIfGitIsInstalled()

		workingFile := c.Args().Get(0)
		if len(workingFile) > 0 {
			sdees.InputDocument = workingFile
		}

		// Check if its Windows
		if runtime.GOOS == "windows" {
			sdees.Extension = ".exe"
		} else {
			sdees.Extension = ""
		}
		sdees.Version = Version

		// Check new Version
		programPath, _ := osext.Executable()
		sdees.CheckNewVersion(programPath, Version, LastCommit, OS)

		// Process some flags
		if ResetConfig {
			sdees.SetupConfig()
		} else if Clean {
			sdees.CleanAll()
		} else {
			sdees.Run()
		}
		return nil
	}
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:        "debug",
			Usage:       "Turn on debug mode",
			Destination: &Debug,
		},
		cli.BoolFlag{
			Name:        "clean",
			Usage:       "Deletes all sdees files",
			Destination: &Clean,
		},
		cli.StringFlag{
			Name:        "search",
			Usage:       "Search for `word`",
			Destination: &sdees.Search,
		},
		cli.BoolFlag{
			Name:        "importold",
			Usage:       "Import `document` (JRNL-format)",
			Destination: &sdees.ImportOldFlag,
		},
		cli.BoolFlag{
			Name:        "import",
			Usage:       "Import `document`",
			Destination: &sdees.ImportFlag,
		},
		cli.BoolFlag{
			Name:        "export",
			Usage:       "Export `document`",
			Destination: &sdees.Export,
		},
		cli.BoolFlag{
			Name:        "config",
			Usage:       "Configure",
			Destination: &ResetConfig,
		},
		cli.BoolFlag{
			Name:        "all, a",
			Usage:       "Edit all of the document",
			Destination: &sdees.All,
		},
		cli.BoolFlag{
			Name:        "delete",
			Usage:       "Delete `X`, where X is a document or entry",
			Destination: &sdees.DeleteFlag,
		},
		cli.BoolFlag{
			Name:        "summary",
			Usage:       "Gets summary",
			Destination: &sdees.Summarize,
		},
	}
	app.Run(os.Args)
}

func CheckIfGitIsInstalled() {
	cmd := exec.Command("git", "--version")
	stdout, err := cmd.Output()
	versionNums := strings.Split(strings.Split(string(stdout), " ")[2], ".")
	major, _ := strconv.Atoi(versionNums[0])
	fmt.Println(major)
	minor, _ := strconv.Atoi(versionNums[1])
	fmt.Println(minor)
	os.Exit(1)
	if err != nil {
		fmt.Println("\ngit is not detected.\n\nPlease install git before proceeding. To install, go to \n\n    https://git-scm.com/downloads \n\nand install the version for your operating system.\nPress enter to continue... ")
		var input string
		fmt.Scanln(&input)
		os.Exit(1)
	}
}

func setBuild() {
	if len(Build) == 0 {
		cwd, _ := os.Getwd()
		defer os.Chdir(cwd)
		Build = "dev"
		Version = "dev"
		BuildTime = time.Now().String()
		err := os.Chdir(path.Join(os.Getenv("GOPATH"), "src", "github.com", "schollz", "sdees"))
		if err != nil {
			return
		}
		cmd := exec.Command("git", "log", "-1", "--pretty=format:'%ad'")
		stdout, err := cmd.Output()
		if err != nil {
			return
		}
		LastCommit = strings.Replace(string(stdout), "'", "", -1)
		BuildTime = LastCommit
	} else {
		Build = Build[0:7]
	}
}
<commit_msg>Minimum version of git enforced<commit_after>package main

import (
	"fmt"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"runtime"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/kardianos/osext"
	sdees "github.com/schollz/sdees/src"
	"github.com/urfave/cli"
)

var (
	Version, BuildTime, Build, OS, LastCommit string
	Debug                                     bool
	DontEncrypt, Clean                        bool
	ResetConfig                               bool
	ImportOldFile, ImportFile                 bool
)

func main() {
	// Delete temp files upon exit
	defer sdees.CleanUp()

	// Handle Ctl+C for cleanUp
	// from http://stackoverflow.com/questions/11268943/golang-is-it-possible-to-capture-a-ctrlc-signal-and-run-a-cleanup-function-in
	c := make(chan os.Signal, 2)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-c
		sdees.CleanUp()
		os.Exit(1)
	}()

	// App information
	setBuild()
	app := cli.NewApp()
	app.Name = "sdees"
	app.Version = Version + " " + Build + " " + BuildTime + " " + OS
	app.Usage = `SDEES Does Editing, Encryption, and Synchronization

	 https://github.com/schollz/sdees

FOLDERS:
	'` + sdees.CachePath + `' stores all encrypted files and repositories
	'` + sdees.ConfigPath + `' stores all configuration files

EXAMPLE USAGE:
   sdees new.txt # edit a new document, new.txt
	 sdees UglyDuckling # edit a entry in a document
   sdees --summary -n 5 # list a summary of last five entries
   sdees --search "dogs cats" # find entries that mention 'dogs' or 'cats'`

	app.Action = func(c *cli.Context) error {
		// Set the log level
		if Debug {
			sdees.DebugMode()
		}

		CheckIfGitIsInstalled()

		workingFile := c.Args().Get(0)
		if len(workingFile) > 0 {
			sdees.InputDocument = workingFile
		}

		// Check if its Windows
		if runtime.GOOS == "windows" {
			sdees.Extension = ".exe"
		} else {
			sdees.Extension = ""
		}
		sdees.Version = Version

		// Check new Version
		programPath, _ := osext.Executable()
		sdees.CheckNewVersion(programPath, Version, LastCommit, OS)

		// Process some flags
		if ResetConfig {
			sdees.SetupConfig()
		} else if Clean {
			sdees.CleanAll()
		} else {
			sdees.Run()
		}
		return nil
	}
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:        "debug",
			Usage:       "Turn on debug mode",
			Destination: &Debug,
		},
		cli.BoolFlag{
			Name:        "clean",
			Usage:       "Deletes all sdees files",
			Destination: &Clean,
		},
		cli.StringFlag{
			Name:        "search",
			Usage:       "Search for `word`",
			Destination: &sdees.Search,
		},
		cli.BoolFlag{
			Name:        "importold",
			Usage:       "Import `document` (JRNL-format)",
			Destination: &sdees.ImportOldFlag,
		},
		cli.BoolFlag{
			Name:        "import",
			Usage:       "Import `document`",
			Destination: &sdees.ImportFlag,
		},
		cli.BoolFlag{
			Name:        "export",
			Usage:       "Export `document`",
			Destination: &sdees.Export,
		},
		cli.BoolFlag{
			Name:        "config",
			Usage:       "Configure",
			Destination: &ResetConfig,
		},
		cli.BoolFlag{
			Name:        "all, a",
			Usage:       "Edit all of the document",
			Destination: &sdees.All,
		},
		cli.BoolFlag{
			Name:        "delete",
			Usage:       "Delete `X`, where X is a document or entry",
			Destination: &sdees.DeleteFlag,
		},
		cli.BoolFlag{
			Name:        "summary",
			Usage:       "Gets summary",
			Destination: &sdees.Summarize,
		},
	}
	app.Run(os.Args)
}

func CheckIfGitIsInstalled() {
	cmd := exec.Command("git", "--version")
	stdout, err := cmd.Output()
	versionNums := strings.Split(strings.Split(string(stdout), " ")[2], ".")
	major, _ := strconv.Atoi(versionNums[0])
	minor, _ := strconv.Atoi(versionNums[1])
	if major < 2 || (major == 2 && minor < 12) {
		fmt.Printf("\n%s detected.\n\nPlease install git version 2.5+ before proceeding. To install, go to \n\n    https://git-scm.com/downloads \n\nand install the version for your operating system.\nPress enter to continue... \n", string(stdout))
		var input string
		fmt.Scanln(&input)
		os.Exit(1)
	}
	os.Exit(1)
	if err != nil {
		fmt.Println("\ngit is not detected.\n\nPlease install git version 2.5+ before proceeding. To install, go to \n\n    https://git-scm.com/downloads \n\nand install the version for your operating system.\nPress enter to continue... ")
		var input string
		fmt.Scanln(&input)
		os.Exit(1)
	}
}

func setBuild() {
	if len(Build) == 0 {
		cwd, _ := os.Getwd()
		defer os.Chdir(cwd)
		Build = "dev"
		Version = "dev"
		BuildTime = time.Now().String()
		err := os.Chdir(path.Join(os.Getenv("GOPATH"), "src", "github.com", "schollz", "sdees"))
		if err != nil {
			return
		}
		cmd := exec.Command("git", "log", "-1", "--pretty=format:'%ad'")
		stdout, err := cmd.Output()
		if err != nil {
			return
		}
		LastCommit = strings.Replace(string(stdout), "'", "", -1)
		BuildTime = LastCommit
	} else {
		Build = Build[0:7]
	}
}
<|endoftext|>


<commit_before>/* Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"flag"
	"fmt"
	"go/ast"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"
)

func init() {
	if err := typesInit(); err != nil {
		log.Fatal(err)
	}
}

func main() {
	flag.Parse()
	if err := checkPaths(flag.Args(), os.Stdout); err != nil {
		log.Fatal(err)
	}
}

type call struct {
	params  []interface{}
	results []interface{}
}

var toToken = map[string]token.Token{
	"byte":    token.INT,
	"int":     token.INT,
	"int8":    token.INT,
	"int16":   token.INT,
	"int32":   token.INT,
	"int64":   token.INT,
	"uint":    token.INT,
	"uint8":   token.INT,
	"uint16":  token.INT,
	"uint32":  token.INT,
	"uint64":  token.INT,
	"string":  token.STRING,
	"float32": token.FLOAT,
	"float64": token.FLOAT,
}

func paramEqual(t1 types.Type, a2 interface{}) bool {
	switch x := a2.(type) {
	case string:
		return t1.String() == x
	case token.Token:
		return toToken[t1.String()] == x
	case nil:
		switch t1.(type) {
		case *types.Slice:
			return true
		case *types.Map:
			return true
		default:
			return false
		}
	default:
		return false
	}
}

func paramsMatch(types1 []types.Type, args2 []interface{}) bool {
	if len(types1) != len(args2) {
		return false
	}
	for i, t1 := range types1 {
		a2 := args2[i]
		if !paramEqual(t1, a2) {
			return false
		}
	}
	return true
}

func resultEqual(t1 types.Type, e2 interface{}) bool {
	switch x := e2.(type) {
	case string:
		return t1.String() == x
	case nil:
		// assigning to _
		return true
	default:
		return false
	}
}

func resultsMatch(types1 []types.Type, exps2 []interface{}) bool {
	if len(exps2) == 0 {
		return true
	}
	if len(types1) != len(exps2) {
		return false
	}
	for i, t1 := range types1 {
		e2 := exps2[i]
		if !resultEqual(t1, e2) {
			return false
		}
	}
	return true
}

func interfaceMatching(calls map[string]call) string {
	matchesIface := func(decls map[string]funcDecl) bool {
		if len(calls) > len(decls) {
			return false
		}
		for n, d := range decls {
			c, e := calls[n]
			if !e {
				return false
			}
			if !paramsMatch(d.params, c.params) {
				return false
			}
			if !resultsMatch(d.results, c.results) {
				return false
			}
		}
		return true
	}
	for name, decls := range parsed {
		if matchesIface(decls) {
			return name
		}
	}
	return ""
}

func isDir(p string) (bool, error) {
	fi, err := os.Stat(p)
	if err != nil {
		return false, err
	}
	return fi.Mode().IsDir(), nil
}

func isGoFile(fi os.FileInfo) bool {
	name := fi.Name()
	if fi.IsDir() {
		return false
	}
	if strings.HasPrefix(name, ".") {
		return false
	}
	if !strings.HasSuffix(name, ".go") {
		return false
	}
	if strings.HasSuffix(name, "_test.go") {
		return false
	}
	return true
}

type pathWalker struct {
	paths   []string
	first   bool
	recurse bool
}

func (pw *pathWalker) visit(path string, info os.FileInfo, err error) error {
	if err != nil {
		return err
	}
	if pw.first {
		pw.first = false
	} else if !pw.recurse && info.IsDir() {
		return filepath.SkipDir
	}
	if isGoFile(info) {
		pw.paths = append(pw.paths, path)
	}
	return nil
}

func getPaths(p string) ([]string, error) {
	pw := &pathWalker{
		first: true,
	}
	if filepath.Base(p) == "..." {
		return nil, fmt.Errorf("TODO: multi-package support")
		p = filepath.Dir(p)
		pw.recurse = true
	}
	dir, err := isDir(p)
	if err != nil {
		return nil, err
	}
	if !dir {
		if pw.recurse {
			return nil, fmt.Errorf("Cannot recurse into file")
		}
		return []string{p}, nil
	}
	if err := filepath.Walk(p, pw.visit); err != nil {
		return nil, err
	}
	return pw.paths, nil
}

func checkPaths(paths []string, w io.Writer) error {
	gp := &goPkg{
		fset: token.NewFileSet(),
	}
	for _, p := range paths {
		paths, err := getPaths(p)
		if err != nil {
			return err
		}
		for _, fp := range paths {
			if err := gp.parsePath(fp); err != nil {
				return err
			}
		}
	}
	if err := gp.check(w); err != nil {
		return err
	}
	return nil
}

type goPkg struct {
	fset  *token.FileSet
	files []*ast.File
}

func (gp *goPkg) parsePath(fp string) error {
	f, err := os.Open(fp)
	if err != nil {
		return err
	}
	defer f.Close()
	if err := gp.parseReader(fp, f); err != nil {
		return err
	}
	return nil
}

func (gp *goPkg) parseReader(name string, r io.Reader) error {
	f, err := parser.ParseFile(gp.fset, name, r, 0)
	if err != nil {
		return err
	}
	gp.files = append(gp.files, f)
	return nil
}

func (gp *goPkg) check(w io.Writer) error {
	conf := types.Config{Importer: importer.Default()}
	pkg, err := conf.Check("", gp.fset, gp.files, nil)
	if err != nil {
		return err
	}

	v := &Visitor{
		w:      w,
		fset:   gp.fset,
		scopes: []*types.Scope{pkg.Scope()},
	}
	for _, f := range gp.files {
		ast.Walk(v, f)
	}
	return nil
}

type Visitor struct {
	w      io.Writer
	fset   *token.FileSet
	scopes []*types.Scope

	nodes []ast.Node

	params map[string]types.Type
	used   map[string]map[string]call
}

func (v *Visitor) scope() *types.Scope {
	return v.scopes[len(v.scopes)-1]
}

func scopeName(e ast.Expr) string {
	switch x := e.(type) {
	case *ast.Ident:
		return x.Name
	case *ast.StarExpr:
		return scopeName(x.X)
	default:
		return ""
	}
}

func (v *Visitor) recvFuncType(tname, fname string) *types.Func {
	_, obj := v.scope().LookupParent(tname, token.NoPos)
	st, ok := obj.(*types.TypeName)
	if !ok {
		return nil
	}
	named, ok := st.Type().(*types.Named)
	if !ok {
		return nil
	}
	for i := 0; i < named.NumMethods(); i++ {
		f := named.Method(i)
		if f.Name() == fname {
			return f
		}
	}
	return nil
}

func (v *Visitor) funcType(fd *ast.FuncDecl) *types.Func {
	fname := fd.Name.Name
	if fd.Recv == nil {
		f, ok := v.scope().Lookup(fname).(*types.Func)
		if !ok {
			return nil
		}
		return f
	}
	if len(fd.Recv.List) > 1 {
		return nil
	}
	tname := scopeName(fd.Recv.List[0].Type)
	return v.recvFuncType(tname, fname)
}

func typeMap(t *types.Tuple) map[string]types.Type {
	m := make(map[string]types.Type, t.Len())
	for i := 0; i < t.Len(); i++ {
		p := t.At(i)
		m[p.Name()] = p.Type()
	}
	return m
}

func (v *Visitor) Visit(node ast.Node) ast.Visitor {
	switch x := node.(type) {
	case *ast.File:
	case *ast.FuncDecl:
		f := v.funcType(x)
		if f == nil {
			return nil
		}
		v.scopes = append(v.scopes, f.Scope())
		sign := f.Type().(*types.Signature)
		v.params = typeMap(sign.Params())
		v.used = make(map[string]map[string]call, 0)
	case *ast.CallExpr:
		v.onCall(x)
	case nil:
		top := v.nodes[len(v.nodes)-1]
		v.nodes = v.nodes[:len(v.nodes)-1]
		fd, ok := top.(*ast.FuncDecl)
		if !ok {
			return nil
		}
		v.scopes = v.scopes[:len(v.scopes)-1]
		v.onFuncEnded(fd)
		v.params = nil
		v.used = nil
	}
	if node != nil {
		v.nodes = append(v.nodes, node)
	}
	return v
}

func (v *Visitor) getType(name string) interface{} {
	_, obj := v.scope().LookupParent(name, token.NoPos)
	if obj == nil {
		return nil
	}
	switch x := obj.(type) {
	case *types.Var:
		return x.Type().String()
	default:
		return nil
	}
}

func (v *Visitor) descType(e ast.Expr) interface{} {
	switch x := e.(type) {
	case *ast.Ident:
		return v.getType(x.Name)
	case *ast.BasicLit:
		return x.Kind
	default:
		return nil
	}
}

func (v *Visitor) onCall(ce *ast.CallExpr) {
	if v.used == nil {
		return
	}
	sel, ok := ce.Fun.(*ast.SelectorExpr)
	if !ok {
		return
	}
	left, ok := sel.X.(*ast.Ident)
	if !ok {
		return
	}
	right := sel.Sel
	vname := left.Name
	tname, _ := v.descType(left).(string)
	fname := right.Name
	c := call{}
	f := v.recvFuncType(tname, fname)
	if f != nil {
		sign := f.Type().(*types.Signature)
		results := sign.Results()
		for i := 0; i < results.Len(); i++ {
			v := results.At(i)
			c.results = append(c.results, v.Type().String())
		}
	}
	for _, a := range ce.Args {
		c.params = append(c.params, v.descType(a))
	}
	if _, e := v.used[vname]; !e {
		v.used[vname] = make(map[string]call)
	}
	v.used[vname][fname] = c
}

func (v *Visitor) onFuncEnded(fd *ast.FuncDecl) {
	for name, methods := range v.used {
		iface := interfaceMatching(methods)
		if iface == "" {
			continue
		}
		param, e := v.params[name]
		if !e {
			continue
		}
		if iface == param.String() {
			continue
		}
		pos := v.fset.Position(fd.Pos())
		fmt.Fprintf(v.w, "%s:%d: %s can be %s\n",
			pos.Filename, pos.Line, name, iface)
	}
}
<commit_msg>Print errors without log<commit_after>/* Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"flag"
	"fmt"
	"go/ast"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"
	"io"
	"os"
	"path/filepath"
	"strings"
)

func init() {
	if err := typesInit(); err != nil {
		errExit(err)
	}
}

func main() {
	flag.Parse()
	if err := checkPaths(flag.Args(), os.Stdout); err != nil {
		errExit(err)
	}
}

func errExit(err error) {
	fmt.Fprintf(os.Stderr, "%v\n", err)
	os.Exit(1)
}

type call struct {
	params  []interface{}
	results []interface{}
}

var toToken = map[string]token.Token{
	"byte":    token.INT,
	"int":     token.INT,
	"int8":    token.INT,
	"int16":   token.INT,
	"int32":   token.INT,
	"int64":   token.INT,
	"uint":    token.INT,
	"uint8":   token.INT,
	"uint16":  token.INT,
	"uint32":  token.INT,
	"uint64":  token.INT,
	"string":  token.STRING,
	"float32": token.FLOAT,
	"float64": token.FLOAT,
}

func paramEqual(t1 types.Type, a2 interface{}) bool {
	switch x := a2.(type) {
	case string:
		return t1.String() == x
	case token.Token:
		return toToken[t1.String()] == x
	case nil:
		switch t1.(type) {
		case *types.Slice:
			return true
		case *types.Map:
			return true
		default:
			return false
		}
	default:
		return false
	}
}

func paramsMatch(types1 []types.Type, args2 []interface{}) bool {
	if len(types1) != len(args2) {
		return false
	}
	for i, t1 := range types1 {
		a2 := args2[i]
		if !paramEqual(t1, a2) {
			return false
		}
	}
	return true
}

func resultEqual(t1 types.Type, e2 interface{}) bool {
	switch x := e2.(type) {
	case string:
		return t1.String() == x
	case nil:
		// assigning to _
		return true
	default:
		return false
	}
}

func resultsMatch(types1 []types.Type, exps2 []interface{}) bool {
	if len(exps2) == 0 {
		return true
	}
	if len(types1) != len(exps2) {
		return false
	}
	for i, t1 := range types1 {
		e2 := exps2[i]
		if !resultEqual(t1, e2) {
			return false
		}
	}
	return true
}

func interfaceMatching(calls map[string]call) string {
	matchesIface := func(decls map[string]funcDecl) bool {
		if len(calls) > len(decls) {
			return false
		}
		for n, d := range decls {
			c, e := calls[n]
			if !e {
				return false
			}
			if !paramsMatch(d.params, c.params) {
				return false
			}
			if !resultsMatch(d.results, c.results) {
				return false
			}
		}
		return true
	}
	for name, decls := range parsed {
		if matchesIface(decls) {
			return name
		}
	}
	return ""
}

func isDir(p string) (bool, error) {
	fi, err := os.Stat(p)
	if err != nil {
		return false, err
	}
	return fi.Mode().IsDir(), nil
}

func isGoFile(fi os.FileInfo) bool {
	name := fi.Name()
	if fi.IsDir() {
		return false
	}
	if strings.HasPrefix(name, ".") {
		return false
	}
	if !strings.HasSuffix(name, ".go") {
		return false
	}
	if strings.HasSuffix(name, "_test.go") {
		return false
	}
	return true
}

type pathWalker struct {
	paths   []string
	first   bool
	recurse bool
}

func (pw *pathWalker) visit(path string, info os.FileInfo, err error) error {
	if err != nil {
		return err
	}
	if pw.first {
		pw.first = false
	} else if !pw.recurse && info.IsDir() {
		return filepath.SkipDir
	}
	if isGoFile(info) {
		pw.paths = append(pw.paths, path)
	}
	return nil
}

func getPaths(p string) ([]string, error) {
	pw := &pathWalker{
		first: true,
	}
	if filepath.Base(p) == "..." {
		return nil, fmt.Errorf("TODO: multi-package support")
		p = filepath.Dir(p)
		pw.recurse = true
	}
	dir, err := isDir(p)
	if err != nil {
		return nil, err
	}
	if !dir {
		if pw.recurse {
			return nil, fmt.Errorf("Cannot recurse into file")
		}
		return []string{p}, nil
	}
	if err := filepath.Walk(p, pw.visit); err != nil {
		return nil, err
	}
	return pw.paths, nil
}

func checkPaths(paths []string, w io.Writer) error {
	gp := &goPkg{
		fset: token.NewFileSet(),
	}
	for _, p := range paths {
		paths, err := getPaths(p)
		if err != nil {
			return err
		}
		for _, fp := range paths {
			if err := gp.parsePath(fp); err != nil {
				return err
			}
		}
	}
	if err := gp.check(w); err != nil {
		return err
	}
	return nil
}

type goPkg struct {
	fset  *token.FileSet
	files []*ast.File
}

func (gp *goPkg) parsePath(fp string) error {
	f, err := os.Open(fp)
	if err != nil {
		return err
	}
	defer f.Close()
	if err := gp.parseReader(fp, f); err != nil {
		return err
	}
	return nil
}

func (gp *goPkg) parseReader(name string, r io.Reader) error {
	f, err := parser.ParseFile(gp.fset, name, r, 0)
	if err != nil {
		return err
	}
	gp.files = append(gp.files, f)
	return nil
}

func (gp *goPkg) check(w io.Writer) error {
	conf := types.Config{Importer: importer.Default()}
	pkg, err := conf.Check("", gp.fset, gp.files, nil)
	if err != nil {
		return err
	}

	v := &Visitor{
		w:      w,
		fset:   gp.fset,
		scopes: []*types.Scope{pkg.Scope()},
	}
	for _, f := range gp.files {
		ast.Walk(v, f)
	}
	return nil
}

type Visitor struct {
	w      io.Writer
	fset   *token.FileSet
	scopes []*types.Scope

	nodes []ast.Node

	params map[string]types.Type
	used   map[string]map[string]call
}

func (v *Visitor) scope() *types.Scope {
	return v.scopes[len(v.scopes)-1]
}

func scopeName(e ast.Expr) string {
	switch x := e.(type) {
	case *ast.Ident:
		return x.Name
	case *ast.StarExpr:
		return scopeName(x.X)
	default:
		return ""
	}
}

func (v *Visitor) recvFuncType(tname, fname string) *types.Func {
	_, obj := v.scope().LookupParent(tname, token.NoPos)
	st, ok := obj.(*types.TypeName)
	if !ok {
		return nil
	}
	named, ok := st.Type().(*types.Named)
	if !ok {
		return nil
	}
	for i := 0; i < named.NumMethods(); i++ {
		f := named.Method(i)
		if f.Name() == fname {
			return f
		}
	}
	return nil
}

func (v *Visitor) funcType(fd *ast.FuncDecl) *types.Func {
	fname := fd.Name.Name
	if fd.Recv == nil {
		f, ok := v.scope().Lookup(fname).(*types.Func)
		if !ok {
			return nil
		}
		return f
	}
	if len(fd.Recv.List) > 1 {
		return nil
	}
	tname := scopeName(fd.Recv.List[0].Type)
	return v.recvFuncType(tname, fname)
}

func typeMap(t *types.Tuple) map[string]types.Type {
	m := make(map[string]types.Type, t.Len())
	for i := 0; i < t.Len(); i++ {
		p := t.At(i)
		m[p.Name()] = p.Type()
	}
	return m
}

func (v *Visitor) Visit(node ast.Node) ast.Visitor {
	switch x := node.(type) {
	case *ast.File:
	case *ast.FuncDecl:
		f := v.funcType(x)
		if f == nil {
			return nil
		}
		v.scopes = append(v.scopes, f.Scope())
		sign := f.Type().(*types.Signature)
		v.params = typeMap(sign.Params())
		v.used = make(map[string]map[string]call, 0)
	case *ast.CallExpr:
		v.onCall(x)
	case nil:
		top := v.nodes[len(v.nodes)-1]
		v.nodes = v.nodes[:len(v.nodes)-1]
		fd, ok := top.(*ast.FuncDecl)
		if !ok {
			return nil
		}
		v.scopes = v.scopes[:len(v.scopes)-1]
		v.onFuncEnded(fd)
		v.params = nil
		v.used = nil
	}
	if node != nil {
		v.nodes = append(v.nodes, node)
	}
	return v
}

func (v *Visitor) getType(name string) interface{} {
	_, obj := v.scope().LookupParent(name, token.NoPos)
	if obj == nil {
		return nil
	}
	switch x := obj.(type) {
	case *types.Var:
		return x.Type().String()
	default:
		return nil
	}
}

func (v *Visitor) descType(e ast.Expr) interface{} {
	switch x := e.(type) {
	case *ast.Ident:
		return v.getType(x.Name)
	case *ast.BasicLit:
		return x.Kind
	default:
		return nil
	}
}

func (v *Visitor) onCall(ce *ast.CallExpr) {
	if v.used == nil {
		return
	}
	sel, ok := ce.Fun.(*ast.SelectorExpr)
	if !ok {
		return
	}
	left, ok := sel.X.(*ast.Ident)
	if !ok {
		return
	}
	right := sel.Sel
	vname := left.Name
	tname, _ := v.descType(left).(string)
	fname := right.Name
	c := call{}
	f := v.recvFuncType(tname, fname)
	if f != nil {
		sign := f.Type().(*types.Signature)
		results := sign.Results()
		for i := 0; i < results.Len(); i++ {
			v := results.At(i)
			c.results = append(c.results, v.Type().String())
		}
	}
	for _, a := range ce.Args {
		c.params = append(c.params, v.descType(a))
	}
	if _, e := v.used[vname]; !e {
		v.used[vname] = make(map[string]call)
	}
	v.used[vname][fname] = c
}

func (v *Visitor) onFuncEnded(fd *ast.FuncDecl) {
	for name, methods := range v.used {
		iface := interfaceMatching(methods)
		if iface == "" {
			continue
		}
		param, e := v.params[name]
		if !e {
			continue
		}
		if iface == param.String() {
			continue
		}
		pos := v.fset.Position(fd.Pos())
		fmt.Fprintf(v.w, "%s:%d: %s can be %s\n",
			pos.Filename, pos.Line, name, iface)
	}
}
<|endoftext|>


<commit_before>package main

import (
    "os"
    "os/exec"

//    "log"

//    "fmt"
    "strings"
    "runtime"
//    "strconv" // For Itoa
//    "encoding/csv"
    "encoding/json"

    "github.com/go-martini/martini"
)

func main() {
    m := martini.Classic()

    // CPU count
    m.Get("/sh/numberofcores.php", func () ([]byte, error) {
        return json.Marshal(runtime.NumCPU())
    })

    // Server's hostname
    m.Get("/sh/hostname.php", func () ([]byte, error) {
        host, err := os.Hostname()

        if err != nil {
            return nil, err
        }

        return json.Marshal(host)
    })

    // PS
    m.Get("/sh/ps.php", func () ([]byte, error) {
        // Run uptime command
        rawOutput, err := exec.Command("ps", "aux").Output()

        if err != nil {
            return nil, err
        }

        return json.Marshal(parseCommandTable(rawOutput))
    })

    // Serve static files
    m.Get("/.*", martini.Static(""))

    m.Run()
}

func parseCommandTable(rawOutput []byte) [][]string {
    // Convert output to a string (it's not binary data, so this is ok)
    output := string(rawOutput[:])

    // We'll add all the parsed lines here
    var entries [][]string

    // Lines of output
    lines := strings.Split(output, "\n")

    // Skip first and last line of output
    for _, str := range lines[1:len(lines)-1] {
        entries = append(entries, strings.Fields(str))
    }

    return entries
}<commit_msg>Add df command<commit_after>package main

import (
    "os"
    "os/exec"

//    "log"

//    "fmt"
    "strings"
    "runtime"
//    "strconv" // For Itoa
//    "encoding/csv"
    "encoding/json"

    "github.com/go-martini/martini"
)

func main() {
    m := martini.Classic()

    // CPU count
    m.Get("/sh/numberofcores.php", func () ([]byte, error) {
        return json.Marshal(runtime.NumCPU())
    })

    // Server's hostname
    m.Get("/sh/hostname.php", func () ([]byte, error) {
        host, err := os.Hostname()

        if err != nil {
            return nil, err
        }

        return json.Marshal(host)
    })

    // PS
    m.Get("/sh/ps.php", func () ([]byte, error) {
        // Run uptime command
        rawOutput, err := exec.Command("ps", "aux").Output()

        if err != nil {
            return nil, err
        }

        return json.Marshal(parseCommandTable(rawOutput))
    })

    m.Get("/sh/df.php", func () ([]byte, error) {
        // Run uptime command
        rawOutput, err := exec.Command("df", "-Ph").Output()

        if err != nil {
            return nil, err
        }

        return json.Marshal(parseCommandTable(rawOutput))
    })

    // Serve static files
    m.Get("/.*", martini.Static(""))

    m.Run()
}

func parseCommandTable(rawOutput []byte) [][]string {
    // Convert output to a string (it's not binary data, so this is ok)
    output := string(rawOutput[:])

    // We'll add all the parsed lines here
    var entries [][]string

    // Lines of output
    lines := strings.Split(output, "\n")

    // Skip first and last line of output
    for _, str := range lines[1:len(lines)-1] {
        entries = append(entries, strings.Fields(str))
    }

    return entries
}<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"net"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/alienth/fastlyctl/util"
	"github.com/alienth/go-fastly"
	"github.com/juju/ratelimit"
	"github.com/urfave/cli"
	"gopkg.in/mcuadros/go-syslog.v2"
)

// getIPs takes in an haproxy log line and returns the client IP and the CDN
// connecting IP which is in the captured request header. For us, the CDN
// connecting IP happens to be the 8th captured request header.
func getIPs(logLine string) (*net.IP, *net.IP) {
	if logLine == "" {
		return nil, nil
	}
	// This string parsing stuff was lifted from TPS
	var a, b int
	if a = strings.Index(logLine, "]:") + 3; a == -1 {
		return nil, nil
	}
	if b = strings.Index(logLine[a:], ":"); b == -1 {
		return nil, nil
	}
	clientIPString := logLine[a : a+b]
	clientIP := net.ParseIP(clientIPString)
	if clientIP == nil {
		return nil, nil
	}

	logLine = logLine[a+b:]
	// The first curly-braced block contains our request headers
	if a = strings.Index(logLine, "{"); a == -1 {
		return &clientIP, nil
	}
	if b = strings.Index(logLine[a:], "}"); b == -1 {
		return &clientIP, nil
	}
	bracketedHeaders := logLine[a : a+b]
	headers := strings.Split(bracketedHeaders, "|")
	if len(headers) < 7 {
		return &clientIP, nil
	}
	ipString := headers[7]
	cdnIP := net.ParseIP(ipString)
	if cdnIP == nil {
		return &clientIP, nil
	}
	return &clientIP, &cdnIP
}

func getLogTimestamp(logLine string) int64 {
	timestampStr := strings.Split(logLine, " ")[6]
	ts, _ := time.Parse("[02/Jan/2006:15:04:05.999]", timestampStr)

	return (ts.Unix())
}

type ipList struct {
	ips  []net.IP
	nets []net.IPNet
}

func readIPList(filename string) (*ipList, error) {
	var list ipList
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.Index(strings.TrimSpace(line), "#") == 0 {
			continue
		}
		if strings.TrimSpace(line) == "" {
			continue
		}
		if strings.Contains(line, "/") {
			_, n, err := net.ParseCIDR(line)
			if err != nil {
				return nil, fmt.Errorf("Unable to parse CIDR.\nLine:\n%s\nError:\n%s\n", line, err)
			}
			if n != nil {
				list.nets = append(list.nets, *n)
			}
		} else {
			ip := net.ParseIP(line)
			if ip != nil {
				list.ips = append(list.ips, ip)
			} else {
				return nil, fmt.Errorf("Unable to parse IP address in list: %s\n", line)
			}
		}

	}
	return &list, nil
}

func (l *ipList) contains(checkIP *net.IP) bool {
	if l == nil {
		return false
	}
	for _, ip := range l.ips {
		if ip.Equal(*checkIP) {
			return true
		}
	}
	for _, net := range l.nets {
		if net.Contains(*checkIP) {
			return true
		}
	}

	return false
}

type ipRate struct {
	ip      *net.IP
	bucket  *ratelimit.Bucket
	entries []*util.ACLEntry

	FirstHit    int64 `json:"first_hit,omitempty"`
	LastHit     int64 `json:"last_hit,omitempty"`
	LastLimit   int64 `json:"last_limit,omitempty"`
	Hits        int   `json:"hits,omitempty"`
	Strikes     int   `json:"strikes,omitempty"`
	Expire      int64 `json:"-"`
	LimitExpire int64 `json:"limit_expire,omitempty"`
}

func (ipr *ipRate) New(ip *net.IP) {
	ipr.ip = ip
	ipr.bucket = ratelimit.NewBucket(time.Duration(3)*time.Minute, 180)
	ipr.Expire = time.Now().Add(time.Duration(1) * time.Hour).Unix()
}

// Records a hit and returns true if it is over limit.
func (ipr *ipRate) Hit() bool {
	// Here we pretend that the IP performed no hits before the limit was removed,
	// since in testing the IP is not actually limited.
	if ipr.LastLimit != 0 && time.Now().Sub(time.Unix(ipr.LastLimit, 0)) < time.Duration(1)*time.Hour {
		return false
	}

	_, isSoonerThanMaxWait := ipr.bucket.TakeMaxDuration(1, 0)
	if ipr.FirstHit == 0 {
		ipr.FirstHit = time.Now().Unix()
	}
	ipr.LastHit = time.Now().Unix()
	ipr.Hits += 1
	ipr.Expire = time.Now().Add(time.Duration(1) * time.Hour).Unix()
	return !isSoonerThanMaxWait
}

// Limit adds an IP to a fastly edge ACL
func (ipr *ipRate) Limit() error {

	service, err := util.GetServiceByNameOrID(client, "teststackoverflow.com")
	if err != nil {
		return err
	}

	acl, err := util.NewACL(client, service.ID, "ratelimit")
	if err != nil {
		return err
	}

	// TODO this is heavy.. cache? do we even need?(see below)
	entries, err := acl.ListEntries()
	if err != nil {
		return err
	}

	var entry *util.ACLEntry

	for _, e := range entries {
		if ipr.ip.String() == e.IP {
			fmt.Printf("IP %s is already limited.\n", e.IP)
			// Not all ACL entries were necessarily created by this script, so silently ignore unmarshal errors.
			if err := json.Unmarshal([]byte(e.Comment), ipr); err != nil {
				break
			}
			entry = &util.ACLEntry{Client: client, ID: e.ID, ACLID: e.ACLID, ServiceID: service.ID}
			break
		}
	}

	ipr.LastLimit = time.Now().Unix()
	ipr.Strikes += 1
	ipr.LimitExpire = time.Now().Add(time.Duration(5) * time.Minute).Unix()
	ipr.Expire = time.Now().Add(time.Duration(24) * time.Hour).Unix()
	comment, err := json.Marshal(ipr)
	if err != nil {
		return err
	}
	if entry == nil {
		entry, err = util.NewACLEntry(client, service.ID, "ratelimit", ipr.ip.String(), 0, string(comment), false)
		if err != nil {
			return err
		}
		entry.Add()
	} else {
		entry.Comment = string(comment)
		//		entry.Update()
	}

	ipr.entries = append(ipr.entries, entry)
	return nil
}

// Removes an IP from ratelimits
func (ipr *ipRate) RemoveLimit() error {
	if len(ipr.entries) > 0 {
		fmt.Printf("Unlimiting IP %s\n", ipr.ip.String())
		for i, entry := range ipr.entries {
			entry.Remove()
			ipr.entries[i] = nil
		}
		ipr.entries = ipr.entries[:0]
	}
	return nil
}

type hitMap struct {
	sync.RWMutex
	m map[string]*ipRate
}

func expireRecords(hits *hitMap) {
	for {
		hits.Lock()
		for ip, ipr := range hits.m {
			if ipr.Expire < time.Now().Unix() {
				ipr.RemoveLimit()
				delete(hits.m, ip)
			}
		}
		hits.Unlock()
		time.Sleep(time.Duration(60) * time.Second)
	}
}

func expireLimits(hits *hitMap) {
	for {
		hits.Lock()
		for _, ipr := range hits.m {
			if ipr.LimitExpire < time.Now().Unix() {
				ipr.RemoveLimit()
			}
		}
		hits.Unlock()
		time.Sleep(time.Duration(15) * time.Second)
	}
}

var client *fastly.Client

func main() {
	app := cli.NewApp()
	app.Name = "fastly-ratelimit"

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "whitelist, w",
			Usage: "Read `FILE` containing addresses which should whitelisted from any bans.",
		},
		cli.StringFlag{
			Name:  "listen, l",
			Usage: "Specify listen `ADDRESS:PORT`.",
			Value: "0.0.0.0:514",
		},
		cli.StringFlag{
			Name:   "fastly-key, K",
			Usage:  "Fastly API Key. Can be read from 'fastly_key' file in CWD.",
			EnvVar: "FASTLY_KEY",
			Value:  util.GetFastlyKey(),
		},
	}
	app.Action = func(c *cli.Context) error {
		client, _ = fastly.NewClient(c.GlobalString("fastly-key"))
		channel := make(syslog.LogPartsChannel)
		handler := syslog.NewChannelHandler(channel)

		server := syslog.NewServer()
		server.SetFormat(syslog.RFC3164)
		server.SetHandler(handler)
		if err := server.ListenUDP(c.GlobalString("listen")); err != nil {
			return cli.NewExitError(fmt.Sprintf("Unable to listen: %s\n", err), -1)
		}
		if err := server.Boot(); err != nil {
			return cli.NewExitError(fmt.Sprintf("Unable to start server: %s\n", err), -1)
		}

		var whitelist *ipList
		var err error
		whitelistFile := c.GlobalString("whitelist")
		if whitelistFile != "" {
			if whitelist, err = readIPList(whitelistFile); err != nil {
				return cli.NewExitError(fmt.Sprintf("Error reading whitelist file: %s", err), -1)
			}
		}

		var hits = hitMap{m: make(map[string]*ipRate)}
		go expireRecords(&hits)
		go expireLimits(&hits)
		go func(channel syslog.LogPartsChannel) {
			for logParts := range channel {
				var line string
				var ok bool
				if line, ok = logParts["content"].(string); !ok || line == "" {
					continue
				}
				clientIP, cdnIP := getIPs(line)
				if cdnIP == nil || clientIP == nil {
					continue
				}
				var ipr *ipRate
				var found bool
				hits.Lock()
				if ipr, found = hits.m[cdnIP.String()]; !found {
					ipr = &ipRate{}
					ipr.New(cdnIP)
					hits.m[cdnIP.String()] = ipr
				}
				hits.Unlock()
				overLimit := ipr.Hit()
				if overLimit {
					if whitelist.contains(cdnIP) {
						//fmt.Println("but is whitelisted so we don't care.")
					} else {
						if err := ipr.Limit(); err != nil {
							fmt.Printf("Error limiting IP: %s", err)
						}
					}
				}
			}
		}(channel)

		server.Wait()

		return nil
	}
	app.Run(os.Args)
}
<commit_msg>bit of cleanup.<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"net"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/alienth/fastlyctl/util"
	"github.com/alienth/go-fastly"
	"github.com/juju/ratelimit"
	"github.com/urfave/cli"
	"gopkg.in/mcuadros/go-syslog.v2"
)

// getIPs takes in an haproxy log line and returns the client IP and the CDN
// connecting IP which is in the captured request header. For us, the CDN
// connecting IP happens to be the 8th captured request header.
func getIPs(logLine string) (*net.IP, *net.IP) {
	if logLine == "" {
		return nil, nil
	}
	// This string parsing stuff was lifted from TPS
	var a, b int
	if a = strings.Index(logLine, "]:") + 3; a == -1 {
		return nil, nil
	}
	if b = strings.Index(logLine[a:], ":"); b == -1 {
		return nil, nil
	}
	clientIPString := logLine[a : a+b]
	clientIP := net.ParseIP(clientIPString)
	if clientIP == nil {
		return nil, nil
	}

	logLine = logLine[a+b:]
	// The first curly-braced block contains our request headers
	if a = strings.Index(logLine, "{"); a == -1 {
		return &clientIP, nil
	}
	if b = strings.Index(logLine[a:], "}"); b == -1 {
		return &clientIP, nil
	}
	bracketedHeaders := logLine[a : a+b]
	headers := strings.Split(bracketedHeaders, "|")
	if len(headers) < 7 {
		return &clientIP, nil
	}
	ipString := headers[7]
	cdnIP := net.ParseIP(ipString)
	if cdnIP == nil {
		return &clientIP, nil
	}
	return &clientIP, &cdnIP
}

func getLogTimestamp(logLine string) int64 {
	timestampStr := strings.Split(logLine, " ")[6]
	ts, _ := time.Parse("[02/Jan/2006:15:04:05.999]", timestampStr)

	return (ts.Unix())
}

type ipList struct {
	ips  []net.IP
	nets []net.IPNet
}

func readIPList(filename string) (*ipList, error) {
	var list ipList
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.Index(strings.TrimSpace(line), "#") == 0 {
			continue
		}
		if strings.TrimSpace(line) == "" {
			continue
		}
		if strings.Contains(line, "/") {
			_, n, err := net.ParseCIDR(line)
			if err != nil {
				return nil, fmt.Errorf("Unable to parse CIDR.\nLine:\n%s\nError:\n%s\n", line, err)
			}
			if n != nil {
				list.nets = append(list.nets, *n)
			}
		} else {
			ip := net.ParseIP(line)
			if ip != nil {
				list.ips = append(list.ips, ip)
			} else {
				return nil, fmt.Errorf("Unable to parse IP address in list: %s\n", line)
			}
		}

	}
	return &list, nil
}

func (l *ipList) contains(checkIP *net.IP) bool {
	if l == nil {
		return false
	}
	for _, ip := range l.ips {
		if ip.Equal(*checkIP) {
			return true
		}
	}
	for _, net := range l.nets {
		if net.Contains(*checkIP) {
			return true
		}
	}

	return false
}

type ipRate struct {
	ip      *net.IP
	bucket  *ratelimit.Bucket
	entries []*util.ACLEntry

	FirstHit    int64 `json:"first_hit,omitempty"`
	LastHit     int64 `json:"last_hit,omitempty"`
	LastLimit   int64 `json:"last_limit,omitempty"`
	Hits        int   `json:"hits,omitempty"`
	Strikes     int   `json:"strikes,omitempty"`
	Expire      int64 `json:"-"`
	LimitExpire int64 `json:"limit_expire,omitempty"`
}

func (ipr *ipRate) New(ip *net.IP) {
	ipr.ip = ip
	ipr.bucket = ratelimit.NewBucket(time.Duration(3)*time.Minute, 180)
	ipr.Expire = time.Now().Add(time.Duration(1) * time.Hour).Unix()
}

// Records a hit and returns true if it is over limit.
func (ipr *ipRate) Hit() bool {
	// Here we pretend that the IP performed no hits before the limit was removed,
	// since in testing the IP is not actually limited.
	if ipr.LastLimit != 0 && time.Now().Sub(time.Unix(ipr.LastLimit, 0)) < time.Duration(1)*time.Hour {
		return false
	}

	_, isSoonerThanMaxWait := ipr.bucket.TakeMaxDuration(1, 0)
	if ipr.FirstHit == 0 {
		ipr.FirstHit = time.Now().Unix()
	}
	ipr.LastHit = time.Now().Unix()
	ipr.Hits++
	ipr.Expire = time.Now().Add(time.Duration(1) * time.Hour).Unix()
	return !isSoonerThanMaxWait
}

// Limit adds an IP to a fastly edge ACL
func (ipr *ipRate) Limit() error {

	service, err := util.GetServiceByNameOrID(client, "teststackoverflow.com")
	if err != nil {
		return err
	}

	acl, err := util.NewACL(client, service.ID, "ratelimit")
	if err != nil {
		return err
	}

	// TODO this is heavy.. cache? do we even need?(see below)
	entries, err := acl.ListEntries()
	if err != nil {
		return err
	}

	var entry *util.ACLEntry

	for _, e := range entries {
		if ipr.ip.String() == e.IP {
			fmt.Printf("IP %s is already limited.\n", e.IP)
			// Not all ACL entries were necessarily created by this script, so silently ignore unmarshal errors.
			if err := json.Unmarshal([]byte(e.Comment), ipr); err != nil {
				break
			}
			entry = &util.ACLEntry{Client: client, ID: e.ID, ACLID: e.ACLID, ServiceID: service.ID}
			break
		}
	}

	ipr.LastLimit = time.Now().Unix()
	ipr.Strikes++
	ipr.LimitExpire = time.Now().Add(time.Duration(5) * time.Minute).Unix()
	ipr.Expire = time.Now().Add(time.Duration(24) * time.Hour).Unix()
	comment, err := json.Marshal(ipr)
	if err != nil {
		return err
	}
	if entry == nil {
		entry, err = util.NewACLEntry(client, service.ID, "ratelimit", ipr.ip.String(), 0, string(comment), false)
		if err != nil {
			return err
		}
		entry.Add()
	} else {
		entry.Comment = string(comment)
		//		entry.Update()
	}

	ipr.entries = append(ipr.entries, entry)
	return nil
}

// Removes an IP from ratelimits
func (ipr *ipRate) RemoveLimit() error {
	if len(ipr.entries) > 0 {
		fmt.Printf("Unlimiting IP %s\n", ipr.ip.String())
		for i, entry := range ipr.entries {
			if err := entry.Remove(); err != nil {
				fmt.Println(err)
			}
			ipr.entries[i] = nil
		}
		ipr.entries = ipr.entries[:0]
	}
	return nil
}

type hitMap struct {
	sync.RWMutex
	m map[string]*ipRate
}

func expireRecords(hits *hitMap) {
	for {
		hits.Lock()
		for ip, ipr := range hits.m {
			if ipr.Expire < time.Now().Unix() {
				ipr.RemoveLimit()
				delete(hits.m, ip)
			}
		}
		hits.Unlock()
		time.Sleep(time.Duration(60) * time.Second)
	}
}

func expireLimits(hits *hitMap) {
	for {
		hits.Lock()
		for _, ipr := range hits.m {
			if ipr.LimitExpire < time.Now().Unix() {
				ipr.RemoveLimit()
			}
		}
		hits.Unlock()
		time.Sleep(time.Duration(15) * time.Second)
	}
}

var client *fastly.Client

func main() {
	app := cli.NewApp()
	app.Name = "fastly-ratelimit"

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "whitelist, w",
			Usage: "Read `FILE` containing addresses which should whitelisted from any bans.",
		},
		cli.StringFlag{
			Name:  "listen, l",
			Usage: "Specify listen `ADDRESS:PORT`.",
			Value: "0.0.0.0:514",
		},
		cli.StringFlag{
			Name:   "fastly-key, K",
			Usage:  "Fastly API Key. Can be read from 'fastly_key' file in CWD.",
			EnvVar: "FASTLY_KEY",
			Value:  util.GetFastlyKey(),
		},
	}
	app.Action = func(c *cli.Context) error {
		client, _ = fastly.NewClient(c.GlobalString("fastly-key"))
		channel := make(syslog.LogPartsChannel)
		handler := syslog.NewChannelHandler(channel)

		server := syslog.NewServer()
		server.SetFormat(syslog.RFC3164)
		server.SetHandler(handler)
		if err := server.ListenUDP(c.GlobalString("listen")); err != nil {
			return cli.NewExitError(fmt.Sprintf("Unable to listen: %s\n", err), -1)
		}
		if err := server.Boot(); err != nil {
			return cli.NewExitError(fmt.Sprintf("Unable to start server: %s\n", err), -1)
		}

		var whitelist *ipList
		var err error
		whitelistFile := c.GlobalString("whitelist")
		if whitelistFile != "" {
			if whitelist, err = readIPList(whitelistFile); err != nil {
				return cli.NewExitError(fmt.Sprintf("Error reading whitelist file: %s", err), -1)
			}
		}

		var hits = hitMap{m: make(map[string]*ipRate)}
		go expireRecords(&hits)
		go expireLimits(&hits)
		go func(channel syslog.LogPartsChannel) {
			for logParts := range channel {
				var line string
				var ok bool
				if line, ok = logParts["content"].(string); !ok || line == "" {
					continue
				}
				clientIP, cdnIP := getIPs(line)
				if cdnIP == nil || clientIP == nil {
					continue
				}
				var ipr *ipRate
				var found bool
				hits.Lock()
				if ipr, found = hits.m[cdnIP.String()]; !found {
					ipr = &ipRate{}
					ipr.New(cdnIP)
					hits.m[cdnIP.String()] = ipr
				}
				hits.Unlock()
				overLimit := ipr.Hit()
				if overLimit {
					if whitelist.contains(cdnIP) {
						//fmt.Println("but is whitelisted so we don't care.")
					} else {
						if err := ipr.Limit(); err != nil {
							fmt.Printf("Error limiting IP: %s", err)
						}
					}
				}
			}
		}(channel)

		server.Wait()

		return nil
	}
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/gob"
	"github.com/bradfitz/gomemcache/memcache"
	"net/http"
)

func main() {
	server := &Server{
		Cache: &MemcacheImageCache{
			memcache: memcache.New("localhost"),
		},
	}

	http.HandleFunc("/", server.HandleHttpRequest)
	http.ListenAndServe(":8080", nil)
}

type Server struct {
	Cache ImageCache
}

func (server *Server) HandleHttpRequest(w http.ResponseWriter, r *http.Request) {

}

type Image struct {
	Type string
	Data []byte
}

func (image *Image) serialize() []byte {
	buffer := bytes.NewBuffer([]byte{})
	encoder := gob.NewEncoder(buffer)
	encoder.Encode(image)
	return buffer.Bytes()
}

func (image *Image) unserialize(data []byte) {
	buffer := bytes.NewBuffer(data)
	decoder := gob.NewDecoder(buffer)
	decoder.Decode(image)
}

type ImageCache interface {
	Get(key string) *Image
	Set(key string, image *Image)
}

type MemcacheImageCache struct {
	prefix   string
	memcache *memcache.Client
}

func (c *MemcacheImageCache) Get(key string) *Image {
	return nil
}

func (c *MemcacheImageCache) Set(key string, image *Image) {
}
<commit_msg>Add base for converter<commit_after>package main

import (
	"bytes"
	"encoding/gob"
	"github.com/bradfitz/gomemcache/memcache"
	"net/http"
)

func main() {
	server := &Server{
		Cache: &MemcacheImageCache{
			memcache: memcache.New("localhost"),
		},
	}

	http.HandleFunc("/", server.HandleHttpRequest)
	http.ListenAndServe(":8080", nil)
}

type Server struct {
	Cache ImageCache
}

func (server *Server) HandleHttpRequest(w http.ResponseWriter, r *http.Request) {

}

type Image struct {
	Type string
	Data []byte
}

func (image *Image) serialize() []byte {
	buffer := bytes.NewBuffer([]byte{})
	encoder := gob.NewEncoder(buffer)
	encoder.Encode(image)
	return buffer.Bytes()
}

func (image *Image) unserialize(data []byte) {
	buffer := bytes.NewBuffer(data)
	decoder := gob.NewDecoder(buffer)
	decoder.Decode(image)
}

type ImageCache interface {
	Get(key string) *Image
	Set(key string, image *Image)
}

type MemcacheImageCache struct {
	prefix   string
	memcache *memcache.Client
}

func (c *MemcacheImageCache) Get(key string) *Image {
	return nil
}

func (c *MemcacheImageCache) Set(key string, image *Image) {
}

type ImageConverter interface {
	Convert(image *Image, parameters *ImageConverterParameters) *Image
}

type ImageConverterParameters struct {
	Width  int
	Height int
}

type GraphicsMagickImageConverter struct {
	executable string
}

func (converter *GraphicsMagickImageConverter) Convert(image *Image, parameters *ImageConverterParameters) *Image {
	return nil
}
<|endoftext|>


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

package main

import (
	"crypto/rand"
	"encoding/hex"
	"errors"
	"flag"
	"fmt" // flag uses fmt anyway
	"io"
	"os"
)

func main() {
	const maxbuf = 4096
	var (
		n   int64
		r   int
		raw bool
	)

	flag.Int64Var(&n, "n", 16, "number of (unencoded) bytes to output")
	flag.IntVar(&r, "r", 1, "number of repititions (output lines)")
	flag.BoolVar(&raw, "raw", false, "raw output")
	flag.Parse()

	var w io.Writer = os.Stdout

	if !raw {
		w = NewHexWriter(w)
	}

	for r > 0 {
		_, err := io.CopyN(w, rand.Reader, n)
		fmt.Println()
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		r--
	}
}

type hexwriter struct {
	w io.Writer
	n int
	b []byte
}

var ErrOddSizeWrite = errors.New("Odd sized write")

func NewHexWriter(w io.Writer) io.Writer {
	return &hexwriter{w: w}
}

func (h *hexwriter) Write(data []byte) (n int, err error) {
	if len(data) > h.n {
		h.n = len(data)
		h.b = make([]byte, hex.EncodedLen(h.n))
	}
	hex.Encode(h.b, data)
	n, err = h.w.Write(h.b)
	if n != len(h.b) {
		if err == nil {
			if n%2 > 0 {
				err = ErrOddSizeWrite
			} else {
				err = io.ErrShortWrite
			}
		}
	}
	n = hex.DecodedLen(n)
	return
}
<commit_msg>Added base64 output.<commit_after>// Copyright 2012 Kevin Gillette. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/hex"
	"errors"
	"flag"
	"fmt" // flag uses fmt anyway
	"io"
	"os"
)

func main() {
	const maxbuf = 4096
	var (
		n   int64
		r   int
		b64 bool
		raw bool
	)

	flag.Int64Var(&n, "n", 16, "number of (unencoded) bytes to output")
	flag.IntVar(&r, "r", 1, "number of repititions (output lines)")
	flag.BoolVar(&raw, "raw", false, "raw output")
	flag.BoolVar(&b64, "64", false, "base64 output")
	flag.Parse()

	var w io.WriteCloser = NopWriteCloser(os.Stdout)

	if !raw {
		if b64 {
			w = base64.NewEncoder(base64.StdEncoding, w)
		} else {
			w = NopWriteCloser(NewHexWriter(w))
		}
	}

	for r > 0 {
		_, err := io.CopyN(w, rand.Reader, n)
		w.Close() // ignoring error
		fmt.Println()
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		r--
	}
}

type writecloser struct {
	io.Writer
}

func (w writecloser) Close() error {
	return nil
}

func NopWriteCloser(w io.Writer) io.WriteCloser {
	return writecloser{w}
}

type hexwriter struct {
	w io.Writer
	n int
	b []byte
}

var ErrOddSizeWrite = errors.New("Odd sized write")

func NewHexWriter(w io.Writer) io.Writer {
	return &hexwriter{w: w}
}

func (h *hexwriter) Write(data []byte) (n int, err error) {
	if len(data) > h.n {
		h.n = len(data)
		h.b = make([]byte, hex.EncodedLen(h.n))
	}
	hex.Encode(h.b, data)
	n, err = h.w.Write(h.b)
	if n != len(h.b) {
		if err == nil {
			if n%2 > 0 {
				err = ErrOddSizeWrite
			} else {
				err = io.ErrShortWrite
			}
		}
	}
	n = hex.DecodedLen(n)
	return
}
<|endoftext|>


<commit_before>package tachymeter

import (
	"fmt"
	"sync"
	"time"
)

type Config struct {
	Size int
	Safe bool // Optionally lock if concurrent access is needed.
}

type timeSlice []time.Duration

type Tachymeter struct {
	sync.Mutex
	Safe          bool
	Times         timeSlice
	TimesPosition int
	TimesUsed     int
	Count         int
}

type Metrics struct {
	Time struct {
		Total    time.Duration
		Avg      time.Duration
		Median   time.Duration
		p95      time.Duration
		Long5p  time.Duration
		Short5p time.Duration
		Max      time.Duration
		Min      time.Duration
	}
	Rate struct {
		Second float64
	}
	Samples int
	Count   int
}

func New(c *Config) *Tachymeter {
	return &Tachymeter{
		Times: make([]time.Duration, c.Size),
		Safe:  c.Safe,
	}
}

// AddTime adds a time.Duration to the Tachymeter.Times
// slice, then increments the position.
func (m *Tachymeter) AddTime(t time.Duration) {
	if m.Safe {
		m.Lock()
		defer m.Unlock()
	}

	// If we're at the end, rollover and
	// start overwriting.
	if m.TimesPosition == len(m.Times) {
		m.TimesPosition = 0
	}

	m.Times[m.TimesPosition] = t
	m.TimesPosition++
	if m.TimesUsed < len(m.Times) {
		m.TimesUsed++
	}
}

// AddCount simply counts events.
func (m *Tachymeter) AddCount(i int) {
	if m.Safe {
		m.Lock()
		defer m.Unlock()
	}

	m.Count += i
}

// Dump prints out a generic output of
// all gathered metrics.
func (m *Tachymeter) Dump() {
	metrics := m.Calc()
	fmt.Printf("%d samples of %d events\n", metrics.Samples, metrics.Count)
	fmt.Printf("Total:\t\t%s\n", metrics.Time.Total)
	fmt.Printf("Avg.:\t\t%s\n", metrics.Time.Avg)
	fmt.Printf("95%%ile:\t\t%s\n", metrics.Time.p95)
	fmt.Printf("Longest 5%%:\t%s\n", metrics.Time.Long10p)
	fmt.Printf("Shortest 5%%:\t%s\n", metrics.Time.Short10p)
	fmt.Printf("Max:\t\t%s\n", metrics.Time.Max)
	fmt.Printf("Min:\t\t%s\n", metrics.Time.Min)
	fmt.Printf("Rate/sec.:\t%.2f\n", metrics.Rate.Second)
}
<commit_msg>fixed var ref<commit_after>package tachymeter

import (
	"fmt"
	"sync"
	"time"
)

type Config struct {
	Size int
	Safe bool // Optionally lock if concurrent access is needed.
}

type timeSlice []time.Duration

type Tachymeter struct {
	sync.Mutex
	Safe          bool
	Times         timeSlice
	TimesPosition int
	TimesUsed     int
	Count         int
}

type Metrics struct {
	Time struct {
		Total    time.Duration
		Avg      time.Duration
		Median   time.Duration
		p95      time.Duration
		Long5p  time.Duration
		Short5p time.Duration
		Max      time.Duration
		Min      time.Duration
	}
	Rate struct {
		Second float64
	}
	Samples int
	Count   int
}

func New(c *Config) *Tachymeter {
	return &Tachymeter{
		Times: make([]time.Duration, c.Size),
		Safe:  c.Safe,
	}
}

// AddTime adds a time.Duration to the Tachymeter.Times
// slice, then increments the position.
func (m *Tachymeter) AddTime(t time.Duration) {
	if m.Safe {
		m.Lock()
		defer m.Unlock()
	}

	// If we're at the end, rollover and
	// start overwriting.
	if m.TimesPosition == len(m.Times) {
		m.TimesPosition = 0
	}

	m.Times[m.TimesPosition] = t
	m.TimesPosition++
	if m.TimesUsed < len(m.Times) {
		m.TimesUsed++
	}
}

// AddCount simply counts events.
func (m *Tachymeter) AddCount(i int) {
	if m.Safe {
		m.Lock()
		defer m.Unlock()
	}

	m.Count += i
}

// Dump prints out a generic output of
// all gathered metrics.
func (m *Tachymeter) Dump() {
	metrics := m.Calc()
	fmt.Printf("%d samples of %d events\n", metrics.Samples, metrics.Count)
	fmt.Printf("Total:\t\t%s\n", metrics.Time.Total)
	fmt.Printf("Avg.:\t\t%s\n", metrics.Time.Avg)
	fmt.Printf("95%%ile:\t\t%s\n", metrics.Time.p95)
	fmt.Printf("Longest 5%%:\t%s\n", metrics.Time.Long5p)
	fmt.Printf("Shortest 5%%:\t%s\n", metrics.Time.Short5p)
	fmt.Printf("Max:\t\t%s\n", metrics.Time.Max)
	fmt.Printf("Min:\t\t%s\n", metrics.Time.Min)
	fmt.Printf("Rate/sec.:\t%.2f\n", metrics.Rate.Second)
}
<|endoftext|>