<commit_before>// Copyright (c) 2011 Mateusz Czapliński (Go port)
// Copyright (c) 2011 Mahir Iqbal (as3 version)
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

// based on http://code.google.com/p/as3polyclip/ (MIT licensed)
// and code by Martínez et al: http://wwwdi.ujaen.es/~fmartin/bool_op.html (public domain)

// Package geomop provides implementation of algorithms for geometry operations.
// For further details, consult the description of Polygon.Construct method.
package geomop

import (
	"fmt"
	"math"
	"reflect"

	"github.com/twpayne/gogeom/geom"
)

// Equals returns true if both p1 and p2 describe the same point within
// a tolerance limit.
func PointEquals(p1, p2 geom.Point) bool {
	return (p1.X == p2.X && p1.Y == p2.Y)
	//return (p1.X == p2.X && p1.Y == p2.Y) ||
	//	(math.Abs(p1.X-p2.X)/math.Abs(p1.X+p2.X) < tolerance &&
	//		math.Abs(p1.Y-p2.Y)/math.Abs(p1.Y+p2.Y) < tolerance)
}

func floatEquals(f1, f2 float64) bool {
	return (f1 == f2)
	//return (f1 == f2) ||
	//	(math.Abs(f1-f2)/math.Abs(f1+f2) < tolerance)
}

func pointSubtract(p1, p2 geom.Point) geom.Point {
	return geom.Point{p1.X - p2.X, p1.Y - p2.Y}
}

// Length returns distance from p to point (0, 0).
func lengthToOrigin(p geom.Point) float64 {
	return math.Sqrt(p.X*p.X + p.Y*p.Y)
}

// Used to represent an edge of a polygon.
type segment struct {
	start, end geom.Point
}

// Contour represents a sequence of vertices connected by line segments, forming a closed shape.
type contour []geom.Point

func (c contour) segment(index int) segment {
	if index == len(c)-1 {
		return segment{c[len(c)-1], c[0]}
	}
	return segment{c[index], c[index+1]}
	// if out-of-bounds, we expect panic detected by runtime
}

// Clone returns a copy of a contour.
func (c contour) Clone() contour {
	return append([]geom.Point{}, c...)
}

// numVertices returns total number of all vertices of all contours of a polygon.
func numVertices(p geom.Polygon) int {
	num := 0
	for _, c := range p.Rings {
		num += len(c)
	}
	return num
}

// Clone returns a duplicate of a polygon.
func Clone(p geom.Polygon) geom.Polygon {
	var r geom.Polygon
	r.Rings = make([][]geom.Point, len(p.Rings))
	for i, rr := range p.Rings {
		r.Rings[i] = make([]geom.Point, len(rr))
		for j, pp := range p.Rings[i] {
			r.Rings[i][j] = pp
		}
	}
	return r
}

// Op describes an operation which can be performed on two polygons.
type Op int

const (
	UNION Op = iota
	INTERSECTION
	DIFFERENCE
	XOR
)

// Function Construct computes a 2D polygon, which is a result of performing
// specified Boolean operation on the provided pair of polygons (p <Op> clipping).
// It uses an algorithm described by F. Martínez, A. J. Rueda, F. R. Feito
// in "A new algorithm for computing Boolean operations on polygons"
// - see: http://wwwdi.ujaen.es/~fmartin/bool_op.html
// The paper describes the algorithm as performing in time O((n+k) log n),
// where n is number of all edges of all polygons in operation, and
// k is number of intersections of all polygon edges.
// "subject" and "clipping" can both be of type geom.Polygon,
// geom.MultiPolygon, geom.LineString, or geom.MultiLineString.
func Construct(subject, clipping geom.T, operation Op) (geom.T, error) {
	if subject == nil && clipping == nil {
		return nil, nil
	} else if subject == nil {
		if operation == INTERSECTION || operation == DIFFERENCE {
			return nil, nil
		} else {
			return clipping, nil
		}
	} else if clipping == nil {
		if operation == INTERSECTION {
			return nil, nil
		} else {
			return subject, nil
		}
	}
	// Prepare the input shapes
	var c clipper
	switch clipping.(type) {
	case geom.Polygon, geom.MultiPolygon:
		c.subject = convertToPolygon(subject)
		c.clipping = convertToPolygon(clipping)
		switch subject.(type) {
		case geom.Polygon, geom.MultiPolygon:
			c.outType = outputPolygons
		case geom.LineString, geom.MultiLineString:
			c.outType = outputLines
		default:
			return nil, newUnsupportedGeometryError(subject)
		}
	case geom.LineString, geom.MultiLineString:
		switch subject.(type) {
		case geom.Polygon, geom.MultiPolygon:
			// swap clipping and subject
			c.subject = convertToPolygon(clipping)
			c.clipping = convertToPolygon(subject)
			c.outType = outputLines
		case geom.LineString, geom.MultiLineString:
			c.subject = convertToPolygon(subject)
			c.clipping = convertToPolygon(clipping)
			c.outType = outputPoints
		default:
			return nil, newUnsupportedGeometryError(subject)
		}
	default:
		return nil, newUnsupportedGeometryError(clipping)
	}
	//resultChan := make(chan geom.T)
	//go func() {
		// Run the clipper, while checking for an infinite loop.
	//	resultChan <- c.compute(operation)
	//}()
	//select {
	//case result := <-resultChan:
	//	return result, nil
	//case <-time.After(30 * time.Minute):
	//	return nil, newInfiniteLoopError(subject, clipping)
	//}
	return c.compute(operation), nil
}

// convert input shapes to polygon to make internal processing easier
func convertToPolygon(g geom.T) geom.Polygon {
	var out geom.Polygon
	switch g.(type) {
	case geom.Polygon:
		out = g.(geom.Polygon)
	case geom.MultiPolygon:
		out.Rings = make([][]geom.Point, 0)
		for _, p := range g.(geom.MultiPolygon).Polygons {
			for _, r := range p.Rings {
				out.Rings = append(out.Rings, r)
			}
		}
	case geom.LineString:
		g2 := g.(geom.LineString)
		out.Rings = make([][]geom.Point, 1)
		out.Rings[0] = make([]geom.Point, len(g2.Points))
		for j, p := range g2.Points {
			out.Rings[0][j] = p
		}
	case geom.MultiLineString:
		g2 := g.(geom.MultiLineString)
		out.Rings = make([][]geom.Point, len(g2.LineStrings))
		for i, ls := range g2.LineStrings {
			out.Rings[i] = make([]geom.Point, len(ls.Points))
			for j, p := range ls.Points {
				out.Rings[i][j] = p
			}
		}
	default:
		panic(newUnsupportedGeometryError(g))
	}
	// The clipper doesn't work well if a shape is made up of only two points.
	// To get around this problem, if there are only 2 points, we add a third
	// one a small distance from the second point.
	// However, if there is only 1 point, we just delete the shape.
	for i, r := range out.Rings {
		if len(r) == 0 {
			continue
		} else if len(r) == 1 {
			out.Rings[i] = make([]geom.Point, 0)
		} else if len(r) == 2 {
			const delta = 0.00001
			newpt := geom.Point{r[1].X + (r[1].X-r[0].X)*delta,
				r[1].Y - (r[1].Y-r[0].Y)*delta}
			out.Rings[i] = append(r, newpt)
		}
	}
	return out
}

type UnsupportedGeometryError struct {
	G geom.T
}

func newUnsupportedGeometryError(g geom.T) UnsupportedGeometryError {
	return UnsupportedGeometryError{g}
}

func (e UnsupportedGeometryError) Error() string {
	if e.G == nil {
		return "Geometry is nil."
	} else {
		return "Unsupported geometry type: " + reflect.TypeOf(e.G).String()
	}
}

type InfiniteLoopError struct {
	s, c geom.T
}

func newInfiniteLoopError(subject, clipping geom.T) InfiniteLoopError {
	return InfiniteLoopError{s: subject, c: clipping}
}

func (e InfiniteLoopError) Error() string {
	return fmt.Sprintf(
		"Function geomop.Construct appears to have fallen into an "+
			"infinite loop. \n\nSubject geometry=%v\n\nClipping geometry=%v",
		e.s, e.c)
}
<commit_msg>Added infinite loop checker back in. Hopefully fixed memory leak<commit_after>// Copyright (c) 2011 Mateusz Czapliński (Go port)
// Copyright (c) 2011 Mahir Iqbal (as3 version)
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

// based on http://code.google.com/p/as3polyclip/ (MIT licensed)
// and code by Martínez et al: http://wwwdi.ujaen.es/~fmartin/bool_op.html (public domain)

// Package geomop provides implementation of algorithms for geometry operations.
// For further details, consult the description of Polygon.Construct method.
package geomop

import (
	"fmt"
	"math"
	"reflect"
	"time"

	"github.com/twpayne/gogeom/geom"
)

// Equals returns true if both p1 and p2 describe the same point within
// a tolerance limit.
func PointEquals(p1, p2 geom.Point) bool {
	return (p1.X == p2.X && p1.Y == p2.Y)
	//return (p1.X == p2.X && p1.Y == p2.Y) ||
	//	(math.Abs(p1.X-p2.X)/math.Abs(p1.X+p2.X) < tolerance &&
	//		math.Abs(p1.Y-p2.Y)/math.Abs(p1.Y+p2.Y) < tolerance)
}

func floatEquals(f1, f2 float64) bool {
	return (f1 == f2)
	//return (f1 == f2) ||
	//	(math.Abs(f1-f2)/math.Abs(f1+f2) < tolerance)
}

func pointSubtract(p1, p2 geom.Point) geom.Point {
	return geom.Point{p1.X - p2.X, p1.Y - p2.Y}
}

// Length returns distance from p to point (0, 0).
func lengthToOrigin(p geom.Point) float64 {
	return math.Sqrt(p.X*p.X + p.Y*p.Y)
}

// Used to represent an edge of a polygon.
type segment struct {
	start, end geom.Point
}

// Contour represents a sequence of vertices connected by line segments, forming a closed shape.
type contour []geom.Point

func (c contour) segment(index int) segment {
	if index == len(c)-1 {
		return segment{c[len(c)-1], c[0]}
	}
	return segment{c[index], c[index+1]}
	// if out-of-bounds, we expect panic detected by runtime
}

// Clone returns a copy of a contour.
func (c contour) Clone() contour {
	return append([]geom.Point{}, c...)
}

// numVertices returns total number of all vertices of all contours of a polygon.
func numVertices(p geom.Polygon) int {
	num := 0
	for _, c := range p.Rings {
		num += len(c)
	}
	return num
}

// Clone returns a duplicate of a polygon.
func Clone(p geom.Polygon) geom.Polygon {
	var r geom.Polygon
	r.Rings = make([][]geom.Point, len(p.Rings))
	for i, rr := range p.Rings {
		r.Rings[i] = make([]geom.Point, len(rr))
		for j, pp := range p.Rings[i] {
			r.Rings[i][j] = pp
		}
	}
	return r
}

// Op describes an operation which can be performed on two polygons.
type Op int

const (
	UNION Op = iota
	INTERSECTION
	DIFFERENCE
	XOR
)

// Function Construct computes a 2D polygon, which is a result of performing
// specified Boolean operation on the provided pair of polygons (p <Op> clipping).
// It uses an algorithm described by F. Martínez, A. J. Rueda, F. R. Feito
// in "A new algorithm for computing Boolean operations on polygons"
// - see: http://wwwdi.ujaen.es/~fmartin/bool_op.html
// The paper describes the algorithm as performing in time O((n+k) log n),
// where n is number of all edges of all polygons in operation, and
// k is number of intersections of all polygon edges.
// "subject" and "clipping" can both be of type geom.Polygon,
// geom.MultiPolygon, geom.LineString, or geom.MultiLineString.
func Construct(subject, clipping geom.T, operation Op) (geom.T, error) {
	if subject == nil && clipping == nil {
		return nil, nil
	} else if subject == nil {
		if operation == INTERSECTION || operation == DIFFERENCE {
			return nil, nil
		} else {
			return clipping, nil
		}
	} else if clipping == nil {
		if operation == INTERSECTION {
			return nil, nil
		} else {
			return subject, nil
		}
	}
	// Prepare the input shapes
	var c clipper
	switch clipping.(type) {
	case geom.Polygon, geom.MultiPolygon:
		c.subject = convertToPolygon(subject)
		c.clipping = convertToPolygon(clipping)
		switch subject.(type) {
		case geom.Polygon, geom.MultiPolygon:
			c.outType = outputPolygons
		case geom.LineString, geom.MultiLineString:
			c.outType = outputLines
		default:
			return nil, newUnsupportedGeometryError(subject)
		}
	case geom.LineString, geom.MultiLineString:
		switch subject.(type) {
		case geom.Polygon, geom.MultiPolygon:
			// swap clipping and subject
			c.subject = convertToPolygon(clipping)
			c.clipping = convertToPolygon(subject)
			c.outType = outputLines
		case geom.LineString, geom.MultiLineString:
			c.subject = convertToPolygon(subject)
			c.clipping = convertToPolygon(clipping)
			c.outType = outputPoints
		default:
			return nil, newUnsupportedGeometryError(subject)
		}
	default:
		return nil, newUnsupportedGeometryError(clipping)
	}
	resultChan := make(chan geom.T)
	go func() {
		// Run the clipper, while checking for an infinite loop.
		resultChan <- c.compute(operation)
	}()
	timer := time.NewTimer(1 * time.Minute)
	select {
	case result := <-resultChan:
		timer.Stop()
		return result, nil
	case <-timer.C:
		return nil, newInfiniteLoopError(subject, clipping)
	}
	//return c.compute(operation), nil
}

// convert input shapes to polygon to make internal processing easier
func convertToPolygon(g geom.T) geom.Polygon {
	var out geom.Polygon
	switch g.(type) {
	case geom.Polygon:
		out = g.(geom.Polygon)
	case geom.MultiPolygon:
		out.Rings = make([][]geom.Point, 0)
		for _, p := range g.(geom.MultiPolygon).Polygons {
			for _, r := range p.Rings {
				out.Rings = append(out.Rings, r)
			}
		}
	case geom.LineString:
		g2 := g.(geom.LineString)
		out.Rings = make([][]geom.Point, 1)
		out.Rings[0] = make([]geom.Point, len(g2.Points))
		for j, p := range g2.Points {
			out.Rings[0][j] = p
		}
	case geom.MultiLineString:
		g2 := g.(geom.MultiLineString)
		out.Rings = make([][]geom.Point, len(g2.LineStrings))
		for i, ls := range g2.LineStrings {
			out.Rings[i] = make([]geom.Point, len(ls.Points))
			for j, p := range ls.Points {
				out.Rings[i][j] = p
			}
		}
	default:
		panic(newUnsupportedGeometryError(g))
	}
	// The clipper doesn't work well if a shape is made up of only two points.
	// To get around this problem, if there are only 2 points, we add a third
	// one a small distance from the second point.
	// However, if there is only 1 point, we just delete the shape.
	for i, r := range out.Rings {
		if len(r) == 0 {
			continue
		} else if len(r) == 1 {
			out.Rings[i] = make([]geom.Point, 0)
		} else if len(r) == 2 {
			const delta = 0.00001
			newpt := geom.Point{r[1].X + (r[1].X-r[0].X)*delta,
				r[1].Y - (r[1].Y-r[0].Y)*delta}
			out.Rings[i] = append(r, newpt)
		}
	}
	return out
}

type UnsupportedGeometryError struct {
	G geom.T
}

func newUnsupportedGeometryError(g geom.T) UnsupportedGeometryError {
	return UnsupportedGeometryError{g}
}

func (e UnsupportedGeometryError) Error() string {
	if e.G == nil {
		return "Geometry is nil."
	} else {
		return "Unsupported geometry type: " + reflect.TypeOf(e.G).String()
	}
}

type InfiniteLoopError struct {
	s, c geom.T
}

func newInfiniteLoopError(subject, clipping geom.T) InfiniteLoopError {
	return InfiniteLoopError{s: subject, c: clipping}
}

func (e InfiniteLoopError) Error() string {
	return fmt.Sprintf(
		"Function geomop.Construct appears to have fallen into an "+
			"infinite loop. \n\nSubject geometry=%v\n\nClipping geometry=%v",
		e.s, e.c)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/goby-lang/goby/compiler"
	"github.com/goby-lang/goby/compiler/parser"
	"github.com/goby-lang/goby/igb"
	_ "github.com/goby-lang/goby/native/db"
	_ "github.com/goby-lang/goby/native/plugin"
	_ "github.com/goby-lang/goby/native/result"
	_ "github.com/goby-lang/goby/native/ripper"
	"github.com/goby-lang/goby/vm"
	"github.com/pkg/profile"
)

const Version string = vm.Version

func main() {
	profileOptionPtr := flag.Bool("p", false, "Profile program execution")
	versionOptionPtr := flag.Bool("v", false, "Show current Goby version")
	interactiveOptionPtr := flag.Bool("i", false, "Run interactive goby")
	issueOptionPtr := flag.Bool("e", false, "Generate reporting format")

	flag.Parse()

	if *interactiveOptionPtr {
		igb.StartIgb(Version)
		os.Exit(0)
	}

	if *profileOptionPtr {
		defer profile.Start().Stop()
	}

	if *versionOptionPtr {
		fmt.Println(Version)
		os.Exit(0)
	}

	var fp string

	switch flag.Arg(0) {
	case "":
		flag.Usage()
		os.Exit(0)
	case "test":
		args := flag.Args()[1:]
		filePath := flag.Arg(1)
		fileInfo, err := os.Stat(filePath)
		reportErrorAndExit(err)

		dir := extractDirFromFilePath(filePath, fileInfo)
		v, err := vm.New(dir, args)

		if fileInfo.Mode().IsDir() {
			fileInfos, err := ioutil.ReadDir(filePath)
			reportErrorAndExit(err)

			for _, fileInfo := range fileInfos {
				fp := filepath.Join(dir, fileInfo.Name())
				reportErrorAndExit(err)

				err := runSpecFile(v, fp)
				reportErrorAndExit(err)
			}
		} else {
			err := runSpecFile(v, filePath)
			reportErrorAndExit(err)
		}

		instructionSets, err := compiler.CompileToInstructions("Spec.run", parser.NormalMode)
		v.ExecInstructions(instructionSets, filePath)
		return
	default:
		fp = flag.Arg(0)

		if !strings.Contains(fp, ".") {
			flag.Usage()
			os.Exit(0)
		}
	}

	// Execute files normally
	dir, _, fileExt := extractFileInfo(fp)
	file := readFile(fp)

	switch fileExt {
	case "gb", "rb":
		args := flag.Args()[1:]
		instructionSets, err := compiler.CompileToInstructions(string(file), parser.NormalMode)
		reportErrorAndExit(err)

		var v *vm.VM

		if *issueOptionPtr {
			fmt.Println("Will generate issue report on error...")
			v, err = vm.InitIssueReportVM(dir, args)
			defer vm.PrintError(v)
		} else {
			v, err = vm.New(dir, args)
		}
		reportErrorAndExit(err)

		fp, err := filepath.Abs(fp)
		reportErrorAndExit(err)

		v.ExecInstructions(instructionSets, fp)
	default:
		fmt.Printf("Unknown file extension: %s", fileExt)
	}
}

func extractFileInfo(fp string) (dir, filename, fileExt string) {
	dir, filename = filepath.Split(fp)
	dir, _ = filepath.Abs(dir)
	fileExt = filepath.Ext(fp)
	splited := strings.Split(filename, ".")
	filename, fileExt = splited[0], splited[1]
	return
}

func extractDirFromFilePath(filePath string, fileInfo os.FileInfo) string {
	if fileInfo.Mode().IsDir() {
		dir, err := filepath.Abs(filePath)
		reportErrorAndExit(err)
		return dir
	}

	filePath, err := filepath.Abs(filePath)
	reportErrorAndExit(err)
	dir, _, _ := extractFileInfo(filePath)
	return dir
}

func readFile(filepath string) (file []byte) {
	file, err := ioutil.ReadFile(filepath)
	reportErrorAndExit(err)
	return
}

func runSpecFile(v *vm.VM, fp string) (err error) {
	file := readFile(fp)
	instructionSets, err := compiler.CompileToInstructions(string(file), parser.NormalMode)

	if err != nil {
		return
	}

	v.ExecInstructions(instructionSets, fp)
	return
}

func reportErrorAndExit(err error) {
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
}
<commit_msg>Add --profile-mem flag and rename -p to --profile-cpu<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/goby-lang/goby/compiler"
	"github.com/goby-lang/goby/compiler/parser"
	"github.com/goby-lang/goby/igb"
	_ "github.com/goby-lang/goby/native/db"
	_ "github.com/goby-lang/goby/native/plugin"
	_ "github.com/goby-lang/goby/native/result"
	_ "github.com/goby-lang/goby/native/ripper"
	"github.com/goby-lang/goby/vm"
	"github.com/pkg/profile"
)

const Version string = vm.Version

func main() {
	profileCPUOptionPtr := flag.Bool("profile-cpu", false, "Profile cpu usage")
	profileMemOptionPtr := flag.Bool("profile-mem", false, "Profile memory allocation")
	versionOptionPtr := flag.Bool("v", false, "Show current Goby version")
	interactiveOptionPtr := flag.Bool("i", false, "Run interactive goby")
	issueOptionPtr := flag.Bool("e", false, "Generate reporting format")

	flag.Parse()

	if *interactiveOptionPtr {
		igb.StartIgb(Version)
		os.Exit(0)
	}

	if *profileCPUOptionPtr {
		defer profile.Start().Stop()
	}

	if *profileMemOptionPtr {
		defer profile.Start(profile.MemProfile).Stop()
	}

	if *versionOptionPtr {
		fmt.Println(Version)
		os.Exit(0)
	}

	var fp string

	switch flag.Arg(0) {
	case "":
		flag.Usage()
		os.Exit(0)
	case "test":
		args := flag.Args()[1:]
		filePath := flag.Arg(1)
		fileInfo, err := os.Stat(filePath)
		reportErrorAndExit(err)

		dir := extractDirFromFilePath(filePath, fileInfo)
		v, err := vm.New(dir, args)

		if fileInfo.Mode().IsDir() {
			fileInfos, err := ioutil.ReadDir(filePath)
			reportErrorAndExit(err)

			for _, fileInfo := range fileInfos {
				fp := filepath.Join(dir, fileInfo.Name())
				reportErrorAndExit(err)

				err := runSpecFile(v, fp)
				reportErrorAndExit(err)
			}
		} else {
			err := runSpecFile(v, filePath)
			reportErrorAndExit(err)
		}

		instructionSets, err := compiler.CompileToInstructions("Spec.run", parser.NormalMode)
		v.ExecInstructions(instructionSets, filePath)
		return
	default:
		fp = flag.Arg(0)

		if !strings.Contains(fp, ".") {
			flag.Usage()
			os.Exit(0)
		}
	}

	// Execute files normally
	dir, _, fileExt := extractFileInfo(fp)
	file := readFile(fp)

	switch fileExt {
	case "gb", "rb":
		args := flag.Args()[1:]
		instructionSets, err := compiler.CompileToInstructions(string(file), parser.NormalMode)
		reportErrorAndExit(err)

		var v *vm.VM

		if *issueOptionPtr {
			fmt.Println("Will generate issue report on error...")
			v, err = vm.InitIssueReportVM(dir, args)
			defer vm.PrintError(v)
		} else {
			v, err = vm.New(dir, args)
		}
		reportErrorAndExit(err)

		fp, err := filepath.Abs(fp)
		reportErrorAndExit(err)

		v.ExecInstructions(instructionSets, fp)
	default:
		fmt.Printf("Unknown file extension: %s", fileExt)
	}
}

func extractFileInfo(fp string) (dir, filename, fileExt string) {
	dir, filename = filepath.Split(fp)
	dir, _ = filepath.Abs(dir)
	fileExt = filepath.Ext(fp)
	splited := strings.Split(filename, ".")
	filename, fileExt = splited[0], splited[1]
	return
}

func extractDirFromFilePath(filePath string, fileInfo os.FileInfo) string {
	if fileInfo.Mode().IsDir() {
		dir, err := filepath.Abs(filePath)
		reportErrorAndExit(err)
		return dir
	}

	filePath, err := filepath.Abs(filePath)
	reportErrorAndExit(err)
	dir, _, _ := extractFileInfo(filePath)
	return dir
}

func readFile(filepath string) (file []byte) {
	file, err := ioutil.ReadFile(filepath)
	reportErrorAndExit(err)
	return
}

func runSpecFile(v *vm.VM, fp string) (err error) {
	file := readFile(fp)
	instructionSets, err := compiler.CompileToInstructions(string(file), parser.NormalMode)

	if err != nil {
		return
	}

	v.ExecInstructions(instructionSets, fp)
	return
}

func reportErrorAndExit(err error) {
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/GeertJohan/go.rice"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"github.com/toqueteos/webbrowser"
	"gopkg.in/alecthomas/kingpin.v2"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"time"
)

type InputArgs struct {
	Port *int
	File *string
}

var args = InputArgs{
	Port: kingpin.Flag("port", "Listening port used by webserver").Short('p').Default("1110").Int(),
	File: kingpin.Arg("file", "Markdown file").String(),
}

type EditorView struct {
	File    string
	Content string
}

func main() {
	// Parse command line arguments
	kingpin.Version("0.0.1")
	kingpin.Parse()

	// Prepare (optionally) embedded resources
	templateBox := rice.MustFindBox("template")
	staticHTTPBox := rice.MustFindBox("static").HTTPBox()
	staticServer := http.StripPrefix("/static/", http.FileServer(staticHTTPBox))

	e := echo.New()

	t := &Template{
		templates: template.Must(template.New("base").Parse(templateBox.MustString("base.html"))),
	}
	e.SetRenderer(t)

	e.Use(middleware.Logger())
	e.Use(middleware.Recover())

	e.Get("/static/*", func(c *echo.Context) error {
		staticServer.ServeHTTP(c.Response().Writer(), c.Request())
		return nil
	})

	edit := e.Group("/edit")
	edit.Get("/*", EditHandler)
	edit.Post("/*", EditHandler)

	go WaitForServer()
	e.Run(fmt.Sprintf("127.0.0.1:%d", *args.Port))
}

type Template struct {
	templates *template.Template
}

func (t *Template) Render(w io.Writer, name string, data interface{}) error {
	return t.templates.ExecuteTemplate(w, name, data)
}

func EditHandler(c *echo.Context) error {
	filepath := c.P(0)
	if c.Request().Method == "POST" {
		c.Request().ParseForm()
		ioutil.WriteFile(filepath, []byte(c.Request().PostForm.Get("content")), 0644)
	}
	content, err := ioutil.ReadFile(filepath)
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, "Unable to read requested file")
		log.Fatalf("Unable to open file " + filepath)
	}
	ev := EditorView{File: filepath, Content: string(content)}
	return c.Render(http.StatusOK, "base", ev)
}

func WaitForServer() {
	log.Printf("Waiting for listener on port %d", *args.Port)
	url := fmt.Sprintf("http://localhost:%d/edit/%s", *args.Port, url.QueryEscape(*args.File))
	for {
		time.Sleep(time.Millisecond * 50)
		resp, err := http.Get(url)
		if err != nil {
			continue
		}
		resp.Body.Close()
		break
	}
	log.Println("Opening " + url)
	if err := webbrowser.Open(url); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Remove dead code<commit_after>package main

import (
	"fmt"
	"github.com/GeertJohan/go.rice"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"github.com/toqueteos/webbrowser"
	"gopkg.in/alecthomas/kingpin.v2"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"time"
)

type InputArgs struct {
	Port *int
	File *string
}

var args = InputArgs{
	Port: kingpin.Flag("port", "Listening port used by webserver").Short('p').Default("1110").Int(),
	File: kingpin.Arg("file", "Markdown file").String(),
}

type EditorView struct {
	File    string
	Content string
}

func main() {
	// Parse command line arguments
	kingpin.Version("0.0.1")
	kingpin.Parse()

	// Prepare (optionally) embedded resources
	templateBox := rice.MustFindBox("template")
	staticHTTPBox := rice.MustFindBox("static").HTTPBox()
	staticServer := http.StripPrefix("/static/", http.FileServer(staticHTTPBox))

	e := echo.New()

	t := &Template{
		templates: template.Must(template.New("base").Parse(templateBox.MustString("base.html"))),
	}
	e.SetRenderer(t)

	e.Use(middleware.Logger())
	e.Use(middleware.Recover())

	e.Get("/static/*", func(c *echo.Context) error {
		staticServer.ServeHTTP(c.Response().Writer(), c.Request())
		return nil
	})

	edit := e.Group("/edit")
	edit.Get("/*", EditHandler)
	edit.Post("/*", EditHandler)

	go WaitForServer()
	e.Run(fmt.Sprintf("127.0.0.1:%d", *args.Port))
}

type Template struct {
	templates *template.Template
}

func (t *Template) Render(w io.Writer, name string, data interface{}) error {
	return t.templates.ExecuteTemplate(w, name, data)
}

func EditHandler(c *echo.Context) error {
	filepath := c.P(0)
	if c.Request().Method == "POST" {
		c.Request().ParseForm()
		ioutil.WriteFile(filepath, []byte(c.Request().PostForm.Get("content")), 0644)
	}
	content, err := ioutil.ReadFile(filepath)
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, "Unable to read requested file")
	}
	ev := EditorView{File: filepath, Content: string(content)}
	return c.Render(http.StatusOK, "base", ev)
}

func WaitForServer() {
	log.Printf("Waiting for listener on port %d", *args.Port)
	url := fmt.Sprintf("http://localhost:%d/edit/%s", *args.Port, url.QueryEscape(*args.File))
	for {
		time.Sleep(time.Millisecond * 50)
		resp, err := http.Get(url)
		if err != nil {
			continue
		}
		resp.Body.Close()
		break
	}
	log.Println("Opening " + url)
	if err := webbrowser.Open(url); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"encoding/xml" // gelbooru parsing
	"errors"
	"fmt"
	"github.com/jcline/goty"
	"html"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strconv"
	"time"
)

var user = ""

var matchAniDBSearch = regexp.MustCompile(`!anidb +(.+) *`)
var matchAmiAmi = regexp.MustCompile(`(?:https?://|)(?:www\.|)amiami.com/([^/ ]+/detail/[^ ]+)`)
var matchGelbooru = regexp.MustCompile(`(?:https?://|)\Qgelbooru.com/index.php?page=post&s=view&id=\E([\d]+)`)
var matchMAL = regexp.MustCompile(`!anime+(.+)`)
var matchReddit = regexp.MustCompile(`(?:http://|)(?:www\.|https://pay\.|)redd(?:\.it|it\.com)/(r/[^/ ]+/comments/[^/ ]+)/?(?: .*|\z)`)
var matchYouTube = regexp.MustCompile(`(?:https?://|)(?:www\.|)(youtu(?:\.be|be\.com)/[^ ]+)`)

func auth(con *goty.IRCConn, writeMessage chan IRCMessage, user string) {
	var pswd string
	fmt.Printf("Password for NickServ:\n")
	_, err := fmt.Scanf("%s", &pswd)
	if err != nil {
		return
	}

	msg := IRCMessage{channel: "NickServ", msg: "IDENTIFY " + user + " " + pswd}
	writeMessage <- msg
}

func main() {
	args := os.Args
	if len(args) < 4 {
		os.Exit(1)
	}

	con, err := goty.Dial(args[1], args[2], args[3])
	user = args[2]
	if err != nil {
		fmt.Fprintf(os.Stderr, "err: %s\n", err.Error())
	}

	writeMessage := make(chan IRCMessage, 1000)
	go messageHandler(con, writeMessage)

	amiAmiEvent := make(chan unparsedMessage, 1000)
	//anidbEvent := make(chan unparsedMessage, 1000)
	bastilleEvent := make(chan unparsedMessage, 1000)
	gelbooruEvent := make(chan unparsedMessage, 1000)
	malEvent := make(chan unparsedMessage, 1000)
	redditEvent := make(chan unparsedMessage, 1000)
	youtubeEvent := make(chan unparsedMessage, 1000)

	go amiami(amiAmiEvent, writeMessage)
	//go anidb(anidbEvent, writeMessage)
	go bastille(bastilleEvent, writeMessage)
	go gelbooru(gelbooruEvent, writeMessage)
	go malSearch(malEvent, writeMessage)
	go reddit(redditEvent, writeMessage)
	go youtube(youtubeEvent, writeMessage)

	auth(con, writeMessage, user)
	con.Write <- "JOIN " + args[4]

	for msg := range con.Read {
		prepared := unparsedMessage{msg, time.Now()}
		fmt.Printf("%s||%s\n", prepared.when, prepared.msg)

		switch {
		case matchAmiAmi.MatchString(prepared.msg):
			amiAmiEvent <- prepared
		case matchGelbooru.MatchString(prepared.msg):
			//gelbooruEvent <- matchGelbooru.FindAllStringSubmatch(prepared.msg, -1)[0][1]
		case matchMAL.MatchString(prepared.msg):
			malEvent <- prepared
		case matchReddit.MatchString(prepared.msg):
			redditEvent <- prepared
		case matchYouTube.MatchString(prepared.msg):
			youtubeEvent <- prepared
		//case matchAniDBSearch.MatchString(prepared.msg):
		//anidbEvent <- prepared
		default:
		}
	}
	con.Close()
}

type unparsedMessage struct {
	msg  string
	when time.Time
}

type IRCMessage struct {
	channel string
	msg     string
	user    string
}

func message(con *goty.IRCConn, msg IRCMessage) {
	con.Write <- "PRIVMSG " + msg.channel + " :" + msg.msg + "\r\n"
}

func messageHandler(con *goty.IRCConn, event chan IRCMessage) {
	allBooks := map[string]time.Time{}
	chanBooks := map[string]time.Time{}
	for msg := range event {
		now := time.Now()
		key := msg.channel + ":" + msg.user
		if now.Sub(allBooks[key]) < time.Second*10 || now.Sub(chanBooks[key]) < time.Second*2 {
			continue
		}
		allBooks[key] = now
		chanBooks[key] = now
		message(con, msg)
	}
}

var PRIVMSG = regexp.MustCompile(`:(.+)![^ ]+ PRIVMSG ([^ ]+) :(.*)`)

func getMsgInfo(msg string) (*IRCMessage, error) {
	// :nick!~realname@0.0.0.0 PRIVMSG #chan :msg
	imsg := new(IRCMessage)
	match := PRIVMSG.FindAllStringSubmatch(msg, -1)
	if len(match) < 1 {
		return imsg, errors.New("could not parse message")
	}
	if len(match[0]) < 3 {
		return imsg, errors.New("could not parse message")
	}
	imsg.user = user
	imsg.channel = match[0][2]
	if imsg.channel == user {
		imsg.channel = match[0][1]
	}
	imsg.msg = match[0][3]
	return imsg, nil
}

func bastille(event chan unparsedMessage, writeMessage chan IRCMessage) {
	msgs := []string{
		"Bastille, yo brodudedudebro!!!!1",
		"Bastille, wat up homie",
		"Bastille, word",
		"Bastille, duuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuude",
		"'sup Bastille?",
	}

	for msg := range event {
		parsed, err := getMsgInfo(msg.msg)
		if err != nil {
			continue
		}
		writeMessage <- IRCMessage{parsed.channel, msgs[rand.Intn(len(msgs))-1], parsed.user}
	}
}

type uriFunc func(*string) (*string, error)
type writeFunc func(*IRCMessage, *string) error
type errFunc func(*IRCMessage, error) error

func scrapeAndSend(event chan unparsedMessage, findUri uriFunc, write writeFunc) {
	var f = func(msg unparsedMessage) {
		parsed, err := getMsgInfo(msg.msg)
		if err != nil {
			fmt.Printf("%v\n", err)
			return
		}

		uri, err := findUri(&parsed.msg)
		if err != nil {
			fmt.Printf("%v\n", err)
			return
		}

		resp, err := http.Get(*uri)
		if err != nil {
			fmt.Printf("%v\n", err)
			return
		}

		bodyBytes, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			fmt.Printf("%v\n", err)
			return
		}
		body := string(bodyBytes)

		err = write(parsed, &body)
		if err != nil {
			fmt.Printf("%v\n", err)
			return
		}
	}

	for msg := range event {
		go f(msg)
	}
}

func getFirstMatch(re *regexp.Regexp, matchee *string) (*string, error) {
	match := re.FindAllStringSubmatch(*matchee, -1)
	if len(match) < 1 {
		return nil, errors.New("Could not match")
	}
	if len(match[0]) < 2 {
		return nil, errors.New("Could not match")
	}
	return &match[0][1], nil
}

func amiami(event chan unparsedMessage, writeMessage chan IRCMessage) {
	matchTitle := regexp.MustCompile(`.*<meta property="og:title" content="(.+)" />.*`)
	matchDiscount := regexp.MustCompile(`[0-9]+\%OFF `)
	scrapeAndSend(event, func(msg *string) (*string, error) {
		uri, err := getFirstMatch(matchAmiAmi, msg)
		if err != nil {
			return nil, err
		}

		fullUri := "http://amiami.com/" + *uri
		return &fullUri, nil
	},
		func(msg *IRCMessage, body *string) error {
			title, err := getFirstMatch(matchTitle, body)
			if err != nil {
				return err
			}

			writeMessage <- IRCMessage{msg.channel, "[AmiAmi]: " + matchDiscount.ReplaceAllLiteralString(*title, ""), msg.user}
			return nil
		})
}

func reddit(event chan unparsedMessage, writeMessage chan IRCMessage) {
	matchTitle := regexp.MustCompile(`.*<title>(.+)</title>.*`)

	scrapeAndSend(event, func(msg *string) (*string, error) {
		uri, err := getFirstMatch(matchReddit, msg)
		if err != nil {
			return nil, err
		}

		fullUri := "http://reddit.com/" + *uri
		return &fullUri, nil
	},
		func(msg *IRCMessage, body *string) error {
			title, err := getFirstMatch(matchTitle, body)
			if err != nil {
				return err
			}

			writeMessage <- IRCMessage{msg.channel, "[Reddit]: " + html.UnescapeString(*title), msg.user}
			return nil
		})
}

func youtube(event chan unparsedMessage, writeMessage chan IRCMessage) {
	matchTitle := regexp.MustCompile(`.*<title>(.+)(?: - YouTube){1}</title>.*`)
	matchUser := regexp.MustCompile(`.*<a[^>]+class="[^"]+yt-user-name[^>]+>([^<]+)</a>.*`)

	scrapeAndSend(event, func(msg *string) (*string, error) {
		uri, err := getFirstMatch(matchYouTube, msg)
		if err != nil {
			return nil, err
		}

		fullUri := "http://" + *uri
		return &fullUri, nil
	},
		func(msg *IRCMessage, body *string) error {
			title, err := getFirstMatch(matchTitle, body)
			if err != nil {
				return err
			}
			user, err := getFirstMatch(matchUser, body)
			if err != nil {
				return err
			}
			writeMessage <- IRCMessage{msg.channel, "[YouTube]: " + html.UnescapeString(*title+" uploaded by "+*user), msg.user}
			return nil
		})
}

func gelbooru(event chan unparsedMessage, writeMessage chan IRCMessage) {
	type Post struct {
		post string
		tags string `xml:",attr"`
	}

	for msg := range event {
		parsed, err := getMsgInfo(msg.msg)
		if err != nil {
			continue
		}

		resp, err := http.Get("http://gelbooru.com/index.php?page=dapi&s=post&q=index&tags&id=" + msg.msg)
		if err != nil {
			fmt.Printf("%v\n", err)
			continue
		}

		body, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			fmt.Printf("%v\n", err)
			continue
		}

		fmt.Printf("%s\n", body)

		var result Post

		err = xml.Unmarshal(body, &result)
		if err != nil {
			fmt.Printf("%v\n", err)
			continue
		}

		fmt.Printf("%s\n", result.tags)
		writeMessage <- IRCMessage{parsed.channel, "tobedone", parsed.user}
	}
}

func anidb(event chan unparsedMessage, writeMessage chan IRCMessage) {
	cache := make(map[string]string)

	for msg := range event {
		parsed, err := getMsgInfo(msg.msg)
		if err != nil {
			continue
		}

		val, ok := cache[msg.msg]
		if ok {
			writeMessage <- IRCMessage{parsed.channel, val, parsed.user}
		} else {
			// totally broken :(
			resp, err := http.Get("http://anisearch.outrance.pl/index.php?task=search&query=" + msg.msg)
			if err != nil {
				fmt.Printf("%v\n", err)
				continue
			}

			body, err := ioutil.ReadAll(resp.Body)
			resp.Body.Close()
			if err != nil {
				fmt.Printf("%v\n", err)
				continue
			}

			fmt.Printf("%s\n", body)

			//cache[msg.msg] = ""
		}
	}
}

func malSearch(event chan unparsedMessage, writeMessage chan IRCMessage) {

	type Anime struct {
		Id int
		Title string
		Classification string
	}

	scrapeAndSend(event, func(msg *string) (*string, error) {
		terms, err := getFirstMatch(matchMAL, msg)
		if err != nil {
			return nil, err
		}
		uri := "http://mal-api.com/anime/search?q=" + url.QueryEscape(*terms)
		return &uri, nil
	},
		func(msg *IRCMessage, body *string) error {
			if len(*body) < 10 {
				writeMessage <- IRCMessage{msg.channel, "┐('～`；)┌", msg.user}
				return errors.New("No results")
			}

			fmt.Printf("%v\n", *body)

			var a []Anime
			err := json.Unmarshal([]byte(*body), &a)
			if err != nil {
				writeMessage <- IRCMessage{msg.channel, "┐('～`；)┌", msg.user}
				return err
			}
			fmt.Printf("%v\n", a)

			var results = ""
			var count = 0
			var nsfw = false

			for count < len(a) && count < 3 {
				results += a[count].Title + " [Rating: " + a[count].Classification + "]: http://myanimelist.net/anime/" + strconv.Itoa(a[count].Id) + "  "
				if a[count].Classification == "Rx" {
					nsfw = true
				}
				count = count + 1
			}

			if nsfw {
				results += "NSFW"
			}

			writeMessage <- IRCMessage{msg.channel, results, msg.user}
			return nil
		})
}
<commit_msg>Defer cleaup, gofmt, output cleanup<commit_after>package main

import (
	"encoding/json"
	"encoding/xml" // gelbooru parsing
	"errors"
	"fmt"
	"github.com/jcline/goty"
	"html"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strconv"
	"time"
)

var user = ""

var matchAniDBSearch = regexp.MustCompile(`!anidb +(.+) *`)
var matchAmiAmi = regexp.MustCompile(`(?:https?://|)(?:www\.|)amiami.com/([^/ ]+/detail/[^ ]+)`)
var matchGelbooru = regexp.MustCompile(`(?:https?://|)\Qgelbooru.com/index.php?page=post&s=view&id=\E([\d]+)`)
var matchMAL = regexp.MustCompile(`!anime+(.+)`)
var matchReddit = regexp.MustCompile(`(?:http://|)(?:www\.|https://pay\.|)redd(?:\.it|it\.com)/(r/[^/ ]+/comments/[^/ ]+)/?(?: .*|\z)`)
var matchYouTube = regexp.MustCompile(`(?:https?://|)(?:www\.|)(youtu(?:\.be|be\.com)/[^ ]+)`)

func auth(con *goty.IRCConn, writeMessage chan IRCMessage, user string) {
	var pswd string
	fmt.Printf("Password for NickServ:\n")
	_, err := fmt.Scanf("%s", &pswd)
	if err != nil {
		return
	}

	msg := IRCMessage{channel: "NickServ", msg: "IDENTIFY " + user + " " + pswd}
	writeMessage <- msg
}

func main() {
	args := os.Args
	if len(args) < 4 {
		os.Exit(1)
	}

	con, err := goty.Dial(args[1], args[2], args[3])
	user = args[2]
	if err != nil {
		fmt.Fprintf(os.Stderr, "err: %s\n", err.Error())
	}

	writeMessage := make(chan IRCMessage, 1000)
	go messageHandler(con, writeMessage)

	amiAmiEvent := make(chan unparsedMessage, 1000)
	//anidbEvent := make(chan unparsedMessage, 1000)
	bastilleEvent := make(chan unparsedMessage, 1000)
	gelbooruEvent := make(chan unparsedMessage, 1000)
	malEvent := make(chan unparsedMessage, 1000)
	redditEvent := make(chan unparsedMessage, 1000)
	youtubeEvent := make(chan unparsedMessage, 1000)

	go amiami(amiAmiEvent, writeMessage)
	//go anidb(anidbEvent, writeMessage)
	go bastille(bastilleEvent, writeMessage)
	go gelbooru(gelbooruEvent, writeMessage)
	go malSearch(malEvent, writeMessage)
	go reddit(redditEvent, writeMessage)
	go youtube(youtubeEvent, writeMessage)

	auth(con, writeMessage, user)
	con.Write <- "JOIN " + args[4]

	for msg := range con.Read {
		prepared := unparsedMessage{msg, time.Now()}
		fmt.Printf("%s||%s\n", prepared.when, prepared.msg)

		switch {
		case matchAmiAmi.MatchString(prepared.msg):
			amiAmiEvent <- prepared
		case matchGelbooru.MatchString(prepared.msg):
			//gelbooruEvent <- matchGelbooru.FindAllStringSubmatch(prepared.msg, -1)[0][1]
		case matchMAL.MatchString(prepared.msg):
			malEvent <- prepared
		case matchReddit.MatchString(prepared.msg):
			redditEvent <- prepared
		case matchYouTube.MatchString(prepared.msg):
			youtubeEvent <- prepared
		//case matchAniDBSearch.MatchString(prepared.msg):
		//anidbEvent <- prepared
		default:
		}
	}
	con.Close()
}

type unparsedMessage struct {
	msg  string
	when time.Time
}

type IRCMessage struct {
	channel string
	msg     string
	user    string
}

func message(con *goty.IRCConn, msg IRCMessage) {
	con.Write <- "PRIVMSG " + msg.channel + " :" + msg.msg + "\r\n"
}

func messageHandler(con *goty.IRCConn, event chan IRCMessage) {
	allBooks := map[string]time.Time{}
	chanBooks := map[string]time.Time{}
	for msg := range event {
		now := time.Now()
		key := msg.channel + ":" + msg.user
		if now.Sub(allBooks[key]) < time.Second*10 || now.Sub(chanBooks[key]) < time.Second*2 {
			continue
		}
		allBooks[key] = now
		chanBooks[key] = now
		message(con, msg)
	}
}

var PRIVMSG = regexp.MustCompile(`:(.+)![^ ]+ PRIVMSG ([^ ]+) :(.*)`)

func getMsgInfo(msg string) (*IRCMessage, error) {
	// :nick!~realname@0.0.0.0 PRIVMSG #chan :msg
	imsg := new(IRCMessage)
	match := PRIVMSG.FindAllStringSubmatch(msg, -1)
	if len(match) < 1 {
		return imsg, errors.New("could not parse message")
	}
	if len(match[0]) < 3 {
		return imsg, errors.New("could not parse message")
	}
	imsg.user = user
	imsg.channel = match[0][2]
	if imsg.channel == user {
		imsg.channel = match[0][1]
	}
	imsg.msg = match[0][3]
	return imsg, nil
}

func bastille(event chan unparsedMessage, writeMessage chan IRCMessage) {
	msgs := []string{
		"Bastille, yo brodudedudebro!!!!1",
		"Bastille, wat up homie",
		"Bastille, word",
		"Bastille, duuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuude",
		"'sup Bastille?",
	}

	for msg := range event {
		parsed, err := getMsgInfo(msg.msg)
		if err != nil {
			continue
		}
		writeMessage <- IRCMessage{parsed.channel, msgs[rand.Intn(len(msgs))-1], parsed.user}
	}
}

type uriFunc func(*string) (*string, error)
type writeFunc func(*IRCMessage, *string) error
type errFunc func(*IRCMessage, error) error

func scrapeAndSend(event chan unparsedMessage, findUri uriFunc, write writeFunc) {
	var f = func(msg unparsedMessage) {
		parsed, err := getMsgInfo(msg.msg)
		if err != nil {
			fmt.Printf("%v\n", err)
			return
		}

		uri, err := findUri(&parsed.msg)
		if err != nil {
			fmt.Printf("%v\n", err)
			return
		}

		resp, err := http.Get(*uri)
		if err != nil {
			fmt.Printf("%v\n", err)
			return
		}

		bodyBytes, err := ioutil.ReadAll(resp.Body)
		defer resp.Body.Close()
		if err != nil {
			fmt.Printf("%v\n", err)
			return
		}
		body := string(bodyBytes)

		err = write(parsed, &body)
		if err != nil {
			fmt.Printf("%v\n", err)
			return
		}
	}

	for msg := range event {
		go f(msg)
	}
}

func getFirstMatch(re *regexp.Regexp, matchee *string) (*string, error) {
	match := re.FindAllStringSubmatch(*matchee, -1)
	if len(match) < 1 {
		return nil, errors.New("Could not match")
	}
	if len(match[0]) < 2 {
		return nil, errors.New("Could not match")
	}
	return &match[0][1], nil
}

func amiami(event chan unparsedMessage, writeMessage chan IRCMessage) {
	matchTitle := regexp.MustCompile(`.*<meta property="og:title" content="(.+)" />.*`)
	matchDiscount := regexp.MustCompile(`[0-9]+\%OFF `)
	scrapeAndSend(event, func(msg *string) (*string, error) {
		uri, err := getFirstMatch(matchAmiAmi, msg)
		if err != nil {
			return nil, err
		}

		fullUri := "http://amiami.com/" + *uri
		return &fullUri, nil
	},
		func(msg *IRCMessage, body *string) error {
			title, err := getFirstMatch(matchTitle, body)
			if err != nil {
				return err
			}

			writeMessage <- IRCMessage{msg.channel, "[AmiAmi] " + matchDiscount.ReplaceAllLiteralString(*title, ""), msg.user}
			return nil
		})
}

func reddit(event chan unparsedMessage, writeMessage chan IRCMessage) {
	matchTitle := regexp.MustCompile(`.*<title>(.+)</title>.*`)

	scrapeAndSend(event, func(msg *string) (*string, error) {
		uri, err := getFirstMatch(matchReddit, msg)
		if err != nil {
			return nil, err
		}

		fullUri := "http://reddit.com/" + *uri
		return &fullUri, nil
	},
		func(msg *IRCMessage, body *string) error {
			title, err := getFirstMatch(matchTitle, body)
			if err != nil {
				return err
			}

			writeMessage <- IRCMessage{msg.channel, "[Reddit] " + html.UnescapeString(*title), msg.user}
			return nil
		})
}

func youtube(event chan unparsedMessage, writeMessage chan IRCMessage) {
	matchTitle := regexp.MustCompile(`.*<title>(.+)(?: - YouTube){1}</title>.*`)
	matchUser := regexp.MustCompile(`.*<a[^>]+class="[^"]+yt-user-name[^>]+>([^<]+)</a>.*`)

	scrapeAndSend(event, func(msg *string) (*string, error) {
		uri, err := getFirstMatch(matchYouTube, msg)
		if err != nil {
			return nil, err
		}

		fullUri := "http://" + *uri
		return &fullUri, nil
	},
		func(msg *IRCMessage, body *string) error {
			title, err := getFirstMatch(matchTitle, body)
			if err != nil {
				return err
			}
			user, err := getFirstMatch(matchUser, body)
			if err != nil {
				return err
			}
			writeMessage <- IRCMessage{msg.channel, "[YouTube] " + html.UnescapeString(*title+" uploaded by "+*user), msg.user}
			return nil
		})
}

func gelbooru(event chan unparsedMessage, writeMessage chan IRCMessage) {
	type Post struct {
		post string
		tags string `xml:",attr"`
	}

	for msg := range event {
		parsed, err := getMsgInfo(msg.msg)
		if err != nil {
			continue
		}

		resp, err := http.Get("http://gelbooru.com/index.php?page=dapi&s=post&q=index&tags&id=" + msg.msg)
		if err != nil {
			fmt.Printf("%v\n", err)
			continue
		}

		body, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			fmt.Printf("%v\n", err)
			continue
		}

		fmt.Printf("%s\n", body)

		var result Post

		err = xml.Unmarshal(body, &result)
		if err != nil {
			fmt.Printf("%v\n", err)
			continue
		}

		fmt.Printf("%s\n", result.tags)
		writeMessage <- IRCMessage{parsed.channel, "tobedone", parsed.user}
	}
}

func anidb(event chan unparsedMessage, writeMessage chan IRCMessage) {
	cache := make(map[string]string)

	for msg := range event {
		parsed, err := getMsgInfo(msg.msg)
		if err != nil {
			continue
		}

		val, ok := cache[msg.msg]
		if ok {
			writeMessage <- IRCMessage{parsed.channel, val, parsed.user}
		} else {
			// totally broken :(
			resp, err := http.Get("http://anisearch.outrance.pl/index.php?task=search&query=" + msg.msg)
			if err != nil {
				fmt.Printf("%v\n", err)
				continue
			}

			body, err := ioutil.ReadAll(resp.Body)
			resp.Body.Close()
			if err != nil {
				fmt.Printf("%v\n", err)
				continue
			}

			fmt.Printf("%s\n", body)

			//cache[msg.msg] = ""
		}
	}
}

func malSearch(event chan unparsedMessage, writeMessage chan IRCMessage) {

	type Anime struct {
		Id             int
		Title          string
		Classification string
	}

	scrapeAndSend(event, func(msg *string) (*string, error) {
		terms, err := getFirstMatch(matchMAL, msg)
		if err != nil {
			return nil, err
		}
		uri := "http://mal-api.com/anime/search?q=" + url.QueryEscape(*terms)
		return &uri, nil
	},
		func(msg *IRCMessage, body *string) error {
			if len(*body) < 10 {
				writeMessage <- IRCMessage{msg.channel, "┐('～`；)┌", msg.user}
				return errors.New("No results")
			}

			fmt.Printf("%v\n", *body)

			var a []Anime
			err := json.Unmarshal([]byte(*body), &a)
			if err != nil {
				writeMessage <- IRCMessage{msg.channel, "┐('～`；)┌", msg.user}
				return err
			}
			fmt.Printf("%v\n", a)

			var results = ""
			var count = 0
			var nsfw = false

			for count < len(a) && count < 3 {
				results += a[count].Title + " [Rating " + a[count].Classification + "] http://myanimelist.net/anime/" + strconv.Itoa(a[count].Id) + "  "
				if a[count].Classification == "Rx" || a[count].Classification == "R+" {
					nsfw = true
				}
				count = count + 1
			}

			if nsfw {
				results += "NSFW"
			}

			writeMessage <- IRCMessage{msg.channel, results, msg.user}
			return nil
		})
}
<|endoftext|>


<commit_before>package gridt

import (
	runewidth "github.com/mattn/go-runewidth"
)

const (
	// LeftToRight is a direction in which the values will be written.
	// It goes from the first cell (0,0) to the end of the line, returning to the beggining of the second line.
	// Exactly the same as a typewritter.
	LeftToRight Direction = iota
	// TopToBottom is a direction in which the values will be written.
	// It goes from the first cell (0,0) to the bottom of the column, returning to the top of the second column.
	// Exactly the same as how `ls` command works by default.
	TopToBottom
)

// Direction represents the direction in which the values will be written.
type Direction int8

// Grid represents the values' grid, that will be exported as a pretty formatted string.
type Grid struct {
	v   []string
	d   Direction
	sep string
}

// New returns a new Grid.
// `d` represents the direction in which the values will be written.
// `sep` represents the separator; a string that will be between each column.
func New(d Direction, sep string) *Grid {
	return &Grid{make([]string, 0), d, sep}
}

// NewWithCells returns a new Grid, with pre-defined cells.
// `d` represents the direction in which the values will be written.
// `sep` represents the separator; a string that will be between each column.
// `s` is the cells that will be added.
func NewWithCells(d Direction, sep string, s ...string) *Grid {
	return New(d, sep).Add(s...)
}

// Cells returns all cells of the grid.
func (g Grid) Cells() []string {
	return g.v
}

// Direction returns the direction in which the grid will be written.
func (g Grid) Direction() Direction {
	return g.d
}

// Separator returns the separator; the string that will be between each column.
func (g Grid) Separator() string {
	return g.sep
}

// Add adds a cell to the grid.
// `s` is the values that will be added.
func (g *Grid) Add(s ...string) *Grid {
	g.v = append(g.v, s...)
	return g
}

// Insert inserts a value in a specified position in the grid.
// `i` the position of the value.
// `s` is the value that will be added.
func (g *Grid) Insert(i int, s ...string) *Grid {
	g.v = g.v[0 : len(g.v)+len(s)]
	copy(g.v[i+len(s):], g.v[i:])
	for si, ss := range s {
		g.v[i+si] = ss
	}
	return g
}

// Delete deletes a value in a specified position in the grid.
// `i` the position of the value.
func (g *Grid) Delete(i ...int) *Grid {
	for _, ii := range i {
		copy(g.v[ii:], g.v[ii+1:])
	}
	g.v = g.v[0 : len(g.v)-len(i)]
	return g
}

// FitIntoWidth formats the grid, based on a maximum width.
// `max` represents the maximum width of the grid, based on characters.
// `dim` represents the dimensions of the grid, used for formatting. See `Dimensions`.
// `ok` says whether the the grid fits in the maximum width informed. If false, discard `dim`.
func (g Grid) FitIntoWidth(max int) (dim Dimensions, ok bool) {
	switch count := len(g.v); count {

	// If the slice is empty, returns empty grid that fits.
	case 0:
		return Dimensions{}, false

	// If it has one item, it is validated.
	case 1:
		if l := runewidth.StringWidth(g.v[0]); l <= max {
			return Dimensions{[]int{l}, 1, g}, true
		}
		return Dimensions{}, false

	// If it has two or more items...
	default:

		// If the maximum size is zero, it is invalid.
		if max <= 0 {
			return Dimensions{}, false
		}

		// `lines` represents the minimum number of lines necessary.
		// This loop will check for every possibility.
		for lines := 1; lines <= count; lines++ {

			// `columns` represents the number of columns, based on the current number of lines.
			// It is the cells count, divided by the number of lines, rounded up.
			columns := divUp(count, lines)

			// Calculates the free space...
			// Which is the maximum size, minus the total width of all the separators.
			// If there is no free space, this possibility is ignored.
			free := max - ((columns - 1) * runewidth.StringWidth(g.sep))
			if free < 0 {
				continue
			}

			// Gets bigger widths from each column. :P
			widths := g.biggerFromEachColumn(lines, columns)

			// If the sum of all widths fits the free space, then the possibility is reality!
			var sum int
			for _, width := range widths {
				sum += width
			}
			if sum <= free {
				return Dimensions{widths, lines, g}, true
			}
		}

		// If no possibility worked, than the cells does not fit the maximum size.
		return Dimensions{}, false
	}
}

// FitIntoColumns formats the grid, based on a maximum quantity of columns.
// `max` represents the maximum quantity of columns of the grid.
// `dim` represents the dimensions of the grid, used for formatting. See `Dimensions`.
// `ok` says whether the the grid fits in the maximum width informed. If false, discard `dim`.
func (g Grid) FitIntoColumns(max int) (dim Dimensions, ok bool) {
	// If the maximum size is zero, it is invalid.
	if max <= 0 {
		return Dimensions{}, false
	}

	// `lines` represents the number of lines.
	// It is the cells count, divided by the number of maximum columns, rounded up.
	l := divUp(len(g.v), max)
	return Dimensions{g.biggerFromEachColumn(l, max), l, g}, true
}

func (g Grid) biggerFromEachColumn(lines, columns int) []int {
	// Creates a slice of the widths of the columns.
	widths := make([]int, columns)
	for i, vv := range g.v {
		// `v` represents the list of values.
		// `widths` represents the list of columns' widths.
		// `i` cannot be the index of the value on `v`, but its index on the line.
		// So, `i` is adjusted, based on the direction of the grid population.
		switch g.d {
		case TopToBottom:
			i /= lines
		case LeftToRight:
			i %= columns
		}

		// Now, `i` represents the index of the column (or cell on the line).
		// `widths[i]` is substituted by the current value, if the latter is bigger.
		// `widths[i]` represents the bigger value on the `i` column.
		if l := runewidth.StringWidth(vv); l > widths[i] {
			widths[i] = l
		}
	}
	return widths
}
<commit_msg>Typo.<commit_after>package gridt

import (
	runewidth "github.com/mattn/go-runewidth"
)

const (
	// LeftToRight is a direction in which the values will be written.
	// It goes from the first cell (0,0) to the end of the line, returning to the beginning of the second line.
	// Exactly the same as a typewritter.
	LeftToRight Direction = iota
	// TopToBottom is a direction in which the values will be written.
	// It goes from the first cell (0,0) to the bottom of the column, returning to the top of the second column.
	// Exactly the same as how `ls` command works by default.
	TopToBottom
)

// Direction represents the direction in which the values will be written.
type Direction int8

// Grid represents the values' grid, that will be exported as a pretty formatted string.
type Grid struct {
	v   []string
	d   Direction
	sep string
}

// New returns a new Grid.
// `d` represents the direction in which the values will be written.
// `sep` represents the separator; a string that will be between each column.
func New(d Direction, sep string) *Grid {
	return &Grid{make([]string, 0), d, sep}
}

// NewWithCells returns a new Grid, with pre-defined cells.
// `d` represents the direction in which the values will be written.
// `sep` represents the separator; a string that will be between each column.
// `s` is the cells that will be added.
func NewWithCells(d Direction, sep string, s ...string) *Grid {
	return New(d, sep).Add(s...)
}

// Cells returns all cells of the grid.
func (g Grid) Cells() []string {
	return g.v
}

// Direction returns the direction in which the grid will be written.
func (g Grid) Direction() Direction {
	return g.d
}

// Separator returns the separator; the string that will be between each column.
func (g Grid) Separator() string {
	return g.sep
}

// Add adds a cell to the grid.
// `s` is the values that will be added.
func (g *Grid) Add(s ...string) *Grid {
	g.v = append(g.v, s...)
	return g
}

// Insert inserts a value in a specified position in the grid.
// `i` the position of the value.
// `s` is the value that will be added.
func (g *Grid) Insert(i int, s ...string) *Grid {
	g.v = g.v[0 : len(g.v)+len(s)]
	copy(g.v[i+len(s):], g.v[i:])
	for si, ss := range s {
		g.v[i+si] = ss
	}
	return g
}

// Delete deletes a value in a specified position in the grid.
// `i` the position of the value.
func (g *Grid) Delete(i ...int) *Grid {
	for _, ii := range i {
		copy(g.v[ii:], g.v[ii+1:])
	}
	g.v = g.v[0 : len(g.v)-len(i)]
	return g
}

// FitIntoWidth formats the grid, based on a maximum width.
// `max` represents the maximum width of the grid, based on characters.
// `dim` represents the dimensions of the grid, used for formatting. See `Dimensions`.
// `ok` says whether the the grid fits in the maximum width informed. If false, discard `dim`.
func (g Grid) FitIntoWidth(max int) (dim Dimensions, ok bool) {
	switch count := len(g.v); count {

	// If the slice is empty, returns empty grid that fits.
	case 0:
		return Dimensions{}, false

	// If it has one item, it is validated.
	case 1:
		if l := runewidth.StringWidth(g.v[0]); l <= max {
			return Dimensions{[]int{l}, 1, g}, true
		}
		return Dimensions{}, false

	// If it has two or more items...
	default:

		// If the maximum size is zero, it is invalid.
		if max <= 0 {
			return Dimensions{}, false
		}

		// `lines` represents the minimum number of lines necessary.
		// This loop will check for every possibility.
		for lines := 1; lines <= count; lines++ {

			// `columns` represents the number of columns, based on the current number of lines.
			// It is the cells count, divided by the number of lines, rounded up.
			columns := divUp(count, lines)

			// Calculates the free space...
			// Which is the maximum size, minus the total width of all the separators.
			// If there is no free space, this possibility is ignored.
			free := max - ((columns - 1) * runewidth.StringWidth(g.sep))
			if free < 0 {
				continue
			}

			// Gets bigger widths from each column. :P
			widths := g.biggerFromEachColumn(lines, columns)

			// If the sum of all widths fits the free space, then the possibility is reality!
			var sum int
			for _, width := range widths {
				sum += width
			}
			if sum <= free {
				return Dimensions{widths, lines, g}, true
			}
		}

		// If no possibility worked, than the cells does not fit the maximum size.
		return Dimensions{}, false
	}
}

// FitIntoColumns formats the grid, based on a maximum quantity of columns.
// `max` represents the maximum quantity of columns of the grid.
// `dim` represents the dimensions of the grid, used for formatting. See `Dimensions`.
// `ok` says whether the the grid fits in the maximum width informed. If false, discard `dim`.
func (g Grid) FitIntoColumns(max int) (dim Dimensions, ok bool) {
	// If the maximum size is zero, it is invalid.
	if max <= 0 {
		return Dimensions{}, false
	}

	// `lines` represents the number of lines.
	// It is the cells count, divided by the number of maximum columns, rounded up.
	l := divUp(len(g.v), max)
	return Dimensions{g.biggerFromEachColumn(l, max), l, g}, true
}

func (g Grid) biggerFromEachColumn(lines, columns int) []int {
	// Creates a slice of the widths of the columns.
	widths := make([]int, columns)
	for i, vv := range g.v {
		// `v` represents the list of values.
		// `widths` represents the list of columns' widths.
		// `i` cannot be the index of the value on `v`, but its index on the line.
		// So, `i` is adjusted, based on the direction of the grid population.
		switch g.d {
		case TopToBottom:
			i /= lines
		case LeftToRight:
			i %= columns
		}

		// Now, `i` represents the index of the column (or cell on the line).
		// `widths[i]` is substituted by the current value, if the latter is bigger.
		// `widths[i]` represents the bigger value on the `i` column.
		if l := runewidth.StringWidth(vv); l > widths[i] {
			widths[i] = l
		}
	}
	return widths
}
<|endoftext|>


<commit_before>package searcher

import (
	"bytes"
	"fmt"
	"net/http"
)

func FormatSearchToURL(question string) (output string) {
	return "https://api.stackexchange.com/2.2/search/advanced?order=desc&sort=votes&accepted=True&site=stackoverflow&q=" + question
}

func SearchByQuery(query string) (output string, err error) {

	url := FormatSearchToURL(query)

	fmt.Println(url)

	response, err := http.Get(url)

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

	defer response.Body.Close()

	buf := new(bytes.Buffer)
	buf.ReadFrom(response.Body)

	return buf.String(), nil
}
<commit_msg>URL encode search parameters<commit_after>package searcher

import (
	"bytes"
	"fmt"
	"net/http"
	"net/url"
)

func URLEncodeString(str string) (string, error) {
	encoded, err := url.Parse(str)

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

	return encoded.String(), nil
}

func FormatSearchToURL(question string) (output string) {
	formatted, _ := URLEncodeString(question)

	return "https://api.stackexchange.com/2.2/search/advanced?order=desc&sort=votes&accepted=True&site=stackoverflow&q=" + formatted
}

func SearchByQuery(query string) (string, error) {

	url := FormatSearchToURL(query)

	fmt.Println(url)

	response, err := http.Get(url)

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

	defer response.Body.Close()

	buf := new(bytes.Buffer)
	buf.ReadFrom(response.Body)

	return buf.String(), nil
}
<|endoftext|>


<commit_before>package quic

import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/lucas-clemente/quic-go/internal/flowcontrol"
	"github.com/lucas-clemente/quic-go/internal/protocol"
	"github.com/lucas-clemente/quic-go/internal/utils"
	"github.com/lucas-clemente/quic-go/internal/wire"
)

type sendStreamI interface {
	SendStream
	handleStopSendingFrame(*wire.StopSendingFrame)
	hasData() bool
	popStreamFrame(maxBytes protocol.ByteCount) (*wire.StreamFrame, bool)
	closeForShutdown(error)
	handleMaxStreamDataFrame(*wire.MaxStreamDataFrame)
}

type sendStream struct {
	mutex sync.Mutex

	ctx       context.Context
	ctxCancel context.CancelFunc

	streamID protocol.StreamID
	sender   streamSender

	writeOffset protocol.ByteCount

	cancelWriteErr      error
	closeForShutdownErr error

	closedForShutdown bool // set when CloseForShutdown() is called
	finishedWriting   bool // set once Close() is called
	canceledWrite     bool // set when CancelWrite() is called, or a STOP_SENDING frame is received
	finSent           bool // set when a STREAM_FRAME with FIN bit has b

	dataForWriting []byte

	writeChan     chan struct{}
	deadline      time.Time
	deadlineTimer *time.Timer // initialized by SetReadDeadline()

	flowController flowcontrol.StreamFlowController

	version protocol.VersionNumber
}

var _ SendStream = &sendStream{}
var _ sendStreamI = &sendStream{}

func newSendStream(
	streamID protocol.StreamID,
	sender streamSender,
	flowController flowcontrol.StreamFlowController,
	version protocol.VersionNumber,
) *sendStream {
	s := &sendStream{
		streamID:       streamID,
		sender:         sender,
		flowController: flowController,
		writeChan:      make(chan struct{}, 1),
		version:        version,
	}
	s.ctx, s.ctxCancel = context.WithCancel(context.Background())
	return s
}

func (s *sendStream) StreamID() protocol.StreamID {
	return s.streamID // same for receiveStream and sendStream
}

func (s *sendStream) Write(p []byte) (int, error) {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	if s.finishedWriting {
		return 0, fmt.Errorf("write on closed stream %d", s.streamID)
	}
	if s.canceledWrite {
		return 0, s.cancelWriteErr
	}
	if s.closeForShutdownErr != nil {
		return 0, s.closeForShutdownErr
	}
	if !s.deadline.IsZero() && !time.Now().Before(s.deadline) {
		return 0, errDeadline
	}
	if len(p) == 0 {
		return 0, nil
	}

	s.dataForWriting = p
	s.sender.onHasStreamData(s.streamID)

	var bytesWritten int
	var err error
	for {
		bytesWritten = len(p) - len(s.dataForWriting)
		if !s.deadline.IsZero() && !time.Now().Before(s.deadline) {
			s.dataForWriting = nil
			err = errDeadline
			break
		}
		if s.dataForWriting == nil || s.canceledWrite || s.closedForShutdown {
			break
		}

		s.mutex.Unlock()
		if s.deadline.IsZero() {
			<-s.writeChan
		} else {
			select {
			case <-s.writeChan:
			case <-s.deadlineTimer.C:
			}
		}
		s.mutex.Lock()
	}

	if s.closeForShutdownErr != nil {
		err = s.closeForShutdownErr
	} else if s.cancelWriteErr != nil {
		err = s.cancelWriteErr
	}
	return bytesWritten, err
}

// popStreamFrame returns the next STREAM frame that is supposed to be sent on this stream
// maxBytes is the maximum length this frame (including frame header) will have.
func (s *sendStream) popStreamFrame(maxBytes protocol.ByteCount) (*wire.StreamFrame, bool /* has more data to send */) {
	completed, frame, hasMoreData := s.popStreamFrameImpl(maxBytes)
	if completed {
		s.sender.onStreamCompleted(s.streamID)
	}
	return frame, hasMoreData
}

func (s *sendStream) popStreamFrameImpl(maxBytes protocol.ByteCount) (bool /* completed */, *wire.StreamFrame, bool /* has more data to send */) {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	if s.closeForShutdownErr != nil {
		return false, nil, false
	}

	frame := &wire.StreamFrame{
		StreamID:       s.streamID,
		Offset:         s.writeOffset,
		DataLenPresent: true,
	}
	maxDataLen := frame.MaxDataLen(maxBytes, s.version)
	if maxDataLen == 0 { // a STREAM frame must have at least one byte of data
		return false, nil, s.dataForWriting != nil
	}
	frame.Data, frame.FinBit = s.getDataForWriting(maxDataLen)
	if len(frame.Data) == 0 && !frame.FinBit {
		// this can happen if:
		// - popStreamFrame is called but there's no data for writing
		// - there's data for writing, but the stream is stream-level flow control blocked
		// - there's data for writing, but the stream is connection-level flow control blocked
		if s.dataForWriting == nil {
			return false, nil, false
		}
		if isBlocked, offset := s.flowController.IsNewlyBlocked(); isBlocked {
			s.sender.queueControlFrame(&wire.StreamDataBlockedFrame{
				StreamID:  s.streamID,
				DataLimit: offset,
			})
			return false, nil, false
		}
		return false, nil, true
	}
	if frame.FinBit {
		s.finSent = true
	}
	return frame.FinBit, frame, s.dataForWriting != nil
}

func (s *sendStream) hasData() bool {
	s.mutex.Lock()
	hasData := len(s.dataForWriting) > 0
	s.mutex.Unlock()
	return hasData
}

func (s *sendStream) getDataForWriting(maxBytes protocol.ByteCount) ([]byte, bool /* should send FIN */) {
	if s.dataForWriting == nil {
		return nil, s.finishedWriting && !s.finSent
	}

	maxBytes = utils.MinByteCount(maxBytes, s.flowController.SendWindowSize())
	if maxBytes == 0 {
		return nil, false
	}

	var ret []byte
	if protocol.ByteCount(len(s.dataForWriting)) > maxBytes {
		ret = make([]byte, int(maxBytes))
		copy(ret, s.dataForWriting[:maxBytes])
		s.dataForWriting = s.dataForWriting[maxBytes:]
	} else {
		ret = make([]byte, len(s.dataForWriting))
		copy(ret, s.dataForWriting)
		s.dataForWriting = nil
		s.signalWrite()
	}
	s.writeOffset += protocol.ByteCount(len(ret))
	s.flowController.AddBytesSent(protocol.ByteCount(len(ret)))
	return ret, s.finishedWriting && s.dataForWriting == nil && !s.finSent
}

func (s *sendStream) Close() error {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	if s.canceledWrite {
		return fmt.Errorf("Close called for canceled stream %d", s.streamID)
	}
	s.finishedWriting = true
	s.sender.onHasStreamData(s.streamID) // need to send the FIN
	s.ctxCancel()
	return nil
}

func (s *sendStream) CancelWrite(errorCode protocol.ApplicationErrorCode) error {
	s.mutex.Lock()
	completed, err := s.cancelWriteImpl(errorCode, fmt.Errorf("Write on stream %d canceled with error code %d", s.streamID, errorCode))
	s.mutex.Unlock()

	if completed {
		s.sender.onStreamCompleted(s.streamID)
	}
	return err
}

// must be called after locking the mutex
func (s *sendStream) cancelWriteImpl(errorCode protocol.ApplicationErrorCode, writeErr error) (bool /*completed */, error) {
	if s.canceledWrite {
		return false, nil
	}
	if s.finishedWriting {
		return false, fmt.Errorf("CancelWrite for closed stream %d", s.streamID)
	}
	s.canceledWrite = true
	s.cancelWriteErr = writeErr
	s.signalWrite()
	s.sender.queueControlFrame(&wire.ResetStreamFrame{
		StreamID:   s.streamID,
		ByteOffset: s.writeOffset,
		ErrorCode:  errorCode,
	})
	// TODO(#991): cancel retransmissions for this stream
	s.ctxCancel()
	return true, nil
}

func (s *sendStream) handleStopSendingFrame(frame *wire.StopSendingFrame) {
	if completed := s.handleStopSendingFrameImpl(frame); completed {
		s.sender.onStreamCompleted(s.streamID)
	}
}

func (s *sendStream) handleMaxStreamDataFrame(frame *wire.MaxStreamDataFrame) {
	s.flowController.UpdateSendWindow(frame.ByteOffset)
	s.mutex.Lock()
	if s.dataForWriting != nil {
		s.sender.onHasStreamData(s.streamID)
	}
	s.mutex.Unlock()
}

// must be called after locking the mutex
func (s *sendStream) handleStopSendingFrameImpl(frame *wire.StopSendingFrame) bool /*completed*/ {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	writeErr := streamCanceledError{
		errorCode: frame.ErrorCode,
		error:     fmt.Errorf("Stream %d was reset with error code %d", s.streamID, frame.ErrorCode),
	}
	errorCode := errorCodeStopping
	completed, _ := s.cancelWriteImpl(errorCode, writeErr)
	return completed
}

func (s *sendStream) Context() context.Context {
	return s.ctx
}

func (s *sendStream) SetWriteDeadline(t time.Time) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	s.deadline = t
	if s.deadline.IsZero() { // skip if there's no deadline to set
		s.signalWrite()
		return nil
	}
	// Lazily initialize the deadline timer.
	if s.deadlineTimer == nil {
		s.deadlineTimer = time.NewTimer(time.Until(t))
		return nil
	}
	// reset the timer to the new deadline
	if !s.deadlineTimer.Stop() {
		<-s.deadlineTimer.C
	}
	s.deadlineTimer.Reset(time.Until(t))
	return nil
}

// CloseForShutdown closes a stream abruptly.
// It makes Write unblock (and return the error) immediately.
// The peer will NOT be informed about this: the stream is closed without sending a FIN or RST.
func (s *sendStream) closeForShutdown(err error) {
	s.mutex.Lock()
	s.closedForShutdown = true
	s.closeForShutdownErr = err
	s.mutex.Unlock()
	s.signalWrite()
	s.ctxCancel()
}

func (s *sendStream) getWriteOffset() protocol.ByteCount {
	return s.writeOffset
}

// signalWrite performs a non-blocking send on the writeChan
func (s *sendStream) signalWrite() {
	select {
	case s.writeChan <- struct{}{}:
	default:
	}
}
<commit_msg>fix deadlock when sending stream data<commit_after>package quic

import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/lucas-clemente/quic-go/internal/flowcontrol"
	"github.com/lucas-clemente/quic-go/internal/protocol"
	"github.com/lucas-clemente/quic-go/internal/utils"
	"github.com/lucas-clemente/quic-go/internal/wire"
)

type sendStreamI interface {
	SendStream
	handleStopSendingFrame(*wire.StopSendingFrame)
	hasData() bool
	popStreamFrame(maxBytes protocol.ByteCount) (*wire.StreamFrame, bool)
	closeForShutdown(error)
	handleMaxStreamDataFrame(*wire.MaxStreamDataFrame)
}

type sendStream struct {
	mutex sync.Mutex

	ctx       context.Context
	ctxCancel context.CancelFunc

	streamID protocol.StreamID
	sender   streamSender

	writeOffset protocol.ByteCount

	cancelWriteErr      error
	closeForShutdownErr error

	closedForShutdown bool // set when CloseForShutdown() is called
	finishedWriting   bool // set once Close() is called
	canceledWrite     bool // set when CancelWrite() is called, or a STOP_SENDING frame is received
	finSent           bool // set when a STREAM_FRAME with FIN bit has b

	dataForWriting []byte

	writeChan     chan struct{}
	deadline      time.Time
	deadlineTimer *time.Timer // initialized by SetReadDeadline()

	flowController flowcontrol.StreamFlowController

	version protocol.VersionNumber
}

var _ SendStream = &sendStream{}
var _ sendStreamI = &sendStream{}

func newSendStream(
	streamID protocol.StreamID,
	sender streamSender,
	flowController flowcontrol.StreamFlowController,
	version protocol.VersionNumber,
) *sendStream {
	s := &sendStream{
		streamID:       streamID,
		sender:         sender,
		flowController: flowController,
		writeChan:      make(chan struct{}, 1),
		version:        version,
	}
	s.ctx, s.ctxCancel = context.WithCancel(context.Background())
	return s
}

func (s *sendStream) StreamID() protocol.StreamID {
	return s.streamID // same for receiveStream and sendStream
}

func (s *sendStream) Write(p []byte) (int, error) {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	if s.finishedWriting {
		return 0, fmt.Errorf("write on closed stream %d", s.streamID)
	}
	if s.canceledWrite {
		return 0, s.cancelWriteErr
	}
	if s.closeForShutdownErr != nil {
		return 0, s.closeForShutdownErr
	}
	if !s.deadline.IsZero() && !time.Now().Before(s.deadline) {
		return 0, errDeadline
	}
	if len(p) == 0 {
		return 0, nil
	}

	s.dataForWriting = p

	var bytesWritten int
	var err error
	var notifiedSender bool
	for {
		bytesWritten = len(p) - len(s.dataForWriting)
		if !s.deadline.IsZero() && !time.Now().Before(s.deadline) {
			s.dataForWriting = nil
			err = errDeadline
			break
		}
		if s.dataForWriting == nil || s.canceledWrite || s.closedForShutdown {
			break
		}

		s.mutex.Unlock()
		if !notifiedSender {
			s.sender.onHasStreamData(s.streamID) // must be called without holding the mutex
			notifiedSender = true
		}
		if s.deadline.IsZero() {
			<-s.writeChan
		} else {
			select {
			case <-s.writeChan:
			case <-s.deadlineTimer.C:
			}
		}
		s.mutex.Lock()
	}

	if s.closeForShutdownErr != nil {
		err = s.closeForShutdownErr
	} else if s.cancelWriteErr != nil {
		err = s.cancelWriteErr
	}
	return bytesWritten, err
}

// popStreamFrame returns the next STREAM frame that is supposed to be sent on this stream
// maxBytes is the maximum length this frame (including frame header) will have.
func (s *sendStream) popStreamFrame(maxBytes protocol.ByteCount) (*wire.StreamFrame, bool /* has more data to send */) {
	completed, frame, hasMoreData := s.popStreamFrameImpl(maxBytes)
	if completed {
		s.sender.onStreamCompleted(s.streamID)
	}
	return frame, hasMoreData
}

func (s *sendStream) popStreamFrameImpl(maxBytes protocol.ByteCount) (bool /* completed */, *wire.StreamFrame, bool /* has more data to send */) {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	if s.closeForShutdownErr != nil {
		return false, nil, false
	}

	frame := &wire.StreamFrame{
		StreamID:       s.streamID,
		Offset:         s.writeOffset,
		DataLenPresent: true,
	}
	maxDataLen := frame.MaxDataLen(maxBytes, s.version)
	if maxDataLen == 0 { // a STREAM frame must have at least one byte of data
		return false, nil, s.dataForWriting != nil
	}
	frame.Data, frame.FinBit = s.getDataForWriting(maxDataLen)
	if len(frame.Data) == 0 && !frame.FinBit {
		// this can happen if:
		// - popStreamFrame is called but there's no data for writing
		// - there's data for writing, but the stream is stream-level flow control blocked
		// - there's data for writing, but the stream is connection-level flow control blocked
		if s.dataForWriting == nil {
			return false, nil, false
		}
		if isBlocked, offset := s.flowController.IsNewlyBlocked(); isBlocked {
			s.sender.queueControlFrame(&wire.StreamDataBlockedFrame{
				StreamID:  s.streamID,
				DataLimit: offset,
			})
			return false, nil, false
		}
		return false, nil, true
	}
	if frame.FinBit {
		s.finSent = true
	}
	return frame.FinBit, frame, s.dataForWriting != nil
}

func (s *sendStream) hasData() bool {
	s.mutex.Lock()
	hasData := len(s.dataForWriting) > 0
	s.mutex.Unlock()
	return hasData
}

func (s *sendStream) getDataForWriting(maxBytes protocol.ByteCount) ([]byte, bool /* should send FIN */) {
	if s.dataForWriting == nil {
		return nil, s.finishedWriting && !s.finSent
	}

	maxBytes = utils.MinByteCount(maxBytes, s.flowController.SendWindowSize())
	if maxBytes == 0 {
		return nil, false
	}

	var ret []byte
	if protocol.ByteCount(len(s.dataForWriting)) > maxBytes {
		ret = make([]byte, int(maxBytes))
		copy(ret, s.dataForWriting[:maxBytes])
		s.dataForWriting = s.dataForWriting[maxBytes:]
	} else {
		ret = make([]byte, len(s.dataForWriting))
		copy(ret, s.dataForWriting)
		s.dataForWriting = nil
		s.signalWrite()
	}
	s.writeOffset += protocol.ByteCount(len(ret))
	s.flowController.AddBytesSent(protocol.ByteCount(len(ret)))
	return ret, s.finishedWriting && s.dataForWriting == nil && !s.finSent
}

func (s *sendStream) Close() error {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	if s.canceledWrite {
		return fmt.Errorf("Close called for canceled stream %d", s.streamID)
	}
	s.finishedWriting = true
	s.sender.onHasStreamData(s.streamID) // need to send the FIN
	s.ctxCancel()
	return nil
}

func (s *sendStream) CancelWrite(errorCode protocol.ApplicationErrorCode) error {
	s.mutex.Lock()
	completed, err := s.cancelWriteImpl(errorCode, fmt.Errorf("Write on stream %d canceled with error code %d", s.streamID, errorCode))
	s.mutex.Unlock()

	if completed {
		s.sender.onStreamCompleted(s.streamID)
	}
	return err
}

// must be called after locking the mutex
func (s *sendStream) cancelWriteImpl(errorCode protocol.ApplicationErrorCode, writeErr error) (bool /*completed */, error) {
	if s.canceledWrite {
		return false, nil
	}
	if s.finishedWriting {
		return false, fmt.Errorf("CancelWrite for closed stream %d", s.streamID)
	}
	s.canceledWrite = true
	s.cancelWriteErr = writeErr
	s.signalWrite()
	s.sender.queueControlFrame(&wire.ResetStreamFrame{
		StreamID:   s.streamID,
		ByteOffset: s.writeOffset,
		ErrorCode:  errorCode,
	})
	// TODO(#991): cancel retransmissions for this stream
	s.ctxCancel()
	return true, nil
}

func (s *sendStream) handleStopSendingFrame(frame *wire.StopSendingFrame) {
	if completed := s.handleStopSendingFrameImpl(frame); completed {
		s.sender.onStreamCompleted(s.streamID)
	}
}

func (s *sendStream) handleMaxStreamDataFrame(frame *wire.MaxStreamDataFrame) {
	s.flowController.UpdateSendWindow(frame.ByteOffset)
	s.mutex.Lock()
	if s.dataForWriting != nil {
		s.sender.onHasStreamData(s.streamID)
	}
	s.mutex.Unlock()
}

// must be called after locking the mutex
func (s *sendStream) handleStopSendingFrameImpl(frame *wire.StopSendingFrame) bool /*completed*/ {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	writeErr := streamCanceledError{
		errorCode: frame.ErrorCode,
		error:     fmt.Errorf("Stream %d was reset with error code %d", s.streamID, frame.ErrorCode),
	}
	errorCode := errorCodeStopping
	completed, _ := s.cancelWriteImpl(errorCode, writeErr)
	return completed
}

func (s *sendStream) Context() context.Context {
	return s.ctx
}

func (s *sendStream) SetWriteDeadline(t time.Time) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	s.deadline = t
	if s.deadline.IsZero() { // skip if there's no deadline to set
		s.signalWrite()
		return nil
	}
	// Lazily initialize the deadline timer.
	if s.deadlineTimer == nil {
		s.deadlineTimer = time.NewTimer(time.Until(t))
		return nil
	}
	// reset the timer to the new deadline
	if !s.deadlineTimer.Stop() {
		<-s.deadlineTimer.C
	}
	s.deadlineTimer.Reset(time.Until(t))
	return nil
}

// CloseForShutdown closes a stream abruptly.
// It makes Write unblock (and return the error) immediately.
// The peer will NOT be informed about this: the stream is closed without sending a FIN or RST.
func (s *sendStream) closeForShutdown(err error) {
	s.mutex.Lock()
	s.closedForShutdown = true
	s.closeForShutdownErr = err
	s.mutex.Unlock()
	s.signalWrite()
	s.ctxCancel()
}

func (s *sendStream) getWriteOffset() protocol.ByteCount {
	return s.writeOffset
}

// signalWrite performs a non-blocking send on the writeChan
func (s *sendStream) signalWrite() {
	select {
	case s.writeChan <- struct{}{}:
	default:
	}
}
<|endoftext|>


<commit_before>package gps

import (
	"bytes"
	"crypto/sha256"
	"io"
	"sort"
)

// HashInputs computes a hash digest of all data in SolveParams and the
// RootManifest that act as function inputs to Solve().
//
// The digest returned from this function is the same as the digest that would
// be included with a Solve() Result. As such, it's appropriate for comparison
// against the digest stored in a lock file, generated by a previous Solve(): if
// the digests match, then manifest and lock are in sync, and a Solve() is
// unnecessary.
//
// (Basically, this is for memoization.)
func (s *solver) HashInputs() (digest []byte) {
	h := sha256.New()
	s.writeHashingInputs(h)

	hd := h.Sum(nil)
	digest = hd[:]
	return
}

func (s *solver) writeHashingInputs(w io.Writer) {
	writeString := func(s string) {
		// Skip zero-length string writes; it doesn't affect the real hash
		// calculation, and keeps misleading newlines from showing up in the
		// debug output.
		if s != "" {
			// All users of writeHashingInputs cannot error on Write(), so just
			// ignore it
			w.Write([]byte(s))
		}
	}

	// Apply overrides to the constraints from the root. Otherwise, the hash
	// would be computed on the basis of a constraint from root that doesn't
	// actually affect solving.
	wc := s.ovr.overrideAll(s.rm.DependencyConstraints().merge(s.rm.TestDependencyConstraints()))

	for _, pd := range wc {
		writeString(string(pd.Ident.ProjectRoot))
		writeString(pd.Ident.Source)
		// FIXME Constraint.String() is a surjective-only transformation - tags
		// and branches with the same name are written out as the same string.
		// This could, albeit rarely, result in erroneously identical inputs
		// when a real change has occurred.
		writeString(pd.Constraint.String())
	}

	// Get the external reach list

	// Write each of the packages, or the errors that were found for a
	// particular subpath, into the hash. We need to do this in a
	// deterministic order, so expand and sort the map.
	var pkgs []PackageOrErr
	for _, perr := range s.rpt.Packages {
		pkgs = append(pkgs, perr)
	}
	sort.Sort(sortPackageOrErr(pkgs))
	for _, perr := range pkgs {
		if perr.Err != nil {
			writeString(perr.Err.Error())
		} else {
			writeString(perr.P.Name)
			writeString(perr.P.CommentPath)
			writeString(perr.P.ImportPath)
			for _, imp := range perr.P.Imports {
				if !isStdLib(imp) {
					writeString(imp)
				}
			}
			for _, imp := range perr.P.TestImports {
				if !isStdLib(imp) {
					writeString(imp)
				}
			}
		}
	}

	// Write any require packages given in the root manifest.
	if len(s.req) > 0 {
		// Dump and sort the reqnores
		req := make([]string, 0, len(s.req))
		for pkg := range s.req {
			req = append(req, pkg)
		}
		sort.Strings(req)

		for _, reqp := range req {
			writeString(reqp)
		}
	}

	// Add the ignored packages, if any.
	if len(s.ig) > 0 {
		// Dump and sort the ignores
		ig := make([]string, 0, len(s.ig))
		for pkg := range s.ig {
			ig = append(ig, pkg)
		}
		sort.Strings(ig)

		for _, igp := range ig {
			writeString(igp)
		}
	}

	for _, pc := range s.ovr.asSortedSlice() {
		writeString(string(pc.Ident.ProjectRoot))
		if pc.Ident.Source != "" {
			writeString(pc.Ident.Source)
		}
		if pc.Constraint != nil {
			writeString(pc.Constraint.String())
		}
	}

	an, av := s.b.AnalyzerInfo()
	writeString(an)
	writeString(av.String())
}

// bytes.Buffer wrapper that injects newlines after each call to Write().
type nlbuf bytes.Buffer

func (buf *nlbuf) Write(p []byte) (n int, err error) {
	n, _ = (*bytes.Buffer)(buf).Write(p)
	(*bytes.Buffer)(buf).WriteByte('\n')
	return n + 1, nil
}

// HashingInputsAsString returns the raw input data used by Solver.HashInputs()
// as a string.
//
// This is primarily intended for debugging purposes.
func HashingInputsAsString(s Solver) string {
	ts := s.(*solver)
	buf := new(nlbuf)
	ts.writeHashingInputs(buf)

	return (*bytes.Buffer)(buf).String()
}

type sortPackageOrErr []PackageOrErr

func (s sortPackageOrErr) Len() int      { return len(s) }
func (s sortPackageOrErr) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

func (s sortPackageOrErr) Less(i, j int) bool {
	a, b := s[i], s[j]
	if a.Err != nil || b.Err != nil {
		// Sort errors last.
		if b.Err == nil {
			return false
		}
		if a.Err == nil {
			return true
		}
		// And then by string.
		return a.Err.Error() < b.Err.Error()
	}
	// And finally, sort by import path.
	return a.P.ImportPath < b.P.ImportPath
}
<commit_msg>Remove pointless ifs<commit_after>package gps

import (
	"bytes"
	"crypto/sha256"
	"io"
	"sort"
)

// HashInputs computes a hash digest of all data in SolveParams and the
// RootManifest that act as function inputs to Solve().
//
// The digest returned from this function is the same as the digest that would
// be included with a Solve() Result. As such, it's appropriate for comparison
// against the digest stored in a lock file, generated by a previous Solve(): if
// the digests match, then manifest and lock are in sync, and a Solve() is
// unnecessary.
//
// (Basically, this is for memoization.)
func (s *solver) HashInputs() (digest []byte) {
	h := sha256.New()
	s.writeHashingInputs(h)

	hd := h.Sum(nil)
	digest = hd[:]
	return
}

func (s *solver) writeHashingInputs(w io.Writer) {
	writeString := func(s string) {
		// Skip zero-length string writes; it doesn't affect the real hash
		// calculation, and keeps misleading newlines from showing up in the
		// debug output.
		if s != "" {
			// All users of writeHashingInputs cannot error on Write(), so just
			// ignore it
			w.Write([]byte(s))
		}
	}

	// Apply overrides to the constraints from the root. Otherwise, the hash
	// would be computed on the basis of a constraint from root that doesn't
	// actually affect solving.
	wc := s.ovr.overrideAll(s.rm.DependencyConstraints().merge(s.rm.TestDependencyConstraints()))

	for _, pd := range wc {
		writeString(string(pd.Ident.ProjectRoot))
		writeString(pd.Ident.Source)
		// FIXME Constraint.String() is a surjective-only transformation - tags
		// and branches with the same name are written out as the same string.
		// This could, albeit rarely, result in erroneously identical inputs
		// when a real change has occurred.
		writeString(pd.Constraint.String())
	}

	// Get the external reach list

	// Write each of the packages, or the errors that were found for a
	// particular subpath, into the hash. We need to do this in a
	// deterministic order, so expand and sort the map.
	var pkgs []PackageOrErr
	for _, perr := range s.rpt.Packages {
		pkgs = append(pkgs, perr)
	}
	sort.Sort(sortPackageOrErr(pkgs))
	for _, perr := range pkgs {
		if perr.Err != nil {
			writeString(perr.Err.Error())
		} else {
			writeString(perr.P.Name)
			writeString(perr.P.CommentPath)
			writeString(perr.P.ImportPath)
			for _, imp := range perr.P.Imports {
				if !isStdLib(imp) {
					writeString(imp)
				}
			}
			for _, imp := range perr.P.TestImports {
				if !isStdLib(imp) {
					writeString(imp)
				}
			}
		}
	}

	// Write any required packages given in the root manifest.
	req := make([]string, 0, len(s.req))
	for pkg := range s.req {
		req = append(req, pkg)
	}
	sort.Strings(req)

	for _, reqp := range req {
		writeString(reqp)
	}

	// Add the ignored packages, if any.
	ig := make([]string, 0, len(s.ig))
	for pkg := range s.ig {
		ig = append(ig, pkg)
	}
	sort.Strings(ig)

	for _, igp := range ig {
		writeString(igp)
	}

	for _, pc := range s.ovr.asSortedSlice() {
		writeString(string(pc.Ident.ProjectRoot))
		if pc.Ident.Source != "" {
			writeString(pc.Ident.Source)
		}
		if pc.Constraint != nil {
			writeString(pc.Constraint.String())
		}
	}

	an, av := s.b.AnalyzerInfo()
	writeString(an)
	writeString(av.String())
}

// bytes.Buffer wrapper that injects newlines after each call to Write().
type nlbuf bytes.Buffer

func (buf *nlbuf) Write(p []byte) (n int, err error) {
	n, _ = (*bytes.Buffer)(buf).Write(p)
	(*bytes.Buffer)(buf).WriteByte('\n')
	return n + 1, nil
}

// HashingInputsAsString returns the raw input data used by Solver.HashInputs()
// as a string.
//
// This is primarily intended for debugging purposes.
func HashingInputsAsString(s Solver) string {
	ts := s.(*solver)
	buf := new(nlbuf)
	ts.writeHashingInputs(buf)

	return (*bytes.Buffer)(buf).String()
}

type sortPackageOrErr []PackageOrErr

func (s sortPackageOrErr) Len() int      { return len(s) }
func (s sortPackageOrErr) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

func (s sortPackageOrErr) Less(i, j int) bool {
	a, b := s[i], s[j]
	if a.Err != nil || b.Err != nil {
		// Sort errors last.
		if b.Err == nil {
			return false
		}
		if a.Err == nil {
			return true
		}
		// And then by string.
		return a.Err.Error() < b.Err.Error()
	}
	// And finally, sort by import path.
	return a.P.ImportPath < b.P.ImportPath
}
<|endoftext|>


<commit_before>package lion

import (
	"net/http"
	"strings"
	"sync"

	"github.com/celrenheit/lion/matcher"
)

const defaultAnyHostKey = "lionDefaultAnyHostKey"

type hostMatcher struct {
	matcher matcher.Matcher
}

func newHostMatcher() *hostMatcher {
	cfg := &matcher.Config{
		ParamChar:        ':',
		WildcardChar:     '*',
		Separators:       ".",
		GetSetterCreator: &hscreator{},
		ParamTransformer: newHostParamTransformer(),
	}
	return &hostMatcher{
		matcher: matcher.Custom(cfg),
	}
}

type registererRMGrabber struct {
	rm RegisterMatcher
}

func (hm *hostMatcher) Register(pattern string) RegisterMatcher {
	host := pattern
	if host == "" {
		host = "*" + defaultAnyHostKey
	}

	rg := &registererRMGrabber{}
	reversedHost := reverseHost(host)
	hm.matcher.Set(reversedHost, rg, nil)
	return rg.rm
}

func (hm *hostMatcher) Match(c *Context, req *http.Request) Handler {
	reversedHost := reverseHost(req.Host)
	value := hm.matcher.GetWithContext(c, reversedHost, nil)
	// Delete wildcard param
	// TODO: Skip this step for performance reasons
	// (Maybe by adding a blacklisted or skiplisted params on host matcher)
	if _, ok := c.ParamOk(defaultAnyHostKey); ok {
		c.Remove(defaultAnyHostKey)
	}

	if rm, ok := value.(RegisterMatcher); ok {
		_, h := rm.Match(c, req)
		return h
	}
	return nil
}

type hostStore struct {
	rm RegisterMatcher
}

func (hs *hostStore) Set(value interface{}, tags matcher.Tags) {
	if rg, ok := value.(*registererRMGrabber); ok {
		rg.rm = hs.rm
	}
}

func (hs *hostStore) Get(tags matcher.Tags) interface{} {
	return hs.rm
}

type hscreator struct{}

func (c *hscreator) New() matcher.GetSetter {
	return &hostStore{
		rm: newPathMatcher(),
	}
}

type hostParamTransformer struct {
	splittedStringPool sync.Pool
}

func newHostParamTransformer() *hostParamTransformer {
	return &hostParamTransformer{
		splittedStringPool: sync.Pool{
			New: func() interface{} {
				return &splittedStringItem{}
			},
		},
	}
}

func (hpt *hostParamTransformer) Transform(input string) string {
	reversedItem := hpt.split(input, ".")
	reversed := reversedItem.slice
	for i, j := 0, len(reversed)-1; i < j; i, j = i+1, j-1 {
		reversed[i], reversed[j] = reversed[j], reversed[i]
	}
	output := strings.Join(reversed, ".")
	hpt.splittedStringPool.Put(reversedItem)
	return output
}

// Taken from Go's standard library
// https://github.com/golang/go/blob/master/src/strings/strings.go#L237-L261
func (hpt *hostParamTransformer) split(s, sep string) *splittedStringItem {
	si := hpt.splittedStringPool.Get().(*splittedStringItem)
	n := strings.Count(s, sep) + 1
	c := sep[0]
	start := 0

	var a []string
	if cap(si.slice) == n {
		a = si.slice
	} else {
		a = make([]string, n)
	}

	na := 0
	for i := 0; i+len(sep) <= len(s) && na+1 < n; i++ {
		if s[i] == c && (len(sep) == 1 || s[i:i+len(sep)] == sep) {
			a[na] = s[start:i]
			na++
			start = i + len(sep)
			i += len(sep) - 1
		}
	}
	a[na] = s[start:]
	si.slice = a[0 : na+1]
	return si
}

type splittedStringItem struct {
	slice []string
}

var hostReverser = newHostParamTransformer()

func reverseHost(input string) string {
	return hostReverser.Transform(input)
}
<commit_msg>Use the host matcher instead of the default path matcher only if the user registers multiple hosts.<commit_after>package lion

import (
	"net/http"
	"strings"
	"sync"

	"github.com/celrenheit/lion/matcher"
)

const (
	defaultAnyHostKey     = "lionDefaultAnyHostKey"
	defaultAnyHostPattern = "*" + defaultAnyHostKey
)

type hostMatcher struct {
	matcher   matcher.Matcher
	defaultRM RegisterMatcher
	multihost bool
}

func newHostMatcher() *hostMatcher {
	cfg := &matcher.Config{
		ParamChar:        ':',
		WildcardChar:     '*',
		Separators:       ".",
		GetSetterCreator: &hscreator{},
		ParamTransformer: newHostParamTransformer(),
	}
	return &hostMatcher{
		matcher:   matcher.Custom(cfg),
		defaultRM: newPathMatcher(),
	}
}

type registererRMGrabber struct {
	rm RegisterMatcher
}

func (hm *hostMatcher) Register(pattern string) RegisterMatcher {
	host := pattern

	// Switch to multihost
	if !hm.multihost && host != "" {
		hm.multihost = true
		hm.matcher.Set(reverseHost(defaultAnyHostPattern), hm.defaultRM, nil)
	}

	if hm.multihost {

		if host == "" {
			host = defaultAnyHostPattern
		}

		rg := &registererRMGrabber{}
		reversedHost := reverseHost(host)
		hm.matcher.Set(reversedHost, rg, nil)
		return rg.rm
	} else {
		return hm.defaultRM
	}
}

func (hm *hostMatcher) Match(c *Context, req *http.Request) Handler {
	if hm.multihost {
		reversedHost := reverseHost(req.Host)
		value := hm.matcher.GetWithContext(c, reversedHost, nil)
		// Delete wildcard param
		// TODO: Skip this step for performance reasons
		// (Maybe by adding a blacklisted or skiplisted params on host matcher)
		if _, ok := c.ParamOk(defaultAnyHostKey); ok {
			c.Remove(defaultAnyHostKey)
		}

		if rm, ok := value.(RegisterMatcher); ok {
			_, h := rm.Match(c, req)
			return h
		}
	} else {
		_, h := hm.defaultRM.Match(c, req)
		return h
	}
	return nil
}

type hostStore struct {
	rm RegisterMatcher
}

func (hs *hostStore) Set(value interface{}, tags matcher.Tags) {
	// Overwrite RegisterMatcher
	if rm, ok := value.(RegisterMatcher); ok {
		hs.rm = rm
	}

	// Little hack to grab the pointer of the underlying RegisterMatcher
	if rg, ok := value.(*registererRMGrabber); ok {
		rg.rm = hs.rm
	}
}

func (hs *hostStore) Get(tags matcher.Tags) interface{} {
	return hs.rm
}

type hscreator struct{}

func (c *hscreator) New() matcher.GetSetter {
	return &hostStore{
		rm: newPathMatcher(),
	}
}

type hostParamTransformer struct {
	splittedStringPool sync.Pool
}

func newHostParamTransformer() *hostParamTransformer {
	return &hostParamTransformer{
		splittedStringPool: sync.Pool{
			New: func() interface{} {
				return &splittedStringItem{}
			},
		},
	}
}

func (hpt *hostParamTransformer) Transform(input string) string {
	reversedItem := hpt.split(input, ".")
	reversed := reversedItem.slice
	for i, j := 0, len(reversed)-1; i < j; i, j = i+1, j-1 {
		reversed[i], reversed[j] = reversed[j], reversed[i]
	}
	output := strings.Join(reversed, ".")
	hpt.splittedStringPool.Put(reversedItem)
	return output
}

// Taken from Go's standard library
// https://github.com/golang/go/blob/master/src/strings/strings.go#L237-L261
func (hpt *hostParamTransformer) split(s, sep string) *splittedStringItem {
	si := hpt.splittedStringPool.Get().(*splittedStringItem)
	n := strings.Count(s, sep) + 1
	c := sep[0]
	start := 0

	var a []string
	if cap(si.slice) == n {
		a = si.slice
	} else {
		a = make([]string, n)
	}

	na := 0
	for i := 0; i+len(sep) <= len(s) && na+1 < n; i++ {
		if s[i] == c && (len(sep) == 1 || s[i:i+len(sep)] == sep) {
			a[na] = s[start:i]
			na++
			start = i + len(sep)
			i += len(sep) - 1
		}
	}
	a[na] = s[start:]
	si.slice = a[0 : na+1]
	return si
}

type splittedStringItem struct {
	slice []string
}

var hostReverser = newHostParamTransformer()

func reverseHost(input string) string {
	return hostReverser.Transform(input)
}
<|endoftext|>