<commit_before>package worker

import (
	"flag"
	"io"
	"net"
	"net/rpc"
	"strings"

	"github.com/dgraph-io/dgraph/conn"
	"github.com/dgraph-io/dgraph/posting"
	"github.com/dgraph-io/dgraph/store"
	"github.com/dgraph-io/dgraph/task"
	"github.com/dgraph-io/dgraph/x"
	"github.com/google/flatbuffers/go"
)

var workers = flag.String("workers", "",
	"Comma separated list of IP addresses of workers")
var workerPort = flag.String("workerport", ":12345",
	"Port used by worker for internal communication.")

var glog = x.Log("worker")
var dataStore *store.Store
var pools []*conn.Pool

func Init(ps *store.Store) {
	dataStore = ps
}

func Connect() {
	w := new(Worker)
	if err := rpc.Register(w); err != nil {
		glog.Fatal(err)
	}
	if err := runServer(*workerPort); err != nil {
		glog.Fatal(err)
	}

	addrs := strings.Split(*workers, ",")
	var pools []*conn.Pool
	for _, addr := range addrs {
		if len(addr) == 0 {
			continue
		}
		pool := conn.NewPool(addr, 5)
		query := new(conn.Query)
		query.Data = []byte("hello")
		reply := new(conn.Reply)
		if err := pool.Call("Worker.Hello", query, reply); err != nil {
			glog.WithField("call", "Worker.Hello").Fatal(err)
		}
		glog.WithField("reply", string(reply.Data)).WithField("addr", addr).
			Info("Got reply from server")
		pools = append(pools, pool)
	}

	glog.Info("Server started. Clients connected.")
}

func ProcessTask(query []byte) (result []byte, rerr error) {
	uo := flatbuffers.GetUOffsetT(query)
	q := new(task.Query)
	q.Init(query, uo)

	b := flatbuffers.NewBuilder(0)
	voffsets := make([]flatbuffers.UOffsetT, q.UidsLength())
	uoffsets := make([]flatbuffers.UOffsetT, q.UidsLength())

	attr := string(q.Attr())
	for i := 0; i < q.UidsLength(); i++ {
		uid := q.Uids(i)
		key := posting.Key(uid, attr)
		pl := posting.GetOrCreate(key, dataStore)

		var valoffset flatbuffers.UOffsetT
		if val, err := pl.Value(); err != nil {
			valoffset = b.CreateByteVector(x.Nilbyte)
		} else {
			valoffset = b.CreateByteVector(val)
		}
		task.ValueStart(b)
		task.ValueAddVal(b, valoffset)
		voffsets[i] = task.ValueEnd(b)

		ulist := pl.GetUids()
		uoffsets[i] = x.UidlistOffset(b, ulist)
	}
	task.ResultStartValuesVector(b, len(voffsets))
	for i := len(voffsets) - 1; i >= 0; i-- {
		b.PrependUOffsetT(voffsets[i])
	}
	valuesVent := b.EndVector(len(voffsets))

	task.ResultStartUidmatrixVector(b, len(uoffsets))
	for i := len(uoffsets) - 1; i >= 0; i-- {
		b.PrependUOffsetT(uoffsets[i])
	}
	matrixVent := b.EndVector(len(uoffsets))

	task.ResultStart(b)
	task.ResultAddValues(b, valuesVent)
	task.ResultAddUidmatrix(b, matrixVent)
	rend := task.ResultEnd(b)
	b.Finish(rend)
	return b.Bytes[b.Head():], nil
}

func NewQuery(attr string, uids []uint64) []byte {
	b := flatbuffers.NewBuilder(0)
	task.QueryStartUidsVector(b, len(uids))
	for i := len(uids) - 1; i >= 0; i-- {
		b.PrependUint64(uids[i])
	}
	vend := b.EndVector(len(uids))

	ao := b.CreateString(attr)
	task.QueryStart(b)
	task.QueryAddAttr(b, ao)
	task.QueryAddUids(b, vend)
	qend := task.QueryEnd(b)
	b.Finish(qend)
	return b.Bytes[b.Head():]
}

type Worker struct {
}

func (w *Worker) Hello(query *conn.Query, reply *conn.Reply) error {
	if string(query.Data) == "hello" {
		reply.Data = []byte("Oh hello there!")
	} else {
		reply.Data = []byte("Hey stranger!")
	}
	return nil
}

func serveRequests(irwc io.ReadWriteCloser) {
	for {
		sc := &conn.ServerCodec{
			Rwc: irwc,
		}
		rpc.ServeRequest(sc)
	}
}

func runServer(address string) error {
	ln, err := net.Listen("tcp", address)
	if err != nil {
		glog.Fatalf("While running server: %v", err)
		return err
	}
	glog.WithField("address", ln.Addr()).Info("Worker listening")

	go func() {
		for {
			cxn, err := ln.Accept()
			if err != nil {
				glog.Fatalf("listen(%q): %s\n", address, err)
				return
			}
			glog.WithField("local", cxn.LocalAddr()).
				WithField("remote", cxn.RemoteAddr()).
				Debug("Worker accepted connection")
			go serveRequests(cxn)
		}
	}()
	return nil
}
<commit_msg>Remove the local pool slice.<commit_after>package worker

import (
	"flag"
	"io"
	"net"
	"net/rpc"
	"strings"

	"github.com/dgraph-io/dgraph/conn"
	"github.com/dgraph-io/dgraph/posting"
	"github.com/dgraph-io/dgraph/store"
	"github.com/dgraph-io/dgraph/task"
	"github.com/dgraph-io/dgraph/x"
	"github.com/google/flatbuffers/go"
)

var workers = flag.String("workers", "",
	"Comma separated list of IP addresses of workers")
var workerPort = flag.String("workerport", ":12345",
	"Port used by worker for internal communication.")

var glog = x.Log("worker")
var dataStore *store.Store
var pools []*conn.Pool

func Init(ps *store.Store) {
	dataStore = ps
}

func Connect() {
	w := new(Worker)
	if err := rpc.Register(w); err != nil {
		glog.Fatal(err)
	}
	if err := runServer(*workerPort); err != nil {
		glog.Fatal(err)
	}

	addrs := strings.Split(*workers, ",")
	for _, addr := range addrs {
		if len(addr) == 0 {
			continue
		}
		pool := conn.NewPool(addr, 5)
		query := new(conn.Query)
		query.Data = []byte("hello")
		reply := new(conn.Reply)
		if err := pool.Call("Worker.Hello", query, reply); err != nil {
			glog.WithField("call", "Worker.Hello").Fatal(err)
		}
		glog.WithField("reply", string(reply.Data)).WithField("addr", addr).
			Info("Got reply from server")
		pools = append(pools, pool)
	}

	glog.Info("Server started. Clients connected.")
}

func ProcessTask(query []byte) (result []byte, rerr error) {
	uo := flatbuffers.GetUOffsetT(query)
	q := new(task.Query)
	q.Init(query, uo)

	b := flatbuffers.NewBuilder(0)
	voffsets := make([]flatbuffers.UOffsetT, q.UidsLength())
	uoffsets := make([]flatbuffers.UOffsetT, q.UidsLength())

	attr := string(q.Attr())
	for i := 0; i < q.UidsLength(); i++ {
		uid := q.Uids(i)
		key := posting.Key(uid, attr)
		pl := posting.GetOrCreate(key, dataStore)

		var valoffset flatbuffers.UOffsetT
		if val, err := pl.Value(); err != nil {
			valoffset = b.CreateByteVector(x.Nilbyte)
		} else {
			valoffset = b.CreateByteVector(val)
		}
		task.ValueStart(b)
		task.ValueAddVal(b, valoffset)
		voffsets[i] = task.ValueEnd(b)

		ulist := pl.GetUids()
		uoffsets[i] = x.UidlistOffset(b, ulist)
	}
	task.ResultStartValuesVector(b, len(voffsets))
	for i := len(voffsets) - 1; i >= 0; i-- {
		b.PrependUOffsetT(voffsets[i])
	}
	valuesVent := b.EndVector(len(voffsets))

	task.ResultStartUidmatrixVector(b, len(uoffsets))
	for i := len(uoffsets) - 1; i >= 0; i-- {
		b.PrependUOffsetT(uoffsets[i])
	}
	matrixVent := b.EndVector(len(uoffsets))

	task.ResultStart(b)
	task.ResultAddValues(b, valuesVent)
	task.ResultAddUidmatrix(b, matrixVent)
	rend := task.ResultEnd(b)
	b.Finish(rend)
	return b.Bytes[b.Head():], nil
}

func NewQuery(attr string, uids []uint64) []byte {
	b := flatbuffers.NewBuilder(0)
	task.QueryStartUidsVector(b, len(uids))
	for i := len(uids) - 1; i >= 0; i-- {
		b.PrependUint64(uids[i])
	}
	vend := b.EndVector(len(uids))

	ao := b.CreateString(attr)
	task.QueryStart(b)
	task.QueryAddAttr(b, ao)
	task.QueryAddUids(b, vend)
	qend := task.QueryEnd(b)
	b.Finish(qend)
	return b.Bytes[b.Head():]
}

type Worker struct {
}

func (w *Worker) Hello(query *conn.Query, reply *conn.Reply) error {
	if string(query.Data) == "hello" {
		reply.Data = []byte("Oh hello there!")
	} else {
		reply.Data = []byte("Hey stranger!")
	}
	return nil
}

func serveRequests(irwc io.ReadWriteCloser) {
	for {
		sc := &conn.ServerCodec{
			Rwc: irwc,
		}
		rpc.ServeRequest(sc)
	}
}

func runServer(address string) error {
	ln, err := net.Listen("tcp", address)
	if err != nil {
		glog.Fatalf("While running server: %v", err)
		return err
	}
	glog.WithField("address", ln.Addr()).Info("Worker listening")

	go func() {
		for {
			cxn, err := ln.Accept()
			if err != nil {
				glog.Fatalf("listen(%q): %s\n", address, err)
				return
			}
			glog.WithField("local", cxn.LocalAddr()).
				WithField("remote", cxn.RemoteAddr()).
				Debug("Worker accepted connection")
			go serveRequests(cxn)
		}
	}()
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	_ "github.com/bmizerany/pq"
	"io/ioutil"
	"log"
	"net/http"
	"time"
)

const OPEN311_API_URI = "http://311api.cityofchicago.org/open311/v2/requests.json?extensions=true&page_size=500"

type Open311Request struct {
	Lat, Long                                                                                float64
	Ward, Police_district                                                                                int
	Service_request_id, Status, Service_name, Service_code, Agency_responsible, Address, Channel, Media_url string
	Requested_datetime, Updated_datetime                                                                    string // FIXME: should these be proper time objects?
	Extended_attributes                                                                                map[string]interface{}
}

func main() {
	// open database
	db, err := sql.Open("postgres", "dbname=cwfy sslmode=disable")
	if err != nil {
		log.Fatal("Cannot open database connection", err)
	}
	defer db.Close()

	// load requests from open311
	requests := fetchRequests()

	for _, request := range requests {
		// for each request, either create or update the
		// corresponding record in the database.

		if request.Service_request_id == "" {
			log.Printf("Ignoring a request type %s because there is no SR number assigned", request.Service_name)
			continue
		}

		insert_stmt, err := db.Prepare("INSERT INTO service_requests(service_request_id," +
			"status, service_name, service_code, agency_responsible, " +
			"address, requested_datetime, updated_datetime, lat, long," +
			"ward, police_district, media_url, channel, duplicate, parent_service_request_id) " +
			"SELECT $1::varchar, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16 " +
			"WHERE NOT EXISTS (SELECT 1 FROM service_requests WHERE service_request_id = $1);")

		if err != nil {
			log.Fatal("error preparing database insert statement", err)
		}

		update_stmt, err := db.Prepare("UPDATE service_requests SET " +
			"status = $2, service_name = $3, service_code = $4, agency_responsible = $5, " +
			"address = $6, requested_datetime = $7, updated_datetime = $8, lat = $9, long = $10," +
			"ward = $11, police_district = $12, media_url = $13, channel = $14, duplicate = $15, " +
			"parent_service_request_id = $16, updated_at = NOW() WHERE service_request_id = $1;")

		if err != nil {
			log.Fatal("error preparing database update statement", err)
		}

		tx, err := db.Begin()

		if err != nil {
			log.Fatal("error beginning transaction", err)
		}

		_, err = tx.Stmt(update_stmt).Exec(request.Service_request_id,
			request.Status,
			request.Service_name,
			request.Service_code,
			request.Agency_responsible,
			request.Address,
			request.Requested_datetime,
			request.Updated_datetime,
			request.Lat,
			request.Long,
			request.Extended_attributes["ward"],
			request.Extended_attributes["police_district"],
			request.Media_url,
			request.Extended_attributes["channel"],
			request.Extended_attributes["duplicate"],
			request.Extended_attributes["parent_service_request_id"])

		if err != nil {
			log.Fatalf("could not update %s because %s", request.Service_request_id, err)
		}

		_, err = tx.Stmt(insert_stmt).Exec(request.Service_request_id,
			request.Status,
			request.Service_name,
			request.Service_code,
			request.Agency_responsible,
			request.Address,
			request.Requested_datetime,
			request.Updated_datetime,
			request.Lat,
			request.Long,
			request.Extended_attributes["ward"],
			request.Extended_attributes["police_district"],
			request.Media_url,
			request.Extended_attributes["channel"],
			request.Extended_attributes["duplicate"],
			request.Extended_attributes["parent_service_request_id"])

		if err != nil {
			log.Fatalf("could not save %s because %s", request.Service_request_id, err)
		} else {
			log.Printf("saved SR %s", request)
		}

		err = tx.Commit()
		if err != nil {
			log.Fatal("error closing transaction", err)
		}
	}
}

func (req Open311Request) String() string {
	// pretty print SR information
	return fmt.Sprintf("%s: %s at %s %f,%f, last update %s", req.Service_request_id, req.Service_name, req.Address, req.Lat, req.Long, req.Updated_datetime)
}

func fetchRequests() (requests []Open311Request) {
	db, err := sql.Open("postgres", "dbname=cwfy sslmode=disable")
	if err != nil {
		log.Fatal("Cannot open database connection", err)
	}
	defer db.Close()

	// find the most recent SR that we know about in the database
	rows, err := db.Query("SELECT MAX(updated_datetime) FROM service_requests;")
	if err != nil {
		log.Fatal("error finding most recent service request", err)
	}

	last_updated_at := time.Now()
	for rows.Next() {
		if err := rows.Scan(&last_updated_at); err != nil {
			log.Fatal(err)
		}

		log.Printf("most recent SR timestamp %s", last_updated_at)
	}

	// janky hack to transform the last updated timestamp into
	// a format that plays nicely with the Open311 API
	// FIXME: there HAS to be a better way to handle this.
	formatted_date_string := last_updated_at.Format(time.RFC3339)
	formatted_date_string_with_tz := formatted_date_string[0:len(formatted_date_string)-1] + "-0500" // trunc the trailing 'Z' and tack on timezone

	// construct the request URI using base params and the proper time
	open311_api_endpoint := OPEN311_API_URI + "&updated_after=" + formatted_date_string_with_tz

	log.Printf("fetching from %s", open311_api_endpoint)
	resp, err := http.Get(open311_api_endpoint)
	defer resp.Body.Close()

	if err != nil {
		log.Fatalln("error fetching from Open311 endpoint", err)
	}

	// load response body
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal("error loading response body", err)
	}

	// parse JSON and load into an array of Open311Request objects
	err = json.Unmarshal(body, &requests)
	if err != nil {
		log.Fatal("error parsing JSON:", err)
	}

	log.Printf("received %d requests from Open311", len(requests))

	return requests
}
<commit_msg>poll on a 30 second interval<commit_after>package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	_ "github.com/bmizerany/pq"
	"io/ioutil"
	"log"
	"net/http"
	"time"
)

const OPEN311_API_URI = "http://311api.cityofchicago.org/open311/v2/requests.json?extensions=true&page_size=500"

type Open311Request struct {
	Lat, Long                                                                                float64
	Ward, Police_district                                                                                int
	Service_request_id, Status, Service_name, Service_code, Agency_responsible, Address, Channel, Media_url string
	Requested_datetime, Updated_datetime                                                                    string // FIXME: should these be proper time objects?
	Extended_attributes                                                                                map[string]interface{}
}

func main() {
	// open database
	db, err := sql.Open("postgres", "dbname=cwfy sslmode=disable")
	if err != nil {
		log.Fatal("Cannot open database connection", err)
	}
	defer db.Close()

	last_run_at := time.Now()

	for {
		switch {
		case time.Since(last_run_at) > (30 * time.Second):
			poll_open311(db)
			last_run_at = time.Now()
		default:
			log.Print("sleeping for 10 seconds")
			time.Sleep(10 * time.Second)
		}
	}
}

func (req Open311Request) String() string {
	// pretty print SR information
	return fmt.Sprintf("%s: %s at %s %f,%f, last update %s", req.Service_request_id, req.Service_name, req.Address, req.Lat, req.Long, req.Updated_datetime)
}

func fetchRequests() (requests []Open311Request) {
	db, err := sql.Open("postgres", "dbname=cwfy sslmode=disable")
	if err != nil {
		log.Fatal("Cannot open database connection", err)
	}
	defer db.Close()

	// find the most recent SR that we know about in the database
	rows, err := db.Query("SELECT MAX(updated_datetime) FROM service_requests;")
	if err != nil {
		log.Fatal("error finding most recent service request", err)
	}

	last_updated_at := time.Now()
	for rows.Next() {
		if err := rows.Scan(&last_updated_at); err != nil {
			log.Fatal(err)
		}

		log.Printf("most recent SR timestamp %s", last_updated_at)
	}

	// janky hack to transform the last updated timestamp into
	// a format that plays nicely with the Open311 API
	// FIXME: there HAS to be a better way to handle this.
	formatted_date_string := last_updated_at.Format(time.RFC3339)
	formatted_date_string_with_tz := formatted_date_string[0:len(formatted_date_string)-1] + "-0500" // trunc the trailing 'Z' and tack on timezone

	// construct the request URI using base params and the proper time
	open311_api_endpoint := OPEN311_API_URI + "&updated_after=" + formatted_date_string_with_tz

	log.Printf("fetching from %s", open311_api_endpoint)
	resp, err := http.Get(open311_api_endpoint)
	defer resp.Body.Close()

	if err != nil {
		log.Fatalln("error fetching from Open311 endpoint", err)
	}

	// load response body
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal("error loading response body", err)
	}

	// parse JSON and load into an array of Open311Request objects
	err = json.Unmarshal(body, &requests)
	if err != nil {
		log.Fatal("error parsing JSON:", err)
	}

	log.Printf("received %d requests from Open311", len(requests))

	return requests
}

func poll_open311(db *sql.DB) {
	// load requests from open311
	requests := fetchRequests()

	for _, request := range requests {
		// for each request, either create or update the
		// corresponding record in the database.

		if request.Service_request_id == "" {
			log.Printf("Ignoring a request type %s because there is no SR number assigned", request.Service_name)
			continue
		}

		insert_stmt, err := db.Prepare("INSERT INTO service_requests(service_request_id," +
			"status, service_name, service_code, agency_responsible, " +
			"address, requested_datetime, updated_datetime, lat, long," +
			"ward, police_district, media_url, channel, duplicate, parent_service_request_id) " +
			"SELECT $1::varchar, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16 " +
			"WHERE NOT EXISTS (SELECT 1 FROM service_requests WHERE service_request_id = $1);")

		if err != nil {
			log.Fatal("error preparing database insert statement", err)
		}

		update_stmt, err := db.Prepare("UPDATE service_requests SET " +
			"status = $2, service_name = $3, service_code = $4, agency_responsible = $5, " +
			"address = $6, requested_datetime = $7, updated_datetime = $8, lat = $9, long = $10," +
			"ward = $11, police_district = $12, media_url = $13, channel = $14, duplicate = $15, " +
			"parent_service_request_id = $16, updated_at = NOW() WHERE service_request_id = $1;")

		if err != nil {
			log.Fatal("error preparing database update statement", err)
		}

		tx, err := db.Begin()

		if err != nil {
			log.Fatal("error beginning transaction", err)
		}

		_, err = tx.Stmt(update_stmt).Exec(request.Service_request_id,
			request.Status,
			request.Service_name,
			request.Service_code,
			request.Agency_responsible,
			request.Address,
			request.Requested_datetime,
			request.Updated_datetime,
			request.Lat,
			request.Long,
			request.Extended_attributes["ward"],
			request.Extended_attributes["police_district"],
			request.Media_url,
			request.Extended_attributes["channel"],
			request.Extended_attributes["duplicate"],
			request.Extended_attributes["parent_service_request_id"])

		if err != nil {
			log.Fatalf("could not update %s because %s", request.Service_request_id, err)
		}

		_, err = tx.Stmt(insert_stmt).Exec(request.Service_request_id,
			request.Status,
			request.Service_name,
			request.Service_code,
			request.Agency_responsible,
			request.Address,
			request.Requested_datetime,
			request.Updated_datetime,
			request.Lat,
			request.Long,
			request.Extended_attributes["ward"],
			request.Extended_attributes["police_district"],
			request.Media_url,
			request.Extended_attributes["channel"],
			request.Extended_attributes["duplicate"],
			request.Extended_attributes["parent_service_request_id"])

		if err != nil {
			log.Fatalf("could not save %s because %s", request.Service_request_id, err)
		} else {
			log.Printf("saved SR %s", request)
		}

		err = tx.Commit()
		if err != nil {
			log.Fatal("error closing transaction", err)
		}
	}
}
<|endoftext|>


<commit_before>package goxp

import (
	"fmt"
	"net/http"
	"reflect"
	"regxp"
	"strconv"
	"sync"
)

// Params is a map of name/value pairs for named routes. An instance of goxp.Params is available to be injected into any route handler.
type Params map[string]string

// Router is GoXp's de-facto interface. Supports HTTP verbs, stacked handlers, and dependency injection
// Idea is to use booster rather than injection
type Router interface {
	Routes

	// Group adds a group where related routes can be added.
	Group(string, func(Router), ...Handler)
	// Get adds a route for a HTTP GET request to the specified mathcing pattern.
	Get(string, ...Handler) Route
	// Patch adds a route for a HTTP PATCH request to the specified matching pattern.
	Patch(string, ...Handler) Route
	// Post adds a route for a HTTP POST request to the specified matching pattern.
	Post(string, ...Handler) Route
	// Put adds a route for a HTTP Put request to the specified matching pattern.
	Put(string, ...Handler) Route
	// Delete adds a route for a HTTP DELETE request to the specified matching pattern.
	Delete(string, ...Handler) Route
	// Options adds a route for HTTP OPTIONS request to the specified matching pattern.
	Options(string, ...Handler) Route
	// Head adds a route for HTTP HEAD request to the specified matching pattern.
	Head(string, ...Handler) Route
	// Any adds a route for a HTTP method request to the specified matching pattern.
	Any(string, ...Handler) Route
	// AddRoute adds a route for a given HTTP method request to the specified matching pattern.
	AddRoute(string, string, ...Handler) Route

	// NotFound sets the handlers that are called when a no route matches a request. Throws a basic 404 by default.
	NotFound(...Handler)

	// Handle is the entry point for routing. This is used as a goxp.Handler
	Handle(http.ResponseWriter, *http.Request, Context)
}

type router struct {
	routes     []*route
	notFounds  []Handler
	groups     []group
	routesLock sync.RWMutex
}

type group struct {
	pattern  string
	handlers []Handler
}

// NewRouter creates a new Router instance.
// If you aren't using ClassicGoXp, then you can add Routes as a
// service with:
//
//     m := goxp.New()
//     r := goxp.NewRouter()
//     m.MapTo(r, (*goxp.Routes)(nil))
//
// If you are using ClassicGoXp, then this is done for you.
func NewRouter() Router {
	return &router{notFounds: []Handler{http.NotFound}, groups: make([]group, 0)}
}

func (r *router) Group(pattern string, fn func(Router), h ...Handler) {
	r.groups = append(r.groups, group{pattern, h})
	fn(r)
	r.groups = r.groups[:len(r.groups)-1]
}

func (r *router) Get(pattern string, h ...Handler) Route {
	return r.addRoute("GET", pattern, h)
}

func (r *router) Patch(pattern string, h ...Handler) Route {
	return r.addRoute("PATCH", pattern, h)
}

func (r *router) Post(pattern string, h ...Handler) Router {
	return r.addRoute("POST", pattern, h)
}

func (r *router) Put(pattern string, h ...Handler) Router {
	return r.addRoute("PUT", pattern, h)
}

func (r *router) Delete(pattern string, h ...Handler) Route {
	return r.addRoute("DELETE", pattern, h)
}
<commit_msg>router.go +func OPTIONS<commit_after>package goxp

import (
	"fmt"
	"net/http"
	"reflect"
	"regxp"
	"strconv"
	"sync"
)

// Params is a map of name/value pairs for named routes. An instance of goxp.Params is available to be injected into any route handler.
type Params map[string]string

// Router is GoXp's de-facto interface. Supports HTTP verbs, stacked handlers, and dependency injection
// Idea is to use booster rather than injection
type Router interface {
	Routes

	// Group adds a group where related routes can be added.
	Group(string, func(Router), ...Handler)
	// Get adds a route for a HTTP GET request to the specified mathcing pattern.
	Get(string, ...Handler) Route
	// Patch adds a route for a HTTP PATCH request to the specified matching pattern.
	Patch(string, ...Handler) Route
	// Post adds a route for a HTTP POST request to the specified matching pattern.
	Post(string, ...Handler) Route
	// Put adds a route for a HTTP Put request to the specified matching pattern.
	Put(string, ...Handler) Route
	// Delete adds a route for a HTTP DELETE request to the specified matching pattern.
	Delete(string, ...Handler) Route
	// Options adds a route for HTTP OPTIONS request to the specified matching pattern.
	Options(string, ...Handler) Route
	// Head adds a route for HTTP HEAD request to the specified matching pattern.
	Head(string, ...Handler) Route
	// Any adds a route for a HTTP method request to the specified matching pattern.
	Any(string, ...Handler) Route
	// AddRoute adds a route for a given HTTP method request to the specified matching pattern.
	AddRoute(string, string, ...Handler) Route

	// NotFound sets the handlers that are called when a no route matches a request. Throws a basic 404 by default.
	NotFound(...Handler)

	// Handle is the entry point for routing. This is used as a goxp.Handler
	Handle(http.ResponseWriter, *http.Request, Context)
}

type router struct {
	routes     []*route
	notFounds  []Handler
	groups     []group
	routesLock sync.RWMutex
}

type group struct {
	pattern  string
	handlers []Handler
}

// NewRouter creates a new Router instance.
// If you aren't using ClassicGoXp, then you can add Routes as a
// service with:
//
//     m := goxp.New()
//     r := goxp.NewRouter()
//     m.MapTo(r, (*goxp.Routes)(nil))
//
// If you are using ClassicGoXp, then this is done for you.
func NewRouter() Router {
	return &router{notFounds: []Handler{http.NotFound}, groups: make([]group, 0)}
}

func (r *router) Group(pattern string, fn func(Router), h ...Handler) {
	r.groups = append(r.groups, group{pattern, h})
	fn(r)
	r.groups = r.groups[:len(r.groups)-1]
}

func (r *router) Get(pattern string, h ...Handler) Route {
	return r.addRoute("GET", pattern, h)
}

func (r *router) Patch(pattern string, h ...Handler) Route {
	return r.addRoute("PATCH", pattern, h)
}

func (r *router) Post(pattern string, h ...Handler) Router {
	return r.addRoute("POST", pattern, h)
}

func (r *router) Put(pattern string, h ...Handler) Router {
	return r.addRoute("PUT", pattern, h)
}

func (r *router) Delete(pattern string, h ...Handler) Route {
	return r.addRoute("DELETE", pattern, h)
}

func (r *router) Options(pattern string, h ...Handler) Route {
	return r.addRoute("OPTIONS", pattern, h)
}
<|endoftext|>


<commit_before>package node

import (
	"log"
)

// The brains of everything, takes in connections and wires them togethor.
type Router interface {
	AddConnection(Connection)
	DataConnection
	GetAddress() PublicKey
}

type routerImpl struct {
	pk PublicKey
	// A chan down which we send packets destined for ourselves.
	incoming chan Packet
	// A map of public key hashes to connections
	connections map[NodeAddress]Connection

	reachability MapHandler
	receipt      ReceiptHandler
	payment      PaymentHandler
}

type NullAction struct{}

func (n NullAction) Receipt(PacketHash) {}

func newRouterImpl(pk PublicKey) Router {
	payment := newPaymentImpl(pk.Hash())
	return &routerImpl{
		pk,
		make(chan Packet),
		make(map[NodeAddress]Connection),
		newMapImpl(pk.Hash()),
		newReceiptImpl(pk.Hash(), payment),
		payment}
}

func (r *routerImpl) GetAddress() PublicKey {
	return r.pk
}

func (r *routerImpl) AddConnection(c Connection) {
	id := c.Key().Hash()
	_, duplicate := r.connections[id]
	if duplicate {
		c.Close()
		return
	}
	r.connections[id] = c

	// Curry the id since the various sub connections don't know about it
	r.reachability.AddConnection(id, c)
	r.receipt.AddConnection(id, c)
	r.payment.AddConnection(id, c)
	go r.handleData(id, c)
}

func (r *routerImpl) handleData(id NodeAddress, p DataConnection) {
	for packet := range p.Packets() {
		err := r.sendPacket(packet, id)
		if err != nil {
			log.Printf("%q: Dropping packet destined to %q: %q", r.pk.Hash(), packet.Destination(), err)
			continue
		}
	}
}

func (r *routerImpl) SendPacket(p Packet) error {
	return r.sendPacket(p, r.pk.Hash())
}

func (r *routerImpl) sendPacket(p Packet, src NodeAddress) error {
	if p.Destination() == r.pk.Hash() {
		log.Printf("%q: Routing packet to self", r.pk.Hash())
		r.incoming <- p
		return nil
	}
	next, err := r.reachability.FindConnection(p.Destination())
	if err != nil {
		return err
	}
	log.Printf("%q: Routing packet to %q", r.pk.Hash(), next)
	go r.receipt.AddSentPacket(p, src, next)
	go r.payment.AddSentPacket(p, src, next)
	return r.connections[next].SendPacket(p)
}

func (r *routerImpl) Packets() <-chan Packet {
	return r.incoming
}

//func (r *routerImpl) handleReceipts(id NodeAddress, p ReceiptConnection) {}
<commit_msg>Expose all external methods from router<commit_after>package node

import (
	"log"
)

// The brains of everything, takes in connections and wires them togethor.
type Router interface {
	AddConnection(Connection)
	DataConnection
	GetAddress() PublicKey
	SendReceipt(PacketReceipt)
	SendPayment(Payment)
}

type routerImpl struct {
	pk PublicKey
	// A chan down which we send packets destined for ourselves.
	incoming chan Packet
	// A map of public key hashes to connections
	connections map[NodeAddress]Connection

	reachability MapHandler
	receipt      ReceiptHandler
	payment      PaymentHandler
}

func newRouterImpl(pk PublicKey) Router {
	payment := newPaymentImpl(pk.Hash())
	return &routerImpl{
		pk,
		make(chan Packet),
		make(map[NodeAddress]Connection),
		newMapImpl(pk.Hash()),
		newReceiptImpl(pk.Hash(), payment),
		payment}
}

func (r *routerImpl) GetAddress() PublicKey {
	return r.pk
}

func (r *routerImpl) SendReceipt(p PacketReceipt) {
	r.receipt.SendReceipt(p)
}

func (r *routerImpl) SendPayment(p Payment) {
	r.payment.SendPayment(p)
}

func (r *routerImpl) AddConnection(c Connection) {
	id := c.Key().Hash()
	_, duplicate := r.connections[id]
	if duplicate {
		c.Close()
		return
	}
	r.connections[id] = c

	// Curry the id since the various sub connections don't know about it
	r.reachability.AddConnection(id, c)
	r.receipt.AddConnection(id, c)
	r.payment.AddConnection(id, c)
	go r.handleData(id, c)
}

func (r *routerImpl) handleData(id NodeAddress, p DataConnection) {
	for packet := range p.Packets() {
		err := r.sendPacket(packet, id)
		if err != nil {
			log.Printf("%q: Dropping packet destined to %q: %q", r.pk.Hash(), packet.Destination(), err)
			continue
		}
	}
}

func (r *routerImpl) SendPacket(p Packet) error {
	return r.sendPacket(p, r.pk.Hash())
}

func (r *routerImpl) sendPacket(p Packet, src NodeAddress) error {
	if p.Destination() == r.pk.Hash() {
		log.Printf("%q: Routing packet to self", r.pk.Hash())
		r.incoming <- p
		return nil
	}
	next, err := r.reachability.FindConnection(p.Destination())
	if err != nil {
		return err
	}
	log.Printf("%q: Routing packet to %q", r.pk.Hash(), next)
	go r.receipt.AddSentPacket(p, src, next)
	go r.payment.AddSentPacket(p, src, next)
	return r.connections[next].SendPacket(p)
}

func (r *routerImpl) Packets() <-chan Packet {
	return r.incoming
}

//func (r *routerImpl) handleReceipts(id NodeAddress, p ReceiptConnection) {}
<|endoftext|>


<commit_before>// Copyright 2015-2017 HenryLee. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package teleport

import (
	"path"
	"reflect"
	"strings"

	"github.com/henrylee2cn/goutil"
	"github.com/henrylee2cn/goutil/errors"
)

// Router the router of pull or push.
type (
	Router struct {
		handlers       map[string]*Handler
		unknownApiType **Handler
		// only for register router
		pathPrefix      string
		pluginContainer PluginContainer
		typ             string
		fn              HandlersMaker
	}
	// Handler pull or push handler type info
	Handler struct {
		name              string
		originStructMaker func(*readHandleCtx) reflect.Value
		method            reflect.Value
		argElem           reflect.Type
		reply             reflect.Type // only for pull handler doc
		pluginContainer   PluginContainer
	}
	// HandlersMaker makes []*Handler
	HandlersMaker func(string, interface{}, PluginContainer) ([]*Handler, error)
)

// Group add handler group.
func (r *Router) Group(pathPrefix string, plugin ...Plugin) *Router {
	pluginContainer, err := r.pluginContainer.cloneAdd(plugin...)
	if err != nil {
		Fatalf("%v", err)
	}
	warnInvaildRouterHooks(plugin)
	return &Router{
		handlers:        r.handlers,
		unknownApiType:  r.unknownApiType,
		pathPrefix:      path.Join(r.pathPrefix, pathPrefix),
		pluginContainer: pluginContainer,
		fn:              r.fn,
	}
}

// Reg registers handler.
func (r *Router) Reg(ctrlStruct interface{}, plugin ...Plugin) {
	pluginContainer, err := r.pluginContainer.cloneAdd(plugin...)
	if err != nil {
		Fatalf("%v", err)
	}
	warnInvaildRouterHooks(plugin)
	handlers, err := r.fn(
		r.pathPrefix,
		ctrlStruct,
		pluginContainer,
	)
	if err != nil {
		Fatalf("%v", err)
	}
	for _, h := range handlers {
		if _, ok := r.handlers[h.name]; ok {
			Fatalf("There is a %s handler conflict: %s", r.typ, h.name)
		}
		if err = pluginContainer.PostReg(h); err != nil {
			Fatalf("%v", err)
		}
		r.handlers[h.name] = h
		Printf("register %s handler: %s", r.typ, h.name)
	}
}

// SetUnknown sets the default handler,
// which is called when no handler for pull or push is found.
func (r *Router) SetUnknown(unknownHandler interface{}, plugin ...Plugin) {
	pluginContainer, err := r.pluginContainer.cloneAdd(plugin...)
	if err != nil {
		Fatalf("%v", err)
	}
	warnInvaildRouterHooks(plugin)
	var name string
	switch r.typ {
	case "pull":
		name = "handle_unknown_pull"
		if _, ok := unknownHandler.(func(ctx UnknownPullCtx, body *[]byte) (reply interface{}, xerr Xerror)); !ok {
			Fatalf("*Router.SetUnknown(): %s handler needs type:\n func(ctx UnknownPullCtx, body *[]byte) (reply interface{}, xerr Xerror)", name)
		}
	case "push":
		name = "handle_unknown_push"
		if _, ok := unknownHandler.(func(ctx UnknownPushCtx, body *[]byte)); !ok {
			Fatalf("*Router.SetUnknown(): %s handler needs type:\n func(ctx UnknownPushCtx, body *[]byte)", name)
		}
	}

	h := &Handler{
		name: name,
		originStructMaker: func(ctx *readHandleCtx) reflect.Value {
			return reflect.ValueOf(ctx)
		},
		method:          reflect.ValueOf(unknownHandler),
		argElem:         reflect.TypeOf([]byte{}),
		pluginContainer: pluginContainer,
	}

	if *r.unknownApiType == nil {
		Printf("set %s handler", name)
	} else {
		Warnf("covered %s handler", name)
	}
	r.unknownApiType = &h
}

func (r *Router) get(uriPath string) (*Handler, bool) {
	t, ok := r.handlers[uriPath]
	if ok {
		return t, true
	}
	if unknown := *r.unknownApiType; unknown != nil {
		return unknown, true
	}
	return nil, false
}

// newPullRouter creates pull packet router.
// Note: ctrlStruct needs to implement PullCtx interface.
func newPullRouter(pluginContainer PluginContainer) *Router {
	return &Router{
		handlers:        make(map[string]*Handler),
		unknownApiType:  new(*Handler),
		pathPrefix:      "/",
		pluginContainer: pluginContainer,
		fn:              pullHandlersMaker,
		typ:             "pull",
	}
}

// Note: ctrlStruct needs to implement PullCtx interface.
func pullHandlersMaker(pathPrefix string, ctrlStruct interface{}, pluginContainer PluginContainer) ([]*Handler, error) {
	var (
		ctype             = reflect.TypeOf(ctrlStruct)
		handlers          = make([]*Handler, 0, 1)
		originStructMaker func(*readHandleCtx) reflect.Value
	)
	if ctype.Kind() != reflect.Ptr {
		return nil, errors.Errorf("register pull handler: the type is not struct point: %s", ctype.String())
	}
	var ctypeElem = ctype.Elem()
	if ctypeElem.Kind() != reflect.Struct {
		return nil, errors.Errorf("register pull handler: the type is not struct point: %s", ctype.String())
	}
	if _, ok := ctrlStruct.(PullCtx); !ok {
		return nil, errors.Errorf("register pull handler: the type is not implemented PullCtx interface: %s", ctype.String())
	} else {
		if iType, ok := ctypeElem.FieldByName("PullCtx"); ok && iType.Anonymous {
			originStructMaker = func(ctx *readHandleCtx) reflect.Value {
				ctrl := reflect.New(ctypeElem)
				ctrl.Elem().FieldByName("PullCtx").Set(reflect.ValueOf(ctx))
				return ctrl
			}
		} else {
			return nil, errors.Errorf("register pull handler: the struct do not have anonymous field PullCtx: %s", ctype.String())
		}
	}

	if pluginContainer == nil {
		pluginContainer = newPluginContainer()
	}

	for m := 0; m < ctype.NumMethod(); m++ {
		method := ctype.Method(m)
		mtype := method.Type
		mname := method.Name
		// Method must be exported.
		if method.PkgPath != "" || isPullCtxMethod(mname) {
			continue
		}
		// Method needs two ins: receiver, *args.
		if mtype.NumIn() != 2 {
			return nil, errors.Errorf("register pull handler: %s.%s needs one in argument, but have %d", ctype.String(), mname, mtype.NumIn())
		}
		// Receiver need be a struct pointer.
		structType := mtype.In(0)
		if structType.Kind() != reflect.Ptr || structType.Elem().Kind() != reflect.Struct {
			return nil, errors.Errorf("register pull handler: %s.%s receiver need be a struct pointer: %s", ctype.String(), mname, structType)
		}
		// First arg need be exported or builtin, and need be a pointer.
		argType := mtype.In(1)
		if !goutil.IsExportedOrBuiltinType(argType) {
			return nil, errors.Errorf("register pull handler: %s.%s args type not exported: %s", ctype.String(), mname, argType)
		}
		if argType.Kind() != reflect.Ptr {
			return nil, errors.Errorf("register pull handler: %s.%s args type need be a pointer: %s", ctype.String(), mname, argType)
		}
		// Method needs two outs: reply error.
		if mtype.NumOut() != 2 {
			return nil, errors.Errorf("register pull handler: %s.%s needs two out arguments, but have %d", ctype.String(), mname, mtype.NumOut())
		}
		// Reply type must be exported.
		replyType := mtype.Out(0)
		if !goutil.IsExportedOrBuiltinType(replyType) {
			return nil, errors.Errorf("register pull handler: %s.%s first reply type not exported: %s", ctype.String(), mname, replyType)
		}
		// The return type of the method must be Error.
		if returnType := mtype.Out(1); strings.TrimPrefix(returnType.Name(), "teleport.") != "Xerror" {
			return nil, errors.Errorf("register pull handler: %s.%s second reply type %s not teleport.Xerror", ctype.String(), mname, returnType)
		}

		handlers = append(handlers, &Handler{
			name:              path.Join(pathPrefix, ctrlStructSnakeName(ctype), goutil.SnakeString(mname)),
			originStructMaker: originStructMaker,
			method:            method.Func,
			argElem:           argType.Elem(),
			reply:             replyType,
			pluginContainer:   pluginContainer,
		})
	}
	return handlers, nil
}

// newPushRouter creates push packet router.
// Note: ctrlStruct needs to implement PushCtx interface.
func newPushRouter(pluginContainer PluginContainer) *Router {
	return &Router{
		handlers:        make(map[string]*Handler),
		unknownApiType:  new(*Handler),
		pathPrefix:      "/",
		pluginContainer: pluginContainer,
		fn:              pushHandlersMaker,
		typ:             "push",
	}
}

// Note: ctrlStruct needs to implement PushCtx interface.
func pushHandlersMaker(pathPrefix string, ctrlStruct interface{}, pluginContainer PluginContainer) ([]*Handler, error) {
	var (
		ctype             = reflect.TypeOf(ctrlStruct)
		handlers          = make([]*Handler, 0, 1)
		originStructMaker func(*readHandleCtx) reflect.Value
	)
	if ctype.Kind() != reflect.Ptr {
		return nil, errors.Errorf("register push handler: the type is not struct point: %s", ctype.String())
	}
	var ctypeElem = ctype.Elem()
	if ctypeElem.Kind() != reflect.Struct {
		return nil, errors.Errorf("register push handler: the type is not struct point: %s", ctype.String())
	}
	if _, ok := ctrlStruct.(PushCtx); !ok {
		return nil, errors.Errorf("register push handler: the type is not implemented PushCtx interface: %s", ctype.String())
	} else {
		if iType, ok := ctypeElem.FieldByName("PushCtx"); ok && iType.Anonymous {
			originStructMaker = func(ctx *readHandleCtx) reflect.Value {
				ctrl := reflect.New(ctypeElem)
				ctrl.Elem().FieldByName("PushCtx").Set(reflect.ValueOf(ctx))
				return ctrl
			}
		} else {
			return nil, errors.Errorf("register push handler: the struct do not have anonymous field PushCtx: %s", ctype.String())
		}
	}

	if pluginContainer == nil {
		pluginContainer = newPluginContainer()
	}
	for m := 0; m < ctype.NumMethod(); m++ {
		method := ctype.Method(m)
		mtype := method.Type
		mname := method.Name
		// Method must be exported.
		if method.PkgPath != "" || isPushCtxMethod(mname) {
			continue
		}
		// Method needs two ins: receiver, *args.
		if mtype.NumIn() != 2 {
			return nil, errors.Errorf("register push handler: %s.%s needs one in argument, but have %d", ctype.String(), mname, mtype.NumIn())
		}
		// Receiver need be a struct pointer.
		structType := mtype.In(0)
		if structType.Kind() != reflect.Ptr || structType.Elem().Kind() != reflect.Struct {
			return nil, errors.Errorf("register push handler: %s.%s receiver need be a struct pointer: %s", ctype.String(), mname, structType)
		}
		// First arg need be exported or builtin, and need be a pointer.
		argType := mtype.In(1)
		if !goutil.IsExportedOrBuiltinType(argType) {
			return nil, errors.Errorf("register push handler: %s.%s args type not exported: %s", ctype.String(), mname, argType)
		}
		if argType.Kind() != reflect.Ptr {
			return nil, errors.Errorf("register push handler: %s.%s args type need be a pointer: %s", ctype.String(), mname, argType)
		}
		// Method does not need out argument.
		if mtype.NumOut() != 0 {
			return nil, errors.Errorf("register push handler: %s.%s does not need out argument, but have %d", ctype.String(), mname, mtype.NumOut())
		}
		handlers = append(handlers, &Handler{
			name:              path.Join(pathPrefix, ctrlStructSnakeName(ctype), goutil.SnakeString(mname)),
			originStructMaker: originStructMaker,
			method:            method.Func,
			argElem:           argType.Elem(),
			pluginContainer:   pluginContainer,
		})
	}
	return handlers, nil
}

func isPullCtxMethod(name string) bool {
	ctype := reflect.TypeOf(PullCtx(new(readHandleCtx)))
	for m := 0; m < ctype.NumMethod(); m++ {
		if name == ctype.Method(m).Name {
			return true
		}
	}
	return false
}

func isPushCtxMethod(name string) bool {
	ctype := reflect.TypeOf(PushCtx(new(readHandleCtx)))
	for m := 0; m < ctype.NumMethod(); m++ {
		if name == ctype.Method(m).Name {
			return true
		}
	}
	return false
}

func ctrlStructSnakeName(ctype reflect.Type) string {
	split := strings.Split(ctype.String(), ".")
	tName := split[len(split)-1]
	return goutil.SnakeString(tName)
}

// Name returns the handler name.
func (h *Handler) Name() string {
	return h.name
}

// ArgElemType returns the handler arg elem type.
func (h *Handler) ArgElemType() reflect.Type {
	return h.argElem
}

// ReplyType returns the handler reply type
func (h *Handler) ReplyType() reflect.Type {
	return h.reply
}

// IsPush checks if it is push handler or not.
func (h *Handler) IsPush() bool {
	return h.reply == nil
}

// IsPull checks if it is pull handler or not.
func (h *Handler) IsPull() bool {
	return !h.IsPush()
}
<commit_msg>handle_unknown_pull -> unknown_pull_handle<commit_after>// Copyright 2015-2017 HenryLee. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package teleport

import (
	"path"
	"reflect"
	"strings"

	"github.com/henrylee2cn/goutil"
	"github.com/henrylee2cn/goutil/errors"
)

// Router the router of pull or push.
type (
	Router struct {
		handlers       map[string]*Handler
		unknownApiType **Handler
		// only for register router
		pathPrefix      string
		pluginContainer PluginContainer
		typ             string
		fn              HandlersMaker
	}
	// Handler pull or push handler type info
	Handler struct {
		name              string
		originStructMaker func(*readHandleCtx) reflect.Value
		method            reflect.Value
		argElem           reflect.Type
		reply             reflect.Type // only for pull handler doc
		pluginContainer   PluginContainer
	}
	// HandlersMaker makes []*Handler
	HandlersMaker func(string, interface{}, PluginContainer) ([]*Handler, error)
)

// Group add handler group.
func (r *Router) Group(pathPrefix string, plugin ...Plugin) *Router {
	pluginContainer, err := r.pluginContainer.cloneAdd(plugin...)
	if err != nil {
		Fatalf("%v", err)
	}
	warnInvaildRouterHooks(plugin)
	return &Router{
		handlers:        r.handlers,
		unknownApiType:  r.unknownApiType,
		pathPrefix:      path.Join(r.pathPrefix, pathPrefix),
		pluginContainer: pluginContainer,
		fn:              r.fn,
	}
}

// Reg registers handler.
func (r *Router) Reg(ctrlStruct interface{}, plugin ...Plugin) {
	pluginContainer, err := r.pluginContainer.cloneAdd(plugin...)
	if err != nil {
		Fatalf("%v", err)
	}
	warnInvaildRouterHooks(plugin)
	handlers, err := r.fn(
		r.pathPrefix,
		ctrlStruct,
		pluginContainer,
	)
	if err != nil {
		Fatalf("%v", err)
	}
	for _, h := range handlers {
		if _, ok := r.handlers[h.name]; ok {
			Fatalf("There is a %s handler conflict: %s", r.typ, h.name)
		}
		if err = pluginContainer.PostReg(h); err != nil {
			Fatalf("%v", err)
		}
		r.handlers[h.name] = h
		Printf("register %s handler: %s", r.typ, h.name)
	}
}

// SetUnknown sets the default handler,
// which is called when no handler for pull or push is found.
func (r *Router) SetUnknown(unknownHandler interface{}, plugin ...Plugin) {
	pluginContainer, err := r.pluginContainer.cloneAdd(plugin...)
	if err != nil {
		Fatalf("%v", err)
	}
	warnInvaildRouterHooks(plugin)
	var name string
	switch r.typ {
	case "pull":
		name = "unknown_pull_handle"
		if _, ok := unknownHandler.(func(ctx UnknownPullCtx, body *[]byte) (reply interface{}, xerr Xerror)); !ok {
			Fatalf("*Router.SetUnknown(): %s handler needs type:\n func(ctx UnknownPullCtx, body *[]byte) (reply interface{}, xerr Xerror)", name)
		}
	case "push":
		name = "unknown_push_handle"
		if _, ok := unknownHandler.(func(ctx UnknownPushCtx, body *[]byte)); !ok {
			Fatalf("*Router.SetUnknown(): %s handler needs type:\n func(ctx UnknownPushCtx, body *[]byte)", name)
		}
	}

	h := &Handler{
		name: name,
		originStructMaker: func(ctx *readHandleCtx) reflect.Value {
			return reflect.ValueOf(ctx)
		},
		method:          reflect.ValueOf(unknownHandler),
		argElem:         reflect.TypeOf([]byte{}),
		pluginContainer: pluginContainer,
	}

	if *r.unknownApiType == nil {
		Printf("set %s handler", name)
	} else {
		Warnf("covered %s handler", name)
	}
	r.unknownApiType = &h
}

func (r *Router) get(uriPath string) (*Handler, bool) {
	t, ok := r.handlers[uriPath]
	if ok {
		return t, true
	}
	if unknown := *r.unknownApiType; unknown != nil {
		return unknown, true
	}
	return nil, false
}

// newPullRouter creates pull packet router.
// Note: ctrlStruct needs to implement PullCtx interface.
func newPullRouter(pluginContainer PluginContainer) *Router {
	return &Router{
		handlers:        make(map[string]*Handler),
		unknownApiType:  new(*Handler),
		pathPrefix:      "/",
		pluginContainer: pluginContainer,
		fn:              pullHandlersMaker,
		typ:             "pull",
	}
}

// Note: ctrlStruct needs to implement PullCtx interface.
func pullHandlersMaker(pathPrefix string, ctrlStruct interface{}, pluginContainer PluginContainer) ([]*Handler, error) {
	var (
		ctype             = reflect.TypeOf(ctrlStruct)
		handlers          = make([]*Handler, 0, 1)
		originStructMaker func(*readHandleCtx) reflect.Value
	)
	if ctype.Kind() != reflect.Ptr {
		return nil, errors.Errorf("register pull handler: the type is not struct point: %s", ctype.String())
	}
	var ctypeElem = ctype.Elem()
	if ctypeElem.Kind() != reflect.Struct {
		return nil, errors.Errorf("register pull handler: the type is not struct point: %s", ctype.String())
	}
	if _, ok := ctrlStruct.(PullCtx); !ok {
		return nil, errors.Errorf("register pull handler: the type is not implemented PullCtx interface: %s", ctype.String())
	} else {
		if iType, ok := ctypeElem.FieldByName("PullCtx"); ok && iType.Anonymous {
			originStructMaker = func(ctx *readHandleCtx) reflect.Value {
				ctrl := reflect.New(ctypeElem)
				ctrl.Elem().FieldByName("PullCtx").Set(reflect.ValueOf(ctx))
				return ctrl
			}
		} else {
			return nil, errors.Errorf("register pull handler: the struct do not have anonymous field PullCtx: %s", ctype.String())
		}
	}

	if pluginContainer == nil {
		pluginContainer = newPluginContainer()
	}

	for m := 0; m < ctype.NumMethod(); m++ {
		method := ctype.Method(m)
		mtype := method.Type
		mname := method.Name
		// Method must be exported.
		if method.PkgPath != "" || isPullCtxMethod(mname) {
			continue
		}
		// Method needs two ins: receiver, *args.
		if mtype.NumIn() != 2 {
			return nil, errors.Errorf("register pull handler: %s.%s needs one in argument, but have %d", ctype.String(), mname, mtype.NumIn())
		}
		// Receiver need be a struct pointer.
		structType := mtype.In(0)
		if structType.Kind() != reflect.Ptr || structType.Elem().Kind() != reflect.Struct {
			return nil, errors.Errorf("register pull handler: %s.%s receiver need be a struct pointer: %s", ctype.String(), mname, structType)
		}
		// First arg need be exported or builtin, and need be a pointer.
		argType := mtype.In(1)
		if !goutil.IsExportedOrBuiltinType(argType) {
			return nil, errors.Errorf("register pull handler: %s.%s args type not exported: %s", ctype.String(), mname, argType)
		}
		if argType.Kind() != reflect.Ptr {
			return nil, errors.Errorf("register pull handler: %s.%s args type need be a pointer: %s", ctype.String(), mname, argType)
		}
		// Method needs two outs: reply error.
		if mtype.NumOut() != 2 {
			return nil, errors.Errorf("register pull handler: %s.%s needs two out arguments, but have %d", ctype.String(), mname, mtype.NumOut())
		}
		// Reply type must be exported.
		replyType := mtype.Out(0)
		if !goutil.IsExportedOrBuiltinType(replyType) {
			return nil, errors.Errorf("register pull handler: %s.%s first reply type not exported: %s", ctype.String(), mname, replyType)
		}
		// The return type of the method must be Error.
		if returnType := mtype.Out(1); strings.TrimPrefix(returnType.Name(), "teleport.") != "Xerror" {
			return nil, errors.Errorf("register pull handler: %s.%s second reply type %s not teleport.Xerror", ctype.String(), mname, returnType)
		}

		handlers = append(handlers, &Handler{
			name:              path.Join(pathPrefix, ctrlStructSnakeName(ctype), goutil.SnakeString(mname)),
			originStructMaker: originStructMaker,
			method:            method.Func,
			argElem:           argType.Elem(),
			reply:             replyType,
			pluginContainer:   pluginContainer,
		})
	}
	return handlers, nil
}

// newPushRouter creates push packet router.
// Note: ctrlStruct needs to implement PushCtx interface.
func newPushRouter(pluginContainer PluginContainer) *Router {
	return &Router{
		handlers:        make(map[string]*Handler),
		unknownApiType:  new(*Handler),
		pathPrefix:      "/",
		pluginContainer: pluginContainer,
		fn:              pushHandlersMaker,
		typ:             "push",
	}
}

// Note: ctrlStruct needs to implement PushCtx interface.
func pushHandlersMaker(pathPrefix string, ctrlStruct interface{}, pluginContainer PluginContainer) ([]*Handler, error) {
	var (
		ctype             = reflect.TypeOf(ctrlStruct)
		handlers          = make([]*Handler, 0, 1)
		originStructMaker func(*readHandleCtx) reflect.Value
	)
	if ctype.Kind() != reflect.Ptr {
		return nil, errors.Errorf("register push handler: the type is not struct point: %s", ctype.String())
	}
	var ctypeElem = ctype.Elem()
	if ctypeElem.Kind() != reflect.Struct {
		return nil, errors.Errorf("register push handler: the type is not struct point: %s", ctype.String())
	}
	if _, ok := ctrlStruct.(PushCtx); !ok {
		return nil, errors.Errorf("register push handler: the type is not implemented PushCtx interface: %s", ctype.String())
	} else {
		if iType, ok := ctypeElem.FieldByName("PushCtx"); ok && iType.Anonymous {
			originStructMaker = func(ctx *readHandleCtx) reflect.Value {
				ctrl := reflect.New(ctypeElem)
				ctrl.Elem().FieldByName("PushCtx").Set(reflect.ValueOf(ctx))
				return ctrl
			}
		} else {
			return nil, errors.Errorf("register push handler: the struct do not have anonymous field PushCtx: %s", ctype.String())
		}
	}

	if pluginContainer == nil {
		pluginContainer = newPluginContainer()
	}
	for m := 0; m < ctype.NumMethod(); m++ {
		method := ctype.Method(m)
		mtype := method.Type
		mname := method.Name
		// Method must be exported.
		if method.PkgPath != "" || isPushCtxMethod(mname) {
			continue
		}
		// Method needs two ins: receiver, *args.
		if mtype.NumIn() != 2 {
			return nil, errors.Errorf("register push handler: %s.%s needs one in argument, but have %d", ctype.String(), mname, mtype.NumIn())
		}
		// Receiver need be a struct pointer.
		structType := mtype.In(0)
		if structType.Kind() != reflect.Ptr || structType.Elem().Kind() != reflect.Struct {
			return nil, errors.Errorf("register push handler: %s.%s receiver need be a struct pointer: %s", ctype.String(), mname, structType)
		}
		// First arg need be exported or builtin, and need be a pointer.
		argType := mtype.In(1)
		if !goutil.IsExportedOrBuiltinType(argType) {
			return nil, errors.Errorf("register push handler: %s.%s args type not exported: %s", ctype.String(), mname, argType)
		}
		if argType.Kind() != reflect.Ptr {
			return nil, errors.Errorf("register push handler: %s.%s args type need be a pointer: %s", ctype.String(), mname, argType)
		}
		// Method does not need out argument.
		if mtype.NumOut() != 0 {
			return nil, errors.Errorf("register push handler: %s.%s does not need out argument, but have %d", ctype.String(), mname, mtype.NumOut())
		}
		handlers = append(handlers, &Handler{
			name:              path.Join(pathPrefix, ctrlStructSnakeName(ctype), goutil.SnakeString(mname)),
			originStructMaker: originStructMaker,
			method:            method.Func,
			argElem:           argType.Elem(),
			pluginContainer:   pluginContainer,
		})
	}
	return handlers, nil
}

func isPullCtxMethod(name string) bool {
	ctype := reflect.TypeOf(PullCtx(new(readHandleCtx)))
	for m := 0; m < ctype.NumMethod(); m++ {
		if name == ctype.Method(m).Name {
			return true
		}
	}
	return false
}

func isPushCtxMethod(name string) bool {
	ctype := reflect.TypeOf(PushCtx(new(readHandleCtx)))
	for m := 0; m < ctype.NumMethod(); m++ {
		if name == ctype.Method(m).Name {
			return true
		}
	}
	return false
}

func ctrlStructSnakeName(ctype reflect.Type) string {
	split := strings.Split(ctype.String(), ".")
	tName := split[len(split)-1]
	return goutil.SnakeString(tName)
}

// Name returns the handler name.
func (h *Handler) Name() string {
	return h.name
}

// ArgElemType returns the handler arg elem type.
func (h *Handler) ArgElemType() reflect.Type {
	return h.argElem
}

// ReplyType returns the handler reply type
func (h *Handler) ReplyType() reflect.Type {
	return h.reply
}

// IsPush checks if it is push handler or not.
func (h *Handler) IsPush() bool {
	return h.reply == nil
}

// IsPull checks if it is pull handler or not.
func (h *Handler) IsPull() bool {
	return !h.IsPush()
}
<|endoftext|>


<commit_before>package flotilla

import (
	"net/http"
	"path/filepath"
	"strings"

	"github.com/julienschmidt/httprouter"
)

type (
	// Information about a route as a unit outside of the router for use & reuse.
	Route struct {
		static      bool
		method      string
		path        string
		staticpath  string
		visiblepath string
		handlers    []HandlerFunc
	}

	// A RouterGroup is associated with a prefix and an array of handlers.
	RouterGroup struct {
		Handlers []HandlerFunc
		prefix   string
		parent   *RouterGroup
		children []*RouterGroup
		routes   []*Route
		engine   *Engine
	}
)

// Adds an number of HandlerFuncs to the RouterGroup.
func (group *RouterGroup) Use(middlewares ...HandlerFunc) {
	for _, handler := range middlewares {
		if !group.handlerExists(handler) {
			group.Handlers = append(group.Handlers, handler)
		}
	}
}

func (group *RouterGroup) handlerExists(outside HandlerFunc) bool {
	for _, inside := range group.Handlers {
		if funcEqual(inside, outside) {
			return true
		}
	}
	return false
}

// Creates a new router group.
func (group *RouterGroup) New(component string, handlers ...HandlerFunc) *RouterGroup {
	prefix := group.pathFor(component)

	newroutergroup := &RouterGroup{
		Handlers: group.combineHandlers(handlers),
		parent:   group,
		prefix:   prefix,
		engine:   group.engine,
	}

	group.children = append(group.children, newroutergroup)

	return newroutergroup
}

func (group *RouterGroup) pathFor(path string) string {
	joined := filepath.Join(group.prefix, path)
	// Append a '/' if the last component had one, but only if it's not there already
	if len(path) > 0 && path[len(path)-1] == '/' && joined[len(joined)-1] != '/' {
		return joined + "/"
	}
	return joined
}

func (group *RouterGroup) pathNoLeadingSlash(path string) string {
	return strings.TrimLeft(strings.Join([]string{group.prefix, path}, "/"), "/")
}

// Handle registers a new request handle and middlewares with the given path and
// method. For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
// functions can be used.
func (group *RouterGroup) Handle(route *Route) {
	handlers := group.combineHandlers(route.handlers)
	route.visiblepath = group.pathFor(route.path)
	group.routes = append(group.routes, route)
	group.engine.router.Handle(route.method, route.visiblepath, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		c := group.engine.getCtx(w, req, params, handlers)
		//fmt.Printf("\nPRE HOOK\n")
		c.Next()
		//fmt.Printf("\nPOST HOOK\n")
		group.engine.cache.Put(c)
	})
}

func (group *RouterGroup) POST(path string, handlers ...HandlerFunc) {
	group.Handle(commonroute("POST", path, handlers))
}

func (group *RouterGroup) GET(path string, handlers ...HandlerFunc) {
	group.Handle(commonroute("GET", path, handlers))
}

func (group *RouterGroup) DELETE(path string, handlers ...HandlerFunc) {
	group.Handle(commonroute("DELETE", path, handlers))
}

func (group *RouterGroup) PATCH(path string, handlers ...HandlerFunc) {
	group.Handle(commonroute("PATCH", path, handlers))
}

func (group *RouterGroup) PUT(path string, handlers ...HandlerFunc) {
	group.Handle(commonroute("PUT", path, handlers))
}

func (group *RouterGroup) OPTIONS(path string, handlers ...HandlerFunc) {
	group.Handle(commonroute("OPTIONS", path, handlers))
}

func (group *RouterGroup) HEAD(path string, handlers ...HandlerFunc) {
	group.Handle(commonroute("HEAD", path, handlers))
}

// Adds a Static route handled by the router, based on the group prefix.
func (group *RouterGroup) Static(staticpath string) {
	group.engine.AddStaticDir(staticpath)
	staticpath = group.pathNoLeadingSlash(staticpath)
	group.Handle(staticroute("GET", staticpath, []HandlerFunc{handleStatic}))
	group.Handle(staticroute("HEAD", staticpath, []HandlerFunc{handleStatic}))
}

func (group *RouterGroup) combineHandlers(handlers []HandlerFunc) []HandlerFunc {
	s := len(group.Handlers) + len(handlers)
	h := make([]HandlerFunc, 0, s)
	h = append(h, group.Handlers...)
	h = append(h, handlers...)
	return h
}

// Creates a Route that is not a static route
func commonroute(method string, path string, handlers []HandlerFunc) *Route {
	return &Route{method: method,
		path:     path,
		handlers: handlers}
}

// Creates a Route for a static route
func staticroute(method string, staticpath string, handlers []HandlerFunc) *Route {
	path := filepath.Join(staticpath, "/*filepath")
	return &Route{static: true,
		method:     method,
		path:       path,
		staticpath: staticpath,
		handlers:   handlers}
}
<commit_msg>documentation and syntax sugar<commit_after>package flotilla

import (
	"net/http"
	"path/filepath"
	"strings"

	"github.com/julienschmidt/httprouter"
)

type (
	// Information about a route as a unit outside of the router for use & reuse.
	Route struct {
		static      bool
		method      string
		path        string
		staticpath  string
		visiblepath string
		handlers    []HandlerFunc
	}

	// A RouterGroup is associated with a prefix and an array of handlers.
	RouterGroup struct {
		Handlers []HandlerFunc
		prefix   string
		parent   *RouterGroup
		children []*RouterGroup
		routes   []*Route
		engine   *Engine
	}
)

// Adds an number of HandlerFuncs to the RouterGroup.
func (group *RouterGroup) Use(middlewares ...HandlerFunc) {
	for _, handler := range middlewares {
		if !group.handlerExists(handler) {
			group.Handlers = append(group.Handlers, handler)
		}
	}
}

func (group *RouterGroup) handlerExists(outside HandlerFunc) bool {
	for _, inside := range group.Handlers {
		if funcEqual(inside, outside) {
			return true
		}
	}
	return false
}

// Creates a new router group.
func (group *RouterGroup) New(component string, handlers ...HandlerFunc) *RouterGroup {
	prefix := group.pathFor(component)

	newroutergroup := &RouterGroup{
		Handlers: group.combineHandlers(handlers),
		parent:   group,
		prefix:   prefix,
		engine:   group.engine,
	}

	group.children = append(group.children, newroutergroup)

	return newroutergroup
}

func (group *RouterGroup) pathFor(path string) string {
	joined := filepath.Join(group.prefix, path)
	// Append a '/' if the last component had one, but only if it's not there already
	if len(path) > 0 && path[len(path)-1] == '/' && joined[len(joined)-1] != '/' {
		return joined + "/"
	}
	return joined
}

func (group *RouterGroup) pathNoLeadingSlash(path string) string {
	return strings.TrimLeft(strings.Join([]string{group.prefix, path}, "/"), "/")
}

// Handle registers a new request handle and middlewares with the given path and
// method. For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
// functions can be used.
func (group *RouterGroup) Handle(route *Route) {
	handlers := group.combineHandlers(route.handlers)
	route.visiblepath = group.pathFor(route.path)
	group.routes = append(group.routes, route)
	group.engine.router.Handle(route.method, route.visiblepath, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		c := group.engine.getCtx(w, req, params, handlers)
		//fmt.Printf("\nPRE HOOK\n")
		c.Next()
		//fmt.Printf("\nPOST HOOK\n")
		group.engine.cache.Put(c)
	})
}

func (group *RouterGroup) POST(path string, handlers ...HandlerFunc) {
	group.Handle(commonroute("POST", path, handlers))
}

func (group *RouterGroup) GET(path string, handlers ...HandlerFunc) {
	group.Handle(commonroute("GET", path, handlers))
}

func (group *RouterGroup) DELETE(path string, handlers ...HandlerFunc) {
	group.Handle(commonroute("DELETE", path, handlers))
}

func (group *RouterGroup) PATCH(path string, handlers ...HandlerFunc) {
	group.Handle(commonroute("PATCH", path, handlers))
}

func (group *RouterGroup) PUT(path string, handlers ...HandlerFunc) {
	group.Handle(commonroute("PUT", path, handlers))
}

func (group *RouterGroup) OPTIONS(path string, handlers ...HandlerFunc) {
	group.Handle(commonroute("OPTIONS", path, handlers))
}

func (group *RouterGroup) HEAD(path string, handlers ...HandlerFunc) {
	group.Handle(commonroute("HEAD", path, handlers))
}

// Adds a Static route handled by the router, based on the group prefix.
func (group *RouterGroup) Static(staticpath string) {
	group.engine.AddStaticDir(staticpath)
	staticpath = group.pathNoLeadingSlash(staticpath)
	group.Handle(staticroute("GET", staticpath, []HandlerFunc{handleStatic}))
	group.Handle(staticroute("HEAD", staticpath, []HandlerFunc{handleStatic}))
}

func (group *RouterGroup) combineHandlers(handlers []HandlerFunc) []HandlerFunc {
	s := len(group.Handlers) + len(handlers)
	h := make([]HandlerFunc, 0, s)
	h = append(h, group.Handlers...)
	h = append(h, handlers...)
	return h
}

func commonroute(method string, path string, handlers []HandlerFunc) *Route {
	return &Route{method: method,
		path:     path,
		handlers: handlers}
}

func staticroute(method string, staticpath string, handlers []HandlerFunc) *Route {
	path := filepath.Join(staticpath, "/*filepath")
	return &Route{static: true,
		method:     method,
		path:       path,
		staticpath: staticpath,
		handlers:   handlers}
}
<|endoftext|>


<commit_before>// Copyright 2017 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package text offers functions to draw texts on an Ebiten's image.
//
// Note: This package is experimental and API might be changed.
//
// For the example using a TTF font, see font package in the examples.
package text

import (
	"image"
	"image/color"
	"math"

	"golang.org/x/image/font"
	"golang.org/x/image/math/fixed"

	"github.com/hajimehoshi/ebiten"
	emath "github.com/hajimehoshi/ebiten/internal/math"
	"github.com/hajimehoshi/ebiten/internal/sync"
)

var (
	monotonicClock int64
)

func now() int64 {
	monotonicClock++
	return monotonicClock
}

var (
	charBounds = map[char]fixed.Rectangle26_6{}
)

type char struct {
	face font.Face
	rune rune
}

func (c *char) bounds() fixed.Rectangle26_6 {
	if b, ok := charBounds[*c]; ok {
		return b
	}
	b, _, _ := c.face.GlyphBounds(c.rune)
	charBounds[*c] = b
	return b
}

func (c *char) size() fixed.Point26_6 {
	b := c.bounds()
	return b.Max.Sub(b.Min)
}

func (c *char) empty() bool {
	s := c.size()
	return s.X == 0 || s.Y == 0
}

func (c *char) atlasGroup() int {
	s := c.size()
	w, h := s.X.Ceil(), s.Y.Ceil()
	t := w
	if t < h {
		t = h
	}

	// Different images for small runes are inefficient.
	// Let's use a same texture atlas for typical character sizes.
	if t < 32 {
		return 32
	}
	return emath.NextPowerOf2Int(t)
}

type glyph struct {
	char  char
	index int
	atime int64
}

func fixed26_6ToFloat64(x fixed.Int26_6) float64 {
	return float64(x) / (1 << 6)
}

func (g *glyph) draw(dst *ebiten.Image, x, y fixed.Int26_6, clr color.Color) {
	cr, cg, cb, ca := clr.RGBA()
	if ca == 0 {
		return
	}

	b := g.char.bounds()
	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(fixed26_6ToFloat64(x), fixed26_6ToFloat64(y))
	op.GeoM.Translate(fixed26_6ToFloat64(b.Min.X), fixed26_6ToFloat64(b.Min.Y))

	rf := float64(cr) / float64(ca)
	gf := float64(cg) / float64(ca)
	bf := float64(cb) / float64(ca)
	af := float64(ca) / 0xffff
	op.ColorM.Scale(rf, gf, bf, af)

	a := atlases[g.char.atlasGroup()]
	sx, sy := a.at(g)
	r := image.Rect(sx, sy, sx+a.glyphSize, sy+a.glyphSize)
	op.SourceRect = &r

	dst.DrawImage(a.image, op)
}

var (
	atlases = map[int]*atlas{}
)

type atlas struct {
	// image is the back-end image to hold glyph cache.
	image *ebiten.Image

	// tmpImage is the temporary image as a renderer source for glyph.
	tmpImage *ebiten.Image

	// glyphSize is the size of one glyph in the cache.
	// This value is always power of 2.
	glyphSize int

	charToGlyph map[char]*glyph
}

func (a *atlas) at(glyph *glyph) (int, int) {
	if a.glyphSize != glyph.char.atlasGroup() {
		panic("not reached")
	}
	w, _ := a.image.Size()
	xnum := w / a.glyphSize
	x, y := glyph.index%xnum, glyph.index/xnum
	return x * a.glyphSize, y * a.glyphSize
}

func (a *atlas) maxGlyphNum() int {
	w, h := a.image.Size()
	xnum := w / a.glyphSize
	ynum := h / a.glyphSize
	return xnum * ynum
}

func (a *atlas) appendGlyph(face font.Face, rune rune, now int64) *glyph {
	g := &glyph{
		char:  char{face, rune},
		atime: now,
	}
	if len(a.charToGlyph) == a.maxGlyphNum() {
		var oldest *glyph
		t := int64(math.MaxInt64)
		for _, g := range a.charToGlyph {
			if g.atime < t {
				t = g.atime
				oldest = g
			}
		}
		if oldest == nil {
			panic("not reached")
		}
		idx := oldest.index
		delete(a.charToGlyph, oldest.char)

		g.index = idx
	} else {
		g.index = len(a.charToGlyph)
	}
	a.charToGlyph[g.char] = g
	a.draw(g)
	return g
}

func (a *atlas) draw(glyph *glyph) {
	if a.tmpImage == nil {
		a.tmpImage, _ = ebiten.NewImage(a.glyphSize, a.glyphSize, ebiten.FilterNearest)
	}

	dst := image.NewRGBA(image.Rect(0, 0, a.glyphSize, a.glyphSize))
	d := font.Drawer{
		Dst:  dst,
		Src:  image.White,
		Face: glyph.char.face,
	}
	b := glyph.char.bounds()
	d.Dot = fixed.Point26_6{-b.Min.X, -b.Min.Y}
	d.DrawString(string(glyph.char.rune))
	a.tmpImage.ReplacePixels(dst.Pix)

	op := &ebiten.DrawImageOptions{}
	x, y := a.at(glyph)
	op.GeoM.Translate(float64(x), float64(y))
	op.CompositeMode = ebiten.CompositeModeCopy
	a.image.DrawImage(a.tmpImage, op)

	a.tmpImage.Clear()
}

func getGlyphFromCache(face font.Face, r rune, now int64) *glyph {
	ch := char{face, r}
	a, ok := atlases[ch.atlasGroup()]
	if ok {
		g, ok := a.charToGlyph[ch]
		if ok {
			g.atime = now
			return g
		}
	}

	if ch.empty() {
		// The glyph doesn't have its size but might have valid 'advance' parameter
		// when ch is e.g. space (U+0020).
		return &glyph{
			char:  ch,
			atime: now,
		}
	}

	if !ok {
		// Don't use ebiten.MaxImageSize here.
		// It's because the back-end image pixels will be restored from GPU
		// whenever a new glyph is rendered on the image, and restoring cost is
		// expensive if the image is big.
		// The back-end image is updated a temporary image, and the temporary image is
		// always cleared after used. This means that there is no clue to restore
		// the back-end image without reading from GPU
		// (see the package 'restorable' implementation).
		//
		// TODO: How about making a new function for 'flagile' image?
		const size = 1024
		i, _ := ebiten.NewImage(size, size, ebiten.FilterNearest)
		a = &atlas{
			image:       i,
			glyphSize:   ch.atlasGroup(),
			charToGlyph: map[char]*glyph{},
		}
		atlases[ch.atlasGroup()] = a
	}

	return a.appendGlyph(ch.face, ch.rune, now)
}

var textM sync.Mutex

// Draw draws a given text on a given destination image dst.
//
// face is the font for text rendering.
// (x, y) represents a 'dot' (period) position.
// Be careful that this doesn't represent left-upper corner position.
// clr is the color for text rendering.
//
// Glyphs used for rendering are cached in least-recently-used way.
// It is OK to call this function with a same text and a same face at every frame in terms of performance.
//
// This function is concurrent-safe.
func Draw(dst *ebiten.Image, text string, face font.Face, x, y int, clr color.Color) {
	textM.Lock()

	n := now()
	fx := fixed.I(x)
	prevC := rune(-1)

	runes := []rune(text)
	for _, c := range runes {
		if prevC >= 0 {
			fx += face.Kern(prevC, c)
		}
		if g := getGlyphFromCache(face, c, n); g != nil {
			if !g.char.empty() {
				g.draw(dst, fx, fixed.I(y), clr)
			}
			a, _ := face.GlyphAdvance(c)
			fx += a
		}
		prevC = c
	}

	textM.Unlock()
}
<commit_msg>text: Now package text is stable<commit_after>// Copyright 2017 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package text offers functions to draw texts on an Ebiten's image.
//
// For the example using a TTF font, see font package in the examples.
package text

import (
	"image"
	"image/color"
	"math"

	"golang.org/x/image/font"
	"golang.org/x/image/math/fixed"

	"github.com/hajimehoshi/ebiten"
	emath "github.com/hajimehoshi/ebiten/internal/math"
	"github.com/hajimehoshi/ebiten/internal/sync"
)

var (
	monotonicClock int64
)

func now() int64 {
	monotonicClock++
	return monotonicClock
}

var (
	charBounds = map[char]fixed.Rectangle26_6{}
)

type char struct {
	face font.Face
	rune rune
}

func (c *char) bounds() fixed.Rectangle26_6 {
	if b, ok := charBounds[*c]; ok {
		return b
	}
	b, _, _ := c.face.GlyphBounds(c.rune)
	charBounds[*c] = b
	return b
}

func (c *char) size() fixed.Point26_6 {
	b := c.bounds()
	return b.Max.Sub(b.Min)
}

func (c *char) empty() bool {
	s := c.size()
	return s.X == 0 || s.Y == 0
}

func (c *char) atlasGroup() int {
	s := c.size()
	w, h := s.X.Ceil(), s.Y.Ceil()
	t := w
	if t < h {
		t = h
	}

	// Different images for small runes are inefficient.
	// Let's use a same texture atlas for typical character sizes.
	if t < 32 {
		return 32
	}
	return emath.NextPowerOf2Int(t)
}

type glyph struct {
	char  char
	index int
	atime int64
}

func fixed26_6ToFloat64(x fixed.Int26_6) float64 {
	return float64(x) / (1 << 6)
}

func (g *glyph) draw(dst *ebiten.Image, x, y fixed.Int26_6, clr color.Color) {
	cr, cg, cb, ca := clr.RGBA()
	if ca == 0 {
		return
	}

	b := g.char.bounds()
	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(fixed26_6ToFloat64(x), fixed26_6ToFloat64(y))
	op.GeoM.Translate(fixed26_6ToFloat64(b.Min.X), fixed26_6ToFloat64(b.Min.Y))

	rf := float64(cr) / float64(ca)
	gf := float64(cg) / float64(ca)
	bf := float64(cb) / float64(ca)
	af := float64(ca) / 0xffff
	op.ColorM.Scale(rf, gf, bf, af)

	a := atlases[g.char.atlasGroup()]
	sx, sy := a.at(g)
	r := image.Rect(sx, sy, sx+a.glyphSize, sy+a.glyphSize)
	op.SourceRect = &r

	dst.DrawImage(a.image, op)
}

var (
	atlases = map[int]*atlas{}
)

type atlas struct {
	// image is the back-end image to hold glyph cache.
	image *ebiten.Image

	// tmpImage is the temporary image as a renderer source for glyph.
	tmpImage *ebiten.Image

	// glyphSize is the size of one glyph in the cache.
	// This value is always power of 2.
	glyphSize int

	charToGlyph map[char]*glyph
}

func (a *atlas) at(glyph *glyph) (int, int) {
	if a.glyphSize != glyph.char.atlasGroup() {
		panic("not reached")
	}
	w, _ := a.image.Size()
	xnum := w / a.glyphSize
	x, y := glyph.index%xnum, glyph.index/xnum
	return x * a.glyphSize, y * a.glyphSize
}

func (a *atlas) maxGlyphNum() int {
	w, h := a.image.Size()
	xnum := w / a.glyphSize
	ynum := h / a.glyphSize
	return xnum * ynum
}

func (a *atlas) appendGlyph(face font.Face, rune rune, now int64) *glyph {
	g := &glyph{
		char:  char{face, rune},
		atime: now,
	}
	if len(a.charToGlyph) == a.maxGlyphNum() {
		var oldest *glyph
		t := int64(math.MaxInt64)
		for _, g := range a.charToGlyph {
			if g.atime < t {
				t = g.atime
				oldest = g
			}
		}
		if oldest == nil {
			panic("not reached")
		}
		idx := oldest.index
		delete(a.charToGlyph, oldest.char)

		g.index = idx
	} else {
		g.index = len(a.charToGlyph)
	}
	a.charToGlyph[g.char] = g
	a.draw(g)
	return g
}

func (a *atlas) draw(glyph *glyph) {
	if a.tmpImage == nil {
		a.tmpImage, _ = ebiten.NewImage(a.glyphSize, a.glyphSize, ebiten.FilterNearest)
	}

	dst := image.NewRGBA(image.Rect(0, 0, a.glyphSize, a.glyphSize))
	d := font.Drawer{
		Dst:  dst,
		Src:  image.White,
		Face: glyph.char.face,
	}
	b := glyph.char.bounds()
	d.Dot = fixed.Point26_6{-b.Min.X, -b.Min.Y}
	d.DrawString(string(glyph.char.rune))
	a.tmpImage.ReplacePixels(dst.Pix)

	op := &ebiten.DrawImageOptions{}
	x, y := a.at(glyph)
	op.GeoM.Translate(float64(x), float64(y))
	op.CompositeMode = ebiten.CompositeModeCopy
	a.image.DrawImage(a.tmpImage, op)

	a.tmpImage.Clear()
}

func getGlyphFromCache(face font.Face, r rune, now int64) *glyph {
	ch := char{face, r}
	a, ok := atlases[ch.atlasGroup()]
	if ok {
		g, ok := a.charToGlyph[ch]
		if ok {
			g.atime = now
			return g
		}
	}

	if ch.empty() {
		// The glyph doesn't have its size but might have valid 'advance' parameter
		// when ch is e.g. space (U+0020).
		return &glyph{
			char:  ch,
			atime: now,
		}
	}

	if !ok {
		// Don't use ebiten.MaxImageSize here.
		// It's because the back-end image pixels will be restored from GPU
		// whenever a new glyph is rendered on the image, and restoring cost is
		// expensive if the image is big.
		// The back-end image is updated a temporary image, and the temporary image is
		// always cleared after used. This means that there is no clue to restore
		// the back-end image without reading from GPU
		// (see the package 'restorable' implementation).
		//
		// TODO: How about making a new function for 'flagile' image?
		const size = 1024
		i, _ := ebiten.NewImage(size, size, ebiten.FilterNearest)
		a = &atlas{
			image:       i,
			glyphSize:   ch.atlasGroup(),
			charToGlyph: map[char]*glyph{},
		}
		atlases[ch.atlasGroup()] = a
	}

	return a.appendGlyph(ch.face, ch.rune, now)
}

var textM sync.Mutex

// Draw draws a given text on a given destination image dst.
//
// face is the font for text rendering.
// (x, y) represents a 'dot' (period) position.
// Be careful that this doesn't represent left-upper corner position.
// clr is the color for text rendering.
//
// Glyphs used for rendering are cached in least-recently-used way.
// It is OK to call this function with a same text and a same face at every frame in terms of performance.
//
// This function is concurrent-safe.
func Draw(dst *ebiten.Image, text string, face font.Face, x, y int, clr color.Color) {
	textM.Lock()

	n := now()
	fx := fixed.I(x)
	prevC := rune(-1)

	runes := []rune(text)
	for _, c := range runes {
		if prevC >= 0 {
			fx += face.Kern(prevC, c)
		}
		if g := getGlyphFromCache(face, c, n); g != nil {
			if !g.char.empty() {
				g.draw(dst, fx, fixed.I(y), clr)
			}
			a, _ := face.GlyphAdvance(c)
			fx += a
		}
		prevC = c
	}

	textM.Unlock()
}
<|endoftext|>


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

package main

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"flag"
	"fmt"
	"go/ast"
	"go/format"
	"go/printer"
	"go/token"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"reflect"
	"sort"

	"golang.org/x/tools/go/loader"
)

var fset = token.NewFileSet()

type Edit struct {
	Pos, End int
}

type editsByPos []Edit

func (e editsByPos) Len() int           { return len(e) }
func (e editsByPos) Less(i, j int) bool { return e[i].Pos < e[j].Pos }
func (e editsByPos) Swap(i, j int)      { e[i], e[j] = e[j], e[i] }

func apply(file string, edits []Edit) {
	if len(edits) == 0 {
		return
	}

	sort.Sort(editsByPos(edits))

	// Check for overlap.
	for i := 1; i < len(edits); i++ {
		if edits[i-1].End > edits[i].Pos {
			log.Fatal("overlap")
		}
	}

	buf, err := ioutil.ReadFile(file)
	if err != nil {
		log.Fatal(err)
	}

	for i := len(edits) - 1; i >= 0; i-- {
		copy(buf[edits[i].Pos:], buf[edits[i].End:])
		buf = buf[:len(buf)-(edits[i].End-edits[i].Pos)]
	}

	buf, err = format.Source(buf)
	if err != nil {
		log.Fatal(err)
	}

	err = ioutil.WriteFile(file, buf, 0)
	if err != nil {
		log.Fatal(err)
	}
}

var (
	flagAll = flag.Bool("all", false, "type check all GOOS and GOARCH combinations")
	flagApply = flag.Bool("apply", false, "apply edits")
	flagGob = flag.Bool("gob", false, "dump edits to stdout as gob")
)

func main() {
	flag.Parse()

	var m map[string][]Edit
	if *flagAll {
		m = mergeEdits()
	} else {
		m = computeEdits()
	}

	if *flagGob {
		err := gob.NewEncoder(os.Stdout).Encode(m)
		if err != nil {
			log.Fatal(err)
		}
		return
	} else if *flagApply {
		for f, e := range m {
			apply(f, e)
		}
	} else {
		err := json.NewEncoder(os.Stdout).Encode(m)
		if err != nil {
			log.Fatal(err)
		}
		return
	}
}

var plats = [...]struct {
	goos, goarch string
}{
	{"linux", "386"},
	{"linux", "amd64"},
	{"linux", "arm"},
	{"linux", "arm64"},
	{"linux", "ppc64"},
	{"linux", "ppc64le"},
	{"nacl", "386"},
	{"nacl", "amd64p32"},
	{"nacl", "arm"},
	{"darwin", "386"},
	{"darwin", "amd64"},
	{"dragonfly", "amd64"},
	{"freebsd", "386"},
	{"freebsd", "amd64"},
	{"freebsd", "arm"},
	{"netbsd", "386"},
	{"netbsd", "amd64"},
	{"netbsd", "arm"},
	{"openbsd", "386"},
	{"openbsd", "amd64"},
	{"openbsd", "arm"},
	{"plan9", "386"},
	{"plan9", "amd64"},
	{"solaris", "amd64"},
	{"windows", "386"},
	{"windows", "amd64"},
}

func mergeEdits() map[string][]Edit {
	ch := make(chan map[string][]Edit)
	for _, plat := range plats {
		go func(goos, goarch string) {
			ch <- doSub(goos, goarch)
		}(plat.goos, plat.goarch)
	}

	m := make(map[string][]Edit)
	for range plats {
		for f, e := range <-ch {
			if e0, ok := m[f]; ok {
				m[f] = intersect(e0, e)
			} else {
				m[f] = e
			}
		}
	}

	return m
}

func intersect(e1, e2 []Edit) []Edit {
	if len(e1) == 0 || len(e2) == 0 {
		return nil
	}

	set := make(map[Edit]bool, len(e1))
	for _, e := range e1 {
		set[e] = true
	}

	var res []Edit
	for _, e := range e2 {
		if set[e] {
			res = append(res, e)
		}
	}
	return res
}

func doSub(goos, goarch string) map[string][]Edit {
	var m map[string][]Edit
	pr, pw := io.Pipe()
	ch := make(chan error)
	go func() {
		ch <- gob.NewDecoder(pr).Decode(&m)
	}()
	cmd := exec.Command("./unconvert", append([]string{"-gob"}, flag.Args()...)...)
	cmd.Stdout = pw
	cmd.Env = append(os.Environ(), "GOOS="+goos, "GOARCH="+goarch)
	if err := cmd.Run(); err != nil {
		log.Fatal(err)
	}
	if err := <-ch; err != nil {
		log.Fatal(err)
	}
	return m
}

func computeEdits() map[string][]Edit {
	var conf loader.Config
	conf.Fset = fset
	for _, arg := range flag.Args() {
		conf.Import(arg)
	}
	prog, err := conf.Load()
	if err != nil {
		log.Fatal(err)
	}

	m := make(map[string][]Edit)
	var v visitor
	for _, pkg := range prog.InitialPackages() {
		v.pkg = pkg
		for _, file := range pkg.Files {
			v.file = fset.File(file.Package)
			ast.Walk(&v, file)
			sort.Sort(editsByPos(v.edits))
			m[v.file.Name()] = v.edits
			v.edits = nil
		}
	}
	return m
}

type visitor struct {
	pkg   *loader.PackageInfo
	file  *token.File
	edits []Edit
	nodes []ast.Node
}

func (v *visitor) Visit(node ast.Node) ast.Visitor {
	if node != nil {
		v.nodes = append(v.nodes, node)
	} else {
		v.nodes = v.nodes[:len(v.nodes)-1]
	}
	if call, ok := node.(*ast.CallExpr); ok {
		v.unconvert(call)
	}
	return v
}

func (v *visitor) unconvert(call *ast.CallExpr) {
	// TODO(mdempsky): Handle useless multi-conversions.

	// Conversions have exactly one argument.
	if len(call.Args) != 1 || call.Ellipsis != token.NoPos {
		return
	}
	ft, ok := v.pkg.Types[call.Fun]
	if !ok {
		fmt.Println("Missing type for function")
		return
	}
	if !ft.IsType() {
		// Not a conversion.
		return
	}
	at, ok := v.pkg.Types[call.Args[0]]
	if !ok {
		fmt.Println("Missing type for argument")
	}
	if ft.Type != at.Type {
		// A real conversion.
		return
	}
	if at.Value != nil || hasUntypedValue(call.Args[0]) {
		// As a workaround for golang.org/issue/13061,
		// skip conversions that contain an untyped value.
		return
	}

	outer := v.nodes[len(v.nodes)-2]
	if keepParen(outer, call, call.Args[0]) {
		v.remove(call.Fun.Pos(), call.Lparen)
	} else {
		v.remove(call.Fun.Pos(), call.Lparen+1)
		v.remove(call.Rparen, call.Rparen+1)
	}
}

func (v *visitor) remove(pos, end token.Pos) {
	v.edits = append(v.edits, Edit{v.file.Offset(pos), v.file.Offset(end)})
}

func keepParen(a ast.Node, b, c ast.Expr) bool {
	// 1. Find the value in a that points to b.
	bp := findExprField(a, b)

	// 2. Try printing a with s/b/c/ and with s/b/(c)/.
	var buf1 bytes.Buffer
	*bp = c
	printer.Fprint(&buf1, fset, a)

	var buf2 bytes.Buffer
	*bp = &ast.ParenExpr{X: c}
	printer.Fprint(&buf2, fset, a)

	*bp = b

	// 3. Return whether they print the same (i.e., the parentheses are necessary).
	return buf1.String() == buf2.String()
}

func findExprField(a ast.Node, b ast.Expr) *ast.Expr {
	v := reflect.ValueOf(a).Elem()
	for i, n := 0, v.NumField(); i < n; i++ {
		// Interesting fields are either ast.Expr or []ast.Expr.
		switch f := v.Field(i).Interface().(type) {
		case ast.Expr:
			if f == b {
				return v.Field(i).Addr().Interface().(*ast.Expr)
			}
		case []ast.Expr:
			for i, e := range f {
				if e == b {
					return &f[i]
				}
			}
		}
	}
	log.Fatal("Failed to find b in a")
	return nil
}

func hasUntypedValue(n ast.Expr) bool {
	var v uvVisitor
	ast.Walk(&v, n)
	return v.found
}

type uvVisitor struct {
	found bool
}

func (v *uvVisitor) Visit(node ast.Node) ast.Visitor {
	// Short circuit.
	if v.found {
		return nil
	}

	switch node := node.(type) {
	case *ast.BinaryExpr:
		switch node.Op {
		case token.SHL, token.SHR, token.EQL, token.NEQ, token.LSS, token.GTR, token.LEQ, token.GEQ:
			// Shifts yield an untyped value if their LHS is untyped.
			// Comparisons yield an untyped boolean value.
			v.found = true
		}
	case *ast.Ident:
		if node.Name == "nil" {
			// Probably the universal untyped zero value.
			v.found = true
		}
	}

	if v.found {
		return nil
	}
	return v
}
<commit_msg>unconvert: moar concurrency<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"flag"
	"fmt"
	"go/ast"
	"go/format"
	"go/printer"
	"go/token"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"reflect"
	"sort"
	"sync"

	"golang.org/x/tools/go/loader"
)

var fset = token.NewFileSet()

type Edit struct {
	Pos, End int
}

type editsByPos []Edit

func (e editsByPos) Len() int           { return len(e) }
func (e editsByPos) Less(i, j int) bool { return e[i].Pos < e[j].Pos }
func (e editsByPos) Swap(i, j int)      { e[i], e[j] = e[j], e[i] }

func apply(file string, edits []Edit) {
	if len(edits) == 0 {
		return
	}

	sort.Sort(editsByPos(edits))

	// Check for overlap.
	for i := 1; i < len(edits); i++ {
		if edits[i-1].End > edits[i].Pos {
			log.Fatal("overlap")
		}
	}

	buf, err := ioutil.ReadFile(file)
	if err != nil {
		log.Fatal(err)
	}

	for i := len(edits) - 1; i >= 0; i-- {
		copy(buf[edits[i].Pos:], buf[edits[i].End:])
		buf = buf[:len(buf)-(edits[i].End-edits[i].Pos)]
	}

	buf, err = format.Source(buf)
	if err != nil {
		log.Fatal(err)
	}

	err = ioutil.WriteFile(file, buf, 0)
	if err != nil {
		log.Fatal(err)
	}
}

var (
	flagAll = flag.Bool("all", false, "type check all GOOS and GOARCH combinations")
	flagApply = flag.Bool("apply", false, "apply edits")
	flagGob = flag.Bool("gob", false, "dump edits to stdout as gob")
)

func main() {
	flag.Parse()

	var m map[string][]Edit
	if *flagAll {
		m = mergeEdits()
	} else {
		m = computeEdits()
	}

	if *flagGob {
		err := gob.NewEncoder(os.Stdout).Encode(m)
		if err != nil {
			log.Fatal(err)
		}
		return
	} else if *flagApply {
		for f, e := range m {
			apply(f, e)
		}
	} else {
		err := json.NewEncoder(os.Stdout).Encode(m)
		if err != nil {
			log.Fatal(err)
		}
		return
	}
}

var plats = [...]struct {
	goos, goarch string
}{
	{"linux", "386"},
	{"linux", "amd64"},
	{"linux", "arm"},
	{"linux", "arm64"},
	{"linux", "ppc64"},
	{"linux", "ppc64le"},
	{"nacl", "386"},
	{"nacl", "amd64p32"},
	{"nacl", "arm"},
	{"darwin", "386"},
	{"darwin", "amd64"},
	{"dragonfly", "amd64"},
	{"freebsd", "386"},
	{"freebsd", "amd64"},
	{"freebsd", "arm"},
	{"netbsd", "386"},
	{"netbsd", "amd64"},
	{"netbsd", "arm"},
	{"openbsd", "386"},
	{"openbsd", "amd64"},
	{"openbsd", "arm"},
	{"plan9", "386"},
	{"plan9", "amd64"},
	{"solaris", "amd64"},
	{"windows", "386"},
	{"windows", "amd64"},
}

func mergeEdits() map[string][]Edit {
	ch := make(chan map[string][]Edit)
	for _, plat := range plats {
		go func(goos, goarch string) {
			ch <- doSub(goos, goarch)
		}(plat.goos, plat.goarch)
	}

	m := make(map[string][]Edit)
	for range plats {
		for f, e := range <-ch {
			if e0, ok := m[f]; ok {
				m[f] = intersect(e0, e)
			} else {
				m[f] = e
			}
		}
	}

	return m
}

func intersect(e1, e2 []Edit) []Edit {
	if len(e1) == 0 || len(e2) == 0 {
		return nil
	}

	set := make(map[Edit]bool, len(e1))
	for _, e := range e1 {
		set[e] = true
	}

	var res []Edit
	for _, e := range e2 {
		if set[e] {
			res = append(res, e)
		}
	}
	return res
}

func doSub(goos, goarch string) map[string][]Edit {
	var m map[string][]Edit
	pr, pw := io.Pipe()
	ch := make(chan error)
	go func() {
		ch <- gob.NewDecoder(pr).Decode(&m)
	}()
	cmd := exec.Command("./unconvert", append([]string{"-gob"}, flag.Args()...)...)
	cmd.Stdout = pw
	cmd.Stderr = os.Stderr
	cmd.Env = append(os.Environ(), "GOOS="+goos, "GOARCH="+goarch)
	if err := cmd.Run(); err != nil {
		log.Fatal(err)
	}
	if err := <-ch; err != nil {
		log.Fatal(err)
	}
	return m
}

func computeEdits() map[string][]Edit {
	var conf loader.Config
	conf.Fset = fset
	for _, arg := range flag.Args() {
		conf.Import(arg)
	}
	prog, err := conf.Load()
	if err != nil {
		log.Fatal(err)
	}

	type res struct { file string; edits []Edit }
	ch := make(chan res)
	var wg sync.WaitGroup
	for _, pkg := range prog.InitialPackages() {
		for _, file := range pkg.Files {
			wg.Add(1)
			go func(pkg *loader.PackageInfo, file *ast.File) {
				defer wg.Done()
				v := visitor{pkg: pkg, file: fset.File(file.Package)}
				ast.Walk(&v, file)
				sort.Sort(editsByPos(v.edits))
				ch <- res{v.file.Name(), v.edits}
			}(pkg, file)
		}
	}
	go func() {
		wg.Wait()
		close(ch)
	}()

	m := make(map[string][]Edit)
	for r := range ch {
		m[r.file] = r.edits
	}
	return m
}

type visitor struct {
	pkg   *loader.PackageInfo
	file  *token.File
	edits []Edit
	nodes []ast.Node
}

func (v *visitor) Visit(node ast.Node) ast.Visitor {
	if node != nil {
		v.nodes = append(v.nodes, node)
	} else {
		v.nodes = v.nodes[:len(v.nodes)-1]
	}
	if call, ok := node.(*ast.CallExpr); ok {
		v.unconvert(call)
	}
	return v
}

func (v *visitor) unconvert(call *ast.CallExpr) {
	// TODO(mdempsky): Handle useless multi-conversions.

	// Conversions have exactly one argument.
	if len(call.Args) != 1 || call.Ellipsis != token.NoPos {
		return
	}
	ft, ok := v.pkg.Types[call.Fun]
	if !ok {
		fmt.Println("Missing type for function")
		return
	}
	if !ft.IsType() {
		// Not a conversion.
		return
	}
	at, ok := v.pkg.Types[call.Args[0]]
	if !ok {
		fmt.Println("Missing type for argument")
	}
	if ft.Type != at.Type {
		// A real conversion.
		return
	}
	if at.Value != nil || hasUntypedValue(call.Args[0]) {
		// As a workaround for golang.org/issue/13061,
		// skip conversions that contain an untyped value.
		return
	}

	outer := v.nodes[len(v.nodes)-2]
	if keepParen(outer, call, call.Args[0]) {
		v.remove(call.Fun.Pos(), call.Lparen)
	} else {
		v.remove(call.Fun.Pos(), call.Lparen+1)
		v.remove(call.Rparen, call.Rparen+1)
	}
}

func (v *visitor) remove(pos, end token.Pos) {
	v.edits = append(v.edits, Edit{v.file.Offset(pos), v.file.Offset(end)})
}

func keepParen(a ast.Node, b, c ast.Expr) bool {
	// 1. Find the value in a that points to b.
	bp := findExprField(a, b)

	// 2. Try printing a with s/b/c/ and with s/b/(c)/.
	var buf1 bytes.Buffer
	*bp = c
	printer.Fprint(&buf1, fset, a)

	var buf2 bytes.Buffer
	*bp = &ast.ParenExpr{X: c}
	printer.Fprint(&buf2, fset, a)

	*bp = b

	// 3. Return whether they print the same (i.e., the parentheses are necessary).
	return buf1.String() == buf2.String()
}

func findExprField(a ast.Node, b ast.Expr) *ast.Expr {
	v := reflect.ValueOf(a).Elem()
	for i, n := 0, v.NumField(); i < n; i++ {
		// Interesting fields are either ast.Expr or []ast.Expr.
		switch f := v.Field(i).Interface().(type) {
		case ast.Expr:
			if f == b {
				return v.Field(i).Addr().Interface().(*ast.Expr)
			}
		case []ast.Expr:
			for i, e := range f {
				if e == b {
					return &f[i]
				}
			}
		}
	}
	log.Fatal("Failed to find b in a")
	return nil
}

func hasUntypedValue(n ast.Expr) bool {
	var v uvVisitor
	ast.Walk(&v, n)
	return v.found
}

type uvVisitor struct {
	found bool
}

func (v *uvVisitor) Visit(node ast.Node) ast.Visitor {
	// Short circuit.
	if v.found {
		return nil
	}

	switch node := node.(type) {
	case *ast.BinaryExpr:
		switch node.Op {
		case token.SHL, token.SHR, token.EQL, token.NEQ, token.LSS, token.GTR, token.LEQ, token.GEQ:
			// Shifts yield an untyped value if their LHS is untyped.
			// Comparisons yield an untyped boolean value.
			v.found = true
		}
	case *ast.Ident:
		if node.Name == "nil" {
			// Probably the universal untyped zero value.
			v.found = true
		}
	}

	if v.found {
		return nil
	}
	return v
}
<|endoftext|>


<commit_before>package sudoku

import (
	"testing"
)

func TestSolveOnlyLegalNumber(t *testing.T) {
	grid := NewGrid()
	//Load up a solved grid
	grid.Load(SOLVED_TEST_GRID)
	cell := grid.Cell(3, 3)
	num := cell.Number()
	cell.SetNumber(0)

	//Now that cell should be filled by this technique.

	solver := &onlyLegalNumberTechnique{}

	step := solver.Apply(grid)

	if step == nil {
		t.Log("The only legal number technique did not solve a puzzle it should have.")
		t.FailNow()
	}
	if step.Col != 3 || step.Row != 3 {
		t.Log("The only legal number technique identified the wrong cell.")
		t.Fail()
	}
	if step.Num != num {
		t.Log("The only legal number technique identified the wrong number.")
		t.Fail()
	}
	if !grid.Solved() {
		t.Log("The only legal number technique did not actually mutate the grid.")
		t.Fail()
	}
}

func TestNecessaryInRow(t *testing.T) {
	grid := NewGrid()
	//Load up a solved grid
	grid.Load(SOLVED_TEST_GRID)

	//We're going to cheat an set up an unrealistic grid.

	impossibles := make([]int, DIM)

	for i := 0; i < DIM-1; i++ {
		impossibles[i] = 0
	}
	impossibles[DIM-1] = 1

	//SetNumber will affect the other cells in row, so do it first.
	for _, cell := range grid.Row(3) {
		cell.number = 0
		copy(cell.impossibles[:], impossibles)
	}

	cell := grid.Cell(3, 3)
	//This is the only cell where DIM will be allowed.
	cell.impossibles[DIM-1] = 0

	//Now that cell should be filled by this technique.

	solver := &necessaryInRowTechnique{}

	step := solver.Apply(grid)

	if step == nil {
		t.Log("The necessary in row technique did not solve a puzzle it should have.")
		t.FailNow()
	}
	if step.Col != 3 || step.Row != 3 {
		t.Log("The necessary in row technique identified the wrong cell.")
		t.Fail()
	}
	if step.Num != DIM {
		t.Log("The necessary in row technique identified the wrong number.")
		t.Fail()
	}
	if !grid.Solved() {
		t.Log("The necessary in row technique did not actually mutate the grid.")
		t.Fail()
	}
}
<commit_msg>Fixed test so ALL TESTS PASS.<commit_after>package sudoku

import (
	"testing"
)

func TestSolveOnlyLegalNumber(t *testing.T) {
	grid := NewGrid()
	//Load up a solved grid
	grid.Load(SOLVED_TEST_GRID)
	cell := grid.Cell(3, 3)
	num := cell.Number()
	cell.SetNumber(0)

	//Now that cell should be filled by this technique.

	solver := &onlyLegalNumberTechnique{}

	step := solver.Apply(grid)

	if step == nil {
		t.Log("The only legal number technique did not solve a puzzle it should have.")
		t.FailNow()
	}
	if step.Col != 3 || step.Row != 3 {
		t.Log("The only legal number technique identified the wrong cell.")
		t.Fail()
	}
	if step.Num != num {
		t.Log("The only legal number technique identified the wrong number.")
		t.Fail()
	}
	if !grid.Solved() {
		t.Log("The only legal number technique did not actually mutate the grid.")
		t.Fail()
	}
}

func TestNecessaryInRow(t *testing.T) {
	grid := NewGrid()
	//Load up a solved grid
	grid.Load(SOLVED_TEST_GRID)

	//We're going to cheat an set up an unrealistic grid.

	impossibles := make([]int, DIM)

	for i := 0; i < DIM-1; i++ {
		impossibles[i] = 0
	}
	impossibles[DIM-1] = 1

	//SetNumber will affect the other cells in row, so do it first.
	for _, cell := range grid.Row(3) {
		cell.number = 0
		copy(cell.impossibles[:], impossibles)
	}

	cell := grid.Cell(3, 3)
	//This is the only cell where DIM will be allowed.
	cell.impossibles[DIM-1] = 0

	//Now that cell should be filled by this technique.

	solver := &necessaryInRowTechnique{}

	step := solver.Apply(grid)

	if step == nil {
		t.Log("The necessary in row technique did not solve a puzzle it should have.")
		t.FailNow()
	}
	if step.Col != 3 || step.Row != 3 {
		t.Log("The necessary in row technique identified the wrong cell.")
		t.Fail()
	}
	if step.Num != DIM {
		t.Log("The necessary in row technique identified the wrong number.")
		t.Fail()
	}
	//Can't check if grid is solved because we un-set all the other cells in the row.
	if cell.Number() == 0 {
		t.Log("The necessary in row technique did not actually mutate the grid.")
		t.Fail()
	}
}
<|endoftext|>