<commit_before>package robo

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

// Objects implementing the Handler interface are capable of serving
// HTTP requests. It is expected to follow the same core conventions as
// the "net/http" equivalent.
type Handler interface {
	ServeRoboHTTP(w ResponseWriter, r *Request)
}

// The HandlerFunc type serves as an adaptor to turn plain functions into
// an implementation of the Handler interface.
type HandlerFunc func(w ResponseWriter, r *Request)

func (h HandlerFunc) ServeRoboHTTP(w ResponseWriter, r *Request) {
	h(w, r)
}

// The httpHandler type adds a ServeRoboHTTP method to implementations of
// the http.Handler interface.
type httpHandler struct {
	h http.Handler
}

func (h httpHandler) ServeRoboHTTP(w ResponseWriter, r *Request) {
	h.h.ServeHTTP(w, r.Request)
}

// The ResponseWriter type mirrors http.ResponseWriter.
type ResponseWriter interface {
	http.ResponseWriter
}

// The Request type extends an http.Request instance with additional
// functionality.
type Request struct {
	*http.Request

	// parsed querystring values
	query url.Values

	// named URL parameters for this request and route
	params map[string]string

	// reference to the queue
	queue *queue
}

// Next yields execution to the next matching handler, if there is one,
// blocking until said handler has returned.
func (r *Request) Next(w ResponseWriter) {
	r.queue.serveNext(w, r.Request)
}

// Query returns the value of a particular querystring parameter, after
// lazily parsing the raw querystring.
func (r *Request) Query(name string) string {
	if r.query == nil {
		r.query = r.URL.Query()
	}
	return r.query.Get(name)
}

// Param returns the value of a named URL parameter.
func (r *Request) Param(name string) string {
	if r.params != nil {
		return r.params[name]
	}
	return ""
}

// Mux is a HTTP router. It multiplexes incoming requests to different
// handlers based on user-provided rules on methods and paths.
//
// The zero value for a Mux is a Mux without any registered handlers,
// ready to use.
type Mux struct {
	routes []route
}

// NewMux returns a new Mux instance.
func NewMux() *Mux {
	return new(Mux)
}

// Any registers a new set of handlers listening to all requests for
// the specified URL pattern.
func (m *Mux) Any(pattern string, handlers ...interface{}) {
	m.add("", pattern, handlers...)
}

// Get registers a new set of handlers listening to GET requests for
// the specified URL pattern.
func (m *Mux) Get(pattern string, handlers ...interface{}) {
	m.add("GET", pattern, handlers...)
}

// Head registers a new set of handlers listening to HEAD requests for
// the specified URL pattern.
func (m *Mux) Head(pattern string, handlers ...interface{}) {
	m.add("HEAD", pattern, handlers...)
}

// Post registers a new set of handlers listening to POST requests for
// the specified URL pattern.
func (m *Mux) Post(pattern string, handlers ...interface{}) {
	m.add("POST", pattern, handlers...)
}

// Put registers a new set of handlers listening to PUT requests for
// the specified URL pattern.
func (m *Mux) Put(pattern string, handlers ...interface{}) {
	m.add("PUT", pattern, handlers...)
}

// Patch registers a new set of handlers listening to PATCH requests for
// the specified URL pattern.
func (m *Mux) Patch(pattern string, handlers ...interface{}) {
	m.add("PATCH", pattern, handlers...)
}

// Delete registers a new set of handlers listening to DELETE requests for
// the specified URL pattern.
func (m *Mux) Delete(pattern string, handlers ...interface{}) {
	m.add("DELETE", pattern, handlers...)
}

// Options registers a new set of handlers listening to OPTIONS requests for
// the specified URL pattern.
func (m *Mux) Options(pattern string, handlers ...interface{}) {
	m.add("OPTIONS", pattern, handlers...)
}

// add registers a set of handlers for the given HTTP method ("" matching
// any method) and URL pattern.
func (m *Mux) add(method, pattern string, handlers ...interface{}) {
	if len(handlers) == 0 {
		panic("no handlers provided")
	}

	// validate the provided set of handlers
	clean := make([]Handler, 0, len(handlers))

	for _, h := range handlers {
		switch h := h.(type) {
		case Handler:
			clean = append(clean, h)
		case func(w ResponseWriter, r *Request):
			clean = append(clean, HandlerFunc(h))
		case http.Handler:
			clean = append(clean, httpHandler{h})
		case func(w http.ResponseWriter, r *http.Request):
			clean = append(clean, httpHandler{http.HandlerFunc(h)})
		default:
			panic("not a valid handler")
		}
	}

	m.routes = append(m.routes, newRoute(method, pattern, clean))
}

// newRoute initializes a new route.
func newRoute(method, pattern string, handlers []Handler) route {
	matcher, err := compileMatcher(pattern)
	if err != nil {
		panic(err)
	}

	return route{method, matcher, handlers}
}

// ServeRoboHTTP dispatches the request to matching routes registered with
// the Mux instance.
func (m *Mux) ServeRoboHTTP(w ResponseWriter, r *Request) {
	q := queue{nil, nil, m.routes}
	q.serveNext(w, r.Request)
}

// ServeHTTP dispatches the request to matching routes registered with
// the Mux instance.
func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	m.ServeRoboHTTP(w, &Request{Request: r})
}

// The route type describes a registered route.
type route struct {
	method   string
	matcher  pathMatcher
	handlers []Handler
}

// check tests whether the route matches a provided method and path. The
// parameter map will always be non-nil when the first is true.
func (r *route) check(method, path string) (bool, map[string]string) {
	if method != r.method && r.method != "" {
		return false, nil
	}

	ok, list := r.matcher.match(path, nil)
	if !ok {
		return false, nil
	}

	// only build the parameter map if we have to
	if len(list) > 0 {
		params := make(map[string]string)
		for i := 0; i < len(list); i += 2 {
			params[list[i]] = list[i+1]
		}
		return true, params
	}

	return true, nil
}

// The queue type holds the routing state of an incoming request.
type queue struct {
	// remaining handlers, and parameter map, for the current route
	handlers []Handler
	params   map[string]string

	// remaining routes to be tested
	routes []route
}

// ServeNext attempts to serve an HTTP request using the next matching
// route/handler in the queue.
func (q *queue) serveNext(w ResponseWriter, hr *http.Request) {
	// does the current route still have handlers left?
	if len(q.handlers) > 0 {
		h := q.handlers[0]
		q.handlers = q.handlers[1:]

		h.ServeRoboHTTP(w, &Request{hr, nil, q.params, q})
		return
	}

	// look for the next matching route
	for len(q.routes) > 0 {
		r := q.routes[0]
		q.routes = q.routes[1:]

		// does this route match the request at hand?
		ok, params := r.check(hr.Method, hr.URL.Path)
		if !ok {
			continue
		}

		q.handlers = r.handlers[1:]
		q.params = params

		// invoke the route's first handler
		r.handlers[0].ServeRoboHTTP(w, &Request{hr, nil, q.params, q})
		return
	}

	// when we run out of routes, send a 404 message
	http.Error(w, "Not found.\n", 404)
}
<commit_msg>Use a shared empty map instead of a nil map when there aren't any captured parameters<commit_after>package robo

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

// Objects implementing the Handler interface are capable of serving
// HTTP requests. It is expected to follow the same core conventions as
// the "net/http" equivalent.
type Handler interface {
	ServeRoboHTTP(w ResponseWriter, r *Request)
}

// The HandlerFunc type serves as an adaptor to turn plain functions into
// an implementation of the Handler interface.
type HandlerFunc func(w ResponseWriter, r *Request)

func (h HandlerFunc) ServeRoboHTTP(w ResponseWriter, r *Request) {
	h(w, r)
}

// The httpHandler type adds a ServeRoboHTTP method to implementations of
// the http.Handler interface.
type httpHandler struct {
	h http.Handler
}

func (h httpHandler) ServeRoboHTTP(w ResponseWriter, r *Request) {
	h.h.ServeHTTP(w, r.Request)
}

// The ResponseWriter type mirrors http.ResponseWriter.
type ResponseWriter interface {
	http.ResponseWriter
}

// The Request type extends an http.Request instance with additional
// functionality.
type Request struct {
	*http.Request

	// parsed querystring values
	query url.Values

	// named URL parameters for this request and route
	params map[string]string

	// reference to the queue
	queue *queue
}

// Next yields execution to the next matching handler, if there is one,
// blocking until said handler has returned.
func (r *Request) Next(w ResponseWriter) {
	r.queue.serveNext(w, r.Request)
}

// Query returns the value of a particular querystring parameter, after
// lazily parsing the raw querystring.
func (r *Request) Query(name string) string {
	if r.query == nil {
		r.query = r.URL.Query()
	}
	return r.query.Get(name)
}

// Param returns the value of a named URL parameter.
func (r *Request) Param(name string) string {
	return r.params[name]
}

// Mux is a HTTP router. It multiplexes incoming requests to different
// handlers based on user-provided rules on methods and paths.
//
// The zero value for a Mux is a Mux without any registered handlers,
// ready to use.
type Mux struct {
	routes []route
}

// NewMux returns a new Mux instance.
func NewMux() *Mux {
	return new(Mux)
}

// Any registers a new set of handlers listening to all requests for
// the specified URL pattern.
func (m *Mux) Any(pattern string, handlers ...interface{}) {
	m.add("", pattern, handlers...)
}

// Get registers a new set of handlers listening to GET requests for
// the specified URL pattern.
func (m *Mux) Get(pattern string, handlers ...interface{}) {
	m.add("GET", pattern, handlers...)
}

// Head registers a new set of handlers listening to HEAD requests for
// the specified URL pattern.
func (m *Mux) Head(pattern string, handlers ...interface{}) {
	m.add("HEAD", pattern, handlers...)
}

// Post registers a new set of handlers listening to POST requests for
// the specified URL pattern.
func (m *Mux) Post(pattern string, handlers ...interface{}) {
	m.add("POST", pattern, handlers...)
}

// Put registers a new set of handlers listening to PUT requests for
// the specified URL pattern.
func (m *Mux) Put(pattern string, handlers ...interface{}) {
	m.add("PUT", pattern, handlers...)
}

// Patch registers a new set of handlers listening to PATCH requests for
// the specified URL pattern.
func (m *Mux) Patch(pattern string, handlers ...interface{}) {
	m.add("PATCH", pattern, handlers...)
}

// Delete registers a new set of handlers listening to DELETE requests for
// the specified URL pattern.
func (m *Mux) Delete(pattern string, handlers ...interface{}) {
	m.add("DELETE", pattern, handlers...)
}

// Options registers a new set of handlers listening to OPTIONS requests for
// the specified URL pattern.
func (m *Mux) Options(pattern string, handlers ...interface{}) {
	m.add("OPTIONS", pattern, handlers...)
}

// add registers a set of handlers for the given HTTP method ("" matching
// any method) and URL pattern.
func (m *Mux) add(method, pattern string, handlers ...interface{}) {
	if len(handlers) == 0 {
		panic("no handlers provided")
	}

	// validate the provided set of handlers
	clean := make([]Handler, 0, len(handlers))

	for _, h := range handlers {
		switch h := h.(type) {
		case Handler:
			clean = append(clean, h)
		case func(w ResponseWriter, r *Request):
			clean = append(clean, HandlerFunc(h))
		case http.Handler:
			clean = append(clean, httpHandler{h})
		case func(w http.ResponseWriter, r *http.Request):
			clean = append(clean, httpHandler{http.HandlerFunc(h)})
		default:
			panic("not a valid handler")
		}
	}

	m.routes = append(m.routes, newRoute(method, pattern, clean))
}

// newRoute initializes a new route.
func newRoute(method, pattern string, handlers []Handler) route {
	matcher, err := compileMatcher(pattern)
	if err != nil {
		panic(err)
	}

	return route{method, matcher, handlers}
}

// ServeRoboHTTP dispatches the request to matching routes registered with
// the Mux instance.
func (m *Mux) ServeRoboHTTP(w ResponseWriter, r *Request) {
	q := queue{nil, nil, m.routes}
	q.serveNext(w, r.Request)
}

// ServeHTTP dispatches the request to matching routes registered with
// the Mux instance.
func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	m.ServeRoboHTTP(w, &Request{Request: r})
}

// The route type describes a registered route.
type route struct {
	method   string
	matcher  pathMatcher
	handlers []Handler
}

var emptyParams = make(map[string]string)

// check tests whether the route matches a provided method and path. The
// parameter map will always be non-nil when the first is true.
func (r *route) check(method, path string) (bool, map[string]string) {
	if method != r.method && r.method != "" {
		return false, nil
	}

	ok, list := r.matcher.match(path, nil)
	if !ok {
		return false, nil
	}

	// don't build the actual parameter map unless we have to
	if len(list) == 0 {
		return true, emptyParams
	}

	params := make(map[string]string)
	for i := 0; i < len(list); i += 2 {
		params[list[i]] = list[i+1]
	}

	return true, params
}

// The queue type holds the routing state of an incoming request.
type queue struct {
	// remaining handlers, and parameter map, for the current route
	handlers []Handler
	params   map[string]string

	// remaining routes to be tested
	routes []route
}

// ServeNext attempts to serve an HTTP request using the next matching
// route/handler in the queue.
func (q *queue) serveNext(w ResponseWriter, hr *http.Request) {
	// does the current route still have handlers left?
	if len(q.handlers) > 0 {
		h := q.handlers[0]
		q.handlers = q.handlers[1:]

		h.ServeRoboHTTP(w, &Request{hr, nil, q.params, q})
		return
	}

	// look for the next matching route
	for len(q.routes) > 0 {
		r := q.routes[0]
		q.routes = q.routes[1:]

		// does this route match the request at hand?
		ok, params := r.check(hr.Method, hr.URL.Path)
		if !ok {
			continue
		}

		q.handlers = r.handlers[1:]
		q.params = params

		// invoke the route's first handler
		r.handlers[0].ServeRoboHTTP(w, &Request{hr, nil, q.params, q})
		return
	}

	// when we run out of routes, send a 404 message
	http.Error(w, "Not found.\n", 404)
}
<|endoftext|>


<commit_before>package http

import (
	"github.com/astaxie/beego/orm"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"time"
)

type Nqm_node struct {
	Id         int
	Nid        string
	Pid        string
	Pname      string
	Pname_abbr string
	Cid        string
	Cname      string
	Cname_abbr string
	Iid        int
	Iname      string
	Status     int
	Note       string
	Addtime    int
}

func getNQMNodes(rw http.ResponseWriter, req *http.Request) {
	var nodes = make(map[string]interface{})
	errors := []string{}
	var result = make(map[string]interface{})
	result["error"] = errors
	items := []interface{}{}
	o := orm.NewOrm()
	o.Using("gz_nqm")
	var NQMNodes []*Nqm_node
	_, err := o.Raw("SELECT nid, pname, cname, status, note FROM gz_nqm.nqm_node ORDER BY nid ASC").QueryRows(&NQMNodes)
	if err != nil {
		setError(err.Error(), result)
	} else {
		for _, node := range NQMNodes {
			idc := map[string]string{
				"idc":      node.Nid,
				"status":   strconv.Itoa(node.Status),
				"province": node.Pname,
				"city":     node.Cname,
				"note":     node.Note,
			}
			items = append(items, idc)
		}
	}
	result["items"] = items
	nodes["result"] = result
	nodes["count"] = len(items)
	setResponse(rw, nodes)
}

func getLatestTimestamp(tableName string, result map[string]interface{}) int64 {
	timestamp := int64(0)
	o := orm.NewOrm()
	o.Using("gz_nqm")
	sqlcmd := "SELECT mtime FROM gz_nqm." + tableName + " ORDER BY mtime DESC LIMIT 1"
	var rows []orm.Params
	num, err := o.Raw(sqlcmd).Values(&rows)
	if err != nil {
		setError(err.Error(), result)
	} else if num > 0 {
		mtime, err := strconv.Atoi(rows[0]["mtime"].(string))
		if err == nil {
			timestamp = int64(mtime)
		}
	}
	return timestamp
}

func getNearestTimestamp(tableName string, bound int64, result map[string]interface{}) int64 {
	timestamp := int64(0)
	o := orm.NewOrm()
	o.Using("gz_nqm")
	sqlcmd := "SELECT mtime FROM gz_nqm." + tableName
	sqlcmd += " WHERE mtime <= ? ORDER BY mtime DESC LIMIT 1"
	var rows []orm.Params
	num, err := o.Raw(sqlcmd, bound).Values(&rows)
	if err != nil {
		setError(err.Error(), result)
	} else if num > 0 {
		mtime, err := strconv.Atoi(rows[0]["mtime"].(string))
		if err == nil {
			timestamp = int64(mtime)
		}
	}
	return timestamp
}

func getSum(slice []float64) float64 {
	sum := float64(0)
	for _, number := range slice {
		sum += number
	}
	return sum
}

func getPacketLossAndAveragePingTime(tableName string, timestamp int64, result map[string]interface{}) map[string]interface{} {
	idc := map[string]interface{}{}
	if timestamp == int64(0) {
		return idc
	}
	sends := []float64{}
	receives := []float64{}
	averages := []float64{}

	o := orm.NewOrm()
	o.Using("gz_nqm")
	sqlcmd := "SELECT send, receive, avg FROM gz_nqm." + tableName + " WHERE mtime = ?"
	var rows []orm.Params
	num, err := o.Raw(sqlcmd, strconv.Itoa(int(timestamp))).Values(&rows)
	if err != nil {
		setError(err.Error(), result)
	} else if num > 0 {
		for _, row := range rows {
			send, err := strconv.ParseFloat(row["send"].(string), 64)
			if err != nil {
				setError(err.Error(), result)
			} else {
				sends = append(sends, send)
			}
			receive, err := strconv.ParseFloat(row["receive"].(string), 64)
			if err != nil {
				setError(err.Error(), result)
			} else {
				receives = append(receives, receive)
			}
			avg, err := strconv.ParseFloat(row["avg"].(string), 64)
			if err != nil {
				setError(err.Error(), result)
			} else {
				averages = append(averages, avg)
			}
		}
	}
	divider := float64(len(sends))
	if divider > 0 {
		packetLossRate := 1 - (getSum(receives) / getSum(sends))
		averagePingTimeMilliseconds := getSum(averages) / divider
		idc = map[string]interface{}{
			"packetLossRate":              packetLossRate,
			"averagePingTimeMilliseconds": averagePingTimeMilliseconds,
			"time": time.Unix(timestamp, 0).Format("2006-01-02 15:04"),
		}
	}
	return idc
}

func getNQMPacketLoss(rw http.ResponseWriter, req *http.Request) {
	var nodes = make(map[string]interface{})
	errors := []string{}
	var result = make(map[string]interface{})
	result["error"] = errors
	items := []interface{}{}

	tablesMap := map[string]interface{}{}
	o := orm.NewOrm()
	o.Using("gz_nqm")
	sqlcmd := "SELECT table_name FROM information_schema.tables WHERE table_name LIKE 'nqm_log_prober_%'"
	var rows []orm.Params
	num, err := o.Raw(sqlcmd).Values(&rows)
	if err != nil {
		setError(err.Error(), result)
	} else if num > 0 {
		for _, row := range rows {
			tablesMap[row["table_name"].(string)] = ""
		}
	}

	nids := []string{}
	pidMap := map[string]interface{}{}
	var NQMNodes []*Nqm_node
	_, err = o.Raw("SELECT nid, pid, status FROM gz_nqm.nqm_node ORDER BY nid ASC").QueryRows(&NQMNodes)
	if err != nil {
		setError(err.Error(), result)
	} else {
		for _, node := range NQMNodes {
			if node.Status > 0 {
				nids = append(nids, node.Nid)
				pidMap[node.Nid] = node.Pid
			}
		}
	}
	idcNames := []string{}
	for _, nid := range nids {
		if _, ok := tablesMap["nqm_log_prober_"+strings.Replace(nid, "-", "_", -1)]; ok {
			idcNames = append(idcNames, nid)
		}
	}
	sort.Strings(idcNames)
	for _, idcName := range idcNames {
		tableName := "nqm_log_" + strings.Replace(idcName, "-", "_", -1)
		timestamp := getLatestTimestamp(tableName, result)
		if timestamp > 0 {
			idc := getPacketLossAndAveragePingTime(tableName, timestamp, result)
			idc["nodeName"] = idcName
			idc["pid"] = pidMap[idcName]
			items = append(items, idc)
		}
	}
	result["items"] = items
	nodes["result"] = result
	nodes["count"] = len(items)
	rw.Header().Set("Access-Control-Allow-Origin", "*")
	setResponse(rw, nodes)
}

func getJaguar(rw http.ResponseWriter, req *http.Request) {
	var nodes = make(map[string]interface{})
	errors := []string{}
	var result = make(map[string]interface{})
	result["error"] = errors
	items := []interface{}{}
	timestamp := int64(0)
	loc, err := time.LoadLocation("Asia/Taipei")
	if err != nil {
		loc = time.Local
	}
	timeFormat := "2006-01-02 15:04"
	timeInput := req.URL.Query().Get("time")
	date, err := time.ParseInLocation(timeFormat, timeInput, loc)
	if err == nil {
		timestamp = date.Unix()
	}
	nodeMap := map[string]map[string]string{}
	nodeNames := []string{}
	o := orm.NewOrm()
	o.Using("gz_nqm")
	var NQMNodes []*Nqm_node
	_, err = o.Raw("SELECT nid, pname, cname, iname FROM gz_nqm.nqm_node ORDER BY nid ASC").QueryRows(&NQMNodes)
	if err != nil {
		setError(err.Error(), result)
	} else {
		for _, node := range NQMNodes {
			nodeName := node.Nid
			node := map[string]string{
				"node":     nodeName,
				"province": node.Pname,
				"city":     node.Cname,
				"isp":      node.Iname,
			}
			nodeMap[nodeName] = node
			nodeNames = append(nodeNames, nodeName)
		}
	}
	sqlcmd := "SELECT nid, ip, note FROM gz_nqm.nqm_dev WHERE nid IN ('"
	sqlcmd += strings.Join(nodeNames, "','") + "')"
	var rows []orm.Params
	_, err = o.Raw(sqlcmd).Values(&rows)
	if err != nil {
		setError(err.Error(), result)
	} else {
		for _, row := range rows {
			nodeName := row["nid"].(string)
			nodeMap[nodeName]["ip"] = row["ip"].(string)
			nodeMap[nodeName]["idc"] = row["note"].(string)
		}
	}
	for _, nodeName := range nodeNames {
		node := nodeMap[nodeName]
		item := map[string]interface{}{
			"node":     node["node"],
			"province": node["province"],
			"city":     node["city"],
			"isp":      node["isp"],
			"ip":       node["ip"],
			"idc":      node["idc"],
			"loss":     nil,
			"ping.ms":  nil,
			"time":     nil,
		}
		tableName := "nqm_log_" + strings.Replace(nodeName, "-", "_", -1)
		timestampLatest := getLatestTimestamp(tableName, result)
		if timestampLatest > 0 {
			timestampNearest := timestampLatest
			if timestamp > 0 {
				timestampNearest = getNearestTimestamp(tableName, timestamp, result)
			}
			resp := getPacketLossAndAveragePingTime(tableName, timestampNearest, result)
			if _, ok := resp["packetLossRate"]; ok {
				item["loss"] = resp["packetLossRate"]
				item["ping.ms"] = resp["averagePingTimeMilliseconds"]
				item["time"] = resp["time"]
			}
		}
		items = append(items, item)
	}
	result["items"] = items
	nodes["result"] = result
	nodes["count"] = len(items)
	rw.Header().Set("Access-Control-Allow-Origin", "*")
	setResponse(rw, nodes)
}

func configNQMRoutes() {
	http.HandleFunc("/api/nqm/nodes", getNQMNodes)
	http.HandleFunc("/api/nqm/loss", getNQMPacketLoss)
	http.HandleFunc("/api/nqm/jaguar", getJaguar)
}
<commit_msg>[OWL-1235][query] fix func getLatestTimestamp()<commit_after>package http

import (
	"github.com/astaxie/beego/orm"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"time"
)

type Nqm_node struct {
	Id         int
	Nid        string
	Pid        string
	Pname      string
	Pname_abbr string
	Cid        string
	Cname      string
	Cname_abbr string
	Iid        int
	Iname      string
	Status     int
	Note       string
	Addtime    int
}

func getNQMNodes(rw http.ResponseWriter, req *http.Request) {
	var nodes = make(map[string]interface{})
	errors := []string{}
	var result = make(map[string]interface{})
	result["error"] = errors
	items := []interface{}{}
	o := orm.NewOrm()
	o.Using("gz_nqm")
	var NQMNodes []*Nqm_node
	_, err := o.Raw("SELECT nid, pname, cname, status, note FROM gz_nqm.nqm_node ORDER BY nid ASC").QueryRows(&NQMNodes)
	if err != nil {
		setError(err.Error(), result)
	} else {
		for _, node := range NQMNodes {
			idc := map[string]string{
				"idc":      node.Nid,
				"status":   strconv.Itoa(node.Status),
				"province": node.Pname,
				"city":     node.Cname,
				"note":     node.Note,
			}
			items = append(items, idc)
		}
	}
	result["items"] = items
	nodes["result"] = result
	nodes["count"] = len(items)
	setResponse(rw, nodes)
}

func getLatestTimestamp(tableName string, result map[string]interface{}) int64 {
	timestamp := int64(0)
	o := orm.NewOrm()
	o.Using("gz_nqm")
	sqlcmd := "SELECT mtime FROM `gz_nqm`.`" + tableName + "` ORDER BY mtime DESC LIMIT 1"
	var rows []orm.Params
	num, err := o.Raw(sqlcmd).Values(&rows)
	if err != nil {
		setError(err.Error(), result)
	} else if num > 0 {
		mtime, err := strconv.Atoi(rows[0]["mtime"].(string))
		if err == nil {
			timestamp = int64(mtime)
		}
	}
	return timestamp
}

func getNearestTimestamp(tableName string, bound int64, result map[string]interface{}) int64 {
	timestamp := int64(0)
	o := orm.NewOrm()
	o.Using("gz_nqm")
	sqlcmd := "SELECT mtime FROM gz_nqm." + tableName
	sqlcmd += " WHERE mtime <= ? ORDER BY mtime DESC LIMIT 1"
	var rows []orm.Params
	num, err := o.Raw(sqlcmd, bound).Values(&rows)
	if err != nil {
		setError(err.Error(), result)
	} else if num > 0 {
		mtime, err := strconv.Atoi(rows[0]["mtime"].(string))
		if err == nil {
			timestamp = int64(mtime)
		}
	}
	return timestamp
}

func getSum(slice []float64) float64 {
	sum := float64(0)
	for _, number := range slice {
		sum += number
	}
	return sum
}

func getPacketLossAndAveragePingTime(tableName string, timestamp int64, result map[string]interface{}) map[string]interface{} {
	idc := map[string]interface{}{}
	if timestamp == int64(0) {
		return idc
	}
	sends := []float64{}
	receives := []float64{}
	averages := []float64{}

	o := orm.NewOrm()
	o.Using("gz_nqm")
	sqlcmd := "SELECT send, receive, avg FROM gz_nqm." + tableName + " WHERE mtime = ?"
	var rows []orm.Params
	num, err := o.Raw(sqlcmd, strconv.Itoa(int(timestamp))).Values(&rows)
	if err != nil {
		setError(err.Error(), result)
	} else if num > 0 {
		for _, row := range rows {
			send, err := strconv.ParseFloat(row["send"].(string), 64)
			if err != nil {
				setError(err.Error(), result)
			} else {
				sends = append(sends, send)
			}
			receive, err := strconv.ParseFloat(row["receive"].(string), 64)
			if err != nil {
				setError(err.Error(), result)
			} else {
				receives = append(receives, receive)
			}
			avg, err := strconv.ParseFloat(row["avg"].(string), 64)
			if err != nil {
				setError(err.Error(), result)
			} else {
				averages = append(averages, avg)
			}
		}
	}
	divider := float64(len(sends))
	if divider > 0 {
		packetLossRate := 1 - (getSum(receives) / getSum(sends))
		averagePingTimeMilliseconds := getSum(averages) / divider
		idc = map[string]interface{}{
			"packetLossRate":              packetLossRate,
			"averagePingTimeMilliseconds": averagePingTimeMilliseconds,
			"time": time.Unix(timestamp, 0).Format("2006-01-02 15:04"),
		}
	}
	return idc
}

func getNQMPacketLoss(rw http.ResponseWriter, req *http.Request) {
	var nodes = make(map[string]interface{})
	errors := []string{}
	var result = make(map[string]interface{})
	result["error"] = errors
	items := []interface{}{}

	tablesMap := map[string]interface{}{}
	o := orm.NewOrm()
	o.Using("gz_nqm")
	sqlcmd := "SELECT table_name FROM information_schema.tables WHERE table_name LIKE 'nqm_log_prober_%'"
	var rows []orm.Params
	num, err := o.Raw(sqlcmd).Values(&rows)
	if err != nil {
		setError(err.Error(), result)
	} else if num > 0 {
		for _, row := range rows {
			tablesMap[row["table_name"].(string)] = ""
		}
	}

	nids := []string{}
	pidMap := map[string]interface{}{}
	var NQMNodes []*Nqm_node
	_, err = o.Raw("SELECT nid, pid, status FROM gz_nqm.nqm_node ORDER BY nid ASC").QueryRows(&NQMNodes)
	if err != nil {
		setError(err.Error(), result)
	} else {
		for _, node := range NQMNodes {
			if node.Status > 0 {
				nids = append(nids, node.Nid)
				pidMap[node.Nid] = node.Pid
			}
		}
	}
	idcNames := []string{}
	for _, nid := range nids {
		if _, ok := tablesMap["nqm_log_prober_"+strings.Replace(nid, "-", "_", -1)]; ok {
			idcNames = append(idcNames, nid)
		}
	}
	sort.Strings(idcNames)
	for _, idcName := range idcNames {
		tableName := "nqm_log_" + strings.Replace(idcName, "-", "_", -1)
		timestamp := getLatestTimestamp(tableName, result)
		if timestamp > 0 {
			idc := getPacketLossAndAveragePingTime(tableName, timestamp, result)
			idc["nodeName"] = idcName
			idc["pid"] = pidMap[idcName]
			items = append(items, idc)
		}
	}
	result["items"] = items
	nodes["result"] = result
	nodes["count"] = len(items)
	rw.Header().Set("Access-Control-Allow-Origin", "*")
	setResponse(rw, nodes)
}

func getJaguar(rw http.ResponseWriter, req *http.Request) {
	var nodes = make(map[string]interface{})
	errors := []string{}
	var result = make(map[string]interface{})
	result["error"] = errors
	items := []interface{}{}
	timestamp := int64(0)
	loc, err := time.LoadLocation("Asia/Taipei")
	if err != nil {
		loc = time.Local
	}
	timeFormat := "2006-01-02 15:04"
	timeInput := req.URL.Query().Get("time")
	date, err := time.ParseInLocation(timeFormat, timeInput, loc)
	if err == nil {
		timestamp = date.Unix()
	}
	nodeMap := map[string]map[string]string{}
	nodeNames := []string{}
	o := orm.NewOrm()
	o.Using("gz_nqm")
	var NQMNodes []*Nqm_node
	_, err = o.Raw("SELECT nid, pname, cname, iname FROM gz_nqm.nqm_node ORDER BY nid ASC").QueryRows(&NQMNodes)
	if err != nil {
		setError(err.Error(), result)
	} else {
		for _, node := range NQMNodes {
			nodeName := node.Nid
			node := map[string]string{
				"node":     nodeName,
				"province": node.Pname,
				"city":     node.Cname,
				"isp":      node.Iname,
			}
			nodeMap[nodeName] = node
			nodeNames = append(nodeNames, nodeName)
		}
	}
	sqlcmd := "SELECT nid, ip, note FROM gz_nqm.nqm_dev WHERE nid IN ('"
	sqlcmd += strings.Join(nodeNames, "','") + "')"
	var rows []orm.Params
	_, err = o.Raw(sqlcmd).Values(&rows)
	if err != nil {
		setError(err.Error(), result)
	} else {
		for _, row := range rows {
			nodeName := row["nid"].(string)
			nodeMap[nodeName]["ip"] = row["ip"].(string)
			nodeMap[nodeName]["idc"] = row["note"].(string)
		}
	}
	for _, nodeName := range nodeNames {
		node := nodeMap[nodeName]
		item := map[string]interface{}{
			"node":     node["node"],
			"province": node["province"],
			"city":     node["city"],
			"isp":      node["isp"],
			"ip":       node["ip"],
			"idc":      node["idc"],
			"loss":     nil,
			"ping.ms":  nil,
			"time":     nil,
		}
		tableName := "nqm_log_" + strings.Replace(nodeName, "-", "_", -1)
		timestampLatest := getLatestTimestamp(tableName, result)
		if timestampLatest > 0 {
			timestampNearest := timestampLatest
			if timestamp > 0 {
				timestampNearest = getNearestTimestamp(tableName, timestamp, result)
			}
			resp := getPacketLossAndAveragePingTime(tableName, timestampNearest, result)
			if _, ok := resp["packetLossRate"]; ok {
				item["loss"] = resp["packetLossRate"]
				item["ping.ms"] = resp["averagePingTimeMilliseconds"]
				item["time"] = resp["time"]
			}
		}
		items = append(items, item)
	}
	result["items"] = items
	nodes["result"] = result
	nodes["count"] = len(items)
	rw.Header().Set("Access-Control-Allow-Origin", "*")
	setResponse(rw, nodes)
}

func configNQMRoutes() {
	http.HandleFunc("/api/nqm/nodes", getNQMNodes)
	http.HandleFunc("/api/nqm/loss", getNQMPacketLoss)
	http.HandleFunc("/api/nqm/jaguar", getJaguar)
}
<|endoftext|>


<commit_before>package memberlist

import (
	"bytes"
	"encoding/binary"
	"encoding/gob"
	"log"
	"net"
)

const (
	pingMsg = iota
	indirectPingMsg
	ackRespMsg
	suspectMsg
	aliveMsg
	deadMsg
	pushPullMsg
	compoundMsg
)

const (
	udpBufSize             = 65536
	udpSendBuf             = 1400
	compoundHeaderOverhead = 2 // Assumed header overhead
	compoundOverhead       = 2 // Assumed overhead per entry in compoundHeader
)

// ping request sent directly to node
type ping struct {
	SeqNo uint32
}

// indirect ping sent to an indirect ndoe
type indirectPingReq struct {
	SeqNo  uint32
	Target []byte
}

// ack response is sent for a ping
type ackResp struct {
	SeqNo uint32
}

// suspect is broadcast when we suspect a node is dead
type suspect struct {
	Incarnation uint32
	Node        string
}

// alive is broadcast when we know a node is alive.
// Overloaded for nodes joining
type alive struct {
	Incarnation uint32
	Node        string
	Addr        []byte
}

// dead is broadcast when we confirm a node is dead
// Overloaded for nodes leaving
type dead struct {
	Incarnation uint32
	Node        string
}

// pushPullHeader is used to inform the
// otherside how many states we are transfering
type pushPullHeader struct {
	Nodes int
}

// pushNodeState is used for pushPullReq when we are
// transfering out node states
type pushNodeState struct {
	Name        string
	Addr        []byte
	Incarnation uint32
	State       int
}

// tcpListen listens for and handles incoming connections
func (m *Memberlist) tcpListen() {
	for {
		conn, err := m.tcpListener.AcceptTCP()
		if err != nil {
			if m.shutdown {
				break
			}
			log.Printf("[ERR] Error accepting TCP connection: %s", err)
			continue
		}
		go m.handleConn(conn)
	}
}

// handleConn handles a single incoming TCP connection
func (m *Memberlist) handleConn(conn *net.TCPConn) {
	defer conn.Close()
	buf := []byte{0}

	// Read the message type
	if _, err := conn.Read(buf); err != nil {
		log.Printf("[ERR] Failed to read the msg type: %s", err)
		return
	}
	msgType := uint8(buf[0])

	// Quit if not push/pull
	if msgType != pushPullMsg {
		log.Printf("[ERR] Invalid TCP request type (%d)", msgType)
		return
	}

	// Read the push/pull header
	var header pushPullHeader
	dec := gob.NewDecoder(conn)
	if err := dec.Decode(&header); err != nil {
		log.Printf("[ERR] Failed to decode Push/Pull header: %s", err)
		return
	}

	// Allocate space for the transfer
	remoteNodes := make([]pushNodeState, header.Nodes)

	// Try to decode all the states
	for i := 0; i < header.Nodes; i++ {
		if err := dec.Decode(&remoteNodes[i]); err != nil {
			log.Printf("[ERR] Failed to decode Push/Pull state (idx: %d / %d): %s", i+1, header.Nodes, err)
			return
		}
	}

	// Prepare the local node state
	m.nodeLock.RLock()
	localNodes := make([]pushNodeState, len(m.nodes))
	for idx, n := range m.nodes {
		localNodes[idx].Name = n.Name
		localNodes[idx].Addr = n.Addr
		localNodes[idx].Incarnation = n.Incarnation
		localNodes[idx].State = n.State
	}
	m.nodeLock.RUnlock()

	// Send our node state
	header.Nodes = len(localNodes)
	enc := gob.NewEncoder(conn)

	// Send the push/pull indicator
	conn.Write([]byte{pushPullMsg})

	if err := enc.Encode(&header); err != nil {
		log.Printf("[ERR] Failed to send Push/Pull header: %s", err)
		goto AFTER_SEND
	}
	for i := 0; i < header.Nodes; i++ {
		if err := enc.Encode(&localNodes[i]); err != nil {
			log.Printf("[ERR] Failed to send Push/Pull state (idx: %d / %d): %s", i+1, header.Nodes, err)
			goto AFTER_SEND
		}
	}

AFTER_SEND:
	// Allow the local state to be updated
	m.mergeState(remoteNodes)
}

// udpListen listens for and handles incoming UDP packets
func (m *Memberlist) udpListen() {
	mainBuf := make([]byte, udpBufSize)
	var n int
	var addr net.Addr
	var err error
	for {
		// Reset buffer
		buf := mainBuf[0:udpBufSize]

		// Read a packet
		n, addr, err = m.udpListener.ReadFrom(buf)
		if err != nil {
			if m.shutdown {
				break
			}
			log.Printf("[ERR] Error reading UDP packet: %s", err)
			continue
		}

		// Check the length
		if n < 1 {
			log.Printf("[ERR] UDP packet too short (%d bytes). From: %s", len(buf), addr)
			continue
		}

		// Handle the command
		m.handleCommand(buf[:n], addr)
	}
}

func (m *Memberlist) handleCommand(buf []byte, from net.Addr) {
	// Decode the message type
	msgType := uint8(buf[0])
	buf = buf[1:]

	// Switch on the msgType
	switch msgType {
	case compoundMsg:
		m.handleCompound(buf, from)
	case pingMsg:
		m.handlePing(buf, from)
	case indirectPingMsg:
		m.handleIndirectPing(buf, from)
	case ackRespMsg:
		m.handleAck(buf, from)
	case suspectMsg:
		m.handleSuspect(buf, from)
	case aliveMsg:
		m.handleAlive(buf, from)
	case deadMsg:
		m.handleDead(buf, from)
	default:
		log.Printf("[ERR] UDP msg type (%d) not supported. From: %s", msgType, from)
	}
}

func (m *Memberlist) handleCompound(buf []byte, from net.Addr) {
	// Ensure we have at least the length byte
	if len(buf) < 1 {
		log.Printf("[ERR] Failed to decode compound request: missing len prefix")
		return
	}
	numParts := uint8(buf[0])
	buf = buf[1:]

	// Check we have enough bytes
	if len(buf) < int(numParts*2) {
		log.Printf("[ERR] Failed to decode compound request: truncated len slice")
		return
	}

	// Decode the lengths
	lengths := make([]uint16, numParts)
	for i := 0; i < int(numParts); i++ {
		lengths[i] = binary.BigEndian.Uint16(buf[i*2 : i*2+2])
	}
	buf = buf[numParts*2:]

	// Handle each message
	for _, msgLen := range lengths {
		if len(buf) < int(msgLen) {
			log.Printf("[ERR] Failed to decode compound request: truncated message (%d / %d)", len(buf), msgLen)
			return
		}

		// Extract the slice, seek past on the buffer
		slice := buf[:msgLen]
		buf = buf[msgLen:]

		// Handle the command
		m.handleCommand(slice, from)
	}
}

func (m *Memberlist) handlePing(buf []byte, from net.Addr) {
	var p ping
	if err := decode(buf, &p); err != nil {
		log.Printf("[ERR] Failed to decode ping request: %s", err)
		return
	}
	ack := ackResp{p.SeqNo}
	if err := m.encodeAndSendMsg(from, ackRespMsg, &ack); err != nil {
		log.Printf("[ERR] Failed to send ack: %s", err)
	}
}

func (m *Memberlist) handleIndirectPing(buf []byte, from net.Addr) {
	var ind indirectPingReq
	if err := decode(buf, &ind); err != nil {
		log.Printf("[ERR] Failed to decode indirect ping request: %s", err)
		return
	}

	// Send a ping to the correct host
	localSeqNo := m.nextSeqNo()
	ping := ping{SeqNo: localSeqNo}
	destAddr := &net.UDPAddr{IP: ind.Target, Port: m.config.UDPPort}

	// Setup a response handler to relay the ack
	respHandler := func() {
		ack := ackResp{ind.SeqNo}
		if err := m.encodeAndSendMsg(from, ackRespMsg, &ack); err != nil {
			log.Printf("[ERR] Failed to forward ack: %s", err)
		}
	}
	m.setAckHandler(localSeqNo, respHandler, m.config.RTT)

	// Send the ping
	if err := m.encodeAndSendMsg(destAddr, pingMsg, &ping); err != nil {
		log.Printf("[ERR] Failed to send ping: %s", err)
	}
}

func (m *Memberlist) handleAck(buf []byte, from net.Addr) {
	var ack ackResp
	if err := decode(buf, &ack); err != nil {
		log.Printf("[ERR] Failed to decode ack response: %s", err)
		return
	}
	m.invokeAckHandler(ack.SeqNo)
}

func (m *Memberlist) handleSuspect(buf []byte, from net.Addr) {
	var sus suspect
	if err := decode(buf, &sus); err != nil {
		log.Printf("[ERR] Failed to decode suspect message: %s", err)
		return
	}
	m.suspectNode(&sus)
}

func (m *Memberlist) handleAlive(buf []byte, from net.Addr) {
	var live alive
	if err := decode(buf, &live); err != nil {
		log.Printf("[ERR] Failed to decode alive message: %s", err)
		return
	}
	m.aliveNode(&live)
}

func (m *Memberlist) handleDead(buf []byte, from net.Addr) {
	var d dead
	if err := decode(buf, &d); err != nil {
		log.Printf("[ERR] Failed to decode dead message: %s", err)
		return
	}
	m.deadNode(&d)
}

// encodeAndSendMsg is used to combine the encoding and sending steps
func (m *Memberlist) encodeAndSendMsg(to net.Addr, msgType int, msg interface{}) error {
	out, err := encode(msgType, msg)
	if err != nil {
		return err
	}
	if err := m.sendMsg(to, out); err != nil {
		return err
	}
	return nil
}

// sendMsg is used to send a UDP message to another host. It will opportunistically
// create a compoundMsg and piggy back other broadcasts
func (m *Memberlist) sendMsg(to net.Addr, msg *bytes.Buffer) error {
	// Check if we can piggy back any messages
	bytesAvail := udpSendBuf - msg.Len() - compoundHeaderOverhead
	extra := m.getBroadcasts(compoundOverhead, bytesAvail)

	// If there is no extra, then just use the raw
	if len(extra) == 0 {
		return m.rawSendMsg(to, msg)
	}

	// Create a local buffer
	var buf bytes.Buffer

	// Write out the type
	(&buf).WriteByte(uint8(compoundMsg))

	// Write out the number of message
	(&buf).WriteByte(uint8(1 + len(extra)))

	// Add the message lengths
	binary.Write(&buf, binary.BigEndian, uint16(msg.Len()))
	for _, b := range extra {
		binary.Write(&buf, binary.BigEndian, uint16(b.Len()))
	}

	// Append the messages
	buf.Write(msg.Bytes())
	for _, b := range extra {
		buf.Write(b.Bytes())
	}

	// Send the message
	return m.rawSendMsg(to, msg)
}

// rawSendMsg is used to send a UDP message to another host without modification
func (m *Memberlist) rawSendMsg(to net.Addr, msg *bytes.Buffer) error {
	_, err := m.udpListener.WriteTo(msg.Bytes(), to)
	return err
}
<commit_msg>Simplify sendMsg<commit_after>package memberlist

import (
	"bytes"
	"encoding/binary"
	"encoding/gob"
	"log"
	"net"
)

const (
	pingMsg = iota
	indirectPingMsg
	ackRespMsg
	suspectMsg
	aliveMsg
	deadMsg
	pushPullMsg
	compoundMsg
)

const (
	udpBufSize             = 65536
	udpSendBuf             = 1400
	compoundHeaderOverhead = 2 // Assumed header overhead
	compoundOverhead       = 2 // Assumed overhead per entry in compoundHeader
)

// ping request sent directly to node
type ping struct {
	SeqNo uint32
}

// indirect ping sent to an indirect ndoe
type indirectPingReq struct {
	SeqNo  uint32
	Target []byte
}

// ack response is sent for a ping
type ackResp struct {
	SeqNo uint32
}

// suspect is broadcast when we suspect a node is dead
type suspect struct {
	Incarnation uint32
	Node        string
}

// alive is broadcast when we know a node is alive.
// Overloaded for nodes joining
type alive struct {
	Incarnation uint32
	Node        string
	Addr        []byte
}

// dead is broadcast when we confirm a node is dead
// Overloaded for nodes leaving
type dead struct {
	Incarnation uint32
	Node        string
}

// pushPullHeader is used to inform the
// otherside how many states we are transfering
type pushPullHeader struct {
	Nodes int
}

// pushNodeState is used for pushPullReq when we are
// transfering out node states
type pushNodeState struct {
	Name        string
	Addr        []byte
	Incarnation uint32
	State       int
}

// tcpListen listens for and handles incoming connections
func (m *Memberlist) tcpListen() {
	for {
		conn, err := m.tcpListener.AcceptTCP()
		if err != nil {
			if m.shutdown {
				break
			}
			log.Printf("[ERR] Error accepting TCP connection: %s", err)
			continue
		}
		go m.handleConn(conn)
	}
}

// handleConn handles a single incoming TCP connection
func (m *Memberlist) handleConn(conn *net.TCPConn) {
	defer conn.Close()
	buf := []byte{0}

	// Read the message type
	if _, err := conn.Read(buf); err != nil {
		log.Printf("[ERR] Failed to read the msg type: %s", err)
		return
	}
	msgType := uint8(buf[0])

	// Quit if not push/pull
	if msgType != pushPullMsg {
		log.Printf("[ERR] Invalid TCP request type (%d)", msgType)
		return
	}

	// Read the push/pull header
	var header pushPullHeader
	dec := gob.NewDecoder(conn)
	if err := dec.Decode(&header); err != nil {
		log.Printf("[ERR] Failed to decode Push/Pull header: %s", err)
		return
	}

	// Allocate space for the transfer
	remoteNodes := make([]pushNodeState, header.Nodes)

	// Try to decode all the states
	for i := 0; i < header.Nodes; i++ {
		if err := dec.Decode(&remoteNodes[i]); err != nil {
			log.Printf("[ERR] Failed to decode Push/Pull state (idx: %d / %d): %s", i+1, header.Nodes, err)
			return
		}
	}

	// Prepare the local node state
	m.nodeLock.RLock()
	localNodes := make([]pushNodeState, len(m.nodes))
	for idx, n := range m.nodes {
		localNodes[idx].Name = n.Name
		localNodes[idx].Addr = n.Addr
		localNodes[idx].Incarnation = n.Incarnation
		localNodes[idx].State = n.State
	}
	m.nodeLock.RUnlock()

	// Send our node state
	header.Nodes = len(localNodes)
	enc := gob.NewEncoder(conn)

	// Send the push/pull indicator
	conn.Write([]byte{pushPullMsg})

	if err := enc.Encode(&header); err != nil {
		log.Printf("[ERR] Failed to send Push/Pull header: %s", err)
		goto AFTER_SEND
	}
	for i := 0; i < header.Nodes; i++ {
		if err := enc.Encode(&localNodes[i]); err != nil {
			log.Printf("[ERR] Failed to send Push/Pull state (idx: %d / %d): %s", i+1, header.Nodes, err)
			goto AFTER_SEND
		}
	}

AFTER_SEND:
	// Allow the local state to be updated
	m.mergeState(remoteNodes)
}

// udpListen listens for and handles incoming UDP packets
func (m *Memberlist) udpListen() {
	mainBuf := make([]byte, udpBufSize)
	var n int
	var addr net.Addr
	var err error
	for {
		// Reset buffer
		buf := mainBuf[0:udpBufSize]

		// Read a packet
		n, addr, err = m.udpListener.ReadFrom(buf)
		if err != nil {
			if m.shutdown {
				break
			}
			log.Printf("[ERR] Error reading UDP packet: %s", err)
			continue
		}

		// Check the length
		if n < 1 {
			log.Printf("[ERR] UDP packet too short (%d bytes). From: %s", len(buf), addr)
			continue
		}

		// Handle the command
		m.handleCommand(buf[:n], addr)
	}
}

func (m *Memberlist) handleCommand(buf []byte, from net.Addr) {
	// Decode the message type
	msgType := uint8(buf[0])
	buf = buf[1:]

	// Switch on the msgType
	switch msgType {
	case compoundMsg:
		m.handleCompound(buf, from)
	case pingMsg:
		m.handlePing(buf, from)
	case indirectPingMsg:
		m.handleIndirectPing(buf, from)
	case ackRespMsg:
		m.handleAck(buf, from)
	case suspectMsg:
		m.handleSuspect(buf, from)
	case aliveMsg:
		m.handleAlive(buf, from)
	case deadMsg:
		m.handleDead(buf, from)
	default:
		log.Printf("[ERR] UDP msg type (%d) not supported. From: %s", msgType, from)
	}
}

func (m *Memberlist) handleCompound(buf []byte, from net.Addr) {
	// Ensure we have at least the length byte
	if len(buf) < 1 {
		log.Printf("[ERR] Failed to decode compound request: missing len prefix")
		return
	}
	numParts := uint8(buf[0])
	buf = buf[1:]

	// Check we have enough bytes
	if len(buf) < int(numParts*2) {
		log.Printf("[ERR] Failed to decode compound request: truncated len slice")
		return
	}

	// Decode the lengths
	lengths := make([]uint16, numParts)
	for i := 0; i < int(numParts); i++ {
		lengths[i] = binary.BigEndian.Uint16(buf[i*2 : i*2+2])
	}
	buf = buf[numParts*2:]

	// Handle each message
	for _, msgLen := range lengths {
		if len(buf) < int(msgLen) {
			log.Printf("[ERR] Failed to decode compound request: truncated message (%d / %d)", len(buf), msgLen)
			return
		}

		// Extract the slice, seek past on the buffer
		slice := buf[:msgLen]
		buf = buf[msgLen:]

		// Handle the command
		m.handleCommand(slice, from)
	}
}

func (m *Memberlist) handlePing(buf []byte, from net.Addr) {
	var p ping
	if err := decode(buf, &p); err != nil {
		log.Printf("[ERR] Failed to decode ping request: %s", err)
		return
	}
	ack := ackResp{p.SeqNo}
	if err := m.encodeAndSendMsg(from, ackRespMsg, &ack); err != nil {
		log.Printf("[ERR] Failed to send ack: %s", err)
	}
}

func (m *Memberlist) handleIndirectPing(buf []byte, from net.Addr) {
	var ind indirectPingReq
	if err := decode(buf, &ind); err != nil {
		log.Printf("[ERR] Failed to decode indirect ping request: %s", err)
		return
	}

	// Send a ping to the correct host
	localSeqNo := m.nextSeqNo()
	ping := ping{SeqNo: localSeqNo}
	destAddr := &net.UDPAddr{IP: ind.Target, Port: m.config.UDPPort}

	// Setup a response handler to relay the ack
	respHandler := func() {
		ack := ackResp{ind.SeqNo}
		if err := m.encodeAndSendMsg(from, ackRespMsg, &ack); err != nil {
			log.Printf("[ERR] Failed to forward ack: %s", err)
		}
	}
	m.setAckHandler(localSeqNo, respHandler, m.config.RTT)

	// Send the ping
	if err := m.encodeAndSendMsg(destAddr, pingMsg, &ping); err != nil {
		log.Printf("[ERR] Failed to send ping: %s", err)
	}
}

func (m *Memberlist) handleAck(buf []byte, from net.Addr) {
	var ack ackResp
	if err := decode(buf, &ack); err != nil {
		log.Printf("[ERR] Failed to decode ack response: %s", err)
		return
	}
	m.invokeAckHandler(ack.SeqNo)
}

func (m *Memberlist) handleSuspect(buf []byte, from net.Addr) {
	var sus suspect
	if err := decode(buf, &sus); err != nil {
		log.Printf("[ERR] Failed to decode suspect message: %s", err)
		return
	}
	m.suspectNode(&sus)
}

func (m *Memberlist) handleAlive(buf []byte, from net.Addr) {
	var live alive
	if err := decode(buf, &live); err != nil {
		log.Printf("[ERR] Failed to decode alive message: %s", err)
		return
	}
	m.aliveNode(&live)
}

func (m *Memberlist) handleDead(buf []byte, from net.Addr) {
	var d dead
	if err := decode(buf, &d); err != nil {
		log.Printf("[ERR] Failed to decode dead message: %s", err)
		return
	}
	m.deadNode(&d)
}

// encodeAndSendMsg is used to combine the encoding and sending steps
func (m *Memberlist) encodeAndSendMsg(to net.Addr, msgType int, msg interface{}) error {
	out, err := encode(msgType, msg)
	if err != nil {
		return err
	}
	if err := m.sendMsg(to, out); err != nil {
		return err
	}
	return nil
}

// sendMsg is used to send a UDP message to another host. It will opportunistically
// create a compoundMsg and piggy back other broadcasts
func (m *Memberlist) sendMsg(to net.Addr, msg *bytes.Buffer) error {
	// Check if we can piggy back any messages
	bytesAvail := udpSendBuf - msg.Len() - compoundHeaderOverhead
	extra := m.getBroadcasts(compoundOverhead, bytesAvail)

	// Fast path if nothing to piggypack
	if len(extra) == 0 {
		return m.rawSendMsg(to, msg)
	}

	// Join all the messages
	msgs := make([]*bytes.Buffer, 0, 1+len(extra))
	msgs = append(msgs, msg)
	msgs = append(msgs, extra...)

	// Create a compound message
	compound := makeCompoundMessage(msgs)

	// Send the message
	return m.rawSendMsg(to, compound)
}

// rawSendMsg is used to send a UDP message to another host without modification
func (m *Memberlist) rawSendMsg(to net.Addr, msg *bytes.Buffer) error {
	_, err := m.udpListener.WriteTo(msg.Bytes(), to)
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"time"
)

// New creates a new issue. If `arg` is numeric, it refers to an existing issue
// number. If it is a string it is the subject of a new issue to be created.
// If it is empty or not present, then we prompt you to type something.
func New(arg string) {
	if CurrentBranch() != "master" {
		fmt.Printf("The current branch is: %#v\n", CurrentBranch())
		fmt.Printf("You need to switch to master to call new.\n")
		fmt.Printf("Switch to master? (enter to continue, Ctrl+C to abort)\n")
		_, _ = bufio.NewReader(os.Stdin).ReadString('\n')

		if err := Run("git", "checkout", "master"); err != nil {
			os.Exit(1)
		}
		fmt.Printf("\n")
	}

	issueNumber, err := strconv.ParseInt(arg, 10, 32)
	if err != nil {
		issueSubject := arg
		for issueSubject == "" {
			fmt.Printf("Whacha tryin to do? I'll create an issue for you.\n")
			fmt.Printf("issue subject: ")
			issueSubject, _ = bufio.NewReader(os.Stdin).ReadString('\n')
			issueSubject = strings.TrimSpace(issueSubject)
		}

		// create a new issue with the given subject
		resp, err := GithubApi("POST",
			fmt.Sprintf("/repos/%s/issues", GithubRepo()),
			map[string]interface{}{
				"title":    issueSubject,
				"assignee": CurrentUser(),
				"labels":   []string{"wip"},
			})
		if err != nil {
			log.Fatalf("create issue: %s", err)
		}
		issueNumber = int64(resp["number"].(float64))
		fmt.Printf("created issue #%d\n", issueNumber)
	}

	branchName := fmt.Sprintf("pr-%d", issueNumber)
	if err := Run("git", "checkout", "-b", branchName); err != nil {
		os.Exit(1)
	}

	if err := Run("git", "push", "-u", "origin", branchName); err != nil {
		os.Exit(1)
	}

	fmt.Printf("Next Steps:\n" +
		" - hack!\n" +
		" - git add / git commit / git push\n" +
		" - cl ptal\n")
}
<commit_msg>fix typo<commit_after>package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
)

// New creates a new issue. If `arg` is numeric, it refers to an existing issue
// number. If it is a string it is the subject of a new issue to be created.
// If it is empty or not present, then we prompt you to type something.
func New(arg string) {
	if CurrentBranch() != "master" {
		fmt.Printf("The current branch is: %#v\n", CurrentBranch())
		fmt.Printf("You need to switch to master to call new.\n")
		fmt.Printf("Switch to master? (enter to continue, Ctrl+C to abort)\n")
		_, _ = bufio.NewReader(os.Stdin).ReadString('\n')

		if err := Run("git", "checkout", "master"); err != nil {
			os.Exit(1)
		}
		fmt.Printf("\n")
	}

	issueNumber, err := strconv.ParseInt(arg, 10, 32)
	if err != nil {
		issueSubject := arg
		for issueSubject == "" {
			fmt.Printf("Whacha tryin to do? I'll create an issue for you.\n")
			fmt.Printf("issue subject: ")
			issueSubject, _ = bufio.NewReader(os.Stdin).ReadString('\n')
			issueSubject = strings.TrimSpace(issueSubject)
		}

		// create a new issue with the given subject
		resp, err := GithubApi("POST",
			fmt.Sprintf("/repos/%s/issues", GithubRepo()),
			map[string]interface{}{
				"title":    issueSubject,
				"assignee": CurrentUser(),
				"labels":   []string{"wip"},
			})
		if err != nil {
			log.Fatalf("create issue: %s", err)
		}
		issueNumber = int64(resp["number"].(float64))
		fmt.Printf("created issue #%d\n", issueNumber)
	}

	branchName := fmt.Sprintf("pr-%d", issueNumber)
	if err := Run("git", "checkout", "-b", branchName); err != nil {
		os.Exit(1)
	}

	if err := Run("git", "push", "-u", "origin", branchName); err != nil {
		os.Exit(1)
	}

	fmt.Printf("Next Steps:\n" +
		" - hack!\n" +
		" - git add / git commit / git push\n" +
		" - cl ptal\n")
}
<|endoftext|>


<commit_before>package memdump

import (
	"errors"
	"io"
)

// delim is used to recognize the end of the memory dump
var delim = []byte{
	130, 4, 133, 49, 108, 178, 125, 95,
	35, 126, 41, 129, 229, 48, 6, 94,
	69, 20, 194, 236, 79, 156, 67, 100,
	239, 152, 149, 93, 91, 56, 8, 183,
}

// ErrBufferTooSmall is returned by delimetedReader if the input buffer
// is smaller than the length of the delimeter
var ErrBufferTooSmall = errors.New(
	"cannot read into buffer of size less than 32 bytes (due to delim)")

// ErrUnexpectedEOF is returned by delimetedReader if EOF is reached
// before finding a delimeter
var ErrUnexpectedEOF = errors.New(
	"got EOF before finding the delimeter")

// delimetedReader reads until the delim above is reached
type delimitedReader struct {
	r       io.Reader
	buf     []byte
	atdelim bool
	eof     bool
}

// newDelimetedReader creates a delimited reader
func newDelimitedReader(r io.Reader) *delimitedReader {
	return &delimitedReader{
		r: r,
	}
}

// Read extracts bytes from the underlying reader and returns EOF
// when the delim is reached.
func (r *delimitedReader) Read(dest []byte) (int, error) {
	if len(dest) < len(delim) {
		return 0, ErrBufferTooSmall
	}

	// check whether we are already at delim
	if r.atdelim {
		return 0, io.EOF
	}

	// first copy from buf
	var nbuf int
	if len(r.buf) > 0 {
		nbuf = copy(dest, r.buf)
		if nbuf == len(r.buf) {
			r.buf = r.buf[:0]
		} else {
			r.buf = r.buf[nbuf:]
		}
	}

	// fill the rest of dest
	var nread int
	if nbuf < len(dest) && !r.eof {
		var err error
		nread, err = r.r.Read(dest[nbuf:])
		if err == io.EOF {
			r.eof = true
		} else if err != nil {
			return nbuf + nread, err
		}
	}

	// should be impossible
	if nread == 0 && nbuf == 0 && !r.eof {
		panic("read zero bytes from both buffer and reader")
	}

	// look for the delimeter
	var errout error
	var nout, nskip, state int
	for i, b := range dest[:nbuf+nread] {
		if b != delim[state] {
			state = 0
		}
		// do not use "else" here because we updated state above
		if b == delim[state] {
			state++
			if state == len(delim) {
				r.atdelim = true
				errout = io.EOF
				nskip = len(delim)
				break
			}
		} else {
			nout = i + 1
		}
	}

	// if we got EOF but no delimeter then we have an error
	if r.eof && !r.atdelim {
		return nout, ErrUnexpectedEOF
	}

	// update buffer
	r.buf = append(r.buf[:0], dest[nout+nskip:nbuf+nread]...)
	return nout, errout
}

// Next proceeds to the next segment and returns true if there is another
// segment to extract.
func (r *delimitedReader) Next() bool {
	if len(r.buf) > 0 {
		r.atdelim = false
		return true
	}
	if r.eof {
		return false
	}
	r.buf = make([]byte, 64)
	n, err := r.Read(r.buf)
	if err == io.EOF {
		r.eof = true
		return n > 0
	}
	return true
}
<commit_msg>simplify Next()<commit_after>package memdump

import (
	"errors"
	"io"
)

// delim is used to recognize the end of the memory dump
var delim = []byte{
	130, 4, 133, 49, 108, 178, 125, 95,
	35, 126, 41, 129, 229, 48, 6, 94,
	69, 20, 194, 236, 79, 156, 67, 100,
	239, 152, 149, 93, 91, 56, 8, 183,
}

// ErrBufferTooSmall is returned by delimetedReader if the input buffer
// is smaller than the length of the delimeter
var ErrBufferTooSmall = errors.New(
	"cannot read into buffer of size less than 32 bytes (due to delim)")

// ErrUnexpectedEOF is returned by delimetedReader if EOF is reached
// before finding a delimeter
var ErrUnexpectedEOF = errors.New(
	"got EOF before finding the delimeter")

// delimetedReader reads until the delim above is reached
type delimitedReader struct {
	r       io.Reader
	buf     []byte
	atdelim bool
	eof     bool
}

// newDelimetedReader creates a delimited reader
func newDelimitedReader(r io.Reader) *delimitedReader {
	return &delimitedReader{
		r: r,
	}
}

// Read extracts bytes from the underlying reader and returns EOF
// when the delim is reached.
func (r *delimitedReader) Read(dest []byte) (int, error) {
	if len(dest) < len(delim) {
		return 0, ErrBufferTooSmall
	}

	// check whether we are already at delim
	if r.atdelim {
		return 0, io.EOF
	}

	// first copy from buf
	var nbuf int
	if len(r.buf) > 0 {
		nbuf = copy(dest, r.buf)
		if nbuf == len(r.buf) {
			r.buf = r.buf[:0]
		} else {
			r.buf = r.buf[nbuf:]
		}
	}

	// fill the rest of dest
	var nread int
	if nbuf < len(dest) && !r.eof {
		var err error
		nread, err = r.r.Read(dest[nbuf:])
		if err == io.EOF {
			r.eof = true
		} else if err != nil {
			return nbuf + nread, err
		}
	}

	// should be impossible
	if nread == 0 && nbuf == 0 && !r.eof {
		panic("read zero bytes from both buffer and reader")
	}

	// look for the delimeter
	var errout error
	var nout, nskip, state int
	for i, b := range dest[:nbuf+nread] {
		if b != delim[state] {
			state = 0
		}
		// do not use "else" here because we updated state above
		if b == delim[state] {
			state++
			if state == len(delim) {
				r.atdelim = true
				errout = io.EOF
				nskip = len(delim)
				break
			}
		} else {
			nout = i + 1
		}
	}

	// if we got EOF but no delimeter then we have an error
	if r.eof && !r.atdelim {
		return nout, ErrUnexpectedEOF
	}

	// update buffer
	r.buf = append(r.buf, dest[nout+nskip:nbuf+nread]...)
	return nout, errout
}

// Next proceeds to the next segment and returns true if there is another
// segment to extract.
func (r *delimitedReader) Next() bool {
	r.atdelim = false
	return true
}
<|endoftext|>


<commit_before>// Copyright 2018 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 source provides core features for use by Go editors and tools.
package source

import (
	"bytes"
	"context"
	"go/ast"
	"go/format"
	"go/parser"
	"go/scanner"
	"go/token"

	"golang.org/x/tools/internal/imports"
	"golang.org/x/tools/internal/lsp/diff"
	"golang.org/x/tools/internal/lsp/protocol"
	"golang.org/x/tools/internal/span"
	"golang.org/x/tools/internal/telemetry/event"
	errors "golang.org/x/xerrors"
)

// Format formats a file with a given range.
func Format(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.TextEdit, error) {
	ctx, done := event.StartSpan(ctx, "source.Format")
	defer done()

	pgh := snapshot.View().Session().Cache().ParseGoHandle(fh, ParseFull)
	file, _, m, parseErrors, err := pgh.Parse(ctx)
	if err != nil {
		return nil, err
	}
	// Even if this file has parse errors, it might still be possible to format it.
	// Using format.Node on an AST with errors may result in code being modified.
	// Attempt to format the source of this file instead.
	if parseErrors != nil {
		formatted, err := formatSource(ctx, fh)
		if err != nil {
			return nil, err
		}
		return computeTextEdits(ctx, snapshot.View(), pgh.File(), m, string(formatted))
	}

	fset := snapshot.View().Session().Cache().FileSet()
	buf := &bytes.Buffer{}

	// format.Node changes slightly from one release to another, so the version
	// of Go used to build the LSP server will determine how it formats code.
	// This should be acceptable for all users, who likely be prompted to rebuild
	// the LSP server on each Go release.
	if err := format.Node(buf, fset, file); err != nil {
		return nil, err
	}
	return computeTextEdits(ctx, snapshot.View(), pgh.File(), m, buf.String())
}

func formatSource(ctx context.Context, fh FileHandle) ([]byte, error) {
	ctx, done := event.StartSpan(ctx, "source.formatSource")
	defer done()

	data, _, err := fh.Read(ctx)
	if err != nil {
		return nil, err
	}
	return format.Source(data)
}

type ImportFix struct {
	Fix   *imports.ImportFix
	Edits []protocol.TextEdit
}

// AllImportsFixes formats f for each possible fix to the imports.
// In addition to returning the result of applying all edits,
// it returns a list of fixes that could be applied to the file, with the
// corresponding TextEdits that would be needed to apply that fix.
func AllImportsFixes(ctx context.Context, snapshot Snapshot, fh FileHandle) (allFixEdits []protocol.TextEdit, editsPerFix []*ImportFix, err error) {
	ctx, done := event.StartSpan(ctx, "source.AllImportsFixes")
	defer done()

	pgh := snapshot.View().Session().Cache().ParseGoHandle(fh, ParseFull)
	if err := snapshot.View().RunProcessEnvFunc(ctx, func(opts *imports.Options) error {
		allFixEdits, editsPerFix, err = computeImportEdits(ctx, snapshot.View(), pgh, opts)
		return err
	}); err != nil {
		return nil, nil, errors.Errorf("computing fix edits: %v", err)
	}
	return allFixEdits, editsPerFix, nil
}

// computeImportEdits computes a set of edits that perform one or all of the
// necessary import fixes.
func computeImportEdits(ctx context.Context, view View, ph ParseGoHandle, options *imports.Options) (allFixEdits []protocol.TextEdit, editsPerFix []*ImportFix, err error) {
	filename := ph.File().Identity().URI.Filename()

	// Build up basic information about the original file.
	origData, _, err := ph.File().Read(ctx)
	if err != nil {
		return nil, nil, err
	}
	origAST, _, origMapper, _, err := ph.Parse(ctx)
	if err != nil {
		return nil, nil, err
	}

	allFixes, err := imports.FixImports(filename, origData, options)
	if err != nil {
		return nil, nil, err
	}

	origImports, origImportOffset := trimToImports(view.Session().Cache().FileSet(), origAST, origData)
	allFixEdits, err = computeFixEdits(view, ph, options, origData, origAST, origMapper, origImports, origImportOffset, allFixes)
	if err != nil {
		return nil, nil, err
	}

	// Apply all of the import fixes to the file.
	// Add the edits for each fix to the result.
	for _, fix := range allFixes {
		edits, err := computeFixEdits(view, ph, options, origData, origAST, origMapper, origImports, origImportOffset, []*imports.ImportFix{fix})
		if err != nil {
			return nil, nil, err
		}
		editsPerFix = append(editsPerFix, &ImportFix{
			Fix:   fix,
			Edits: edits,
		})
	}
	return allFixEdits, editsPerFix, nil
}

func computeOneImportFixEdits(ctx context.Context, view View, ph ParseGoHandle, fix *imports.ImportFix) ([]protocol.TextEdit, error) {
	origData, _, err := ph.File().Read(ctx)
	if err != nil {
		return nil, err
	}
	origAST, _, origMapper, _, err := ph.Parse(ctx)
	if err != nil {
		return nil, err
	}
	origImports, origImportOffset := trimToImports(view.Session().Cache().FileSet(), origAST, origData)

	options := &imports.Options{
		// Defaults.
		AllErrors:  true,
		Comments:   true,
		Fragment:   true,
		FormatOnly: false,
		TabIndent:  true,
		TabWidth:   8,
	}
	return computeFixEdits(view, ph, options, origData, origAST, origMapper, origImports, origImportOffset, []*imports.ImportFix{fix})
}

func computeFixEdits(view View, ph ParseGoHandle, options *imports.Options, origData []byte, origAST *ast.File, origMapper *protocol.ColumnMapper, origImports []byte, origImportOffset int, fixes []*imports.ImportFix) ([]protocol.TextEdit, error) {
	filename := ph.File().Identity().URI.Filename()
	// Apply the fixes and re-parse the file so that we can locate the
	// new imports.
	fixedData, err := imports.ApplyFixes(fixes, filename, origData, options, parser.ImportsOnly)
	fixedData = append(fixedData, '\n') // ApplyFixes comes out missing the newline, go figure.
	if err != nil {
		return nil, err
	}
	fixedFset := token.NewFileSet()
	fixedAST, err := parser.ParseFile(fixedFset, filename, fixedData, parser.ImportsOnly)
	// Any error here prevents us from computing the edits.
	if err != nil {
		return nil, err
	}
	fixedImports, fixedImportsOffset := trimToImports(fixedFset, fixedAST, fixedData)

	// Prepare the diff. If both sides had import statements, we can diff
	// just those sections against each other, then shift the resulting
	// edits to the right lines in the original file.
	left, right := origImports, fixedImports
	converter := span.NewContentConverter(filename, origImports)
	offset := origImportOffset

	// If one side or the other has no imports, we won't know where to
	// anchor the diffs. Instead, use the beginning of the file, up to its
	// first non-imports decl. We know the imports code will insert
	// somewhere before that.
	if origImportOffset == 0 || fixedImportsOffset == 0 {
		left, _ = trimToFirstNonImport(view.Session().Cache().FileSet(), origAST, origData, nil)
		fixedData, err = imports.ApplyFixes(fixes, filename, origData, options, 0)
		if err != nil {
			return nil, err
		}
		// We need the whole file here, not just the ImportsOnly versions we made above.
		fixedAST, err = parser.ParseFile(fixedFset, filename, fixedData, 0)
		if fixedAST == nil {
			return nil, err
		}
		var ok bool
		right, ok = trimToFirstNonImport(fixedFset, fixedAST, fixedData, err)
		if !ok {
			return nil, errors.Errorf("error %v detected in the import block", err)
		}
		// We're now working with a prefix of the original file, so we can
		// use the original converter, and there is no offset on the edits.
		converter = origMapper.Converter
		offset = 0
	}

	// Perform the diff and adjust the results for the trimming, if any.
	edits := view.Options().ComputeEdits(ph.File().Identity().URI, string(left), string(right))
	for i := range edits {
		s, err := edits[i].Span.WithPosition(converter)
		if err != nil {
			return nil, err
		}
		start := span.NewPoint(s.Start().Line()+offset, s.Start().Column(), -1)
		end := span.NewPoint(s.End().Line()+offset, s.End().Column(), -1)
		edits[i].Span = span.New(s.URI(), start, end)
	}
	return ToProtocolEdits(origMapper, edits)
}

// trimToImports returns a section of the source file that covers all of the
// import declarations, and the line offset into the file that section starts at.
func trimToImports(fset *token.FileSet, f *ast.File, src []byte) ([]byte, int) {
	var firstImport, lastImport ast.Decl
	for _, decl := range f.Decls {
		if gen, ok := decl.(*ast.GenDecl); ok && gen.Tok == token.IMPORT {
			if firstImport == nil {
				firstImport = decl
			}
			lastImport = decl
		}
	}

	if firstImport == nil {
		return nil, 0
	}
	tok := fset.File(f.Pos())
	start := firstImport.Pos()
	end := lastImport.End()
	// The parser will happily feed us nonsense. See golang/go#36610.
	tokStart, tokEnd := token.Pos(tok.Base()), token.Pos(tok.Base()+tok.Size())
	if start < tokStart || start > tokEnd || end < tokStart || end > tokEnd {
		return nil, 0
	}
	if nextLine := fset.Position(end).Line + 1; tok.LineCount() >= nextLine {
		end = fset.File(f.Pos()).LineStart(nextLine)
	}

	startLineOffset := fset.Position(start).Line - 1 // lines are 1-indexed.
	return src[fset.Position(firstImport.Pos()).Offset:fset.Position(end).Offset], startLineOffset
}

// trimToFirstNonImport returns src from the beginning to the first non-import
// declaration, or the end of the file if there is no such decl.
func trimToFirstNonImport(fset *token.FileSet, f *ast.File, src []byte, err error) ([]byte, bool) {
	var firstDecl ast.Decl
	for _, decl := range f.Decls {
		if gen, ok := decl.(*ast.GenDecl); ok && gen.Tok == token.IMPORT {
			continue
		}
		firstDecl = decl
		break
	}
	tok := fset.File(f.Pos())
	if tok == nil {
		return nil, false
	}
	end := f.End()
	if firstDecl != nil {
		if firstDeclLine := fset.Position(firstDecl.Pos()).Line; firstDeclLine > 1 {
			end = tok.LineStart(firstDeclLine - 1)
		}
	}
	// Any errors in the file must be after the part of the file that we care about.
	switch err := err.(type) {
	case *scanner.Error:
		pos := tok.Pos(err.Pos.Offset)
		if pos <= end {
			return nil, false
		}
	case scanner.ErrorList:
		if err.Len() > 0 {
			pos := tok.Pos(err[0].Pos.Offset)
			if pos <= end {
				return nil, false
			}
		}
	}
	return src[0:fset.Position(end).Offset], true
}

func computeTextEdits(ctx context.Context, view View, fh FileHandle, m *protocol.ColumnMapper, formatted string) ([]protocol.TextEdit, error) {
	ctx, done := event.StartSpan(ctx, "source.computeTextEdits")
	defer done()

	data, _, err := fh.Read(ctx)
	if err != nil {
		return nil, err
	}
	edits := view.Options().ComputeEdits(fh.Identity().URI, string(data), formatted)
	return ToProtocolEdits(m, edits)
}

func ToProtocolEdits(m *protocol.ColumnMapper, edits []diff.TextEdit) ([]protocol.TextEdit, error) {
	if edits == nil {
		return nil, nil
	}
	result := make([]protocol.TextEdit, len(edits))
	for i, edit := range edits {
		rng, err := m.Range(edit.Span)
		if err != nil {
			return nil, err
		}
		result[i] = protocol.TextEdit{
			Range:   rng,
			NewText: edit.NewText,
		}
	}
	return result, nil
}

func FromProtocolEdits(m *protocol.ColumnMapper, edits []protocol.TextEdit) ([]diff.TextEdit, error) {
	if edits == nil {
		return nil, nil
	}
	result := make([]diff.TextEdit, len(edits))
	for i, edit := range edits {
		spn, err := m.RangeSpan(edit.Range)
		if err != nil {
			return nil, err
		}
		result[i] = diff.TextEdit{
			Span:    spn,
			NewText: edit.NewText,
		}
	}
	return result, nil
}
<commit_msg>internal/lsp: add an extra bounds check to avoid nil pointers<commit_after>// Copyright 2018 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 source provides core features for use by Go editors and tools.
package source

import (
	"bytes"
	"context"
	"go/ast"
	"go/format"
	"go/parser"
	"go/scanner"
	"go/token"

	"golang.org/x/tools/internal/imports"
	"golang.org/x/tools/internal/lsp/diff"
	"golang.org/x/tools/internal/lsp/protocol"
	"golang.org/x/tools/internal/span"
	"golang.org/x/tools/internal/telemetry/event"
	errors "golang.org/x/xerrors"
)

// Format formats a file with a given range.
func Format(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.TextEdit, error) {
	ctx, done := event.StartSpan(ctx, "source.Format")
	defer done()

	pgh := snapshot.View().Session().Cache().ParseGoHandle(fh, ParseFull)
	file, _, m, parseErrors, err := pgh.Parse(ctx)
	if err != nil {
		return nil, err
	}
	// Even if this file has parse errors, it might still be possible to format it.
	// Using format.Node on an AST with errors may result in code being modified.
	// Attempt to format the source of this file instead.
	if parseErrors != nil {
		formatted, err := formatSource(ctx, fh)
		if err != nil {
			return nil, err
		}
		return computeTextEdits(ctx, snapshot.View(), pgh.File(), m, string(formatted))
	}

	fset := snapshot.View().Session().Cache().FileSet()
	buf := &bytes.Buffer{}

	// format.Node changes slightly from one release to another, so the version
	// of Go used to build the LSP server will determine how it formats code.
	// This should be acceptable for all users, who likely be prompted to rebuild
	// the LSP server on each Go release.
	if err := format.Node(buf, fset, file); err != nil {
		return nil, err
	}
	return computeTextEdits(ctx, snapshot.View(), pgh.File(), m, buf.String())
}

func formatSource(ctx context.Context, fh FileHandle) ([]byte, error) {
	ctx, done := event.StartSpan(ctx, "source.formatSource")
	defer done()

	data, _, err := fh.Read(ctx)
	if err != nil {
		return nil, err
	}
	return format.Source(data)
}

type ImportFix struct {
	Fix   *imports.ImportFix
	Edits []protocol.TextEdit
}

// AllImportsFixes formats f for each possible fix to the imports.
// In addition to returning the result of applying all edits,
// it returns a list of fixes that could be applied to the file, with the
// corresponding TextEdits that would be needed to apply that fix.
func AllImportsFixes(ctx context.Context, snapshot Snapshot, fh FileHandle) (allFixEdits []protocol.TextEdit, editsPerFix []*ImportFix, err error) {
	ctx, done := event.StartSpan(ctx, "source.AllImportsFixes")
	defer done()

	pgh := snapshot.View().Session().Cache().ParseGoHandle(fh, ParseFull)
	if err := snapshot.View().RunProcessEnvFunc(ctx, func(opts *imports.Options) error {
		allFixEdits, editsPerFix, err = computeImportEdits(ctx, snapshot.View(), pgh, opts)
		return err
	}); err != nil {
		return nil, nil, errors.Errorf("computing fix edits: %v", err)
	}
	return allFixEdits, editsPerFix, nil
}

// computeImportEdits computes a set of edits that perform one or all of the
// necessary import fixes.
func computeImportEdits(ctx context.Context, view View, ph ParseGoHandle, options *imports.Options) (allFixEdits []protocol.TextEdit, editsPerFix []*ImportFix, err error) {
	filename := ph.File().Identity().URI.Filename()

	// Build up basic information about the original file.
	origData, _, err := ph.File().Read(ctx)
	if err != nil {
		return nil, nil, err
	}
	origAST, _, origMapper, _, err := ph.Parse(ctx)
	if err != nil {
		return nil, nil, err
	}

	allFixes, err := imports.FixImports(filename, origData, options)
	if err != nil {
		return nil, nil, err
	}

	origImports, origImportOffset := trimToImports(view.Session().Cache().FileSet(), origAST, origData)
	allFixEdits, err = computeFixEdits(view, ph, options, origData, origAST, origMapper, origImports, origImportOffset, allFixes)
	if err != nil {
		return nil, nil, err
	}

	// Apply all of the import fixes to the file.
	// Add the edits for each fix to the result.
	for _, fix := range allFixes {
		edits, err := computeFixEdits(view, ph, options, origData, origAST, origMapper, origImports, origImportOffset, []*imports.ImportFix{fix})
		if err != nil {
			return nil, nil, err
		}
		editsPerFix = append(editsPerFix, &ImportFix{
			Fix:   fix,
			Edits: edits,
		})
	}
	return allFixEdits, editsPerFix, nil
}

func computeOneImportFixEdits(ctx context.Context, view View, ph ParseGoHandle, fix *imports.ImportFix) ([]protocol.TextEdit, error) {
	origData, _, err := ph.File().Read(ctx)
	if err != nil {
		return nil, err
	}
	origAST, _, origMapper, _, err := ph.Parse(ctx)
	if err != nil {
		return nil, err
	}
	origImports, origImportOffset := trimToImports(view.Session().Cache().FileSet(), origAST, origData)

	options := &imports.Options{
		// Defaults.
		AllErrors:  true,
		Comments:   true,
		Fragment:   true,
		FormatOnly: false,
		TabIndent:  true,
		TabWidth:   8,
	}
	return computeFixEdits(view, ph, options, origData, origAST, origMapper, origImports, origImportOffset, []*imports.ImportFix{fix})
}

func computeFixEdits(view View, ph ParseGoHandle, options *imports.Options, origData []byte, origAST *ast.File, origMapper *protocol.ColumnMapper, origImports []byte, origImportOffset int, fixes []*imports.ImportFix) ([]protocol.TextEdit, error) {
	filename := ph.File().Identity().URI.Filename()
	// Apply the fixes and re-parse the file so that we can locate the
	// new imports.
	fixedData, err := imports.ApplyFixes(fixes, filename, origData, options, parser.ImportsOnly)
	fixedData = append(fixedData, '\n') // ApplyFixes comes out missing the newline, go figure.
	if err != nil {
		return nil, err
	}
	fixedFset := token.NewFileSet()
	fixedAST, err := parser.ParseFile(fixedFset, filename, fixedData, parser.ImportsOnly)
	// Any error here prevents us from computing the edits.
	if err != nil {
		return nil, err
	}
	fixedImports, fixedImportsOffset := trimToImports(fixedFset, fixedAST, fixedData)

	// Prepare the diff. If both sides had import statements, we can diff
	// just those sections against each other, then shift the resulting
	// edits to the right lines in the original file.
	left, right := origImports, fixedImports
	converter := span.NewContentConverter(filename, origImports)
	offset := origImportOffset

	// If one side or the other has no imports, we won't know where to
	// anchor the diffs. Instead, use the beginning of the file, up to its
	// first non-imports decl. We know the imports code will insert
	// somewhere before that.
	if origImportOffset == 0 || fixedImportsOffset == 0 {
		left, _ = trimToFirstNonImport(view.Session().Cache().FileSet(), origAST, origData, nil)
		fixedData, err = imports.ApplyFixes(fixes, filename, origData, options, 0)
		if err != nil {
			return nil, err
		}
		// We need the whole file here, not just the ImportsOnly versions we made above.
		fixedAST, err = parser.ParseFile(fixedFset, filename, fixedData, 0)
		if fixedAST == nil {
			return nil, err
		}
		var ok bool
		right, ok = trimToFirstNonImport(fixedFset, fixedAST, fixedData, err)
		if !ok {
			return nil, errors.Errorf("error %v detected in the import block", err)
		}
		// We're now working with a prefix of the original file, so we can
		// use the original converter, and there is no offset on the edits.
		converter = origMapper.Converter
		offset = 0
	}

	// Perform the diff and adjust the results for the trimming, if any.
	edits := view.Options().ComputeEdits(ph.File().Identity().URI, string(left), string(right))
	for i := range edits {
		s, err := edits[i].Span.WithPosition(converter)
		if err != nil {
			return nil, err
		}
		start := span.NewPoint(s.Start().Line()+offset, s.Start().Column(), -1)
		end := span.NewPoint(s.End().Line()+offset, s.End().Column(), -1)
		edits[i].Span = span.New(s.URI(), start, end)
	}
	return ToProtocolEdits(origMapper, edits)
}

// trimToImports returns a section of the source file that covers all of the
// import declarations, and the line offset into the file that section starts at.
func trimToImports(fset *token.FileSet, f *ast.File, src []byte) ([]byte, int) {
	var firstImport, lastImport ast.Decl
	for _, decl := range f.Decls {
		if gen, ok := decl.(*ast.GenDecl); ok && gen.Tok == token.IMPORT {
			if firstImport == nil {
				firstImport = decl
			}
			lastImport = decl
		}
	}

	if firstImport == nil {
		return nil, 0
	}
	tok := fset.File(f.Pos())
	start := firstImport.Pos()
	end := lastImport.End()
	// The parser will happily feed us nonsense. See golang/go#36610.
	tokStart, tokEnd := token.Pos(tok.Base()), token.Pos(tok.Base()+tok.Size())
	if start < tokStart || start > tokEnd || end < tokStart || end > tokEnd {
		return nil, 0
	}
	if nextLine := fset.Position(end).Line + 1; tok.LineCount() >= nextLine {
		end = fset.File(f.Pos()).LineStart(nextLine)
	}
	if start > end {
		return nil, 0
	}

	startLineOffset := fset.Position(start).Line - 1 // lines are 1-indexed.
	return src[fset.Position(start).Offset:fset.Position(end).Offset], startLineOffset
}

// trimToFirstNonImport returns src from the beginning to the first non-import
// declaration, or the end of the file if there is no such decl.
func trimToFirstNonImport(fset *token.FileSet, f *ast.File, src []byte, err error) ([]byte, bool) {
	var firstDecl ast.Decl
	for _, decl := range f.Decls {
		if gen, ok := decl.(*ast.GenDecl); ok && gen.Tok == token.IMPORT {
			continue
		}
		firstDecl = decl
		break
	}
	tok := fset.File(f.Pos())
	if tok == nil {
		return nil, false
	}
	end := f.End()
	if firstDecl != nil {
		if firstDeclLine := fset.Position(firstDecl.Pos()).Line; firstDeclLine > 1 {
			end = tok.LineStart(firstDeclLine - 1)
		}
	}
	// Any errors in the file must be after the part of the file that we care about.
	switch err := err.(type) {
	case *scanner.Error:
		pos := tok.Pos(err.Pos.Offset)
		if pos <= end {
			return nil, false
		}
	case scanner.ErrorList:
		if err.Len() > 0 {
			pos := tok.Pos(err[0].Pos.Offset)
			if pos <= end {
				return nil, false
			}
		}
	}
	return src[0:fset.Position(end).Offset], true
}

func computeTextEdits(ctx context.Context, view View, fh FileHandle, m *protocol.ColumnMapper, formatted string) ([]protocol.TextEdit, error) {
	ctx, done := event.StartSpan(ctx, "source.computeTextEdits")
	defer done()

	data, _, err := fh.Read(ctx)
	if err != nil {
		return nil, err
	}
	edits := view.Options().ComputeEdits(fh.Identity().URI, string(data), formatted)
	return ToProtocolEdits(m, edits)
}

func ToProtocolEdits(m *protocol.ColumnMapper, edits []diff.TextEdit) ([]protocol.TextEdit, error) {
	if edits == nil {
		return nil, nil
	}
	result := make([]protocol.TextEdit, len(edits))
	for i, edit := range edits {
		rng, err := m.Range(edit.Span)
		if err != nil {
			return nil, err
		}
		result[i] = protocol.TextEdit{
			Range:   rng,
			NewText: edit.NewText,
		}
	}
	return result, nil
}

func FromProtocolEdits(m *protocol.ColumnMapper, edits []protocol.TextEdit) ([]diff.TextEdit, error) {
	if edits == nil {
		return nil, nil
	}
	result := make([]diff.TextEdit, len(edits))
	for i, edit := range edits {
		spn, err := m.RangeSpan(edit.Range)
		if err != nil {
			return nil, err
		}
		result[i] = diff.TextEdit{
			Span:    spn,
			NewText: edit.NewText,
		}
	}
	return result, nil
}
<|endoftext|>


<commit_before>// Package siv_aead wraps the functions provided by siv
// in a crypto.AEAD interface.
package siv_aead

import (
	"crypto/cipher"
	"log"

	"github.com/jacobsa/crypto/siv"
)

type sivAead struct {
	key []byte
}

var _ cipher.AEAD = &sivAead{}

const (
	// KeyLen is the required key length. The SIV algorithm supports other lengths,
	// but we only support 64.
	KeyLen = 64
)

// New returns a new cipher.AEAD implementation.
func New(key []byte) cipher.AEAD {
	if len(key) != KeyLen {
		// SIV supports 32, 48 or 64-byte keys, but in gocryptfs we
		// exclusively use 64.
		log.Panicf("Key must be %d byte long (you passed %d)", KeyLen, len(key))
	}
	return new2(key)
}

// Same as "New" without the 64-byte restriction.
func new2(key []byte) cipher.AEAD {
	return &sivAead{
		key: key,
	}
}

func (s *sivAead) NonceSize() int {
	// SIV supports any nonce size, but in gocryptfs we exclusively use 16.
	return 16
}

func (s *sivAead) Overhead() int {
	return 16
}

// Seal encrypts "in" using "nonce" and "authData" and append the result to "dst"
func (s *sivAead) Seal(dst, nonce, plaintext, authData []byte) []byte {
	if len(nonce) != 16 {
		// SIV supports any nonce size, but in gocryptfs we exclusively use 16.
		log.Panic("nonce must be 16 bytes long")
	}
	// https://github.com/jacobsa/crypto/blob/master/siv/encrypt.go#L48:
	// As per RFC 5297 section 3, you may use this function for nonce-based
	// authenticated encryption by passing a nonce as the last associated
	// data element.
	associated := [][]byte{authData, nonce}
	out, err := siv.Encrypt(dst, s.key, plaintext, associated)
	if err != nil {
		log.Panic(err)
	}
	return out
}

// Open decrypts "in" using "nonce" and "authData" and append the result to "dst"
func (s *sivAead) Open(dst, nonce, ciphertext, authData []byte) ([]byte, error) {
	if len(nonce) != 16 {
		// SIV supports any nonce size, but in gocryptfs we exclusively use 16.
		log.Panic("nonce must be 16 bytes long")
	}
	associated := [][]byte{authData, nonce}
	dec, err := siv.Decrypt(s.key, ciphertext, associated)
	return append(dst, dec...), err
}
<commit_msg>siv_aead: fix trivial typo in comment<commit_after>// Package siv_aead wraps the functions provided by siv
// in a crypto.AEAD interface.
package siv_aead

import (
	"crypto/cipher"
	"log"

	"github.com/jacobsa/crypto/siv"
)

type sivAead struct {
	key []byte
}

var _ cipher.AEAD = &sivAead{}

const (
	// KeyLen is the required key length. The SIV algorithm supports other lengths,
	// but we only support 64.
	KeyLen = 64
)

// New returns a new cipher.AEAD implementation.
func New(key []byte) cipher.AEAD {
	if len(key) != KeyLen {
		// SIV supports 32, 48 or 64-byte keys, but in gocryptfs we
		// exclusively use 64.
		log.Panicf("Key must be %d byte long (you passed %d)", KeyLen, len(key))
	}
	return new2(key)
}

// Same as "New" without the 64-byte restriction.
func new2(key []byte) cipher.AEAD {
	return &sivAead{
		key: key,
	}
}

func (s *sivAead) NonceSize() int {
	// SIV supports any nonce size, but in gocryptfs we exclusively use 16.
	return 16
}

func (s *sivAead) Overhead() int {
	return 16
}

// Seal encrypts "in" using "nonce" and "authData" and appends the result to "dst"
func (s *sivAead) Seal(dst, nonce, plaintext, authData []byte) []byte {
	if len(nonce) != 16 {
		// SIV supports any nonce size, but in gocryptfs we exclusively use 16.
		log.Panic("nonce must be 16 bytes long")
	}
	// https://github.com/jacobsa/crypto/blob/master/siv/encrypt.go#L48:
	// As per RFC 5297 section 3, you may use this function for nonce-based
	// authenticated encryption by passing a nonce as the last associated
	// data element.
	associated := [][]byte{authData, nonce}
	out, err := siv.Encrypt(dst, s.key, plaintext, associated)
	if err != nil {
		log.Panic(err)
	}
	return out
}

// Open decrypts "in" using "nonce" and "authData" and appends the result to "dst"
func (s *sivAead) Open(dst, nonce, ciphertext, authData []byte) ([]byte, error) {
	if len(nonce) != 16 {
		// SIV supports any nonce size, but in gocryptfs we exclusively use 16.
		log.Panic("nonce must be 16 bytes long")
	}
	associated := [][]byte{authData, nonce}
	dec, err := siv.Decrypt(s.key, ciphertext, associated)
	return append(dst, dec...), err
}
<|endoftext|>