<commit_before>package air

import (
	"net/url"
	ppath "path"
	"strings"
	"sync"
)

// router is a registry of all registered routes.
type router struct {
	sync.Mutex

	a                    *Air
	routeTree            *routeNode
	registeredRoutes     map[string]bool
	maxRouteParams       int
	routePalPool         *sync.Pool
	routeParamValuesPool *sync.Pool
}

// newRouter returns a new instance of the `router` with the a.
func newRouter(a *Air) *router {
	r := &router{
		a: a,
		routeTree: &routeNode{
			handlers: map[string]Handler{},
		},
		registeredRoutes: map[string]bool{},
		routePalPool: &sync.Pool{
			New: func() interface{} {
				return &routePal{}
			},
		},
	}
	r.routeParamValuesPool = &sync.Pool{
		New: func() interface{} {
			return make([]string, r.maxRouteParams)
		},
	}

	return r
}

// register registers a new route for the method and the path with the matching
// h in the r with the optional route-level gases.
func (r *router) register(method, path string, h Handler, gases ...Gas) {
	r.Lock()
	defer r.Unlock()

	if path == "" {
		panic("air: route path cannot be empty")
	} else if h == nil {
		panic("air: route handler cannot be nil")
	}

	path = ppath.Clean(path)
	path = url.PathEscape(path)
	path = strings.Replace(path, "%2F", "/", -1)
	path = strings.Replace(path, "%2A", "*", -1)
	if path[0] != '/' {
		panic("air: route path must start with /")
	} else if strings.Count(path, ":") > 1 {
		ps := strings.Split(path, "/")
		for _, p := range ps {
			if strings.Count(p, ":") > 1 {
				panic("air: adjacent param names in route " +
					"path must be separated by /")
			}
		}
	} else if strings.Contains(path, "*") {
		if strings.Count(path, "*") > 1 {
			panic("air: only one * is allowed in route path")
		} else if path[len(path)-1] != '*' {
			panic("air: * can only appear at end of route path")
		} else if strings.Contains(
			path[strings.LastIndex(path, "/"):],
			":",
		) {
			panic("air: adjacent param name and * in route path " +
				"must be separated by /")
		}
	}

	routeName := method + path
	for i, l := len(method), len(routeName); i < l; i++ {
		if routeName[i] == ':' {
			j := i + 1

			for ; i < l && routeName[i] != '/'; i++ {
			}

			routeName = routeName[:j] + routeName[i:]
			i, l = j, len(routeName)

			if i == l {
				break
			}
		}
	}

	if r.registeredRoutes[routeName] {
		panic("air: route already exists")
	} else {
		r.registeredRoutes[routeName] = true
	}

	rh := func(req *Request, res *Response) error {
		h := h
		for i := len(gases) - 1; i >= 0; i-- {
			h = gases[i](h)
		}

		return h(req, res)
	}

	paramNames := []string{}
	for i, l := 0, len(path); i < l; i++ {
		if path[i] == ':' {
			j := i + 1

			r.insert(
				method,
				path[:i],
				nil,
				routeNodeTypeStatic,
				nil,
			)

			for ; i < l && path[i] != '/'; i++ {
			}

			paramName := path[j:i]

			for _, pn := range paramNames {
				if pn == paramName {
					panic("air: route path cannot have " +
						"duplicate param names")
				}
			}

			paramNames = append(paramNames, paramName)
			path = path[:j] + path[i:]

			if i, l = j, len(path); i == l {
				r.insert(
					method,
					path,
					rh,
					routeNodeTypeParam,
					paramNames,
				)
				return
			}

			r.insert(
				method,
				path[:i],
				nil,
				routeNodeTypeParam,
				paramNames,
			)
		} else if path[i] == '*' {
			r.insert(
				method,
				path[:i],
				nil,
				routeNodeTypeStatic,
				nil,
			)
			paramNames = append(paramNames, "*")
			r.insert(
				method,
				path[:i+1],
				rh,
				routeNodeTypeAny,
				paramNames,
			)
			return
		}
	}

	r.insert(method, path, rh, routeNodeTypeStatic, paramNames)
}

// insert inserts a new route into the `r.routeTree`.
func (r *router) insert(
	method string,
	path string,
	h Handler,
	nt routeNodeType,
	paramNames []string,
) {
	if l := len(paramNames); l > r.maxRouteParams {
		r.maxRouteParams = l
	}

	var (
		s  = path        // Search
		cn = r.routeTree // Current node
		nn *routeNode    // Next node
		sl int           // Search length
		pl int           // Prefix length
		ll int           // LCP length
		ml int           // Minimum length of sl and pl
	)

	for {
		sl = len(s)
		pl = len(cn.prefix)
		ll = 0

		ml = pl
		if sl < ml {
			ml = sl
		}

		for ; ll < ml && s[ll] == cn.prefix[ll]; ll++ {
		}

		if ll == 0 { // At root node
			cn.label = s[0]
			cn.nType = nt
			cn.prefix = s
			cn.paramNames = paramNames
			if h != nil {
				cn.handlers[method] = h
			}
		} else if ll < pl { // Split node
			nn = &routeNode{
				label:      cn.prefix[ll],
				nType:      cn.nType,
				prefix:     cn.prefix[ll:],
				children:   cn.children,
				paramNames: cn.paramNames,
				handlers:   cn.handlers,
			}

			// Reset current node.
			cn.label = cn.prefix[0]
			cn.nType = routeNodeTypeStatic
			cn.prefix = cn.prefix[:ll]
			cn.children = []*routeNode{nn}
			cn.paramNames = nil
			cn.handlers = map[string]Handler{}

			if ll == sl { // At current node
				cn.nType = nt
				cn.paramNames = paramNames
				if h != nil {
					cn.handlers[method] = h
				}
			} else { // Create child node
				nn = &routeNode{
					label:      s[ll],
					nType:      nt,
					prefix:     s[ll:],
					paramNames: paramNames,
					handlers:   map[string]Handler{},
				}
				if h != nil {
					nn.handlers[method] = h
				}

				cn.children = append(cn.children, nn)
			}
		} else if ll < sl {
			s = s[ll:]
			if nn = cn.childByLabel(s[0]); nn != nil {
				// Go deeper.
				cn = nn
				continue
			}

			// Create child node.
			nn = &routeNode{
				label:      s[0],
				nType:      nt,
				prefix:     s,
				handlers:   map[string]Handler{},
				paramNames: paramNames,
			}
			if h != nil {
				nn.handlers[method] = h
			}

			cn.children = append(cn.children, nn)
		} else { // Node already exists
			if len(cn.paramNames) == 0 {
				cn.paramNames = paramNames
			}

			if h != nil {
				cn.handlers[method] = h
			}
		}

		break
	}
}

// route returns a handler registered for the req.
func (r *router) route(req *Request) Handler {
	p := r.routePalPool.Get().(*routePal)
	p.s, _ = splitPathQuery(req.Path)
	p.cn = r.routeTree
	p.nnt = routeNodeTypeStatic
	p.sn = nil
	p.ss = ""
	p.pi = 0

	// Search order: static route > param route > any route.
	for {
		if p.s == "" {
			break
		}

		p.i = 1
		p.sl = len(p.s)
		for p.i < p.sl && p.s[p.i] == '/' {
			p.i++
		}

		p.s = p.s[p.i-1:]

		p.pl = 0
		p.ll = 0

		if p.cn.label != ':' {
			p.pl = len(p.cn.prefix)

			p.ml = p.pl
			if p.sl = len(p.s); p.sl < p.ml {
				p.ml = p.sl
			}

			for p.ll < p.ml && p.s[p.ll] == p.cn.prefix[p.ll] {
				p.ll++
			}
		}

		if p.ll != p.pl {
			goto Struggle
		}

		if p.s = p.s[p.ll:]; p.s == "" {
			if len(p.cn.handlers) == 0 {
				if p.cn.childByType(routeNodeTypeParam) != nil {
					goto Param
				}

				if p.cn.childByType(routeNodeTypeAny) != nil {
					goto Any
				}
			}

			break
		}

		// Static node.
		if p.nn = p.cn.child(p.s[0], routeNodeTypeStatic); p.nn != nil {
			// Save next.
			if p.pl = len(p.cn.prefix); p.pl > 0 &&
				p.cn.prefix[p.pl-1] == '/' {
				p.nnt = routeNodeTypeParam
				p.sn = p.cn
				p.ss = p.s
			}

			p.cn = p.nn

			continue
		}

		// Param node.
	Param:
		if p.nn = p.cn.childByType(routeNodeTypeParam); p.nn != nil {
			// Save next.
			if p.pl = len(p.cn.prefix); p.pl > 0 &&
				p.cn.prefix[p.pl-1] == '/' {
				p.nnt = routeNodeTypeAny
				p.sn = p.cn
				p.ss = p.s
			}

			p.cn = p.nn

			p.i = 0
			p.sl = len(p.s)
			for p.i < p.sl && p.s[p.i] != '/' {
				p.i++
			}

			if req.routeParamValues == nil {
				req.routeParamValues = r.routeParamValuesPool.
					Get().([]string)
			}

			req.routeParamValues[p.pi] = p.s[:p.i]
			p.pi++

			p.s = p.s[p.i:]

			continue
		}

		// Any node.
	Any:
		if p.cn = p.cn.childByType(routeNodeTypeAny); p.cn != nil {
			if req.routeParamValues == nil {
				req.routeParamValues = r.routeParamValuesPool.
					Get().([]string)
			}

			req.routeParamValues[len(p.cn.paramNames)-1] = p.s

			break
		}

		// Struggle for the former node.
	Struggle:
		if p.sn != nil {
			p.cn = p.sn
			p.sn = nil
			p.s = p.ss

			switch p.nnt {
			case routeNodeTypeParam:
				goto Param
			case routeNodeTypeAny:
				goto Any
			}
		}

		r.routePalPool.Put(p)

		return r.a.NotFoundHandler
	}

	h := p.cn.handlers[req.Method]
	if h != nil {
		req.routeParamNames = p.cn.paramNames
	} else if len(p.cn.handlers) != 0 {
		h = r.a.MethodNotAllowedHandler
	} else {
		h = r.a.NotFoundHandler
	}

	r.routePalPool.Put(p)

	return h
}

// routeNode is the node of the route radix tree.
type routeNode struct {
	label      byte
	nType      routeNodeType
	prefix     string
	children   []*routeNode
	paramNames []string
	handlers   map[string]Handler
}

// child returns a child node of the rn by the l and the t.
func (rn *routeNode) child(l byte, t routeNodeType) *routeNode {
	for _, c := range rn.children {
		if c.label == l && c.nType == t {
			return c
		}
	}

	return nil
}

// childByLabel returns a child node of the rn by the l.
func (rn *routeNode) childByLabel(l byte) *routeNode {
	for _, c := range rn.children {
		if c.label == l {
			return c
		}
	}

	return nil
}

// childByType returns a child node of the rn by the t.
func (rn *routeNode) childByType(t routeNodeType) *routeNode {
	for _, c := range rn.children {
		if c.nType == t {
			return c
		}
	}

	return nil
}

// routeNodeType is the type of the `routeNode`.
type routeNodeType uint8

// The route node types.
const (
	routeNodeTypeStatic routeNodeType = iota
	routeNodeTypeParam
	routeNodeTypeAny
)

// routePal is the pal struct of the `router#route()`.
type routePal struct {
	s   string        // Search
	cn  *routeNode    // Current node
	nn  *routeNode    // Next node
	nnt routeNodeType // Next node type
	sn  *routeNode    // Saved node
	ss  string        // Saved search
	sl  int           // Search length
	pl  int           // Prefix length
	ll  int           // LCP length
	ml  int           // Minimum length of sl and pl
	i   int           // Index
	pi  int           // Param index
}

// splitPathQuery splits the p of the form "path?query" into path and query.
func splitPathQuery(p string) (path, query string) {
	i, l := 0, len(p)
	for ; i < l && p[i] != '?'; i++ {
	}

	if i < l {
		return p[:i], p[i+1:]
	}

	return p, ""
}
<commit_msg>revert: refactor: refine `router#route()`<commit_after>package air

import (
	"net/url"
	ppath "path"
	"strings"
	"sync"
)

// router is a registry of all registered routes.
type router struct {
	sync.Mutex

	a                    *Air
	routeTree            *routeNode
	registeredRoutes     map[string]bool
	maxRouteParams       int
	routeParamValuesPool *sync.Pool
}

// newRouter returns a new instance of the `router` with the a.
func newRouter(a *Air) *router {
	r := &router{
		a: a,
		routeTree: &routeNode{
			handlers: map[string]Handler{},
		},
		registeredRoutes: map[string]bool{},
	}
	r.routeParamValuesPool = &sync.Pool{
		New: func() interface{} {
			return make([]string, r.maxRouteParams)
		},
	}

	return r
}

// register registers a new route for the method and the path with the matching
// h in the r with the optional route-level gases.
func (r *router) register(method, path string, h Handler, gases ...Gas) {
	r.Lock()
	defer r.Unlock()

	if path == "" {
		panic("air: route path cannot be empty")
	} else if h == nil {
		panic("air: route handler cannot be nil")
	}

	path = ppath.Clean(path)
	path = url.PathEscape(path)
	path = strings.Replace(path, "%2F", "/", -1)
	path = strings.Replace(path, "%2A", "*", -1)
	if path[0] != '/' {
		panic("air: route path must start with /")
	} else if strings.Count(path, ":") > 1 {
		ps := strings.Split(path, "/")
		for _, p := range ps {
			if strings.Count(p, ":") > 1 {
				panic("air: adjacent param names in route " +
					"path must be separated by /")
			}
		}
	} else if strings.Contains(path, "*") {
		if strings.Count(path, "*") > 1 {
			panic("air: only one * is allowed in route path")
		} else if path[len(path)-1] != '*' {
			panic("air: * can only appear at end of route path")
		} else if strings.Contains(
			path[strings.LastIndex(path, "/"):],
			":",
		) {
			panic("air: adjacent param name and * in route path " +
				"must be separated by /")
		}
	}

	routeName := method + path
	for i, l := len(method), len(routeName); i < l; i++ {
		if routeName[i] == ':' {
			j := i + 1

			for ; i < l && routeName[i] != '/'; i++ {
			}

			routeName = routeName[:j] + routeName[i:]
			i, l = j, len(routeName)

			if i == l {
				break
			}
		}
	}

	if r.registeredRoutes[routeName] {
		panic("air: route already exists")
	} else {
		r.registeredRoutes[routeName] = true
	}

	rh := func(req *Request, res *Response) error {
		h := h
		for i := len(gases) - 1; i >= 0; i-- {
			h = gases[i](h)
		}

		return h(req, res)
	}

	paramNames := []string{}
	for i, l := 0, len(path); i < l; i++ {
		if path[i] == ':' {
			j := i + 1

			r.insert(
				method,
				path[:i],
				nil,
				routeNodeTypeStatic,
				nil,
			)

			for ; i < l && path[i] != '/'; i++ {
			}

			paramName := path[j:i]

			for _, pn := range paramNames {
				if pn == paramName {
					panic("air: route path cannot have " +
						"duplicate param names")
				}
			}

			paramNames = append(paramNames, paramName)
			path = path[:j] + path[i:]

			if i, l = j, len(path); i == l {
				r.insert(
					method,
					path,
					rh,
					routeNodeTypeParam,
					paramNames,
				)
				return
			}

			r.insert(
				method,
				path[:i],
				nil,
				routeNodeTypeParam,
				paramNames,
			)
		} else if path[i] == '*' {
			r.insert(
				method,
				path[:i],
				nil,
				routeNodeTypeStatic,
				nil,
			)
			paramNames = append(paramNames, "*")
			r.insert(
				method,
				path[:i+1],
				rh,
				routeNodeTypeAny,
				paramNames,
			)
			return
		}
	}

	r.insert(method, path, rh, routeNodeTypeStatic, paramNames)
}

// insert inserts a new route into the `r.routeTree`.
func (r *router) insert(
	method string,
	path string,
	h Handler,
	nt routeNodeType,
	paramNames []string,
) {
	if l := len(paramNames); l > r.maxRouteParams {
		r.maxRouteParams = l
	}

	var (
		s  = path        // Search
		cn = r.routeTree // Current node
		nn *routeNode    // Next node
		sl int           // Search length
		pl int           // Prefix length
		ll int           // LCP length
		ml int           // Minimum length of sl and pl
	)

	for {
		sl = len(s)
		pl = len(cn.prefix)
		ll = 0

		ml = pl
		if sl < ml {
			ml = sl
		}

		for ; ll < ml && s[ll] == cn.prefix[ll]; ll++ {
		}

		if ll == 0 { // At root node
			cn.label = s[0]
			cn.nType = nt
			cn.prefix = s
			cn.paramNames = paramNames
			if h != nil {
				cn.handlers[method] = h
			}
		} else if ll < pl { // Split node
			nn = &routeNode{
				label:      cn.prefix[ll],
				nType:      cn.nType,
				prefix:     cn.prefix[ll:],
				children:   cn.children,
				paramNames: cn.paramNames,
				handlers:   cn.handlers,
			}

			// Reset current node.
			cn.label = cn.prefix[0]
			cn.nType = routeNodeTypeStatic
			cn.prefix = cn.prefix[:ll]
			cn.children = []*routeNode{nn}
			cn.paramNames = nil
			cn.handlers = map[string]Handler{}

			if ll == sl { // At current node
				cn.nType = nt
				cn.paramNames = paramNames
				if h != nil {
					cn.handlers[method] = h
				}
			} else { // Create child node
				nn = &routeNode{
					label:      s[ll],
					nType:      nt,
					prefix:     s[ll:],
					paramNames: paramNames,
					handlers:   map[string]Handler{},
				}
				if h != nil {
					nn.handlers[method] = h
				}

				cn.children = append(cn.children, nn)
			}
		} else if ll < sl {
			s = s[ll:]
			if nn = cn.childByLabel(s[0]); nn != nil {
				// Go deeper.
				cn = nn
				continue
			}

			// Create child node.
			nn = &routeNode{
				label:      s[0],
				nType:      nt,
				prefix:     s,
				handlers:   map[string]Handler{},
				paramNames: paramNames,
			}
			if h != nil {
				nn.handlers[method] = h
			}

			cn.children = append(cn.children, nn)
		} else { // Node already exists
			if len(cn.paramNames) == 0 {
				cn.paramNames = paramNames
			}

			if h != nil {
				cn.handlers[method] = h
			}
		}

		break
	}
}

// route returns a handler registered for the req.
func (r *router) route(req *Request) Handler {
	var (
		s, _ = splitPathQuery(req.Path) // Search
		cn   = r.routeTree              // Current node
		nn   *routeNode                 // Next node
		nnt  routeNodeType              // Next node type
		sn   *routeNode                 // Saved node
		ss   string                     // Saved search
		sl   int                        // Search length
		pl   int                        // Prefix length
		ll   int                        // LCP length
		ml   int                        // Minimum length of sl and pl
		i    int                        // Index
		pi   int                        // Param index
	)

	// Search order: static route > param route > any route.
	for {
		if s == "" {
			break
		}

		for i, sl = 1, len(s); i < sl && s[i] == '/'; i++ {
		}

		s = s[i-1:]

		pl = 0
		ll = 0

		if cn.label != ':' {
			pl = len(cn.prefix)

			ml = pl
			if sl = len(s); sl < ml {
				ml = sl
			}

			for ; ll < ml && s[ll] == cn.prefix[ll]; ll++ {
			}
		}

		if ll != pl {
			goto Struggle
		}

		if s = s[ll:]; s == "" {
			if len(cn.handlers) == 0 {
				if cn.childByType(routeNodeTypeParam) != nil {
					goto Param
				}

				if cn.childByType(routeNodeTypeAny) != nil {
					goto Any
				}
			}

			break
		}

		// Static node.
		if nn = cn.child(s[0], routeNodeTypeStatic); nn != nil {
			// Save next.
			if l := len(cn.prefix); l > 0 && cn.prefix[l-1] == '/' {
				nnt = routeNodeTypeParam
				sn = cn
				ss = s
			}

			cn = nn

			continue
		}

		// Param node.
	Param:
		if nn = cn.childByType(routeNodeTypeParam); nn != nil {
			// Save next.
			if l := len(cn.prefix); l > 0 && cn.prefix[l-1] == '/' {
				nnt = routeNodeTypeAny
				sn = cn
				ss = s
			}

			cn = nn

			for i, sl = 0, len(s); i < sl && s[i] != '/'; i++ {
			}

			if req.routeParamValues == nil {
				req.routeParamValues = r.routeParamValuesPool.
					Get().([]string)
			}

			req.routeParamValues[pi] = s[:i]
			pi++

			s = s[i:]

			continue
		}

		// Any node.
	Any:
		if cn = cn.childByType(routeNodeTypeAny); cn != nil {
			if req.routeParamValues == nil {
				req.routeParamValues = r.routeParamValuesPool.
					Get().([]string)
			}

			req.routeParamValues[len(cn.paramNames)-1] = s

			break
		}

		// Struggle for the former node.
	Struggle:
		if sn != nil {
			cn = sn
			sn = nil
			s = ss

			switch nnt {
			case routeNodeTypeParam:
				goto Param
			case routeNodeTypeAny:
				goto Any
			}
		}

		return r.a.NotFoundHandler
	}

	if h := cn.handlers[req.Method]; h != nil {
		req.routeParamNames = cn.paramNames
		return h
	} else if len(cn.handlers) != 0 {
		return r.a.MethodNotAllowedHandler
	}

	return r.a.NotFoundHandler
}

// routeNode is the node of the route radix tree.
type routeNode struct {
	label      byte
	nType      routeNodeType
	prefix     string
	children   []*routeNode
	paramNames []string
	handlers   map[string]Handler
}

// child returns a child node of the rn by the l and the t.
func (rn *routeNode) child(l byte, t routeNodeType) *routeNode {
	for _, c := range rn.children {
		if c.label == l && c.nType == t {
			return c
		}
	}

	return nil
}

// childByLabel returns a child node of the rn by the l.
func (rn *routeNode) childByLabel(l byte) *routeNode {
	for _, c := range rn.children {
		if c.label == l {
			return c
		}
	}

	return nil
}

// childByType returns a child node of the rn by the t.
func (rn *routeNode) childByType(t routeNodeType) *routeNode {
	for _, c := range rn.children {
		if c.nType == t {
			return c
		}
	}

	return nil
}

// routeNodeType is the type of the `routeNode`.
type routeNodeType uint8

// The route node types.
const (
	routeNodeTypeStatic routeNodeType = iota
	routeNodeTypeParam
	routeNodeTypeAny
)

// splitPathQuery splits the p of the form "path?query" into path and query.
func splitPathQuery(p string) (path, query string) {
	i, l := 0, len(p)
	for ; i < l && p[i] != '?'; i++ {
	}

	if i < l {
		return p[:i], p[i+1:]
	}

	return p, ""
}
<|endoftext|>


<commit_before>// Copyright 2016 Marcel Gotsch. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package goserv

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

// A Router dispatches incoming requests to matching routes and routers.
//
// Note that most methods return the Router itself to allow method chaining.
// Some methods like .Route or .SubRouter return the created instances instead.
type Router struct {
	// Handles errors set on the RequestContext with .Error, not found errors
	// and recovered panics.
	ErrorHandler ErrorHandlerFunc

	// Defines how Routes treat the trailing slash in a path.
	//
	// When enabled routes with a trailing slash are considered to be different routes
	// than routes without a trailing slash.
	StrictSlash bool

	// Enables/Disables panic recovery
	PanicRecovery bool

	path          string
	paramHandlers paramHandlerMap
	routes        []*Route
}

// All registers the specified HandlerFunc for the given path for
// all http methods.
func (r *Router) All(path string, fn http.HandlerFunc) *Router {
	r.Route(path).All(fn)
	return r
}

// Method registers the specified HandlerFunc for the given path
// and method.
func (r *Router) Method(method, path string, fn http.HandlerFunc) *Router {
	r.Route(path).Method(method, fn)
	return r
}

// Methods is an adapter for Method for registering a HandlerFunc on a path for multiple methods
// in a single call.
func (r *Router) Methods(methods []string, path string, fn http.HandlerFunc) *Router {
	r.Route(path).Methods(methods, fn)
	return r
}

// Get is an adapter for Method registering a HandlerFunc for the "GET" method on path.
func (r *Router) Get(path string, fn http.HandlerFunc) *Router {
	return r.Method(http.MethodGet, path, fn)
}

// Post is an adapter for Method registering a HandlerFunc for the "POST" method on path.
func (r *Router) Post(path string, fn http.HandlerFunc) *Router {
	return r.Method(http.MethodPost, path, fn)
}

// Put is an adapter for Method registering a HandlerFunc for the "PUT" method on path.
func (r *Router) Put(path string, fn http.HandlerFunc) *Router {
	return r.Method(http.MethodPut, path, fn)
}

// Delete is an adapter for Method registering a HandlerFunc for the "DELETE" method on path.
func (r *Router) Delete(path string, fn http.HandlerFunc) *Router {
	return r.Method(http.MethodDelete, path, fn)
}

// Patch is an adapter for Method registering a HandlerFunc for the "PATCH" method on path.
func (r *Router) Patch(path string, fn http.HandlerFunc) *Router {
	return r.Method(http.MethodPatch, path, fn)
}

// Use registers the specified function as middleware.
// Middleware is always processed before any dispatching happens.
func (r *Router) Use(fn http.HandlerFunc) *Router {
	r.Route("/*").All(fn)
	return r
}

// UseHandler is an adapter for Use to register a Handler as middleware.
func (r *Router) UseHandler(handler http.Handler) *Router {
	r.Route("/*").All(handler.ServeHTTP)
	return r
}

// Param registers a handler for the specified parameter name (without the leading ":").
//
// Parameter handlers are invoked with the extracted value before any route is processed.
// All handlers are only invoked once per request, even though the request may be dispatched
// to multiple routes.
func (r *Router) Param(name string, fn ParamHandlerFunc) *Router {
	r.paramHandlers[name] = append(r.paramHandlers[name], fn)
	return r
}

// SubRouter returns a new sub router mounted on the specified prefix.
//
// All sub routers automatically inherit their StrictSlash behaviour,
// have the full mount path and no error handler. It is possible though
// to set a custom error handler for a sub router.
//
// Note that this function returns the new sub router instead of the
// parent router!
func (r *Router) SubRouter(prefix string) *Router {
	router := newRouter()
	router.StrictSlash = r.StrictSlash
	router.path = r.path + prefix

	r.addRoute(newRoute(prefix, r.StrictSlash, true).All(router.serveHTTP))

	return router
}

// Route returns a new Route for the given path.
func (r *Router) Route(path string) *Route {
	route := newRoute(path, r.StrictSlash, false)
	r.addRoute(route)
	return route
}

// Path returns the routers full mount path.
func (r *Router) Path() string {
	return r.path
}

func (r *Router) serveHTTP(res http.ResponseWriter, req *http.Request) {
	if r.PanicRecovery {
		defer r.handleRecovery(res, req)
	}

	ctx := Context(req)

	r.invokeHandlers(res, req, ctx)

	if res.(*responseWriter).Written() || r.ErrorHandler == nil {
		return
	}

	if ctx.err == nil {
		ctx.Error(ErrNotFound, http.StatusNotFound)
	}

	r.ErrorHandler(res, req, ctx.err)
}

func (r *Router) invokeHandlers(res http.ResponseWriter, req *http.Request, ctx *RequestContext) {
	path := strings.TrimPrefix(SanitizePath(req.URL.Path), r.path)

	paramInvoked := make(map[string]bool)

	for _, route := range r.routes {
		if !route.match(path) {
			continue
		}

		// Call param handlers in the same order in which the parameters appear in the path.
		route.fillParams(req, ctx.params)
		for _, name := range route.params() {
			if paramInvoked[name] {
				continue
			}

			value := ctx.Param(name)

			for _, paramHandler := range r.paramHandlers[name] {
				paramHandler(res, req, value)

				if doneProcessing(res.(*responseWriter), ctx) {
					return
				}
			}

			paramInvoked[name] = true
		}

		route.serveHTTP(res, req)

		if doneProcessing(res.(*responseWriter), ctx) {
			return
		}
	}
}

func (r *Router) handleRecovery(res http.ResponseWriter, req *http.Request) {
	if err := recover(); err != nil {
		r.ErrorHandler(res, req, &ContextError{fmt.Errorf("Panic: %v", err), http.StatusInternalServerError})
	}
}

func (r *Router) addRoute(route *Route) *Router {
	r.routes = append(r.routes, route)
	return r
}

func newRouter() *Router {
	return &Router{paramHandlers: make(paramHandlerMap)}
}

type paramHandlerMap map[string][]ParamHandlerFunc
<commit_msg>Fix nil pointer deref in panic recovery<commit_after>// Copyright 2016 Marcel Gotsch. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package goserv

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

// A Router dispatches incoming requests to matching routes and routers.
//
// Note that most methods return the Router itself to allow method chaining.
// Some methods like .Route or .SubRouter return the created instances instead.
type Router struct {
	// Handles errors set on the RequestContext with .Error, not found errors
	// and recovered panics.
	ErrorHandler ErrorHandlerFunc

	// Defines how Routes treat the trailing slash in a path.
	//
	// When enabled routes with a trailing slash are considered to be different routes
	// than routes without a trailing slash.
	StrictSlash bool

	// Enables/Disables panic recovery
	PanicRecovery bool

	path          string
	paramHandlers paramHandlerMap
	routes        []*Route
}

// All registers the specified HandlerFunc for the given path for
// all http methods.
func (r *Router) All(path string, fn http.HandlerFunc) *Router {
	r.Route(path).All(fn)
	return r
}

// Method registers the specified HandlerFunc for the given path
// and method.
func (r *Router) Method(method, path string, fn http.HandlerFunc) *Router {
	r.Route(path).Method(method, fn)
	return r
}

// Methods is an adapter for Method for registering a HandlerFunc on a path for multiple methods
// in a single call.
func (r *Router) Methods(methods []string, path string, fn http.HandlerFunc) *Router {
	r.Route(path).Methods(methods, fn)
	return r
}

// Get is an adapter for Method registering a HandlerFunc for the "GET" method on path.
func (r *Router) Get(path string, fn http.HandlerFunc) *Router {
	return r.Method(http.MethodGet, path, fn)
}

// Post is an adapter for Method registering a HandlerFunc for the "POST" method on path.
func (r *Router) Post(path string, fn http.HandlerFunc) *Router {
	return r.Method(http.MethodPost, path, fn)
}

// Put is an adapter for Method registering a HandlerFunc for the "PUT" method on path.
func (r *Router) Put(path string, fn http.HandlerFunc) *Router {
	return r.Method(http.MethodPut, path, fn)
}

// Delete is an adapter for Method registering a HandlerFunc for the "DELETE" method on path.
func (r *Router) Delete(path string, fn http.HandlerFunc) *Router {
	return r.Method(http.MethodDelete, path, fn)
}

// Patch is an adapter for Method registering a HandlerFunc for the "PATCH" method on path.
func (r *Router) Patch(path string, fn http.HandlerFunc) *Router {
	return r.Method(http.MethodPatch, path, fn)
}

// Use registers the specified function as middleware.
// Middleware is always processed before any dispatching happens.
func (r *Router) Use(fn http.HandlerFunc) *Router {
	r.Route("/*").All(fn)
	return r
}

// UseHandler is an adapter for Use to register a Handler as middleware.
func (r *Router) UseHandler(handler http.Handler) *Router {
	r.Route("/*").All(handler.ServeHTTP)
	return r
}

// Param registers a handler for the specified parameter name (without the leading ":").
//
// Parameter handlers are invoked with the extracted value before any route is processed.
// All handlers are only invoked once per request, even though the request may be dispatched
// to multiple routes.
func (r *Router) Param(name string, fn ParamHandlerFunc) *Router {
	r.paramHandlers[name] = append(r.paramHandlers[name], fn)
	return r
}

// SubRouter returns a new sub router mounted on the specified prefix.
//
// All sub routers automatically inherit their StrictSlash behaviour,
// have the full mount path and no error handler. It is possible though
// to set a custom error handler for a sub router.
//
// Note that this function returns the new sub router instead of the
// parent router!
func (r *Router) SubRouter(prefix string) *Router {
	router := newRouter()
	router.StrictSlash = r.StrictSlash
	router.path = r.path + prefix

	r.addRoute(newRoute(prefix, r.StrictSlash, true).All(router.serveHTTP))

	return router
}

// Route returns a new Route for the given path.
func (r *Router) Route(path string) *Route {
	route := newRoute(path, r.StrictSlash, false)
	r.addRoute(route)
	return route
}

// Path returns the routers full mount path.
func (r *Router) Path() string {
	return r.path
}

func (r *Router) serveHTTP(res http.ResponseWriter, req *http.Request) {
	if r.PanicRecovery {
		defer r.handleRecovery(res, req)
	}

	ctx := Context(req)

	r.invokeHandlers(res, req, ctx)

	if res.(*responseWriter).Written() || r.ErrorHandler == nil {
		return
	}

	if ctx.err == nil {
		ctx.Error(ErrNotFound, http.StatusNotFound)
	}

	r.ErrorHandler(res, req, ctx.err)
}

func (r *Router) invokeHandlers(res http.ResponseWriter, req *http.Request, ctx *RequestContext) {
	path := strings.TrimPrefix(SanitizePath(req.URL.Path), r.path)

	paramInvoked := make(map[string]bool)

	for _, route := range r.routes {
		if !route.match(path) {
			continue
		}

		// Call param handlers in the same order in which the parameters appear in the path.
		route.fillParams(req, ctx.params)
		for _, name := range route.params() {
			if paramInvoked[name] {
				continue
			}

			value := ctx.Param(name)

			for _, paramHandler := range r.paramHandlers[name] {
				paramHandler(res, req, value)

				if doneProcessing(res.(*responseWriter), ctx) {
					return
				}
			}

			paramInvoked[name] = true
		}

		route.serveHTTP(res, req)

		if doneProcessing(res.(*responseWriter), ctx) {
			return
		}
	}
}

func (r *Router) handleRecovery(res http.ResponseWriter, req *http.Request) {
	if err := recover(); err != nil && r.ErrorHandler != nil {
		r.ErrorHandler(res, req, &ContextError{fmt.Errorf("Panic: %v", err), http.StatusInternalServerError})
	}
}

func (r *Router) addRoute(route *Route) *Router {
	r.routes = append(r.routes, route)
	return r
}

func newRouter() *Router {
	return &Router{paramHandlers: make(paramHandlerMap)}
}

type paramHandlerMap map[string][]ParamHandlerFunc
<|endoftext|>


<commit_before>package main

import (
	"github.com/gin-gonic/gin"
	csh_auth "github.com/liam-middlebrook/csh-auth"
	log "github.com/sirupsen/logrus"
	"image"
	_ "image/jpeg"
	_ "image/png"
	"io"
	"net/http"
	"strings"
	"time"
)

func protectedProfile(c *gin.Context) {
	claims, ok := c.Value(csh_auth.AuthKey).(csh_auth.CSHClaims)
	if !ok {
		log.Fatal("error finding claims")
		return
	}
	c.String(http.StatusOK, "uid %s email %s name %s uuid %s", claims.UserInfo.Username, claims.UserInfo.Email, claims.UserInfo.FullName, claims.UserInfo.Subject)
}

func index(c *gin.Context) {
	c.Redirect(http.StatusFound, "/upload")
}

func action(c *gin.Context) {
	plug := GetPlug()
	url := S3PresignPlug(plug)

	claims, ok := c.Value(csh_auth.AuthKey).(csh_auth.CSHClaims)
	if !ok {
		log.Fatal("error finding claims")
		return
	}
	log.WithFields(log.Fields{
		"uid":           claims.UserInfo.Username,
		"plug_id":       plug.ID,
		"plug_s3id":     plug.S3ID,
		"presigned_uri": url.String(),
	}).Info("Presigned URI Generated")
	c.Redirect(http.StatusFound, url.String())
}

func upload(c *gin.Context) {
	plug := Plug{}

	claims, ok := c.Value(csh_auth.AuthKey).(csh_auth.CSHClaims)
	if !ok {
		log.Fatal("error finding claims")
		return
	}

	plug.Owner = claims.UserInfo.Username
	plug.ViewsRemaining = 100

	file, err := c.FormFile("file")
	if err != nil {
		log.Error(err)
		c.String(http.StatusBadRequest, "Error Reading File")
		return
	}
	data, err := file.Open()
	if err != nil {
		log.Error(err)
		c.String(http.StatusBadRequest, "Error Reading File")
		return
	}
	defer data.Close()
	imageData, _, err := image.DecodeConfig(data)
	if err != nil {
		log.Error(err)
		c.String(http.StatusUnsupportedMediaType, "Please upload either a JPG or PNG!")
		return
	}
	data.Seek(0, 0)
	if imageData.Width == 728 && imageData.Height == 200 {
		mime := getMime(data)
		data.Seek(0, 0)

		if !DecrementCredits(plug.Owner, 1) {
			c.String(http.StatusPaymentRequired, "Get More Credits!")
			return
		}

		plug.S3ID = time.Now().Format("2006/01/02/150405") + "-" + plug.Owner + "-" + file.Filename
		S3AddFile(plug, data, mime)

		MakePlug(plug)
	} else {
		log.Error("invalid file dimensions")
		c.String(http.StatusBadRequest, "Please upload a 728x200 pixel image!")
		return
	}
	AddLog(1, "uid: "+claims.UserInfo.Username+"uploaded plug id: "+string(plug.ID))
	c.Data(http.StatusOK, "text/html", []byte(`

Uploaded a Plug!

Take a look at what you uploaded! (This does not count towards the views for your Plug!)

Due to High Volumes your Plug MUST be approved by a member of any of the following groups (drink, eboard, rtp)! Check /admin

			<img src="`+S3PresignPlug(plug).String()+`">

	`))
	log.WithFields(log.Fields{
		"uid":       claims.UserInfo.Username,
		"plug_id":   plug.ID,
		"plug_s3id": plug.S3ID,
	}).Info("Uploaded new Plug!")
}

func upload_view(c *gin.Context) {
	c.Data(http.StatusOK, "text/html", []byte(`

Upload a Plug!

You will lose 1 drink credit in exchange for a 100 view-limit plug!

Plugs must be 728x200 pixels and in PNG, or JPG format!

Due to High Volumes your Plug MUST be approved by a member of any of the following groups (drink, eboard, rtp)! Check /admin

			<form action="/upload" method="post" enctype="multipart/form-data">
				<input type="file" name="file" id="file">
				<input type="submit" value="Upload" name="submit">
			</form>

			CSH: Plug on GitHub

	`))
}

func get_pending_plugs(c *gin.Context) {
	claims, ok := c.Value(csh_auth.AuthKey).(csh_auth.CSHClaims)
	if !ok {
		log.Fatal("error finding claims")
		return
	}

	if !CheckIfAdmin(claims.UserInfo.Username) {
		c.Redirect(http.StatusFound, "/")
		return
	}
	plugs := GetPendingPlugs()
	var out_plugs []Plug

	for _, plug := range plugs {
		new := plug
		new.PresignedURL = S3PresignPlug(plug).String()
		out_plugs = append(out_plugs, new)
	}
	c.HTML(http.StatusOK, "view_plugs.tmpl", gin.H{
		"plugs": out_plugs,
	})
}

func plug_approval(c *gin.Context) {
	claims, ok := c.Value(csh_auth.AuthKey).(csh_auth.CSHClaims)
	if !ok {
		log.Fatal("error finding claims")
		return
	}

	if !CheckIfAdmin(claims.UserInfo.Username) {
		c.Redirect(http.StatusFound, "/")
		return
	}

	var plugList PlugList
	c.Bind(&plugList)

	log.WithFields(log.Fields{
		"uid":            claims.UserInfo.Username,
		"plugs_approved": strings.Join(plugList.Data, ","),
	}).Info("Changed Approved Plug List")

	AddLog(1, "uid: "+claims.UserInfo.Username+"approved: "+strings.Join(plugList.Data, ","))

	SetPendingPlugs(plugList.Data)
	c.Redirect(http.StatusFound, "/admin")
}

func getMime(data io.Reader) string {
	buffer := make([]byte, 512)
	n, err := data.Read(buffer)
	if err != nil {
		log.Error(err)
	}
	return http.DetectContentType(buffer[:n])
}
<commit_msg>log s3id on upload<commit_after>package main

import (
	"github.com/gin-gonic/gin"
	csh_auth "github.com/liam-middlebrook/csh-auth"
	log "github.com/sirupsen/logrus"
	"image"
	_ "image/jpeg"
	_ "image/png"
	"io"
	"net/http"
	"strings"
	"time"
)

func protectedProfile(c *gin.Context) {
	claims, ok := c.Value(csh_auth.AuthKey).(csh_auth.CSHClaims)
	if !ok {
		log.Fatal("error finding claims")
		return
	}
	c.String(http.StatusOK, "uid %s email %s name %s uuid %s", claims.UserInfo.Username, claims.UserInfo.Email, claims.UserInfo.FullName, claims.UserInfo.Subject)
}

func index(c *gin.Context) {
	c.Redirect(http.StatusFound, "/upload")
}

func action(c *gin.Context) {
	plug := GetPlug()
	url := S3PresignPlug(plug)

	claims, ok := c.Value(csh_auth.AuthKey).(csh_auth.CSHClaims)
	if !ok {
		log.Fatal("error finding claims")
		return
	}
	log.WithFields(log.Fields{
		"uid":           claims.UserInfo.Username,
		"plug_id":       plug.ID,
		"plug_s3id":     plug.S3ID,
		"presigned_uri": url.String(),
	}).Info("Presigned URI Generated")
	c.Redirect(http.StatusFound, url.String())
}

func upload(c *gin.Context) {
	plug := Plug{}

	claims, ok := c.Value(csh_auth.AuthKey).(csh_auth.CSHClaims)
	if !ok {
		log.Fatal("error finding claims")
		return
	}

	plug.Owner = claims.UserInfo.Username
	plug.ViewsRemaining = 100

	file, err := c.FormFile("file")
	if err != nil {
		log.Error(err)
		c.String(http.StatusBadRequest, "Error Reading File")
		return
	}
	data, err := file.Open()
	if err != nil {
		log.Error(err)
		c.String(http.StatusBadRequest, "Error Reading File")
		return
	}
	defer data.Close()
	imageData, _, err := image.DecodeConfig(data)
	if err != nil {
		log.Error(err)
		c.String(http.StatusUnsupportedMediaType, "Please upload either a JPG or PNG!")
		return
	}
	data.Seek(0, 0)
	if imageData.Width == 728 && imageData.Height == 200 {
		mime := getMime(data)
		data.Seek(0, 0)

		if !DecrementCredits(plug.Owner, 1) {
			c.String(http.StatusPaymentRequired, "Get More Credits!")
			return
		}

		plug.S3ID = time.Now().Format("2006/01/02/150405") + "-" + plug.Owner + "-" + file.Filename
		S3AddFile(plug, data, mime)

		MakePlug(plug)
	} else {
		log.Error("invalid file dimensions")
		c.String(http.StatusBadRequest, "Please upload a 728x200 pixel image!")
		return
	}
	AddLog(1, "uid: "+plug.Owner+"uploaded plug s3id"+plug.S3ID)
	c.Data(http.StatusOK, "text/html", []byte(`

Uploaded a Plug!

Take a look at what you uploaded! (This does not count towards the views for your Plug!)

Due to High Volumes your Plug MUST be approved by a member of any of the following groups (drink, eboard, rtp)! Check /admin

			<img src="`+S3PresignPlug(plug).String()+`">

	`))
	log.WithFields(log.Fields{
		"uid":       claims.UserInfo.Username,
		"plug_id":   plug.ID,
		"plug_s3id": plug.S3ID,
	}).Info("Uploaded new Plug!")
}

func upload_view(c *gin.Context) {
	c.Data(http.StatusOK, "text/html", []byte(`

Upload a Plug!

You will lose 1 drink credit in exchange for a 100 view-limit plug!

Plugs must be 728x200 pixels and in PNG, or JPG format!

Due to High Volumes your Plug MUST be approved by a member of any of the following groups (drink, eboard, rtp)! Check /admin

			<form action="/upload" method="post" enctype="multipart/form-data">
				<input type="file" name="file" id="file">
				<input type="submit" value="Upload" name="submit">
			</form>

			CSH: Plug on GitHub

	`))
}

func get_pending_plugs(c *gin.Context) {
	claims, ok := c.Value(csh_auth.AuthKey).(csh_auth.CSHClaims)
	if !ok {
		log.Fatal("error finding claims")
		return
	}

	if !CheckIfAdmin(claims.UserInfo.Username) {
		c.Redirect(http.StatusFound, "/")
		return
	}
	plugs := GetPendingPlugs()
	var out_plugs []Plug

	for _, plug := range plugs {
		new := plug
		new.PresignedURL = S3PresignPlug(plug).String()
		out_plugs = append(out_plugs, new)
	}
	c.HTML(http.StatusOK, "view_plugs.tmpl", gin.H{
		"plugs": out_plugs,
	})
}

func plug_approval(c *gin.Context) {
	claims, ok := c.Value(csh_auth.AuthKey).(csh_auth.CSHClaims)
	if !ok {
		log.Fatal("error finding claims")
		return
	}

	if !CheckIfAdmin(claims.UserInfo.Username) {
		c.Redirect(http.StatusFound, "/")
		return
	}

	var plugList PlugList
	c.Bind(&plugList)

	log.WithFields(log.Fields{
		"uid":            claims.UserInfo.Username,
		"plugs_approved": strings.Join(plugList.Data, ","),
	}).Info("Changed Approved Plug List")

	AddLog(1, "uid: "+claims.UserInfo.Username+"approved: "+strings.Join(plugList.Data, ","))

	SetPendingPlugs(plugList.Data)
	c.Redirect(http.StatusFound, "/admin")
}

func getMime(data io.Reader) string {
	buffer := make([]byte, 512)
	n, err := data.Read(buffer)
	if err != nil {
		log.Error(err)
	}
	return http.DetectContentType(buffer[:n])
}
<|endoftext|>


<commit_before>package stream

import (
	"math/rand"
	"time"
)

// Sample picks n pseudo-randomly chosen input items.
func Sample(n int) Filter {
	return FilterFunc(func(arg Arg) error {
		// Could speed this up by using Algorithm Z from Vitter.
		r := rand.New(rand.NewSource(time.Now().UnixNano()))
		reservoir := make([]string, 0, n)
		i := 0
		for s := range arg.In {
			switch {
			case i < n:
				reservoir = append(reservoir, s)
			case r.Float32() < float32(n)/float32(i+1):
				reservoir[r.Intn(n)] = s
			}
			i++
		}
		for _, s := range reservoir {
			arg.Out <- s
		}
		return nil
	})
}
<commit_msg>Added comments based on feedback from Nigel<commit_after>package stream

import (
	"math/rand"
	"time"
)

// Sample picks n pseudo-randomly chosen input items.
// TODO: Maybe add SampleWithSeed.
func Sample(n int) Filter {
	return FilterFunc(func(arg Arg) error {
		// Could speed this up by using Algorithm Z from Vitter.
		r := rand.New(rand.NewSource(time.Now().UnixNano()))
		reservoir := make([]string, 0, n)
		i := 0
		for s := range arg.In {
			switch {
			case i < n:
				reservoir = append(reservoir, s)
			case r.Float32() < float32(n)/float32(i+1):
				// TODO: Maybe use float64.  Or try
				//   j := r.Intn(i+1)
				//   if j < n { reservoir[j] = s }
				reservoir[r.Intn(n)] = s
			}
			i++
		}
		for _, s := range reservoir {
			arg.Out <- s
		}
		return nil
	})
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 VMware, Inc. 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 config

import (
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/vmware/harbor/src/common/utils/test"
)

// test functions under package ui/config
func TestConfig(t *testing.T) {
	server, err := test.NewAdminserver(nil)
	if err != nil {
		t.Fatalf("failed to create a mock admin server: %v", err)
	}
	defer server.Close()

	if err := os.Setenv("ADMINSERVER_URL", server.URL); err != nil {
		t.Fatalf("failed to set env %s: %v", "ADMINSERVER_URL", err)
	}

	secretKeyPath := "/tmp/secretkey"
	_, err = test.GenerateKey(secretKeyPath)
	if err != nil {
		t.Errorf("failed to generate secret key: %v", err)
		return
	}
	defer os.Remove(secretKeyPath)
	assert := assert.New(t)

	if err := os.Setenv("KEY_PATH", secretKeyPath); err != nil {
		t.Fatalf("failed to set env %s: %v", "KEY_PATH", err)
	}

	if err := Init(); err != nil {
		t.Fatalf("failed to initialize configurations: %v", err)
	}

	if err := Load(); err != nil {
		t.Fatalf("failed to load configurations: %v", err)
	}

	if err := Upload(map[string]interface{}{}); err != nil {
		t.Fatalf("failed to upload configurations: %v", err)
	}

	if _, err := GetSystemCfg(); err != nil {
		t.Fatalf("failed to get system configurations: %v", err)
	}

	mode, err := AuthMode()
	if err != nil {
		t.Fatalf("failed to get auth mode: %v", err)
	}
	if mode != "db_auth" {
		t.Errorf("unexpected mode: %s != %s", mode, "db_auth")
	}

	if _, err := LDAP(); err != nil {
		t.Fatalf("failed to get ldap settings: %v", err)
	}

	if _, err := TokenExpiration(); err != nil {
		t.Fatalf("failed to get token expiration: %v", err)
	}

	if _, err := ExtEndpoint(); err != nil {
		t.Fatalf("failed to get domain name: %v", err)
	}

	if _, err := SecretKey(); err != nil {
		t.Fatalf("failed to get secret key: %v", err)
	}

	if _, err := SelfRegistration(); err != nil {
		t.Fatalf("failed to get self registration: %v", err)
	}

	if _, err := RegistryURL(); err != nil {
		t.Fatalf("failed to get registry URL: %v", err)
	}

	if len(InternalJobServiceURL()) == 0 {
		t.Error("the internal job service url is null")
	}

	if len(InternalTokenServiceEndpoint()) == 0 {
		t.Error("the internal token service endpoint is null")
	}

	if _, err := InitialAdminPassword(); err != nil {
		t.Fatalf("failed to get initial admin password: %v", err)
	}

	if _, err := OnlyAdminCreateProject(); err != nil {
		t.Fatalf("failed to get onldy admin create project: %v", err)
	}

	if _, err := Email(); err != nil {
		t.Fatalf("failed to get email settings: %v", err)
	}

	if _, err := Database(); err != nil {
		t.Fatalf("failed to get database: %v", err)
	}

	if _, err := ClairDB(); err != nil {
		t.Fatalf("failed to get clair DB %v", err)
	}
	if InternalNotaryEndpoint() != "http://notary-server:4443" {
		t.Errorf("Unexpected notary endpoint: %s", InternalNotaryEndpoint())
	}
	if WithNotary() {
		t.Errorf("Withnotary should be false")
	}
	if WithClair() {
		t.Errorf("WithClair should be false")
	}
	if !WithAdmiral() {
		t.Errorf("WithAdmiral should be true")
	}
	if AdmiralEndpoint() != "http://www.vmware.com" {
		t.Errorf("Unexpected admiral endpoint: %s", AdmiralEndpoint())
	}

	extURL, err := ExtURL()
	if err != nil {
		t.Errorf("Unexpected error getting external URL: %v", err)
	}
	if extURL != "host01.com" {
		t.Errorf(`extURL should be "host01.com".`)
	}

	// reset configurations
	if err = Reset(); err != nil {
		t.Errorf("failed to reset configurations: %v", err)
		return
	}
	mode, err = AuthMode()
	if err != nil {
		t.Fatalf("failed to get auth mode: %v", err)
	}
	if mode != "db_auth" {
		t.Errorf("unexpected mode: %s != %s", mode, "db_auth")
	}

	if s := ScanAllPolicy(); s.Type != "daily" {
		t.Errorf("unexpected scan all policy %v", s)
	}

	us, err := UAASettings()
	if err != nil {
		t.Fatalf("failed to get UAA setting, error: %v", err)
	}

	if us.ClientID != "testid" || us.ClientSecret != "testsecret" || us.Endpoint != "10.192.168.5" || us.VerifyCert {
		t.Errorf("Unexpected UAA setting: %+v", *us)
	}
	assert.Equal("http://myjob:8888", InternalJobServiceURL())
	assert.Equal("http://myui:8888/service/token", InternalTokenServiceEndpoint())

}
<commit_msg>Add test case for Clair DB configuration<commit_after>// Copyright (c) 2017 VMware, Inc. 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 config

import (
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/vmware/harbor/src/common/utils/test"
	"github.com/vmware/harbor/src/common"
)

// test functions under package ui/config
func TestConfig(t *testing.T) {
	server, err := test.NewAdminserver(nil)
	if err != nil {
		t.Fatalf("failed to create a mock admin server: %v", err)
	}
	defer server.Close()

	if err := os.Setenv("ADMINSERVER_URL", server.URL); err != nil {
		t.Fatalf("failed to set env %s: %v", "ADMINSERVER_URL", err)
	}

	secretKeyPath := "/tmp/secretkey"
	_, err = test.GenerateKey(secretKeyPath)
	if err != nil {
		t.Errorf("failed to generate secret key: %v", err)
		return
	}
	defer os.Remove(secretKeyPath)
	assert := assert.New(t)

	if err := os.Setenv("KEY_PATH", secretKeyPath); err != nil {
		t.Fatalf("failed to set env %s: %v", "KEY_PATH", err)
	}

	if err := Init(); err != nil {
		t.Fatalf("failed to initialize configurations: %v", err)
	}

	if err := Load(); err != nil {
		t.Fatalf("failed to load configurations: %v", err)
	}

	if err := Upload(map[string]interface{}{}); err != nil {
		t.Fatalf("failed to upload configurations: %v", err)
	}

	if _, err := GetSystemCfg(); err != nil {
		t.Fatalf("failed to get system configurations: %v", err)
	}

	mode, err := AuthMode()
	if err != nil {
		t.Fatalf("failed to get auth mode: %v", err)
	}
	if mode != "db_auth" {
		t.Errorf("unexpected mode: %s != %s", mode, "db_auth")
	}

	if _, err := LDAP(); err != nil {
		t.Fatalf("failed to get ldap settings: %v", err)
	}

	if _, err := TokenExpiration(); err != nil {
		t.Fatalf("failed to get token expiration: %v", err)
	}

	if _, err := ExtEndpoint(); err != nil {
		t.Fatalf("failed to get domain name: %v", err)
	}

	if _, err := SecretKey(); err != nil {
		t.Fatalf("failed to get secret key: %v", err)
	}

	if _, err := SelfRegistration(); err != nil {
		t.Fatalf("failed to get self registration: %v", err)
	}

	if _, err := RegistryURL(); err != nil {
		t.Fatalf("failed to get registry URL: %v", err)
	}

	if len(InternalJobServiceURL()) == 0 {
		t.Error("the internal job service url is null")
	}

	if len(InternalTokenServiceEndpoint()) == 0 {
		t.Error("the internal token service endpoint is null")
	}

	if _, err := InitialAdminPassword(); err != nil {
		t.Fatalf("failed to get initial admin password: %v", err)
	}

	if _, err := OnlyAdminCreateProject(); err != nil {
		t.Fatalf("failed to get onldy admin create project: %v", err)
	}

	if _, err := Email(); err != nil {
		t.Fatalf("failed to get email settings: %v", err)
	}

	if _, err := Database(); err != nil {
		t.Fatalf("failed to get database: %v", err)
	}

	clairDB, err := ClairDB();
	if err != nil {
		t.Fatalf("failed to get clair DB %v", err)
	}
	adminServerDefaultConfig := test.GetDefaultConfigMap()
	assert.Equal(adminServerDefaultConfig[common.ClairDB],clairDB.Database)
	assert.Equal(adminServerDefaultConfig[common.ClairDBUsername],clairDB.Username)
	assert.Equal(adminServerDefaultConfig[common.ClairDBPassword],clairDB.Password)
	assert.Equal(adminServerDefaultConfig[common.ClairDBHost], clairDB.Host)
	assert.Equal(adminServerDefaultConfig[common.ClairDBPort], clairDB.Port)

	if InternalNotaryEndpoint() != "http://notary-server:4443" {
		t.Errorf("Unexpected notary endpoint: %s", InternalNotaryEndpoint())
	}
	if WithNotary() {
		t.Errorf("Withnotary should be false")
	}
	if WithClair() {
		t.Errorf("WithClair should be false")
	}
	if !WithAdmiral() {
		t.Errorf("WithAdmiral should be true")
	}
	if AdmiralEndpoint() != "http://www.vmware.com" {
		t.Errorf("Unexpected admiral endpoint: %s", AdmiralEndpoint())
	}

	extURL, err := ExtURL()
	if err != nil {
		t.Errorf("Unexpected error getting external URL: %v", err)
	}
	if extURL != "host01.com" {
		t.Errorf(`extURL should be "host01.com".`)
	}

	// reset configurations
	if err = Reset(); err != nil {
		t.Errorf("failed to reset configurations: %v", err)
		return
	}
	mode, err = AuthMode()
	if err != nil {
		t.Fatalf("failed to get auth mode: %v", err)
	}
	if mode != "db_auth" {
		t.Errorf("unexpected mode: %s != %s", mode, "db_auth")
	}

	if s := ScanAllPolicy(); s.Type != "daily" {
		t.Errorf("unexpected scan all policy %v", s)
	}

	us, err := UAASettings()
	if err != nil {
		t.Fatalf("failed to get UAA setting, error: %v", err)
	}

	if us.ClientID != "testid" || us.ClientSecret != "testsecret" || us.Endpoint != "10.192.168.5" || us.VerifyCert {
		t.Errorf("Unexpected UAA setting: %+v", *us)
	}
	assert.Equal("http://myjob:8888", InternalJobServiceURL())
	assert.Equal("http://myui:8888/service/token", InternalTokenServiceEndpoint())

}
<|endoftext|>


<commit_before>// Copyright 2016 fatedier, fatedier@gmail.com
//
// 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 version

import (
	"strconv"
	"strings"
)

var version string = "0.8.0"

func Full() string {
	return version
}

func Proto(v string) int64 {
	arr := strings.Split(v, ".")
	if len(arr) < 2 {
		return 0
	}
	res, _ := strconv.ParseInt(arr[0], 10, 64)
	return res
}

func Major(v string) int64 {
	arr := strings.Split(v, ".")
	if len(arr) < 2 {
		return 0
	}
	res, _ := strconv.ParseInt(arr[1], 10, 64)
	return res
}

func Minor(v string) int64 {
	arr := strings.Split(v, ".")
	if len(arr) < 2 {
		return 0
	}
	res, _ := strconv.ParseInt(arr[2], 10, 64)
	return res
}

// add every case there if server will not accept client's protocol and return false
func Compat(client string, server string) bool {
	return true
}
<commit_msg>bump version to 0.8.1<commit_after>// Copyright 2016 fatedier, fatedier@gmail.com
//
// 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 version

import (
	"strconv"
	"strings"
)

var version string = "0.8.1"

func Full() string {
	return version
}

func Proto(v string) int64 {
	arr := strings.Split(v, ".")
	if len(arr) < 2 {
		return 0
	}
	res, _ := strconv.ParseInt(arr[0], 10, 64)
	return res
}

func Major(v string) int64 {
	arr := strings.Split(v, ".")
	if len(arr) < 2 {
		return 0
	}
	res, _ := strconv.ParseInt(arr[1], 10, 64)
	return res
}

func Minor(v string) int64 {
	arr := strings.Split(v, ".")
	if len(arr) < 2 {
		return 0
	}
	res, _ := strconv.ParseInt(arr[2], 10, 64)
	return res
}

// add every case there if server will not accept client's protocol and return false
func Compat(client string, server string) bool {
	return true
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"errors"
	"fmt"
	"sort"
)

const (
	evalInf = 50000
)

var (
	errCheckmate checkmateError
	errStalemate = errors.New("stalemate")
	errFiftyMove = errors.New("fifty-move rule")
	// TODO: threefold repetition
)

// A checkmateError indicates the number of plies until a forced checkmate can be delivered.
// The zero value of type checkmateError indicates that the current position is checkmate.
type checkmateError int

func (n checkmateError) Error() string {
	if n&1 == 0 {
		return fmt.Sprintf("-#%d", n/2)
	}
	return fmt.Sprintf("#%d", (n+1)/2)
}

// Prev returns the checkmateError corresponding to the previous ply.
func (n checkmateError) Prev() checkmateError { return n + 1 }

// SearchPosition searches a Position to the specified depth via iterative deepening
// and returns the search results.
func SearchPosition(ctx context.Context, pos Position, depth int) Results {
	var results Results
	s := Search{
		allowCutoff: true,
		counters:    make([]int, depth+1),
	}
	for d := 1; d <= depth; d++ {
		_, results, _ = s.negamax(ctx, pos, results, Window{-evalInf, evalInf}, d)
		if ctx.Err() != nil {
			break
		}
	}
	return results
}

type Search struct {
	allowCutoff bool
	counters    []int
}

// negamax recursively searches a Position to the specified depth and returns the evaluation score
// relative to the side to move and the search results. It employs alpha-beta pruning outside of
// the specified Window. If recommended is zero length, negamax will generate and search all legal
// moves; if recommended moves are provided, they must all be legal, and only they will be searched.
func (s *Search) negamax(
	ctx context.Context,
	pos Position,
	recommended Results,
	w Window,
	depth int,
) (bestScore RelScore, results Results, err error) {
	s.counters[len(s.counters)-1-depth]++

	score, recommended, err := checkDone(pos, recommended)
	if err != nil && (s.allowCutoff || err != errInsufficient) {
		// Do not cut off during perft in the case of insufficient material
		return score, nil, err
	}
	// Invariant: len(recommended) > 0 and recommended contains only legal moves
	if depth == 0 {
		score, err := Eval(pos)
		return score.Rel(pos.ToMove), nil, err
	}

	results = recommended
	defer func() { results.SortFor(pos.ToMove) }()

	for _, r := range recommended {
		if r.err != nil && s.allowCutoff {
			// The move is already known not to avoid a game-ending state; no need to search it further
			results.Update(r)
			continue
		}

		score, cont, err := s.negamax(ctx, Make(pos, r.move), r.cont, w.Neg(), depth-1)
		score *= -1

		results.Update(Result{move: r.move, score: score.Abs(pos.ToMove), depth: depth - 1, cont: cont, err: err})

		if depth >= 3 && ctx.Err() != nil {
			break
		}

		var constrained, ok bool
		w, constrained, ok = w.Constrain(score)
		if constrained {
			// improved lower bound
		}
		if !ok && s.allowCutoff {
			break
		}
	}
	if err, ok := results[0].err.(checkmateError); ok {
		return w.alpha, results, err.Prev()
	}
	return w.alpha, results, results[0].err
}

// checkDone reports whether pos represents a game-ending position.
// If so, the RelScore and error value indicate the type of ending.
// If not, the RelScore is 0 and the error is nil.
// In either case, checkDone returns a Results of all legal moves in pos.
// If recommended is not provided, checkDone generates the legal moves first.
func checkDone(pos Position, recommended Results) (RelScore, Results, error) {
	if len(recommended) == 0 {
		moves := Candidates(pos) // pseudo-legal
		recommended = make(Results, 0, len(moves))
		for _, m := range moves {
			if !IsLegal(Make(pos, m)) {
				continue
			}
			recommended = append(recommended, Result{move: m})
		}
	}
	if len(recommended) == 0 {
		// no legal moves
		if IsCheck(pos) {
			return -evalInf, nil, errCheckmate
		}
		return 0, nil, errStalemate
	}
	if pos.HalfMove == 100 {
		// fifty-move rule
		return 0, nil, errFiftyMove
	}
	return 0, recommended, nil
}

// IsPseudoLegal returns whether a Move is pseudo-legal in a Position.
// A move is pseudo-legal if the square to be moved from contains the specified piece
// and the piece is capable of moving to the target square if doing so would not put the king in check.
func IsPseudoLegal(pos Position, move Move) bool {
	for _, m := range PieceMoves[move.Piece](pos) {
		if m == move {
			return true
		}
	}
	return false
}

// IsLegal returns whether a Position is legal.
// A position is illegal if the king of the side that just moved is in check.
func IsLegal(pos Position) bool {
	return !IsAttacked(pos, pos.KingSquare[pos.Opp()], pos.ToMove)
}

// IsCheck returns whether the king of the side to move is in check.
func IsCheck(pos Position) bool {
	return IsAttacked(pos, pos.KingSquare[pos.ToMove], pos.Opp())
}

// IsTerminal returns whether or not a Position is checkmate or stalemate.
// A position is checkmate or stalemate if the side to move has no legal moves.
func IsTerminal(pos Position) bool { return !anyLegal(pos, Candidates(pos)) }

// anyLegal returns whether any of the given Moves are legal in the given Position.
func anyLegal(pos Position, moves []Move) bool {
	for _, m := range moves {
		if IsLegal(Make(pos, m)) {
			return true
		}
	}
	return false
}

// A Result holds a searched Move along with its evaluated Score,
// the search depth, and the continuation Results of the search.
type Result struct {
	move  Move
	score Score
	depth int
	cont  Results
	err   error
}

// String returns a string representation of r, including its principal variation.
func (r Result) String() string {
	s := fmt.Sprintf("%v (%v) %v", r.score, r.depth, r.PV())
	if r.err != nil {
		return s + " " + r.err.Error()
	}
	return s
}

// PV returns a string representation of r's principal variation.
func (r Result) PV() string {
	if r.depth == 0 || len(r.cont) == 0 {
		return LongAlgebraic(r.move)
	}
	return LongAlgebraic(r.move) + " " + r.cont[0].PV()
}

// A Results contains the results of a search.
// Functions returning a Results should sort it before returning.
type Results []Result

// SortFor sorts rs beginning with the best move for c.
// The sort is not guaranteed to be stable.
func (rs Results) SortFor(c Color) {
	// Sort first by mate condition, then by depth decreasing,
	// then by Score increasing/decreasing for White/Black,
	// and then by origin and destination Square increasing
	sort.Slice(rs, func(i, j int) bool {
		if less, ok := rs.mateSort(i, j); ok {
			return less
		}
		if rs[i].depth != rs[j].depth {
			return rs[i].depth > rs[j].depth
		}
		if rs[i].score != rs[j].score {
			return (rs[i].score > rs[j].score) == (c == White)
		}
		return rs.squareSort(i, j)
	})
}

// SortBySquares sorts rs first by the Result moves' origin Squares and then by their destination Squares,
// and finally by promotion piece in the case of pawn promotions.
func (rs Results) SortBySquares() { sort.Slice(rs, rs.squareSort) }

// mateSort reports how two Result elements should be sorted if at least one of them leads to checkmate.
// ok reports whether either Result contains an error of type checkmateError.
// If ok is true, less reports whether the Result with index i should sort before the Result with index j.
// If ok is false, the value of less is meaningless.
func (rs Results) mateSort(i, j int) (less bool, ok bool) {
	ich, iok := rs[i].err.(checkmateError)
	jch, jok := rs[j].err.(checkmateError)
	switch {
	case iok && jok:
		switch iwin, jwin := ich&1 == 0, jch&1 == 0; {
		case iwin && jwin:
			return ich < jch, true
		case !iwin && !jwin:
			return ich > jch, true
		default:
			return iwin, true
		}
	case iok:
		return ich&1 == 0, true
	case jok:
		return jch&1 != 0, true
	}
	return false, false
}

// squareSort reports how two Result elements should be sorted by their moves' origin and destination Squares,
// and then finally by promotion piece in the case of promoting pawns.
func (rs Results) squareSort(i, j int) bool {
	if rs[i].move.From != rs[j].move.From {
		return rs[i].move.From < rs[j].move.From
	}
	if rs[i].move.To != rs[j].move.To {
		return rs[i].move.To < rs[j].move.To
	}
	// From and To are non-unique in the case of pawn promotion
	return rs[i].move.PromotePiece < rs[j].move.PromotePiece
}

// Update finds the Result in rs with the same Move as r and replaces it with r.
// It panics if rs does not contain any elements with r's Move.
func (rs Results) Update(r Result) {
	for i := range rs {
		if rs[i].move == r.move {
			rs[i] = r
			return
		}
	}
	// rs should contain all legal moves
	panic("unreached")
}

// String returns a string representation of all Result values in r.
func (rs Results) String() string {
	var s string
	for _, r := range rs {
		s += fmt.Sprintf("%v\n", r.String())
	}
	return s
}

// A Window represents the bounds of a position's evaluation.
type Window struct{ alpha, beta RelScore }

// Constrain updates the lower bound of w, if applicable, and returns the updated window,
// whether the lower bound was changed, and whether the returned Window remains valid.
func (w Window) Constrain(n RelScore) (c Window, constrained bool, ok bool) {
	if n <= w.alpha {
		return w, false, true
	}
	return Window{n, w.beta}, true, n <= w.beta
}

// Neg returns the additive inverse of w.
func (w Window) Neg() Window { return Window{-w.beta, -w.alpha} }
<commit_msg>use stored Results when it is already deep enough<commit_after>package main

import (
	"context"
	"errors"
	"fmt"
	"sort"
)

const (
	evalInf = 50000
)

var (
	errCheckmate checkmateError
	errStalemate = errors.New("stalemate")
	errFiftyMove = errors.New("fifty-move rule")
	// TODO: threefold repetition
)

// A checkmateError indicates the number of plies until a forced checkmate can be delivered.
// The zero value of type checkmateError indicates that the current position is checkmate.
type checkmateError int

func (n checkmateError) Error() string {
	if n&1 == 0 {
		return fmt.Sprintf("-#%d", n/2)
	}
	return fmt.Sprintf("#%d", (n+1)/2)
}

// Prev returns the checkmateError corresponding to the previous ply.
func (n checkmateError) Prev() checkmateError { return n + 1 }

// SearchPosition searches a Position to the specified depth via iterative deepening
// and returns the search results.
func SearchPosition(ctx context.Context, pos Position, depth int) Results {
	var results Results
	s := Search{
		allowCutoff: true,
		counters:    make([]int, depth+1),
	}
	for d := 1; d <= depth; d++ {
		_, results, _ = s.negamax(ctx, pos, results, Window{-evalInf, evalInf}, d)
		if ctx.Err() != nil {
			break
		}
	}
	return results
}

type Search struct {
	allowCutoff bool
	counters    []int
}

// negamax recursively searches a Position to the specified depth and returns the evaluation score
// relative to the side to move and the search results. It employs alpha-beta pruning outside of
// the specified Window. If recommended is zero length, negamax will generate and search all legal
// moves; if recommended moves are provided, they must all be legal, and only they will be searched.
func (s *Search) negamax(
	ctx context.Context,
	pos Position,
	recommended Results,
	w Window,
	depth int,
) (bestScore RelScore, results Results, err error) {
	s.counters[len(s.counters)-1-depth]++

	score, recommended, err := checkDone(pos, recommended)
	if err != nil && (s.allowCutoff || err != errInsufficient) {
		// Do not cut off during perft in the case of insufficient material
		return score, nil, err
	}
	// Invariant: len(recommended) > 0 and recommended contains only legal moves
	if depth == 0 {
		score, err := Eval(pos)
		return score.Rel(pos.ToMove), nil, err
	}
	if s.allowCutoff && deepEnough(recommended, depth) {
		return recommended[0].score.Rel(pos.ToMove), recommended, recommended[0].err
	}

	results = recommended
	defer func() { results.SortFor(pos.ToMove) }()

	for _, r := range recommended {
		if r.err != nil && s.allowCutoff {
			// The move is already known not to avoid a game-ending state; no need to search it further
			results.Update(r)
			continue
		}

		score, cont, err := s.negamax(ctx, Make(pos, r.move), r.cont, w.Neg(), depth-1)
		score *= -1

		results.Update(Result{move: r.move, score: score.Abs(pos.ToMove), depth: depth - 1, cont: cont, err: err})

		if depth >= 3 && ctx.Err() != nil {
			break
		}

		var constrained, ok bool
		w, constrained, ok = w.Constrain(score)
		if constrained {
			// improved lower bound
		}
		if !ok && s.allowCutoff {
			break
		}
	}
	if err, ok := results[0].err.(checkmateError); ok {
		return w.alpha, results, err.Prev()
	}
	return w.alpha, results, results[0].err
}

// checkDone reports whether pos represents a game-ending position.
// If so, the RelScore and error value indicate the type of ending.
// If not, the RelScore is 0 and the error is nil.
// In either case, checkDone returns a Results of all legal moves in pos.
// If recommended is not provided, checkDone generates the legal moves first.
func checkDone(pos Position, recommended Results) (RelScore, Results, error) {
	if len(recommended) == 0 {
		moves := Candidates(pos) // pseudo-legal
		recommended = make(Results, 0, len(moves))
		for _, m := range moves {
			if !IsLegal(Make(pos, m)) {
				continue
			}
			recommended = append(recommended, Result{move: m})
		}
	}
	if len(recommended) == 0 {
		// no legal moves
		if IsCheck(pos) {
			return -evalInf, nil, errCheckmate
		}
		return 0, nil, errStalemate
	}
	if pos.HalfMove == 100 {
		// fifty-move rule
		return 0, nil, errFiftyMove
	}
	return 0, recommended, nil
}

// deepEnough reports whether rs stores the results of a position search to at least the specified depth.
func deepEnough(rs Results, depth int) bool {
	// rs is already deep enough if all non-terminal elements have been searched to at least depth-1,
	// as long as they have been searched to non-zero depth
	for _, r := range rs {
		if (r.depth == 0 || r.depth < depth-1) && r.err == nil {
			return false
		}
	}
	return true
}

// IsPseudoLegal returns whether a Move is pseudo-legal in a Position.
// A move is pseudo-legal if the square to be moved from contains the specified piece
// and the piece is capable of moving to the target square if doing so would not put the king in check.
func IsPseudoLegal(pos Position, move Move) bool {
	for _, m := range PieceMoves[move.Piece](pos) {
		if m == move {
			return true
		}
	}
	return false
}

// IsLegal returns whether a Position is legal.
// A position is illegal if the king of the side that just moved is in check.
func IsLegal(pos Position) bool {
	return !IsAttacked(pos, pos.KingSquare[pos.Opp()], pos.ToMove)
}

// IsCheck returns whether the king of the side to move is in check.
func IsCheck(pos Position) bool {
	return IsAttacked(pos, pos.KingSquare[pos.ToMove], pos.Opp())
}

// IsTerminal returns whether or not a Position is checkmate or stalemate.
// A position is checkmate or stalemate if the side to move has no legal moves.
func IsTerminal(pos Position) bool { return !anyLegal(pos, Candidates(pos)) }

// anyLegal returns whether any of the given Moves are legal in the given Position.
func anyLegal(pos Position, moves []Move) bool {
	for _, m := range moves {
		if IsLegal(Make(pos, m)) {
			return true
		}
	}
	return false
}

// A Result holds a searched Move along with its evaluated Score,
// the search depth, and the continuation Results of the search.
type Result struct {
	move  Move
	score Score
	depth int
	cont  Results
	err   error
}

// String returns a string representation of r, including its principal variation.
func (r Result) String() string {
	s := fmt.Sprintf("%v (%v) %v", r.score, r.depth, r.PV())
	if r.err != nil {
		return s + " " + r.err.Error()
	}
	return s
}

// PV returns a string representation of r's principal variation.
func (r Result) PV() string {
	if r.depth == 0 || len(r.cont) == 0 {
		return LongAlgebraic(r.move)
	}
	return LongAlgebraic(r.move) + " " + r.cont[0].PV()
}

// A Results contains the results of a search.
// Functions returning a Results should sort it before returning.
type Results []Result

// SortFor sorts rs beginning with the best move for c.
// The sort is not guaranteed to be stable.
func (rs Results) SortFor(c Color) {
	// Sort first by mate condition, then by depth decreasing,
	// then by Score increasing/decreasing for White/Black,
	// and then by origin and destination Square increasing
	sort.Slice(rs, func(i, j int) bool {
		if less, ok := rs.mateSort(i, j); ok {
			return less
		}
		if rs[i].depth != rs[j].depth {
			return rs[i].depth > rs[j].depth
		}
		if rs[i].score != rs[j].score {
			return (rs[i].score > rs[j].score) == (c == White)
		}
		return rs.squareSort(i, j)
	})
}

// SortBySquares sorts rs first by the Result moves' origin Squares and then by their destination Squares,
// and finally by promotion piece in the case of pawn promotions.
func (rs Results) SortBySquares() { sort.Slice(rs, rs.squareSort) }

// mateSort reports how two Result elements should be sorted if at least one of them leads to checkmate.
// ok reports whether either Result contains an error of type checkmateError.
// If ok is true, less reports whether the Result with index i should sort before the Result with index j.
// If ok is false, the value of less is meaningless.
func (rs Results) mateSort(i, j int) (less bool, ok bool) {
	ich, iok := rs[i].err.(checkmateError)
	jch, jok := rs[j].err.(checkmateError)
	switch {
	case iok && jok:
		switch iwin, jwin := ich&1 == 0, jch&1 == 0; {
		case iwin && jwin:
			return ich < jch, true
		case !iwin && !jwin:
			return ich > jch, true
		default:
			return iwin, true
		}
	case iok:
		return ich&1 == 0, true
	case jok:
		return jch&1 != 0, true
	}
	return false, false
}

// squareSort reports how two Result elements should be sorted by their moves' origin and destination Squares,
// and then finally by promotion piece in the case of promoting pawns.
func (rs Results) squareSort(i, j int) bool {
	if rs[i].move.From != rs[j].move.From {
		return rs[i].move.From < rs[j].move.From
	}
	if rs[i].move.To != rs[j].move.To {
		return rs[i].move.To < rs[j].move.To
	}
	// From and To are non-unique in the case of pawn promotion
	return rs[i].move.PromotePiece < rs[j].move.PromotePiece
}

// Update finds the Result in rs with the same Move as r and replaces it with r.
// It panics if rs does not contain any elements with r's Move.
func (rs Results) Update(r Result) {
	for i := range rs {
		if rs[i].move == r.move {
			rs[i] = r
			return
		}
	}
	// rs should contain all legal moves
	panic("unreached")
}

// String returns a string representation of all Result values in r.
func (rs Results) String() string {
	var s string
	for _, r := range rs {
		s += fmt.Sprintf("%v\n", r.String())
	}
	return s
}

// A Window represents the bounds of a position's evaluation.
type Window struct{ alpha, beta RelScore }

// Constrain updates the lower bound of w, if applicable, and returns the updated window,
// whether the lower bound was changed, and whether the returned Window remains valid.
func (w Window) Constrain(n RelScore) (c Window, constrained bool, ok bool) {
	if n <= w.alpha {
		return w, false, true
	}
	return Window{n, w.beta}, true, n <= w.beta
}

// Neg returns the additive inverse of w.
func (w Window) Neg() Window { return Window{-w.beta, -w.alpha} }
<|endoftext|>


<commit_before>package opt

import (
	"sexp"
	"sexpconv"
)

// ReduceStrength replaces operations with their less expensive
// equivalents.
func ReduceStrength(form sexp.Form) sexp.Form {
	switch form := form.(type) {
	case *sexp.Add:
		return weakenAdd(form)
	case *sexp.Sub:
		return weakenSub(form)

	case *sexp.Bind:
		form.Init = ReduceStrength(form.Init)
	case *sexp.Rebind:
		form.Expr = ReduceStrength(form.Expr)
	case *sexp.Block:
		form.Forms = reduceStrength(form.Forms)
	case *sexp.FormList:
		form.Forms = reduceStrength(form.Forms)

	case *sexp.While:
		form.Cond = ReduceStrength(form.Cond)
		ReduceStrength(form.Body)

	case *sexp.ArrayLit:
		return weakenArrayLit(form)
	case *sexp.SparseArrayLit:
		return weakenSparseArrayLit(form)
	}

	return form
}

func reduceStrength(forms []sexp.Form) []sexp.Form {
	for i, form := range forms {
		forms[i] = ReduceStrength(form)
	}
	return forms
}

func weakenAdd(form *sexp.Add) sexp.Form {
	weaken := func(a, b int) sexp.Form {
		if numEq(form.Args[a], 1) {
			return addX(form.Args[b], 1)
		}
		if numEq(form.Args[a], 2) {
			return addX(form.Args[b], 2)
		}
		// Addition of negative number = substraction.
		if numEq(form.Args[a], -1) {
			return subX(form.Args[b], 1)
		}
		if numEq(form.Args[a], -2) {
			return subX(form.Args[b], 2)
		}
		return nil
	}

	if form := weaken(0, 1); form != nil {
		return form
	}
	// Because "+" is commutative, we can try to apply
	// same patterns against other argument.
	if form := weaken(1, 0); form != nil {
		return form
	}
	return form
}

func weakenSub(form *sexp.Sub) sexp.Form {
	if numEq(form.Args[1], 1) {
		return subX(form.Args[0], 1)
	}
	if numEq(form.Args[1], 2) {
		return subX(form.Args[0], 2)
	}
	// Substraction of negative number = addition.
	if numEq(form.Args[1], -1) {
		return addX(form.Args[0], 1)
	}
	if numEq(form.Args[1], -2) {
		return addX(form.Args[0], 2)
	}
	return form
}

func weakenArrayLit(form *sexp.ArrayLit) sexp.Form {
	// #TODO: recognize array where all elements are the same.
	//        Replace with "make-vector" call.
	return form
}

func weakenSparseArrayLit(form *sexp.SparseArrayLit) sexp.Form {
	toArrayLit := func(form *sexp.SparseArrayLit) *sexp.ArrayLit {
		zv := sexpconv.ZeroValue(form.Typ.Elem())
		vals := make([]sexp.Form, int(form.Typ.Len()))
		for _, val := range form.Vals {
			vals[val.Index] = val.Expr
		}
		for i := range vals {
			if vals[i] == nil {
				vals[i] = zv
			}
		}
		return &sexp.ArrayLit{Vals: vals}
	}

	// If array is very small, it is better to use "vector".
	if form.Typ.Len() <= 4 {
		return toArrayLit(form)
	}

	// Sparse arrays worth it only when zero values are prevalent.
	zeroVals := form.Typ.Len() - int64(len(form.Vals))
	if zeroVals*4 < form.Typ.Len()*3 {
		// Count of zero values < 75%.
		// Convert to ArrayLit.
		return toArrayLit(form)
	}

	return form
}
<commit_msg>important array->slice->string optimization<commit_after>package opt

import (
	"sexp"
	"sexpconv"
)

// ReduceStrength replaces operations with their less expensive
// equivalents.
func ReduceStrength(form sexp.Form) sexp.Form {
	switch form := form.(type) {
	case *sexp.Add:
		return weakenAdd(form)
	case *sexp.Sub:
		return weakenSub(form)

	case *sexp.Bind:
		form.Init = ReduceStrength(form.Init)
	case *sexp.Rebind:
		form.Expr = ReduceStrength(form.Expr)
	case *sexp.Block:
		form.Forms = reduceStrength(form.Forms)
	case *sexp.FormList:
		form.Forms = reduceStrength(form.Forms)

	case *sexp.While:
		form.Cond = ReduceStrength(form.Cond)
		ReduceStrength(form.Body)

	case *sexp.ArrayLit:
		return weakenArrayLit(form)
	case *sexp.SparseArrayLit:
		return weakenSparseArrayLit(form)

	case *sexp.StrCast:
		return weakenStrCast(form)
	case *sexp.Call:
		form.Args = reduceStrength(form.Args)
	case sexp.CallStmt:
		ReduceStrength(form.Call)
	}

	return form
}

func reduceStrength(forms []sexp.Form) []sexp.Form {
	for i, form := range forms {
		forms[i] = ReduceStrength(form)
	}
	return forms
}

func weakenAdd(form *sexp.Add) sexp.Form {
	weaken := func(a, b int) sexp.Form {
		if numEq(form.Args[a], 1) {
			return addX(form.Args[b], 1)
		}
		if numEq(form.Args[a], 2) {
			return addX(form.Args[b], 2)
		}
		// Addition of negative number = substraction.
		if numEq(form.Args[a], -1) {
			return subX(form.Args[b], 1)
		}
		if numEq(form.Args[a], -2) {
			return subX(form.Args[b], 2)
		}
		return nil
	}

	if form := weaken(0, 1); form != nil {
		return form
	}
	// Because "+" is commutative, we can try to apply
	// same patterns against other argument.
	if form := weaken(1, 0); form != nil {
		return form
	}
	return form
}

func weakenSub(form *sexp.Sub) sexp.Form {
	if numEq(form.Args[1], 1) {
		return subX(form.Args[0], 1)
	}
	if numEq(form.Args[1], 2) {
		return subX(form.Args[0], 2)
	}
	// Substraction of negative number = addition.
	if numEq(form.Args[1], -1) {
		return addX(form.Args[0], 1)
	}
	if numEq(form.Args[1], -2) {
		return addX(form.Args[0], 2)
	}
	return form
}

func weakenArrayLit(form *sexp.ArrayLit) sexp.Form {
	// #TODO: recognize array where all elements are the same.
	//        Replace with "make-vector" call.
	return form
}

func weakenSparseArrayLit(form *sexp.SparseArrayLit) sexp.Form {
	toArrayLit := func(form *sexp.SparseArrayLit) *sexp.ArrayLit {
		zv := sexpconv.ZeroValue(form.Typ.Elem())
		vals := make([]sexp.Form, int(form.Typ.Len()))
		for _, val := range form.Vals {
			vals[val.Index] = val.Expr
		}
		for i := range vals {
			if vals[i] == nil {
				vals[i] = zv
			}
		}
		return &sexp.ArrayLit{Vals: vals}
	}

	// If array is very small, it is better to use "vector".
	if form.Typ.Len() <= 4 {
		return toArrayLit(form)
	}

	// Sparse arrays worth it only when zero values are prevalent.
	zeroVals := form.Typ.Len() - int64(len(form.Vals))
	if zeroVals*4 < form.Typ.Len()*3 {
		// Count of zero values < 75%.
		// Convert to ArrayLit.
		return toArrayLit(form)
	}

	return form
}

func weakenStrCast(form *sexp.StrCast) sexp.Form {
	if arg, ok := form.Arg.(*sexp.ArraySlice); ok {
		// It is possible to convert array to string without
		// creating a slice.
		switch arg.Kind() {
		case sexp.SpanWhole:
			return sexp.NewConcat(arg.Array, sexp.String(""))
		default:
			sub := sexp.NewSubstr(arg.Array, arg.Low, arg.High)
			return sexp.NewConcat(sub, sexp.String(""))
		}
	}

	return form
}
<|endoftext|>


<commit_before>package tm

import (
	"fmt"
	"sort"
	"time"
)

// Series represents a set of sequential Frames in a timeseries.
//
// Series search functions: some of the principal
// primitives for querying a Series are the following
// for functions.
//
// FirstInForceBefore(), LastInForceBefore(),
// FirstAtOrBefore(), and LastAtOrBefore()
// provide searching functionality for through a
// timeseries that may have duplicated timestamps.
//
// See the LastInForceBefore() for the most detailed
// description. The other three functions are analogous.
//
type Series struct {
	Frames []*Frame
}

// create a new Series from a set of Frame pointers
func NewSeriesFromFrames(fr []*Frame) *Series {
	s := &Series{
		Frames: fr,
	}
	return s
}

// result of searching with JustBefore() and AtOrAfter()
type SearchStatus int

const (
	InPast   SearchStatus = 0
	Avail    SearchStatus = 1
	InFuture SearchStatus = 2
)

// Stringify the SearchStatus, for printing.
func (s SearchStatus) String() string {
	switch s {
	case InPast:
		return "InPast"
	case Avail:
		return "Avail"
	case InFuture:
		return "InFuture"
	}
	panic(fmt.Sprintf("unknown SearchStatus %v", s))
}

// LastInForceBefore():
//
// If tm is greater than any seen Frame, LastInForceBefore()
// will return the last seen Frame and a SearchStatus of InFuture.
//
// If tm is smaller than the oldest Frame available,
// LastInForceBefore will return (nil, InPast). Otherwise,
// it returns the Frame where Frame.Tm() is strictly before
// the tm (using 10 nanosecond resolution; truncating tm using
// the TimeToPrimTm(tm) function.
//
// The 3rd returned argument provides the integer index of
// the returned frame in s.Frames, or -1 if SearchStatus is InPast.
//
// In summary:
//
// LastInForceBefore(): looking at the ties for the nearest timestamp s < tm, return
// the most recent (last in chronological order) of these ties at s. Nearest means
// that there is no other timestamp r such that s < r < tm.
func (s *Series) LastInForceBefore(tm time.Time) (*Frame, SearchStatus, int) {

	m := len(s.Frames)
	utm := TimeToPrimTm(tm)

	// Search returns the smallest index i in [0, m) at which f(i) is true.
	// If i == m, this means no such index had f(i) true.
	i := sort.Search(m, func(i int) bool {
		return s.Frames[i].Tm() >= utm
	})
	if i == m {
		return s.Frames[m-1], InFuture, m - 1
	}

	if i == 0 {
		return nil, InPast, -1
	}

	return s.Frames[i-1], Avail, i - 1
}

// FirstInForceBefore(): looking at the ties for the nearest timestamp s < tm, return
// the earliest (first in chronological order) of these ties at s. Nearest means
// that there is no other timestamp r such that s < r < tm.
func (s *Series) FirstInForceBefore(tm time.Time) (*Frame, SearchStatus, int) {

	m := len(s.Frames)
	utm := TimeToPrimTm(tm)

	// Search returns the smallest index i in [0, m) at which f(i) is true.
	// If i == m, this means no such index had f(i) true.
	i := sort.Search(m, func(i int) bool {
		return s.Frames[i].Tm() >= utm
	})
	if i == m {
		// all frames Tm < utm
		rtm := s.Frames[m-1].Tm()

		// Handling repeated timestamps:
		// Need to search back to the first Frame at rtm.
		// For worst case efficiency of O(log(n)), rather
		// than O(n), use Search() again to
		// find the smallest index such that Tm >= rtm.
		k := sort.Search(m, func(i int) bool {
			return s.Frames[i].Tm() >= rtm
		})
		// k == m is impossible, rtm came from a Frame in s.Frames
		return s.Frames[k], InFuture, k
	}
	// INVAR: at least one entry had Tm >= utm

	if i == 0 {
		return nil, InPast, -1
	}

	// i is the smallest Frame such that itm >= utm.
	// Since we want to go strictly before that i,
	// start at j = i - 1; then find the first of any ties
	// at the Frames[j].Tm() timestamp. If we don't
	// find any, just return s.Frame[j].
	j := i - 1
	jtm := s.Frames[j].Tm()

	// Handling repeated timestamps:
	// Search foward to the last Frame at itm.
	// For worst case efficiency of O(log(n)), rather
	// than O(n), use Search() again to
	// find the smallest index such that Tm > itm,
	// then subtract 1.
	k := sort.Search(m, func(i int) bool {
		return s.Frames[i].Tm() >= jtm
	})

	// k == m is impossible since jtm comes from the j Frame
	return s.Frames[k], Avail, k
}

// FirstAtOrBefore(): looking at the ties for the nearest timestamp s <= tm, return
// the earliest (first in chronological order) of these ties at s.  Nearest means
// that there is no other timestamp r such that s <= r <= tm.
func (s *Series) FirstAtOrBefore(tm time.Time) (*Frame, SearchStatus, int) {

	m := len(s.Frames)
	utm := TimeToPrimTm(tm)

	// Search returns the smallest index i in [0, m) at which f(i) is true.
	// If i == m, this means no such index had f(i) true.
	i := sort.Search(m, func(i int) bool {
		return s.Frames[i].Tm() >= utm
	})

	if i == m {
		// all frames Tm < utm
		rtm := s.Frames[m-1].Tm()

		// Handling repeated timestamps:
		// Need to search back to the first Frame at rtm.
		// For worst case efficiency of O(log(n)), rather
		// than O(n), use Search() again to
		// find the smallest index such that Tm == rtm.
		k := sort.Search(m, func(i int) bool {
			return s.Frames[i].Tm() >= rtm
		})
		// k == m is impossible, rtm came from a Frame in s.Frames
		return s.Frames[k], InFuture, k
	}
	// INVAR: at least one entry had Tm >= utm

	itm := s.Frames[i].Tm()
	if i == 0 {
		if itm == utm {
			return s.Frames[i], Avail, i
		}
		// even s.Frames[0] was > utm
		return nil, InPast, -1
	}
	if itm == utm {
		return s.Frames[i], Avail, i
	}
	return s.Frames[i-1], Avail, i - 1
}

// LastAtOrBefore(): looking at the ties for the nearest timestamp s <= tm, return
// the newest (last in chronological order) of these ties at timestamp s.  Nearest means
// that there is no other timestamp r such that s <= r <= tm.
func (s *Series) LastAtOrBefore(tm time.Time) (*Frame, SearchStatus, int) {

	m := len(s.Frames)
	utm := TimeToPrimTm(tm)

	// Search returns the smallest index i in [0, m) at which f(i) is true.
	// If i == m, this means no such index had f(i) true.
	i := sort.Search(m, func(i int) bool {
		return s.Frames[i].Tm() >= utm
	})

	if i == m {
		// all frames Tm < utm
		return s.Frames[m-1], InFuture, m - 1
	}
	// INVAR: at least one entry had Tm >= utm.

	// i is the smallest Frame such that itm >= utm.
	itm := s.Frames[i].Tm()
	if i == 0 {
		if itm > utm {
			return nil, InPast, -1
		}
	}
	// But: there can be many at itm and we want the largest index that ties.

	// Handling repeated timestamps:
	// Search foward to the last Frame at itm.
	// For worst case efficiency of O(log(n)), rather
	// than O(n), use Search() again to
	// find the smallest index such that Tm > itm,
	// then subtract 1.
	k := sort.Search(m, func(i int) bool {
		return s.Frames[i].Tm() > itm
	})

	if k == m {
		// no Frames had Tm > itm
		return s.Frames[m-1], Avail, m - 1
	}
	// k == 0 is impossible, since itm came from an existing Frame in s.Frames.
	return s.Frames[k-1], Avail, k - 1
}
<commit_msg>atg. fix go vet warning.<commit_after>package tm

import (
	"fmt"
	"sort"
	"time"
)

// Series represents a set of sequential Frames in a timeseries.
//
// Series search functions: some of the principal
// primitives for querying a Series are the following
// for functions.
//
// FirstInForceBefore(), LastInForceBefore(),
// FirstAtOrBefore(), and LastAtOrBefore()
// provide searching functionality for through a
// timeseries that may have duplicated timestamps.
//
// See the LastInForceBefore() for the most detailed
// description. The other three functions are analogous.
//
type Series struct {
	Frames []*Frame
}

// create a new Series from a set of Frame pointers
func NewSeriesFromFrames(fr []*Frame) *Series {
	s := &Series{
		Frames: fr,
	}
	return s
}

// result of searching with JustBefore() and AtOrAfter()
type SearchStatus int

const (
	InPast   SearchStatus = 0
	Avail    SearchStatus = 1
	InFuture SearchStatus = 2
)

// Stringify the SearchStatus, for printing.
func (s SearchStatus) String() string {
	switch s {
	case InPast:
		return "InPast"
	case Avail:
		return "Avail"
	case InFuture:
		return "InFuture"
	}
	panic(fmt.Sprintf("unknown SearchStatus %d", int(s)))
}

// LastInForceBefore():
//
// If tm is greater than any seen Frame, LastInForceBefore()
// will return the last seen Frame and a SearchStatus of InFuture.
//
// If tm is smaller than the oldest Frame available,
// LastInForceBefore will return (nil, InPast). Otherwise,
// it returns the Frame where Frame.Tm() is strictly before
// the tm (using 10 nanosecond resolution; truncating tm using
// the TimeToPrimTm(tm) function.
//
// The 3rd returned argument provides the integer index of
// the returned frame in s.Frames, or -1 if SearchStatus is InPast.
//
// In summary:
//
// LastInForceBefore(): looking at the ties for the nearest timestamp s < tm, return
// the most recent (last in chronological order) of these ties at s. Nearest means
// that there is no other timestamp r such that s < r < tm.
func (s *Series) LastInForceBefore(tm time.Time) (*Frame, SearchStatus, int) {

	m := len(s.Frames)
	utm := TimeToPrimTm(tm)

	// Search returns the smallest index i in [0, m) at which f(i) is true.
	// If i == m, this means no such index had f(i) true.
	i := sort.Search(m, func(i int) bool {
		return s.Frames[i].Tm() >= utm
	})
	if i == m {
		return s.Frames[m-1], InFuture, m - 1
	}

	if i == 0 {
		return nil, InPast, -1
	}

	return s.Frames[i-1], Avail, i - 1
}

// FirstInForceBefore(): looking at the ties for the nearest timestamp s < tm, return
// the earliest (first in chronological order) of these ties at s. Nearest means
// that there is no other timestamp r such that s < r < tm.
func (s *Series) FirstInForceBefore(tm time.Time) (*Frame, SearchStatus, int) {

	m := len(s.Frames)
	utm := TimeToPrimTm(tm)

	// Search returns the smallest index i in [0, m) at which f(i) is true.
	// If i == m, this means no such index had f(i) true.
	i := sort.Search(m, func(i int) bool {
		return s.Frames[i].Tm() >= utm
	})
	if i == m {
		// all frames Tm < utm
		rtm := s.Frames[m-1].Tm()

		// Handling repeated timestamps:
		// Need to search back to the first Frame at rtm.
		// For worst case efficiency of O(log(n)), rather
		// than O(n), use Search() again to
		// find the smallest index such that Tm >= rtm.
		k := sort.Search(m, func(i int) bool {
			return s.Frames[i].Tm() >= rtm
		})
		// k == m is impossible, rtm came from a Frame in s.Frames
		return s.Frames[k], InFuture, k
	}
	// INVAR: at least one entry had Tm >= utm

	if i == 0 {
		return nil, InPast, -1
	}

	// i is the smallest Frame such that itm >= utm.
	// Since we want to go strictly before that i,
	// start at j = i - 1; then find the first of any ties
	// at the Frames[j].Tm() timestamp. If we don't
	// find any, just return s.Frame[j].
	j := i - 1
	jtm := s.Frames[j].Tm()

	// Handling repeated timestamps:
	// Search foward to the last Frame at itm.
	// For worst case efficiency of O(log(n)), rather
	// than O(n), use Search() again to
	// find the smallest index such that Tm > itm,
	// then subtract 1.
	k := sort.Search(m, func(i int) bool {
		return s.Frames[i].Tm() >= jtm
	})

	// k == m is impossible since jtm comes from the j Frame
	return s.Frames[k], Avail, k
}

// FirstAtOrBefore(): looking at the ties for the nearest timestamp s <= tm, return
// the earliest (first in chronological order) of these ties at s.  Nearest means
// that there is no other timestamp r such that s <= r <= tm.
func (s *Series) FirstAtOrBefore(tm time.Time) (*Frame, SearchStatus, int) {

	m := len(s.Frames)
	utm := TimeToPrimTm(tm)

	// Search returns the smallest index i in [0, m) at which f(i) is true.
	// If i == m, this means no such index had f(i) true.
	i := sort.Search(m, func(i int) bool {
		return s.Frames[i].Tm() >= utm
	})

	if i == m {
		// all frames Tm < utm
		rtm := s.Frames[m-1].Tm()

		// Handling repeated timestamps:
		// Need to search back to the first Frame at rtm.
		// For worst case efficiency of O(log(n)), rather
		// than O(n), use Search() again to
		// find the smallest index such that Tm == rtm.
		k := sort.Search(m, func(i int) bool {
			return s.Frames[i].Tm() >= rtm
		})
		// k == m is impossible, rtm came from a Frame in s.Frames
		return s.Frames[k], InFuture, k
	}
	// INVAR: at least one entry had Tm >= utm

	itm := s.Frames[i].Tm()
	if i == 0 {
		if itm == utm {
			return s.Frames[i], Avail, i
		}
		// even s.Frames[0] was > utm
		return nil, InPast, -1
	}
	if itm == utm {
		return s.Frames[i], Avail, i
	}
	return s.Frames[i-1], Avail, i - 1
}

// LastAtOrBefore(): looking at the ties for the nearest timestamp s <= tm, return
// the newest (last in chronological order) of these ties at timestamp s.  Nearest means
// that there is no other timestamp r such that s <= r <= tm.
func (s *Series) LastAtOrBefore(tm time.Time) (*Frame, SearchStatus, int) {

	m := len(s.Frames)
	utm := TimeToPrimTm(tm)

	// Search returns the smallest index i in [0, m) at which f(i) is true.
	// If i == m, this means no such index had f(i) true.
	i := sort.Search(m, func(i int) bool {
		return s.Frames[i].Tm() >= utm
	})

	if i == m {
		// all frames Tm < utm
		return s.Frames[m-1], InFuture, m - 1
	}
	// INVAR: at least one entry had Tm >= utm.

	// i is the smallest Frame such that itm >= utm.
	itm := s.Frames[i].Tm()
	if i == 0 {
		if itm > utm {
			return nil, InPast, -1
		}
	}
	// But: there can be many at itm and we want the largest index that ties.

	// Handling repeated timestamps:
	// Search foward to the last Frame at itm.
	// For worst case efficiency of O(log(n)), rather
	// than O(n), use Search() again to
	// find the smallest index such that Tm > itm,
	// then subtract 1.
	k := sort.Search(m, func(i int) bool {
		return s.Frames[i].Tm() > itm
	})

	if k == m {
		// no Frames had Tm > itm
		return s.Frames[m-1], Avail, m - 1
	}
	// k == 0 is impossible, since itm came from an existing Frame in s.Frames.
	return s.Frames[k-1], Avail, k - 1
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"net"

	baps3 "github.com/UniversityRadioYork/baps3-go"
)

type Client struct {
	conn     net.Conn
	reader   *bufio.Reader
	writer   *bufio.Writer
	serverCh chan []byte
	Outgoing chan string
}

func MakeClient(conn net.Conn, clientCh chan []byte) *Client {
	client := &Client{
		conn:     conn,
		reader:   bufio.NewReader(conn),
		writer:   bufio.NewWriter(conn),
		serverCh: clientCh,
		Outgoing: make(chan string),
	}

	go client.Read()
	go client.write()

	client.Outgoing <- "Hello!\n" // XXX Actual welcome message

	return client
}

func (c *Client) Close() {
	c.conn.Close()
}

func (c *Client) Read() {
	for {
		data, _ := c.reader.ReadBytes('\n')
		c.serverCh <- data // Each client doesn't care what it got, that's for the server to handle
	}
}

func (c *Client) write() {
	for data := range c.Outgoing {
		c.writer.WriteString(data)
		c.writer.Flush()
	}
}

type Server struct {
	listener   net.Listener
	serverCh   chan baps3.Message
	clientComm chan []byte
	tok        *baps3.Tokeniser
	clients    []*Client
}

func MakeServer(addr string, port string, serverCh chan baps3.Message) (*Server, error) {
	listener, err := net.Listen("tcp", addr+":"+port)
	if err != nil {
		return nil, err
	}
	server := &Server{
		listener:   listener,
		tok:        baps3.NewTokeniser(),
		clientComm: make(chan []byte),
		serverCh:   serverCh,
	}
	return server, nil
}

func (s *Server) Broadcast(msg baps3.Message) {
	// Probably should have \n at the end
	for _, c := range s.clients {
		c.Outgoing <- msg.String()
	}
}

func (s *Server) ReceiveLoop() {
	for data := range s.clientComm {
		_, _, err := s.tok.Tokenise(data)
		if err != nil {
			continue // TODO: Do something?
		}
		println("hmm, a client spoke!")
	}
}

func (s *Server) run() {
	go s.ReceiveLoop()
	for {
		conn, err := s.listener.Accept()
		if err != nil {
			continue
		}
		client := MakeClient(conn, s.clientComm)
		s.clients = append(s.clients, client)
	}
}
<commit_msg>Packing and byte slices<commit_after>package main

import (
	"bufio"
	"net"

	baps3 "github.com/UniversityRadioYork/baps3-go"
)

type Client struct {
	conn     net.Conn
	reader   *bufio.Reader
	writer   *bufio.Writer
	serverCh chan []byte
	Outgoing chan []byte
}

func MakeClient(conn net.Conn, clientCh chan []byte) *Client {
	client := &Client{
		conn:     conn,
		reader:   bufio.NewReader(conn),
		writer:   bufio.NewWriter(conn),
		serverCh: clientCh,
		Outgoing: make(chan []byte),
	}

	go client.Read()
	go client.write()

	client.Outgoing <- []byte("Hello!\n") // XXX Actual welcome message

	return client
}

func (c *Client) Close() {
	c.conn.Close()
}

func (c *Client) Read() {
	for {
		data, _ := c.reader.ReadBytes('\n')
		c.serverCh <- data // Each client doesn't care what it got, that's for the server to handle
	}
}

func (c *Client) write() {
	for data := range c.Outgoing {
		_, err := c.writer.Write(data)
		if err != nil {
			continue // TODO Handle
		}
		c.writer.Flush()
	}
}

type Server struct {
	listener   net.Listener
	serverCh   chan baps3.Message
	clientComm chan []byte
	tok        *baps3.Tokeniser
	clients    []*Client
}

func MakeServer(addr string, port string, serverCh chan baps3.Message) (*Server, error) {
	listener, err := net.Listen("tcp", addr+":"+port)
	if err != nil {
		return nil, err
	}
	server := &Server{
		listener:   listener,
		tok:        baps3.NewTokeniser(),
		clientComm: make(chan []byte),
		serverCh:   serverCh,
	}
	return server, nil
}

func (s *Server) Broadcast(msg baps3.Message) {
	// Probably should have \n at the end
	for _, c := range s.clients {
		data, err := msg.Pack()
		if err != nil {
			continue // TODO Handle
		}
		c.Outgoing <- data
	}
}

func (s *Server) ReceiveLoop() {
	for data := range s.clientComm {
		_, _, err := s.tok.Tokenise(data)
		if err != nil {
			continue // TODO: Do something?
		}
	}
}

func (s *Server) run() {
	go s.ReceiveLoop()
	for {
		conn, err := s.listener.Accept()
		if err != nil {
			continue
		}
		client := MakeClient(conn, s.clientComm)
		s.clients = append(s.clients, client)
	}
}
<|endoftext|>