<commit_before>// Package denco provides fast URL router.
package denco

import (
	"fmt"
	"sort"
	"strings"
)

const (
	// A special character for path parameter.
	ParamCharacter = ':'

	// A special character for wildcard path parameter.
	WildcardCharacter = '*'

	// TerminalCharacter is a special character for end of path.
	TerminationCharacter = '#'
)

// Router represents a URL router.
type Router struct {
	static map[string]interface{}
	param  *doubleArray
}

// New returns a new Router.
func New() *Router {
	return &Router{
		static: make(map[string]interface{}),
		param:  newDoubleArray(),
	}
}

// Lookup returns data and path parameters that associated with path.
// params is a slice of the Param that arranged in the order in which parameters appeared.
// e.g. when built routing path is "/path/:id/:name" and given path is "/path/to/1/alice". params order is [{"id": "1"}, {"name": "alice"}], not [{"name": "alice"}, {"id": "1"}].
func (rt *Router) Lookup(path string) (data interface{}, params []Param, found bool) {
	if data, found := rt.static[path]; found {
		return data, nil, true
	}
	nd, params, found := rt.param.lookup(path, 1)
	if !found {
		return nil, nil, false
	}
	for i := 0; i < len(params); i++ {
		params[i].Name = nd.paramNames[i]
	}
	return nd.data, params, true
}

// Build builds URL router from records.
func (rt *Router) Build(records []Record) error {
	statics, params := makeRecords(records)
	for _, r := range statics {
		rt.static[r.Key] = r.Value
	}
	if err := rt.param.build(params, 1, 0, make(map[int]struct{})); err != nil {
		return err
	}
	return nil
}

// Param represents name and value of path parameter.
type Param struct {
	Name  string
	Value string
}

type doubleArray struct {
	bc   []baseCheck
	node []*node
}

func newDoubleArray() *doubleArray {
	return &doubleArray{
		bc:   []baseCheck{0},
		node: []*node{nil}, // A start index is adjusting to 1 because 0 will be used as a mark of non-existent node.
	}
}

// baseCheck contains BASE, CHECK and Extra flags.
// From the top, 22bits of BASE, 2bits of Extra flags and 8bits of CHECK.
//
//  BASE (22bit) | Extra flags (2bit) | CHECK (8bit)
// |----------------------|--|--------|
// 32                    10  8         0
type baseCheck uint32

func (bc baseCheck) Base() int {
	return int(bc >> 10)
}

func (bc *baseCheck) SetBase(base int) {
	*bc |= baseCheck(base) << 10
}

func (bc baseCheck) Check() byte {
	return byte(bc)
}

func (bc *baseCheck) SetCheck(check byte) {
	*bc |= baseCheck(check)
}

func (bc baseCheck) IsEmpty() bool {
	return bc&0xfffffcff == 0
}

func (bc baseCheck) Index() int {
	return int(bc >> 10)
}

func (bc *baseCheck) SetIndex(i int) {
	*bc |= baseCheck(i) << 10
}

func (bc baseCheck) IsSingleParam() bool {
	return bc&paramTypeSingle == paramTypeSingle
}

func (bc baseCheck) IsWildcardParam() bool {
	return bc&paramTypeWildcard == paramTypeWildcard
}

func (bc baseCheck) IsAnyParam() bool {
	return bc&paramTypeAny != 0
}

func (bc *baseCheck) SetSingleParam() {
	*bc |= (1 << 8)
}

func (bc *baseCheck) SetWildcardParam() {
	*bc |= (1 << 9)
}

const (
	paramTypeSingle   = 0x0100
	paramTypeWildcard = 0x0200
	paramTypeAny      = 0x0300
)

func (da *doubleArray) lookup(path string, idx int) (*node, []Param, bool) {
	indices := make([]uint64, 0, 1)
	for i := 0; i < len(path); i++ {
		if da.bc[idx].IsAnyParam() {
			indices = append(indices, (uint64(i)<<32)|uint64(idx&0xffffffff))
		}
		c := path[i]
		next := nextIndex(da.bc[idx].Base(), c)
		if next >= len(da.bc) || da.bc[next].Check() != c {
			goto BACKTRACKING
		}
		idx = next
	}
	if next := nextIndex(da.bc[idx].Base(), TerminationCharacter); da.bc[next].Check() == TerminationCharacter {
		return da.node[da.bc[next].Index()], nil, true
	}
	return nil, nil, false
BACKTRACKING:
	for j := len(indices) - 1; j >= 0; j-- {
		i, idx := int(indices[j]>>32), int(indices[j]&0xffffffff)
		if da.bc[idx].IsSingleParam() {
			idx := nextIndex(da.bc[idx].Base(), ParamCharacter)
			if idx >= len(da.bc) {
				break
			}
			next := NextSeparator(path, i)
			if nd, params, found := da.lookup(path[next:], idx); found {
				return nd, append([]Param{{Value: path[i:next]}}, params...), true
			}
		}
		if da.bc[idx].IsWildcardParam() {
			idx := nextIndex(da.bc[idx].Base(), WildcardCharacter)
			params := []Param{{Value: path[i:]}}
			return da.node[da.bc[idx].Index()], params, true
		}
	}
	return nil, nil, false
}

// build builds double-array from records.
func (da *doubleArray) build(srcs []*record, idx, depth int, usedBase map[int]struct{}) error {
	sort.Sort(recordSlice(srcs))
	base, siblings, leaf, err := da.arrange(srcs, idx, depth, usedBase)
	if err != nil {
		return err
	}
	if leaf != nil {
		nd, err := makeNode(leaf)
		if err != nil {
			return err
		}
		da.bc[idx].SetIndex(len(da.node))
		da.node = append(da.node, nd)
	}
	for _, sib := range siblings {
		da.setCheck(nextIndex(base, sib.c), sib.c)
	}
	for _, sib := range siblings {
		records := srcs[sib.start:sib.end]
		switch sib.c {
		case ParamCharacter:
			for _, r := range records {
				next := NextSeparator(r.Key, depth+1)
				name := r.Key[depth+1 : next]
				r.paramNames = append(r.paramNames, name)
				r.Key = r.Key[next:]
			}
			da.bc[idx].SetSingleParam()
			if err := da.build(records, nextIndex(base, sib.c), 0, usedBase); err != nil {
				return err
			}
		case WildcardCharacter:
			r := records[0]
			name := r.Key[depth+1 : len(r.Key)-1]
			r.paramNames = append(r.paramNames, name)
			r.Key = ""
			da.bc[idx].SetWildcardParam()
			if err := da.build(records, nextIndex(base, sib.c), 0, usedBase); err != nil {
				return err
			}
		default:
			if err := da.build(records, nextIndex(base, sib.c), depth+1, usedBase); err != nil {
				return err
			}
		}
	}
	return nil
}

// setBase sets BASE.
func (da *doubleArray) setBase(i, base int) {
	da.bc[i].SetBase(base)
}

// setCheck sets CHECK.
func (da *doubleArray) setCheck(i int, check byte) {
	da.bc[i].SetCheck(check)
}

// findEmptyIndex returns an index of unused BASE/CHECK node.
func (da *doubleArray) findEmptyIndex(start int) int {
	i := start
	for ; i < len(da.bc); i++ {
		if da.bc[i].IsEmpty() {
			break
		}
	}
	return i
}

// findBase returns good BASE.
func (da *doubleArray) findBase(siblings []sibling, start int, usedBase map[int]struct{}) (base int) {
	for idx, firstChar := start+1, siblings[0].c; ; idx = da.findEmptyIndex(idx + 1) {
		base = nextIndex(idx, firstChar)
		if _, used := usedBase[base]; used {
			continue
		}
		i := 0
		for ; i < len(siblings); i++ {
			next := nextIndex(base, siblings[i].c)
			if len(da.bc) <= next {
				da.bc = append(da.bc, make([]baseCheck, next-len(da.bc)+1)...)
			}
			if !da.bc[next].IsEmpty() {
				break
			}
		}
		if i == len(siblings) {
			break
		}
	}
	usedBase[base] = struct{}{}
	return base
}

func (da *doubleArray) arrange(records []*record, idx, depth int, usedBase map[int]struct{}) (base int, siblings []sibling, leaf *record, err error) {
	siblings, leaf, err = makeSiblings(records, depth)
	if err != nil {
		return -1, nil, nil, err
	}
	if len(siblings) < 1 {
		return -1, nil, leaf, nil
	}
	base = da.findBase(siblings, idx, usedBase)
	da.setBase(idx, base)
	return base, siblings, leaf, err
}

// node represents a node of Double-Array.
type node struct {
	data interface{}

	// Names of path parameters.
	paramNames []string
}

// makeNode returns a new node from record.
func makeNode(r *record) (*node, error) {
	dups := make(map[string]bool)
	for _, name := range r.paramNames {
		if dups[name] {
			return nil, fmt.Errorf("denco: path parameter `%v' is duplicated in the key `%v'", name, r.Key)
		}
		dups[name] = true
	}
	return &node{data: r.Value, paramNames: r.paramNames}, nil
}

// sibling represents an intermediate data of build for Double-Array.
type sibling struct {
	// An index of start of duplicated characters.
	start int

	// An index of end of duplicated characters.
	end int

	// A character of sibling.
	c byte
}

// nextIndex returns a next index of array of BASE/CHECK.
func nextIndex(base int, c byte) int {
	return base ^ int(c)
}

// makeSiblings returns slice of sibling.
func makeSiblings(records []*record, depth int) (sib []sibling, leaf *record, err error) {
	var (
		pc byte
		n  int
	)
	for i, r := range records {
		if len(r.Key) <= depth {
			leaf = r
			continue
		}
		c := r.Key[depth]
		switch {
		case pc < c:
			sib = append(sib, sibling{start: i, c: c})
		case pc == c:
			continue
		default:
			return nil, nil, fmt.Errorf("denco: BUG: routing table hasn't been sorted")
		}
		if n > 0 {
			sib[n-1].end = i
		}
		pc = c
		n++
	}
	if n == 0 {
		return nil, leaf, nil
	}
	sib[n-1].end = len(records)
	return sib, leaf, nil
}

// Record represents a record data for router construction.
type Record struct {
	// Key for router construction.
	Key string

	// Result value for Key.
	Value interface{}
}

// NewRecord returns a new Record.
func NewRecord(key string, value interface{}) Record {
	return Record{
		Key:   key,
		Value: value,
	}
}

// record represents a record that use to build the Double-Array.
type record struct {
	Record
	paramNames []string
}

// makeRecords returns the records that use to build Double-Arrays.
func makeRecords(srcs []Record) (statics, params []*record) {
	spChars := string([]byte{ParamCharacter, WildcardCharacter})
	termChar := string(TerminationCharacter)
	for _, r := range srcs {
		if strings.ContainsAny(r.Key, spChars) {
			r.Key += termChar
			params = append(params, &record{Record: r})
		} else {
			statics = append(statics, &record{Record: r})
		}
	}
	return statics, params
}

// recordSlice represents a slice of Record for sort and implements the sort.Interface.
type recordSlice []*record

// Len implements the sort.Interface.Len.
func (rs recordSlice) Len() int {
	return len(rs)
}

// Less implements the sort.Interface.Less.
func (rs recordSlice) Less(i, j int) bool {
	return rs[i].Key < rs[j].Key
}

// Swap implements the sort.Interface.Swap.
func (rs recordSlice) Swap(i, j int) {
	rs[i], rs[j] = rs[j], rs[i]
}
<commit_msg>Modify da.lookup() to take a []Param slice to optimize the memory allocation<commit_after>// Package denco provides fast URL router.
package denco

import (
	"fmt"
	"sort"
	"strings"
)

const (
	// A special character for path parameter.
	ParamCharacter = ':'

	// A special character for wildcard path parameter.
	WildcardCharacter = '*'

	// TerminalCharacter is a special character for end of path.
	TerminationCharacter = '#'
)

// Router represents a URL router.
type Router struct {
	static map[string]interface{}
	param  *doubleArray
}

// New returns a new Router.
func New() *Router {
	return &Router{
		static: make(map[string]interface{}),
		param:  newDoubleArray(),
	}
}

// Lookup returns data and path parameters that associated with path.
// params is a slice of the Param that arranged in the order in which parameters appeared.
// e.g. when built routing path is "/path/:id/:name" and given path is "/path/to/1/alice". params order is [{"id": "1"}, {"name": "alice"}], not [{"name": "alice"}, {"id": "1"}].
func (rt *Router) Lookup(path string) (data interface{}, params []Param, found bool) {
	if data, found := rt.static[path]; found {
		return data, nil, true
	}
	nd, params, found := rt.param.lookup(path, params, 1)
	if !found {
		return nil, nil, false
	}
	for i := 0; i < len(params); i++ {
		params[i].Name = nd.paramNames[i]
	}
	return nd.data, params, true
}

// Build builds URL router from records.
func (rt *Router) Build(records []Record) error {
	statics, params := makeRecords(records)
	for _, r := range statics {
		rt.static[r.Key] = r.Value
	}
	if err := rt.param.build(params, 1, 0, make(map[int]struct{})); err != nil {
		return err
	}
	return nil
}

// Param represents name and value of path parameter.
type Param struct {
	Name  string
	Value string
}

type doubleArray struct {
	bc   []baseCheck
	node []*node
}

func newDoubleArray() *doubleArray {
	return &doubleArray{
		bc:   []baseCheck{0},
		node: []*node{nil}, // A start index is adjusting to 1 because 0 will be used as a mark of non-existent node.
	}
}

// baseCheck contains BASE, CHECK and Extra flags.
// From the top, 22bits of BASE, 2bits of Extra flags and 8bits of CHECK.
//
//  BASE (22bit) | Extra flags (2bit) | CHECK (8bit)
// |----------------------|--|--------|
// 32                    10  8         0
type baseCheck uint32

func (bc baseCheck) Base() int {
	return int(bc >> 10)
}

func (bc *baseCheck) SetBase(base int) {
	*bc |= baseCheck(base) << 10
}

func (bc baseCheck) Check() byte {
	return byte(bc)
}

func (bc *baseCheck) SetCheck(check byte) {
	*bc |= baseCheck(check)
}

func (bc baseCheck) IsEmpty() bool {
	return bc&0xfffffcff == 0
}

func (bc baseCheck) Index() int {
	return int(bc >> 10)
}

func (bc *baseCheck) SetIndex(i int) {
	*bc |= baseCheck(i) << 10
}

func (bc baseCheck) IsSingleParam() bool {
	return bc&paramTypeSingle == paramTypeSingle
}

func (bc baseCheck) IsWildcardParam() bool {
	return bc&paramTypeWildcard == paramTypeWildcard
}

func (bc baseCheck) IsAnyParam() bool {
	return bc&paramTypeAny != 0
}

func (bc *baseCheck) SetSingleParam() {
	*bc |= (1 << 8)
}

func (bc *baseCheck) SetWildcardParam() {
	*bc |= (1 << 9)
}

const (
	paramTypeSingle   = 0x0100
	paramTypeWildcard = 0x0200
	paramTypeAny      = 0x0300
)

func (da *doubleArray) lookup(path string, params []Param, idx int) (*node, []Param, bool) {
	indices := make([]uint64, 0, 1)
	for i := 0; i < len(path); i++ {
		if da.bc[idx].IsAnyParam() {
			indices = append(indices, (uint64(i)<<32)|uint64(idx&0xffffffff))
		}
		c := path[i]
		next := nextIndex(da.bc[idx].Base(), c)
		if next >= len(da.bc) || da.bc[next].Check() != c {
			goto BACKTRACKING
		}
		idx = next
	}
	if next := nextIndex(da.bc[idx].Base(), TerminationCharacter); da.bc[next].Check() == TerminationCharacter {
		return da.node[da.bc[next].Index()], params, true
	}
	return nil, nil, false
BACKTRACKING:
	for j := len(indices) - 1; j >= 0; j-- {
		i, idx := int(indices[j]>>32), int(indices[j]&0xffffffff)
		if da.bc[idx].IsSingleParam() {
			idx := nextIndex(da.bc[idx].Base(), ParamCharacter)
			if idx >= len(da.bc) {
				break
			}
			next := NextSeparator(path, i)
			params := append(params, Param{Value: path[i:next]})
			if nd, params, found := da.lookup(path[next:], params, idx); found {
				return nd, params, true
			}
		}
		if da.bc[idx].IsWildcardParam() {
			idx := nextIndex(da.bc[idx].Base(), WildcardCharacter)
			params := append(params, Param{Value: path[i:]})
			return da.node[da.bc[idx].Index()], params, true
		}
	}
	return nil, nil, false
}

// build builds double-array from records.
func (da *doubleArray) build(srcs []*record, idx, depth int, usedBase map[int]struct{}) error {
	sort.Sort(recordSlice(srcs))
	base, siblings, leaf, err := da.arrange(srcs, idx, depth, usedBase)
	if err != nil {
		return err
	}
	if leaf != nil {
		nd, err := makeNode(leaf)
		if err != nil {
			return err
		}
		da.bc[idx].SetIndex(len(da.node))
		da.node = append(da.node, nd)
	}
	for _, sib := range siblings {
		da.setCheck(nextIndex(base, sib.c), sib.c)
	}
	for _, sib := range siblings {
		records := srcs[sib.start:sib.end]
		switch sib.c {
		case ParamCharacter:
			for _, r := range records {
				next := NextSeparator(r.Key, depth+1)
				name := r.Key[depth+1 : next]
				r.paramNames = append(r.paramNames, name)
				r.Key = r.Key[next:]
			}
			da.bc[idx].SetSingleParam()
			if err := da.build(records, nextIndex(base, sib.c), 0, usedBase); err != nil {
				return err
			}
		case WildcardCharacter:
			r := records[0]
			name := r.Key[depth+1 : len(r.Key)-1]
			r.paramNames = append(r.paramNames, name)
			r.Key = ""
			da.bc[idx].SetWildcardParam()
			if err := da.build(records, nextIndex(base, sib.c), 0, usedBase); err != nil {
				return err
			}
		default:
			if err := da.build(records, nextIndex(base, sib.c), depth+1, usedBase); err != nil {
				return err
			}
		}
	}
	return nil
}

// setBase sets BASE.
func (da *doubleArray) setBase(i, base int) {
	da.bc[i].SetBase(base)
}

// setCheck sets CHECK.
func (da *doubleArray) setCheck(i int, check byte) {
	da.bc[i].SetCheck(check)
}

// findEmptyIndex returns an index of unused BASE/CHECK node.
func (da *doubleArray) findEmptyIndex(start int) int {
	i := start
	for ; i < len(da.bc); i++ {
		if da.bc[i].IsEmpty() {
			break
		}
	}
	return i
}

// findBase returns good BASE.
func (da *doubleArray) findBase(siblings []sibling, start int, usedBase map[int]struct{}) (base int) {
	for idx, firstChar := start+1, siblings[0].c; ; idx = da.findEmptyIndex(idx + 1) {
		base = nextIndex(idx, firstChar)
		if _, used := usedBase[base]; used {
			continue
		}
		i := 0
		for ; i < len(siblings); i++ {
			next := nextIndex(base, siblings[i].c)
			if len(da.bc) <= next {
				da.bc = append(da.bc, make([]baseCheck, next-len(da.bc)+1)...)
			}
			if !da.bc[next].IsEmpty() {
				break
			}
		}
		if i == len(siblings) {
			break
		}
	}
	usedBase[base] = struct{}{}
	return base
}

func (da *doubleArray) arrange(records []*record, idx, depth int, usedBase map[int]struct{}) (base int, siblings []sibling, leaf *record, err error) {
	siblings, leaf, err = makeSiblings(records, depth)
	if err != nil {
		return -1, nil, nil, err
	}
	if len(siblings) < 1 {
		return -1, nil, leaf, nil
	}
	base = da.findBase(siblings, idx, usedBase)
	da.setBase(idx, base)
	return base, siblings, leaf, err
}

// node represents a node of Double-Array.
type node struct {
	data interface{}

	// Names of path parameters.
	paramNames []string
}

// makeNode returns a new node from record.
func makeNode(r *record) (*node, error) {
	dups := make(map[string]bool)
	for _, name := range r.paramNames {
		if dups[name] {
			return nil, fmt.Errorf("denco: path parameter `%v' is duplicated in the key `%v'", name, r.Key)
		}
		dups[name] = true
	}
	return &node{data: r.Value, paramNames: r.paramNames}, nil
}

// sibling represents an intermediate data of build for Double-Array.
type sibling struct {
	// An index of start of duplicated characters.
	start int

	// An index of end of duplicated characters.
	end int

	// A character of sibling.
	c byte
}

// nextIndex returns a next index of array of BASE/CHECK.
func nextIndex(base int, c byte) int {
	return base ^ int(c)
}

// makeSiblings returns slice of sibling.
func makeSiblings(records []*record, depth int) (sib []sibling, leaf *record, err error) {
	var (
		pc byte
		n  int
	)
	for i, r := range records {
		if len(r.Key) <= depth {
			leaf = r
			continue
		}
		c := r.Key[depth]
		switch {
		case pc < c:
			sib = append(sib, sibling{start: i, c: c})
		case pc == c:
			continue
		default:
			return nil, nil, fmt.Errorf("denco: BUG: routing table hasn't been sorted")
		}
		if n > 0 {
			sib[n-1].end = i
		}
		pc = c
		n++
	}
	if n == 0 {
		return nil, leaf, nil
	}
	sib[n-1].end = len(records)
	return sib, leaf, nil
}

// Record represents a record data for router construction.
type Record struct {
	// Key for router construction.
	Key string

	// Result value for Key.
	Value interface{}
}

// NewRecord returns a new Record.
func NewRecord(key string, value interface{}) Record {
	return Record{
		Key:   key,
		Value: value,
	}
}

// record represents a record that use to build the Double-Array.
type record struct {
	Record
	paramNames []string
}

// makeRecords returns the records that use to build Double-Arrays.
func makeRecords(srcs []Record) (statics, params []*record) {
	spChars := string([]byte{ParamCharacter, WildcardCharacter})
	termChar := string(TerminationCharacter)
	for _, r := range srcs {
		if strings.ContainsAny(r.Key, spChars) {
			r.Key += termChar
			params = append(params, &record{Record: r})
		} else {
			statics = append(statics, &record{Record: r})
		}
	}
	return statics, params
}

// recordSlice represents a slice of Record for sort and implements the sort.Interface.
type recordSlice []*record

// Len implements the sort.Interface.Len.
func (rs recordSlice) Len() int {
	return len(rs)
}

// Less implements the sort.Interface.Less.
func (rs recordSlice) Less(i, j int) bool {
	return rs[i].Key < rs[j].Key
}

// Swap implements the sort.Interface.Swap.
func (rs recordSlice) Swap(i, j int) {
	rs[i], rs[j] = rs[j], rs[i]
}
<|endoftext|>


<commit_before>package baa

import (
	"fmt"
	"sync"
)

const (
	// RouteMaxLength set length limit of route pattern
	routeMaxLength = 256
	// RouterParamMaxLength set length limit of route pattern param
	routerParamMaxLength = 32
)

const (
	// method key in routeMap
	GET int = iota
	POST
	PUT
	DELETE
	PATCH
	OPTIONS
	HEAD
	// RouteLength route table length
	RouteLength
)

// methods declare support HTTP method
var methods = map[string]bool{
	"GET":     true,
	"POST":    true,
	"PUT":     true,
	"DELETE":  true,
	"PATCH":   true,
	"OPTIONS": true,
	"HEAD":    true,
}

// methodKeys declare method key in routeMap
var methodKeys = map[string]int{
	"GET":     GET,
	"POST":    POST,
	"PUT":     PUT,
	"DELETE":  DELETE,
	"PATCH":   PATCH,
	"OPTIONS": OPTIONS,
	"HEAD":    HEAD,
}

// optimize ...
var _radix [routeMaxLength]byte
var _param [routerParamMaxLength]byte

// Router provlider router for baa
type Router struct {
	autoHead        bool
	mu              sync.RWMutex
	notFoundHandler HandlerFunc
	routeMap        [RouteLength]*Route
	routeNamedMap   map[string]string
	groups          []*group
}

// Route is a tree node
// route use radix tree
type Route struct {
	pattern  string
	hasParam bool
	parent   *Route
	router   *Router
	children []*Route
	handlers []HandlerFunc
}

// group route
type group struct {
	pattern  string
	handlers []HandlerFunc
}

// newRouter create a router instance
func newRouter() *Router {
	r := new(Router)
	for i := 0; i < len(r.routeMap); i++ {
		r.routeMap[i] = newRoute("/", nil, nil)
	}
	r.routeNamedMap = make(map[string]string)
	r.groups = make([]*group, 0)
	return r
}

// newRoute create a route item
func newRoute(pattern string, handles []HandlerFunc, router *Router) *Route {
	r := new(Route)
	r.pattern = pattern
	r.handlers = handles
	r.router = router
	r.children = make([]*Route, 0)
	return r
}

// newGroup create a group router
func newGroup() *group {
	g := new(group)
	g.handlers = make([]HandlerFunc, 0)
	return g
}

// Match match the uri for handler
func (r *Router) match(method, uri string, c *Context) *Route {
	return r.lookup(uri, r.routeMap[methodKeys[method]], c)
}

// urlFor use named route return format url
func (r *Router) urlFor(name string, args ...interface{}) string {
	if name == "" {
		return ""
	}
	url := r.routeNamedMap[name]
	if url == "" {
		return ""
	}
	return fmt.Sprintf(url, args...)
}

// groupAdd add a group route has same prefix and handle chain
func (r *Router) groupAdd(pattern string, f func(), handlers []HandlerFunc) {
	g := newGroup()
	g.pattern = pattern
	g.handlers = handlers
	r.groups = append(r.groups, g)

	f()

	r.groups = r.groups[:len(r.groups)-1]
}

// Handle registers a new request handle with the given pattern, method and handlers.
func (r *Router) add(method string, pattern string, handlers []HandlerFunc) *Route {
	if _, ok := methods[method]; !ok {
		panic("unsupport http method [" + method + "]")
	}
	if pattern == "" {
		panic("route pattern can not be emtpy!")
	}
	if pattern[0] != '/' {
		panic("route pattern must begin /")
	}
	if len(pattern) > routeMaxLength {
		panic(fmt.Sprintf("route pattern max length limit %d", routeMaxLength))
	}
	r.mu.Lock()
	defer r.mu.Unlock()

	// check group set
	if len(r.groups) > 0 {
		var gpattern string
		var ghandlers []HandlerFunc
		for i := range r.groups {
			gpattern += r.groups[i].pattern
			if len(r.groups[i].handlers) > 0 {
				ghandlers = append(ghandlers, r.groups[i].handlers...)
			}
		}
		pattern = gpattern + pattern
		ghandlers = append(ghandlers, handlers...)
		handlers = ghandlers
	}

	for i := 0; i < len(handlers); i++ {
		handlers[i] = wrapHandlerFunc(handlers[i])
	}

	root := r.routeMap[methodKeys[method]]
	radix := _radix[:0]
	var i, k int
	var tru *Route
	for i = 0; i < len(pattern); i++ {
		//param route
		if pattern[i] == ':' {
			// clear static route
			if len(radix) > 0 {
				root = r.insert(root, newRoute(string(radix), nil, nil))
				radix = _radix[:0]
			}
			// set param route
			param := _param[:0]
			k = 0
			for i = i + 1; i < len(pattern); i++ {
				if !isParamChar(pattern[i]) {
					i--
					break
				}
				param = append(param, pattern[i])
				k++
			}
			if k == 0 {
				panic("route pattern param is empty")
			}
			if k > routerParamMaxLength {
				panic(fmt.Sprintf("route pattern param max length limit %d", routerParamMaxLength))
			}
			// check last character
			p := ":" + string(param[:k])
			if i == len(pattern) {
				tru = newRoute(p, handlers, r)
			} else {
				tru = newRoute(p, nil, nil)
			}
			tru.hasParam = true
			root = r.insert(root, tru)
			continue
		}
		radix = append(radix, pattern[i])
	}

	// static route
	if len(radix) > 0 {
		tru = newRoute(string(radix), handlers, r)
		r.insert(root, tru)
	}

	return newRoute(pattern, handlers, r)
}

// insert build the route tree
func (r *Router) insert(root *Route, node *Route) *Route {
	fmt.Printf("->insert: root: %s, node: %s\n", root.pattern, node.pattern)
	// same route
	if root.pattern == node.pattern {
		if node.handlers != nil {
			root.handlers = node.handlers
		}
		return root
	}

	// param route
	if root.hasParam && node.hasParam {
		if root.parent == nil {
			panic("Router.insert error route has no parent")
		}
		return root.parent.insertChild(node)
	}

	if !root.hasParam && node.hasParam {
		return root.insertChild(node)
	}

	var i int
	if root.hasParam && !node.hasParam {
		for i = range root.children {
			if root.children[i].pattern == node.pattern {
				if node.handlers != nil {
					root.children[i].handlers = node.handlers
				}
				return root.children[i]
			}
			if root.children[i].hasPrefix(node) > 0 {
				return r.insert(root.children[i], node)
			}
		}

		root.insertChild(node)
		return node
	}

	// find radix
	pos := root.hasPrefix(node)
	if pos == 0 {
		panic("Router.insert error root[" + root.pattern + "] and node[" + node.pattern + "] not have both prefix")
	}
	if pos == len(node.pattern) {
		root.parent.deleteChild(root)
		root.parent.insertChild(node)
		root.pattern = root.pattern[pos:]
		node.insertChild(root)
		return node
	}

	node.pattern = node.pattern[pos:]
	if pos == len(root.pattern) {
		for i = range root.children {
			if root.children[i].pattern == node.pattern {
				if node.handlers != nil {
					root.children[i].handlers = node.handlers
				}
				return root.children[i]
			}
			if root.children[i].hasPrefix(node) > 0 {
				return r.insert(root.children[i], node)
			}
		}

		root.insertChild(node)
		return node
	}

	// _parent root and ru has new parent
	_parent := newRoute(root.pattern[:pos], nil, r)
	root.parent.deleteChild(root)
	root.parent.insertChild(_parent)
	root.pattern = root.pattern[pos:]
	_parent.insertChild(root)
	_parent.insertChild(node)

	return node
}

func (r *Router) lookup(pattern string, root *Route, c *Context) *Route {
	var ru *Route
	var i int
	// static route
	if !root.hasParam {
		if pattern == root.pattern {
			return root
		}
		if len(pattern) >= len(root.pattern) && pattern[0:len(root.pattern)] == root.pattern {
			pattern = pattern[len(root.pattern):]
		} else {
			return nil
		}
	} else {
		if len(root.children) == 0 {
			i = len(pattern)
		} else {
			for i = 0; i < len(pattern) && isParamChar(pattern[i]); i++ {
			}
		}
		c.SetParam(root.pattern[1:], pattern[:i])
		if i == len(pattern) {
			return root
		}
		pattern = pattern[i:]
	}
	if len(root.children) == 0 {
		return nil
	}

	// first, static route
	for i = range root.children {
		if ru = r.lookup(pattern, root.children[i], c); ru != nil {
			if ru.handlers != nil {
				return ru
			}
		}
	}

	return nil
}

// print the route map
func (r *Router) print(prefix string, root *Route) {
	if root == nil {
		for m := range r.routeMap {
			fmt.Println(m)
			r.print("", r.routeMap[m])
		}
		return
	}
	fmt.Println(prefix + " -> " + root.pattern)
	for i := range root.children {
		r.print(prefix+" -> "+root.pattern, root.children[i])
	}
}

// Name set name of route
func (r *Route) Name(name string) {
	if name == "" {
		return
	}
	p := make([]byte, 0, len(r.pattern))
	for i := 0; i < len(r.pattern); i++ {
		if r.pattern[i] != ':' {
			p = append(p, r.pattern[i])
			continue
		}
		p = append(p, '%')
		p = append(p, 'v')
		for i = i + 1; i < len(r.pattern); i++ {
			if !isParamChar(r.pattern[i]) {
				i--
				break
			}
		}
	}
	r.router.routeNamedMap[name] = string(p)
}

// deleteChild find child and delete from root route
func (r *Route) deleteChild(child *Route) {
	for i := 0; i < len(r.children); i++ {
		if r.children[i].pattern != child.pattern {
			continue
		}
		if len(r.children) == 1 {
			r.children = r.children[:0]
			return
		}
		if i == 0 {
			r.children = r.children[1:]
			return
		}
		if i+1 == len(r.children) {
			r.children = r.children[:i]
			return
		}
		r.children = append(r.children[:i], r.children[i+1:]...)
		return
	}
}

// insertChild insert child into root route, and returns the child route
func (r *Route) insertChild(child *Route) *Route {
	for i := 0; i < len(r.children); i++ {
		if r.children[i].pattern == child.pattern {
			if child.handlers != nil {
				r.children[i].handlers = child.handlers
			}
			return r.children[i]
		}
	}
	child.parent = r
	r.children = append(r.children, child)
	return child
}

// hasChild check root has child, if yes return child route, or reutrn nil
func (r *Route) hasChild(child *Route) *Route {
	for i := 0; i < len(r.children); i++ {
		if r.children[i].pattern == child.pattern {
			return r.children[i]
		}
	}
	return nil
}

// hasPrefix returns the same prefix position, if none return 0
func (r *Route) hasPrefix(ru *Route) int {
	l := len(r.pattern)
	var i int
	for i = 0; i < len(ru.pattern) && i < l && ru.pattern[i] == r.pattern[i]; i++ {
	}
	return i
}

// wrapHandlerFunc wrap for context handler chain
func wrapHandlerFunc(h HandlerFunc) HandlerFunc {
	return func(c *Context) {
		h(c)
		c.Next()
	}
}

// isParamChar check the char can used for route params
// a-z->65:90, A-Z->97:122, 0-9->48->57, _->95, :->58
func isParamChar(c byte) bool {
	if (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || (c >= 48 && c <= 58) || c == 95 {
		return true
	}
	return false
}
<commit_msg>delete debug code<commit_after>package baa

import (
	"fmt"
	"sync"
)

const (
	// RouteMaxLength set length limit of route pattern
	routeMaxLength = 256
	// RouterParamMaxLength set length limit of route pattern param
	routerParamMaxLength = 32
)

const (
	// method key in routeMap
	GET int = iota
	POST
	PUT
	DELETE
	PATCH
	OPTIONS
	HEAD
	// RouteLength route table length
	RouteLength
)

// methods declare support HTTP method
var methods = map[string]bool{
	"GET":     true,
	"POST":    true,
	"PUT":     true,
	"DELETE":  true,
	"PATCH":   true,
	"OPTIONS": true,
	"HEAD":    true,
}

// methodKeys declare method key in routeMap
var methodKeys = map[string]int{
	"GET":     GET,
	"POST":    POST,
	"PUT":     PUT,
	"DELETE":  DELETE,
	"PATCH":   PATCH,
	"OPTIONS": OPTIONS,
	"HEAD":    HEAD,
}

// optimize ...
var _radix [routeMaxLength]byte
var _param [routerParamMaxLength]byte

// Router provlider router for baa
type Router struct {
	autoHead        bool
	mu              sync.RWMutex
	notFoundHandler HandlerFunc
	routeMap        [RouteLength]*Route
	routeNamedMap   map[string]string
	groups          []*group
}

// Route is a tree node
// route use radix tree
type Route struct {
	pattern  string
	hasParam bool
	parent   *Route
	router   *Router
	children []*Route
	handlers []HandlerFunc
}

// group route
type group struct {
	pattern  string
	handlers []HandlerFunc
}

// newRouter create a router instance
func newRouter() *Router {
	r := new(Router)
	for i := 0; i < len(r.routeMap); i++ {
		r.routeMap[i] = newRoute("/", nil, nil)
	}
	r.routeNamedMap = make(map[string]string)
	r.groups = make([]*group, 0)
	return r
}

// newRoute create a route item
func newRoute(pattern string, handles []HandlerFunc, router *Router) *Route {
	r := new(Route)
	r.pattern = pattern
	r.handlers = handles
	r.router = router
	r.children = make([]*Route, 0)
	return r
}

// newGroup create a group router
func newGroup() *group {
	g := new(group)
	g.handlers = make([]HandlerFunc, 0)
	return g
}

// Match match the uri for handler
func (r *Router) match(method, uri string, c *Context) *Route {
	return r.lookup(uri, r.routeMap[methodKeys[method]], c)
}

// urlFor use named route return format url
func (r *Router) urlFor(name string, args ...interface{}) string {
	if name == "" {
		return ""
	}
	url := r.routeNamedMap[name]
	if url == "" {
		return ""
	}
	return fmt.Sprintf(url, args...)
}

// groupAdd add a group route has same prefix and handle chain
func (r *Router) groupAdd(pattern string, f func(), handlers []HandlerFunc) {
	g := newGroup()
	g.pattern = pattern
	g.handlers = handlers
	r.groups = append(r.groups, g)

	f()

	r.groups = r.groups[:len(r.groups)-1]
}

// Handle registers a new request handle with the given pattern, method and handlers.
func (r *Router) add(method string, pattern string, handlers []HandlerFunc) *Route {
	if _, ok := methods[method]; !ok {
		panic("unsupport http method [" + method + "]")
	}
	if pattern == "" {
		panic("route pattern can not be emtpy!")
	}
	if pattern[0] != '/' {
		panic("route pattern must begin /")
	}
	if len(pattern) > routeMaxLength {
		panic(fmt.Sprintf("route pattern max length limit %d", routeMaxLength))
	}
	r.mu.Lock()
	defer r.mu.Unlock()

	// check group set
	if len(r.groups) > 0 {
		var gpattern string
		var ghandlers []HandlerFunc
		for i := range r.groups {
			gpattern += r.groups[i].pattern
			if len(r.groups[i].handlers) > 0 {
				ghandlers = append(ghandlers, r.groups[i].handlers...)
			}
		}
		pattern = gpattern + pattern
		ghandlers = append(ghandlers, handlers...)
		handlers = ghandlers
	}

	for i := 0; i < len(handlers); i++ {
		handlers[i] = wrapHandlerFunc(handlers[i])
	}

	root := r.routeMap[methodKeys[method]]
	radix := _radix[:0]
	var i, k int
	var tru *Route
	for i = 0; i < len(pattern); i++ {
		//param route
		if pattern[i] == ':' {
			// clear static route
			if len(radix) > 0 {
				root = r.insert(root, newRoute(string(radix), nil, nil))
				radix = _radix[:0]
			}
			// set param route
			param := _param[:0]
			k = 0
			for i = i + 1; i < len(pattern); i++ {
				if !isParamChar(pattern[i]) {
					i--
					break
				}
				param = append(param, pattern[i])
				k++
			}
			if k == 0 {
				panic("route pattern param is empty")
			}
			if k > routerParamMaxLength {
				panic(fmt.Sprintf("route pattern param max length limit %d", routerParamMaxLength))
			}
			// check last character
			p := ":" + string(param[:k])
			if i == len(pattern) {
				tru = newRoute(p, handlers, r)
			} else {
				tru = newRoute(p, nil, nil)
			}
			tru.hasParam = true
			root = r.insert(root, tru)
			continue
		}
		radix = append(radix, pattern[i])
	}

	// static route
	if len(radix) > 0 {
		tru = newRoute(string(radix), handlers, r)
		r.insert(root, tru)
	}

	return newRoute(pattern, handlers, r)
}

// insert build the route tree
func (r *Router) insert(root *Route, node *Route) *Route {
	// same route
	if root.pattern == node.pattern {
		if node.handlers != nil {
			root.handlers = node.handlers
		}
		return root
	}

	// param route
	if root.hasParam && node.hasParam {
		if root.parent == nil {
			panic("Router.insert error route has no parent")
		}
		return root.parent.insertChild(node)
	}

	if !root.hasParam && node.hasParam {
		return root.insertChild(node)
	}

	var i int
	if root.hasParam && !node.hasParam {
		for i = range root.children {
			if root.children[i].pattern == node.pattern {
				if node.handlers != nil {
					root.children[i].handlers = node.handlers
				}
				return root.children[i]
			}
			if root.children[i].hasPrefix(node) > 0 {
				return r.insert(root.children[i], node)
			}
		}

		root.insertChild(node)
		return node
	}

	// find radix
	pos := root.hasPrefix(node)
	if pos == 0 {
		panic("Router.insert error root[" + root.pattern + "] and node[" + node.pattern + "] not have both prefix")
	}
	if pos == len(node.pattern) {
		root.parent.deleteChild(root)
		root.parent.insertChild(node)
		root.pattern = root.pattern[pos:]
		node.insertChild(root)
		return node
	}

	node.pattern = node.pattern[pos:]
	if pos == len(root.pattern) {
		for i = range root.children {
			if root.children[i].pattern == node.pattern {
				if node.handlers != nil {
					root.children[i].handlers = node.handlers
				}
				return root.children[i]
			}
			if root.children[i].hasPrefix(node) > 0 {
				return r.insert(root.children[i], node)
			}
		}

		root.insertChild(node)
		return node
	}

	// _parent root and ru has new parent
	_parent := newRoute(root.pattern[:pos], nil, r)
	root.parent.deleteChild(root)
	root.parent.insertChild(_parent)
	root.pattern = root.pattern[pos:]
	_parent.insertChild(root)
	_parent.insertChild(node)

	return node
}

func (r *Router) lookup(pattern string, root *Route, c *Context) *Route {
	var ru *Route
	var i int
	// static route
	if !root.hasParam {
		if pattern == root.pattern {
			return root
		}
		if len(pattern) >= len(root.pattern) && pattern[0:len(root.pattern)] == root.pattern {
			pattern = pattern[len(root.pattern):]
		} else {
			return nil
		}
	} else {
		if len(root.children) == 0 {
			i = len(pattern)
		} else {
			for i = 0; i < len(pattern) && isParamChar(pattern[i]); i++ {
			}
		}
		c.SetParam(root.pattern[1:], pattern[:i])
		if i == len(pattern) {
			return root
		}
		pattern = pattern[i:]
	}
	if len(root.children) == 0 {
		return nil
	}

	// first, static route
	for i = range root.children {
		if ru = r.lookup(pattern, root.children[i], c); ru != nil {
			if ru.handlers != nil {
				return ru
			}
		}
	}

	return nil
}

// print the route map
func (r *Router) print(prefix string, root *Route) {
	if root == nil {
		for m := range r.routeMap {
			fmt.Println(m)
			r.print("", r.routeMap[m])
		}
		return
	}
	fmt.Println(prefix + " -> " + root.pattern)
	for i := range root.children {
		r.print(prefix+" -> "+root.pattern, root.children[i])
	}
}

// Name set name of route
func (r *Route) Name(name string) {
	if name == "" {
		return
	}
	p := make([]byte, 0, len(r.pattern))
	for i := 0; i < len(r.pattern); i++ {
		if r.pattern[i] != ':' {
			p = append(p, r.pattern[i])
			continue
		}
		p = append(p, '%')
		p = append(p, 'v')
		for i = i + 1; i < len(r.pattern); i++ {
			if !isParamChar(r.pattern[i]) {
				i--
				break
			}
		}
	}
	r.router.routeNamedMap[name] = string(p)
}

// deleteChild find child and delete from root route
func (r *Route) deleteChild(child *Route) {
	for i := 0; i < len(r.children); i++ {
		if r.children[i].pattern != child.pattern {
			continue
		}
		if len(r.children) == 1 {
			r.children = r.children[:0]
			return
		}
		if i == 0 {
			r.children = r.children[1:]
			return
		}
		if i+1 == len(r.children) {
			r.children = r.children[:i]
			return
		}
		r.children = append(r.children[:i], r.children[i+1:]...)
		return
	}
}

// insertChild insert child into root route, and returns the child route
func (r *Route) insertChild(child *Route) *Route {
	for i := 0; i < len(r.children); i++ {
		if r.children[i].pattern == child.pattern {
			if child.handlers != nil {
				r.children[i].handlers = child.handlers
			}
			return r.children[i]
		}
	}
	child.parent = r
	r.children = append(r.children, child)
	return child
}

// hasChild check root has child, if yes return child route, or reutrn nil
func (r *Route) hasChild(child *Route) *Route {
	for i := 0; i < len(r.children); i++ {
		if r.children[i].pattern == child.pattern {
			return r.children[i]
		}
	}
	return nil
}

// hasPrefix returns the same prefix position, if none return 0
func (r *Route) hasPrefix(ru *Route) int {
	l := len(r.pattern)
	var i int
	for i = 0; i < len(ru.pattern) && i < l && ru.pattern[i] == r.pattern[i]; i++ {
	}
	return i
}

// wrapHandlerFunc wrap for context handler chain
func wrapHandlerFunc(h HandlerFunc) HandlerFunc {
	return func(c *Context) {
		h(c)
		c.Next()
	}
}

// isParamChar check the char can used for route params
// a-z->65:90, A-Z->97:122, 0-9->48->57, _->95, :->58
func isParamChar(c byte) bool {
	if (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || (c >= 48 && c <= 58) || c == 95 {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>// Copyright 2014-present Codehack. All rights reserved.
// For mobile and web development visit http://codehack.com
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package relax

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

/*
Router defines the routing system. Objects that implement it have functions
that add routes, find a handle to resources and provide information about routes.

Relax's default router is trieRegexpRouter. It takes full routes, with HTTP method and path, and
inserts them in a trie that can use regular expressions to match individual path segments.

PSE: trieRegexpRouter's path segment expressions (PSE) are match strings that are pre-compiled as
regular expressions. PSE's provide a simple layer of security when accepting values from
the path. Each PSE is made out of a {type:varname} format, where type is the expected type
for a value and varname is the name to give the variable that matches the value.

	"{word:varname}" // matches any word; alphanumeric and underscore.

	"{uint:varname}" // matches an unsigned integer.

	"{int:varname}" // matches a signed integer.

	"{float:varname}" // matches a floating-point number in decimal notation.

	"{date:varname}" // matches a date in ISO 8601 format.

	"{geo:varname}" // matches a geo location as described in RFC 5870

	"{hex:varname}" // matches a hex number, with optional "0x" prefix.

	"{varname}" // catch-all; matches anything. it may overlap other matches.

	"*" // translated into "{wild}"

	"{re:pattern}" // custom regexp pattern.

Some sample routes supported by trieRegexpRouter:

	GET /api/users/@{word:name}

	GET /api/users/{uint:id}/*

	POST /api/users/{uint:id}/profile

	DELETE /api/users/{date:from}/to/{date:to}

	GET /api/cities/{geo:location}

	PUT /api/investments/\${float:dollars}/fund

	GET /api/todos/month/{re:([0][1-9]|[1][0-2])}

Since PSE's are compiled to regexp, care must be taken to escape characters that
might break the compilation.
*/
type Router interface {
	// FindHandler should match request parameters to an existing resource handler and
	// return it. If no match is found, it should return an StatusError error which will
	// be sent to the requester. The default errors ErrRouteNotFound and
	// ErrRouteBadMethod cover the default cases.
	FindHandler(string, string, *url.Values) (HandlerFunc, error)

	// AddRoute is used to create new routes to resources. It expects the HTTP method
	// (GET, POST, ...) followed by the resource path and the handler function.
	AddRoute(string, string, HandlerFunc)

	// PathMethods returns a comma-separated list of HTTP methods that are matched
	// to a path. It will do PSE expansion.
	PathMethods(string) string
}

// These are errors returned by the default routing engine. You are encouraged to
// reuse them with your own Router.
var (
	// ErrRouteNotFound is returned when the path searched didn't reach a resource handler.
	ErrRouteNotFound = &StatusError{http.StatusNotFound, "That route was not found.", nil}

	// ErrRouteBadMethod is returned when the path did not match a given HTTP method.
	ErrRouteBadMethod = &StatusError{http.StatusMethodNotAllowed, "That method is not supported", nil}
)

// pathRegexpCache is a cache of all compiled regexp's so they can be reused.
var pathRegexpCache = make(map[string]*regexp.Regexp, 0)

// trieRegexpRouter implements Router with a trie that can store regular expressions.
// root points to the top of the tree from which all routes are searched and matched.
// methods is a list of all the methods used in routes.
type trieRegexpRouter struct {
	root    *trieNode
	methods []string
}

// trieNode contains the routing information.
// handler, if not nil, points to the resource handler served by a specific route.
// numExp is non-zero if the current path segment has regexp links.
// depth is the path depth of the current segment; 0 == HTTP verb.
// links are the contiguous path segments.
//
// For example, given the following route and handler:
//		"GET /api/users/111" -> users.GetUser()
//        - the path segment links are ["GET", "api", "users", "111"]
//        - "GET" has depth=0 and "111" has depth=3
//        - suppose "111" might be matched via regexp, then "users".numExp > 0
//        - "111" segment will point to the handler users.GetUser()
type trieNode struct {
	handler HandlerFunc
	numExp  int
	depth   int
	links   map[string]*trieNode
}

// segmentExp compiles the pattern string into a regexp so it can used in a
// path segment match. This function will panic if the regexp compilation fails.
func segmentExp(pattern string) *regexp.Regexp {
	// custom regexp pattern.
	if strings.HasPrefix(pattern, "{re:") {
		return regexp.MustCompile(pattern[4 : len(pattern)-1])
	}

	// turn "*" => "{wild}"
	pattern = strings.Replace(pattern, "*", `{wild}`, -1)
	// any: catch-all pattern
	p := regexp.MustCompile(`\{\w+\}`).
		ReplaceAllStringFunc(pattern, func(m string) string {
		return fmt.Sprintf(`(?P<%s>.+)`, m[1:len(m)-1])
	})
	// word: matches an alphanumeric word, with underscores.
	p = regexp.MustCompile(`\{(?:word\:)\w+\}`).
		ReplaceAllStringFunc(p, func(m string) string {
		return fmt.Sprintf(`(?P<%s>\w+)`, m[6:len(m)-1])
	})
	// date: matches a date as described in ISO 8601. see: https://en.wikipedia.org/wiki/ISO_8601
	// accepted values:
	// 	YYYY
	// 	YYYY-MM
	// 	YYYY-MM-DD
	// 	YYYY-MM-DDTHH
	// 	YYYY-MM-DDTHH:MM
	// 	YYYY-MM-DDTHH:MM:SS[.NN]
	// 	YYYY-MM-DDTHH:MM:SS[.NN]Z
	// 	YYYY-MM-DDTHH:MM:SS[.NN][+-]HH
	// 	YYYY-MM-DDTHH:MM:SS[.NN][+-]HH:MM
	//
	p = regexp.MustCompile(`\{(?:date\:)\w+\}`).
		ReplaceAllStringFunc(p, func(m string) string {
		name := m[6 : len(m)-1]
		return fmt.Sprintf(`(?P<%[1]s>(`+
			`(?P<%[1]s_year>\d{4})([/-]?`+
			`(?P<%[1]s_mon>(0[1-9])|(1[012]))([/-]?`+
			`(?P<%[1]s_mday>(0[1-9])|([12]\d)|(3[01])))?)?`+
			`(?:T(?P<%[1]s_hour>([01][0-9])|(?:2[0123]))(\:?(?P<%[1]s_min>[0-5][0-9])(\:?(?P<%[1]s_sec>[0-5][0-9]([\,\.]\d{1,10})?))?)?(?:Z|([\-+](?:([01][0-9])|(?:2[0123]))(\:?(?:[0-5][0-9]))?))?)?`+
			`))`, name)
	})
	// geo: geo location in decimal. See http://tools.ietf.org/html/rfc5870
	// accepted values:
	// 	lat,lon           (point)
	// 	lat,lon,alt       (3d point)
	// 	lag,lon;u=unc     (circle)
	// 	lat,lon,alt;u=unc (sphere)
	// 	lat,lon;crs=name  (point with coordinate reference system (CRS) value)
	p = regexp.MustCompile(`\{(?:geo\:)\w+\}`).ReplaceAllStringFunc(p, func(m string) string {
		name := m[5 : len(m)-1]
		return fmt.Sprintf(`(?P<%[1]s_lat>\-?\d+(\.\d+)?)[,;]`+
			`(?P<%[1]s_lon>\-?\d+(\.\d+)?)([,;]`+
			`(?P<%[1]s_alt>\-?\d+(\.\d+)?))?(((?:;crs=)`+
			`(?P<%[1]s_crs>[\w\-]+))?((?:;u=)`+
			`(?P<%[1]s_u>\-?\d+(\.\d+)?))?)?`, name)
	})
	// hex: matches a hexadecimal number (assume 32bit)
	// accepted value: 0xNN
	p = regexp.MustCompile(`\{(?:hex\:)\w+\}`).
		ReplaceAllStringFunc(p, func(m string) string {
		return fmt.Sprintf(`(?P<%s>(?:0x)?[[:xdigit:]]+)`, m[5:len(m)-1])
	})
	// float: matches a floating-point number
	p = regexp.MustCompile(`\{(?:float\:)\w+\}`).
		ReplaceAllStringFunc(p, func(m string) string {
		return fmt.Sprintf(`(?P<%s>[\-+]?\d+\.\d+)`, m[7:len(m)-1])
	})
	// uint: matches an unsigned integer number (64bit)
	p = regexp.MustCompile(`\{(?:uint\:)\w+\}`).
		ReplaceAllStringFunc(p, func(m string) string {
		return fmt.Sprintf(`(?P<%s>\d{1,18})`, m[6:len(m)-1])
	})
	// int: matches a signed integer number (64bit)
	p = regexp.MustCompile(`\{(?:int\:)\w+\}`).
		ReplaceAllStringFunc(p, func(m string) string {
		return fmt.Sprintf(`(?P<%s>[-+]?\d{1,18})`, m[5:len(m)-1])
	})
	return regexp.MustCompile(p)
}

// AddRoute breaks a path into segments and inserts them in the tree. If a
// segment contains matching {}'s then it is tried as a regexp segment, otherwise it is
// treated as a regular string segment.
func (router *trieRegexpRouter) AddRoute(method, path string, handler HandlerFunc) {
	node := router.root
	pseg := strings.Split(method+strings.TrimRight(path, "/"), "/")
	for i := range pseg {
		if (strings.Contains(pseg[i], "{") && strings.Contains(pseg[i], "}")) || strings.Contains(pseg[i], "*") {
			if _, ok := pathRegexpCache[pseg[i]]; !ok {
				pathRegexpCache[pseg[i]] = segmentExp(pseg[i])
			}
			node.numExp++
		}
		if node.links[pseg[i]] == nil {
			if node.links == nil {
				node.links = make(map[string]*trieNode, 0)
			}
			node.links[pseg[i]] = &trieNode{depth: node.depth + 1}
		}
		node = node.links[pseg[i]]
	}

	node.handler = handler

	// update methods list
	if !strings.Contains(strings.Join(router.methods, ","), method) {
		router.methods = append(router.methods, method)
	}
}

// matchSegment tries to match a path segment 'pseg' to the node's regexp links.
// This function will return any path values matched so they can be used in
// Request.PathValues.
func (node *trieNode) matchSegment(pseg string, depth int, values *url.Values) *trieNode {
	if node.numExp == 0 {
		return node.links[pseg]
	}
	for pexp := range node.links {
		rx := pathRegexpCache[pexp]
		if rx == nil {
			continue
		}
		// this prevents the matching to be side-tracked by smaller paths.
		if depth > node.links[pexp].depth && node.links[pexp].links == nil {
			continue
		}
		m := rx.FindStringSubmatch(pseg)
		if len(m) > 1 && m[0] == pseg {
			if values != nil {
				if *values == nil {
					*values = make(url.Values)
				}
				sub := rx.SubexpNames()
				for i, n := 1, len(*values)/2; i < len(m); i++ {
					_n := fmt.Sprintf("_%d", n+i)
					(*values).Set(_n, m[i])
					if sub[i] != "" {
						(*values).Add(sub[i], m[i])
					}
				}
			}
			return node.links[pexp]
		}
	}
	return node.links[pseg]
}

// FindHandler returns a resource handler that matches the requested route; or
// an error (StatusError) if none found.
// method is the HTTP verb.
// path is the relative URI path.
// values is a pointer to an url.Values map to store parameters from the path.
func (router *trieRegexpRouter) FindHandler(method, path string, values *url.Values) (HandlerFunc, error) {
	if method == "HEAD" {
		method = "GET"
	}
	node := router.root
	pseg := strings.Split(method+strings.TrimRight(path, "/"), "/") // ex: GET/api/users
	slen := len(pseg)
	for i := range make([]struct{}, slen) {
		if node == nil {
			if i <= 1 {
				return nil, ErrRouteBadMethod
			}
			return nil, ErrRouteNotFound
		}
		node = node.matchSegment(pseg[i], slen, values)
	}

	if node == nil || node.handler == nil {
		return nil, ErrRouteNotFound
	}
	return node.handler, nil
}

// PathMethods returns a string with comma-separated HTTP methods that match
// the path. This list is suitable for Allow header response. Note that this
// function only lists the methods, not if they are allowed.
func (router *trieRegexpRouter) PathMethods(path string) string {
	var node *trieNode
	methods := "HEAD" // cheat
	pseg := strings.Split("*"+strings.TrimRight(path, "/"), "/")
	slen := len(pseg)
	for _, method := range router.methods {
		node = router.root
		pseg[0] = method
		for i := range pseg {
			if node == nil {
				continue
			}
			node = node.matchSegment(pseg[i], slen, nil)
		}
		if node == nil || node.handler == nil {
			continue
		}
		methods += ", " + method
	}
	return methods
}

// newRouter returns a new trieRegexpRouter object with an initialized tree.
func newRouter() *trieRegexpRouter {
	return &trieRegexpRouter{root: new(trieNode)}
}
<commit_msg>added PSE for UUIDs<commit_after>// Copyright 2014-present Codehack. All rights reserved.
// For mobile and web development visit http://codehack.com
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package relax

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

/*
Router defines the routing system. Objects that implement it have functions
that add routes, find a handle to resources and provide information about routes.

Relax's default router is trieRegexpRouter. It takes full routes, with HTTP method and path, and
inserts them in a trie that can use regular expressions to match individual path segments.

PSE: trieRegexpRouter's path segment expressions (PSE) are match strings that are pre-compiled as
regular expressions. PSE's provide a simple layer of security when accepting values from
the path. Each PSE is made out of a {type:varname} format, where type is the expected type
for a value and varname is the name to give the variable that matches the value.

	"{word:varname}" // matches any word; alphanumeric and underscore.

	"{uint:varname}" // matches an unsigned integer.

	"{int:varname}" // matches a signed integer.

	"{float:varname}" // matches a floating-point number in decimal notation.

	"{date:varname}" // matches a date in ISO 8601 format.

	"{geo:varname}" // matches a geo location as described in RFC 5870

	"{hex:varname}" // matches a hex number, with optional "0x" prefix.

	"{uuid:varname}" // matches an UUID.

	"{varname}" // catch-all; matches anything. it may overlap other matches.

	"*" // translated into "{wild}"

	"{re:pattern}" // custom regexp pattern.

Some sample routes supported by trieRegexpRouter:

	GET /api/users/@{word:name}

	GET /api/users/{uint:id}/*

	POST /api/users/{uint:id}/profile

	DELETE /api/users/{date:from}/to/{date:to}

	GET /api/cities/{geo:location}

	PUT /api/investments/\${float:dollars}/fund

	GET /api/todos/month/{re:([0][1-9]|[1][0-2])}

Since PSE's are compiled to regexp, care must be taken to escape characters that
might break the compilation.
*/
type Router interface {
	// FindHandler should match request parameters to an existing resource handler and
	// return it. If no match is found, it should return an StatusError error which will
	// be sent to the requester. The default errors ErrRouteNotFound and
	// ErrRouteBadMethod cover the default cases.
	FindHandler(string, string, *url.Values) (HandlerFunc, error)

	// AddRoute is used to create new routes to resources. It expects the HTTP method
	// (GET, POST, ...) followed by the resource path and the handler function.
	AddRoute(string, string, HandlerFunc)

	// PathMethods returns a comma-separated list of HTTP methods that are matched
	// to a path. It will do PSE expansion.
	PathMethods(string) string
}

// These are errors returned by the default routing engine. You are encouraged to
// reuse them with your own Router.
var (
	// ErrRouteNotFound is returned when the path searched didn't reach a resource handler.
	ErrRouteNotFound = &StatusError{http.StatusNotFound, "That route was not found.", nil}

	// ErrRouteBadMethod is returned when the path did not match a given HTTP method.
	ErrRouteBadMethod = &StatusError{http.StatusMethodNotAllowed, "That method is not supported", nil}
)

// pathRegexpCache is a cache of all compiled regexp's so they can be reused.
var pathRegexpCache = make(map[string]*regexp.Regexp, 0)

// trieRegexpRouter implements Router with a trie that can store regular expressions.
// root points to the top of the tree from which all routes are searched and matched.
// methods is a list of all the methods used in routes.
type trieRegexpRouter struct {
	root    *trieNode
	methods []string
}

// trieNode contains the routing information.
// handler, if not nil, points to the resource handler served by a specific route.
// numExp is non-zero if the current path segment has regexp links.
// depth is the path depth of the current segment; 0 == HTTP verb.
// links are the contiguous path segments.
//
// For example, given the following route and handler:
//		"GET /api/users/111" -> users.GetUser()
//        - the path segment links are ["GET", "api", "users", "111"]
//        - "GET" has depth=0 and "111" has depth=3
//        - suppose "111" might be matched via regexp, then "users".numExp > 0
//        - "111" segment will point to the handler users.GetUser()
type trieNode struct {
	handler HandlerFunc
	numExp  int
	depth   int
	links   map[string]*trieNode
}

// segmentExp compiles the pattern string into a regexp so it can used in a
// path segment match. This function will panic if the regexp compilation fails.
func segmentExp(pattern string) *regexp.Regexp {
	// custom regexp pattern.
	if strings.HasPrefix(pattern, "{re:") {
		return regexp.MustCompile(pattern[4 : len(pattern)-1])
	}

	// turn "*" => "{wild}"
	pattern = strings.Replace(pattern, "*", `{wild}`, -1)
	// any: catch-all pattern
	p := regexp.MustCompile(`\{\w+\}`).
		ReplaceAllStringFunc(pattern, func(m string) string {
		return fmt.Sprintf(`(?P<%s>.+)`, m[1:len(m)-1])
	})
	// word: matches an alphanumeric word, with underscores.
	p = regexp.MustCompile(`\{(?:word\:)\w+\}`).
		ReplaceAllStringFunc(p, func(m string) string {
		return fmt.Sprintf(`(?P<%s>\w+)`, m[6:len(m)-1])
	})
	// date: matches a date as described in ISO 8601. see: https://en.wikipedia.org/wiki/ISO_8601
	// accepted values:
	// 	YYYY
	// 	YYYY-MM
	// 	YYYY-MM-DD
	// 	YYYY-MM-DDTHH
	// 	YYYY-MM-DDTHH:MM
	// 	YYYY-MM-DDTHH:MM:SS[.NN]
	// 	YYYY-MM-DDTHH:MM:SS[.NN]Z
	// 	YYYY-MM-DDTHH:MM:SS[.NN][+-]HH
	// 	YYYY-MM-DDTHH:MM:SS[.NN][+-]HH:MM
	//
	p = regexp.MustCompile(`\{(?:date\:)\w+\}`).
		ReplaceAllStringFunc(p, func(m string) string {
		name := m[6 : len(m)-1]
		return fmt.Sprintf(`(?P<%[1]s>(`+
			`(?P<%[1]s_year>\d{4})([/-]?`+
			`(?P<%[1]s_mon>(0[1-9])|(1[012]))([/-]?`+
			`(?P<%[1]s_mday>(0[1-9])|([12]\d)|(3[01])))?)?`+
			`(?:T(?P<%[1]s_hour>([01][0-9])|(?:2[0123]))(\:?(?P<%[1]s_min>[0-5][0-9])(\:?(?P<%[1]s_sec>[0-5][0-9]([\,\.]\d{1,10})?))?)?(?:Z|([\-+](?:([01][0-9])|(?:2[0123]))(\:?(?:[0-5][0-9]))?))?)?`+
			`))`, name)
	})
	// geo: geo location in decimal. See http://tools.ietf.org/html/rfc5870
	// accepted values:
	// 	lat,lon           (point)
	// 	lat,lon,alt       (3d point)
	// 	lag,lon;u=unc     (circle)
	// 	lat,lon,alt;u=unc (sphere)
	// 	lat,lon;crs=name  (point with coordinate reference system (CRS) value)
	p = regexp.MustCompile(`\{(?:geo\:)\w+\}`).ReplaceAllStringFunc(p, func(m string) string {
		name := m[5 : len(m)-1]
		return fmt.Sprintf(`(?P<%[1]s_lat>\-?\d+(\.\d+)?)[,;]`+
			`(?P<%[1]s_lon>\-?\d+(\.\d+)?)([,;]`+
			`(?P<%[1]s_alt>\-?\d+(\.\d+)?))?(((?:;crs=)`+
			`(?P<%[1]s_crs>[\w\-]+))?((?:;u=)`+
			`(?P<%[1]s_u>\-?\d+(\.\d+)?))?)?`, name)
	})
	// hex: matches a hexadecimal number.
	// accepted value: 0xNN
	p = regexp.MustCompile(`\{(?:hex\:)\w+\}`).
		ReplaceAllStringFunc(p, func(m string) string {
		return fmt.Sprintf(`(?P<%s>(?:0x)?[[:xdigit:]]+)`, m[5:len(m)-1])
	})
	// uuid: matches an UUID using hex octets, with optional dashes.
	// accepted value: NNNNNNNN-NNNN-NNNN-NNNN-NNNNNNNNNNNN
	p = regexp.MustCompile(`\{(?:uuid\:)\w+\}`).
		ReplaceAllStringFunc(p, func(m string) string {
		return fmt.Sprintf(`(?P<%s>[[:xdigit:]]{8}\-?`+
			`[[:xdigit:]]{4}\-?`+
			`[[:xdigit:]]{4}\-?`+
			`[[:xdigit:]]{4}\-?`+
			`[[:xdigit:]]{12})`, m[6:len(m)-1])
	})
	// float: matches a floating-point number
	p = regexp.MustCompile(`\{(?:float\:)\w+\}`).
		ReplaceAllStringFunc(p, func(m string) string {
		return fmt.Sprintf(`(?P<%s>[\-+]?\d+\.\d+)`, m[7:len(m)-1])
	})
	// uint: matches an unsigned integer number (64bit)
	p = regexp.MustCompile(`\{(?:uint\:)\w+\}`).
		ReplaceAllStringFunc(p, func(m string) string {
		return fmt.Sprintf(`(?P<%s>\d{1,18})`, m[6:len(m)-1])
	})
	// int: matches a signed integer number (64bit)
	p = regexp.MustCompile(`\{(?:int\:)\w+\}`).
		ReplaceAllStringFunc(p, func(m string) string {
		return fmt.Sprintf(`(?P<%s>[-+]?\d{1,18})`, m[5:len(m)-1])
	})
	return regexp.MustCompile(p)
}

// AddRoute breaks a path into segments and inserts them in the tree. If a
// segment contains matching {}'s then it is tried as a regexp segment, otherwise it is
// treated as a regular string segment.
func (router *trieRegexpRouter) AddRoute(method, path string, handler HandlerFunc) {
	node := router.root
	pseg := strings.Split(method+strings.TrimRight(path, "/"), "/")
	for i := range pseg {
		if (strings.Contains(pseg[i], "{") && strings.Contains(pseg[i], "}")) || strings.Contains(pseg[i], "*") {
			if _, ok := pathRegexpCache[pseg[i]]; !ok {
				pathRegexpCache[pseg[i]] = segmentExp(pseg[i])
			}
			node.numExp++
		}
		if node.links[pseg[i]] == nil {
			if node.links == nil {
				node.links = make(map[string]*trieNode, 0)
			}
			node.links[pseg[i]] = &trieNode{depth: node.depth + 1}
		}
		node = node.links[pseg[i]]
	}

	node.handler = handler

	// update methods list
	if !strings.Contains(strings.Join(router.methods, ","), method) {
		router.methods = append(router.methods, method)
	}
}

// matchSegment tries to match a path segment 'pseg' to the node's regexp links.
// This function will return any path values matched so they can be used in
// Request.PathValues.
func (node *trieNode) matchSegment(pseg string, depth int, values *url.Values) *trieNode {
	if node.numExp == 0 {
		return node.links[pseg]
	}
	for pexp := range node.links {
		rx := pathRegexpCache[pexp]
		if rx == nil {
			continue
		}
		// this prevents the matching to be side-tracked by smaller paths.
		if depth > node.links[pexp].depth && node.links[pexp].links == nil {
			continue
		}
		m := rx.FindStringSubmatch(pseg)
		if len(m) > 1 && m[0] == pseg {
			if values != nil {
				if *values == nil {
					*values = make(url.Values)
				}
				sub := rx.SubexpNames()
				for i, n := 1, len(*values)/2; i < len(m); i++ {
					_n := fmt.Sprintf("_%d", n+i)
					(*values).Set(_n, m[i])
					if sub[i] != "" {
						(*values).Add(sub[i], m[i])
					}
				}
			}
			return node.links[pexp]
		}
	}
	return node.links[pseg]
}

// FindHandler returns a resource handler that matches the requested route; or
// an error (StatusError) if none found.
// method is the HTTP verb.
// path is the relative URI path.
// values is a pointer to an url.Values map to store parameters from the path.
func (router *trieRegexpRouter) FindHandler(method, path string, values *url.Values) (HandlerFunc, error) {
	if method == "HEAD" {
		method = "GET"
	}
	node := router.root
	pseg := strings.Split(method+strings.TrimRight(path, "/"), "/") // ex: GET/api/users
	slen := len(pseg)
	for i := range make([]struct{}, slen) {
		if node == nil {
			if i <= 1 {
				return nil, ErrRouteBadMethod
			}
			return nil, ErrRouteNotFound
		}
		node = node.matchSegment(pseg[i], slen, values)
	}

	if node == nil || node.handler == nil {
		return nil, ErrRouteNotFound
	}
	return node.handler, nil
}

// PathMethods returns a string with comma-separated HTTP methods that match
// the path. This list is suitable for Allow header response. Note that this
// function only lists the methods, not if they are allowed.
func (router *trieRegexpRouter) PathMethods(path string) string {
	var node *trieNode
	methods := "HEAD" // cheat
	pseg := strings.Split("*"+strings.TrimRight(path, "/"), "/")
	slen := len(pseg)
	for _, method := range router.methods {
		node = router.root
		pseg[0] = method
		for i := range pseg {
			if node == nil {
				continue
			}
			node = node.matchSegment(pseg[i], slen, nil)
		}
		if node == nil || node.handler == nil {
			continue
		}
		methods += ", " + method
	}
	return methods
}

// newRouter returns a new trieRegexpRouter object with an initialized tree.
func newRouter() *trieRegexpRouter {
	return &trieRegexpRouter{root: new(trieNode)}
}
<|endoftext|>


<commit_before>package goat

import (
	"net/http"
	"sort"

	"github.com/julienschmidt/httprouter"
)

// Router represents a router
type Router struct {
	// Tree structure
	parent   *Router
	children []*Router

	// The prefix, default: /
	prefix string

	// The index, maps titles to urls
	index map[string]string

	// The router
	router *httprouter.Router
}

// Handle describes the function that should be used by handlers
type Handle func(http.ResponseWriter, *http.Request, Params)

// notFoundHandler handles (as you already know) the 404 error
func (r *Router) notFoundHandler(w http.ResponseWriter, req *http.Request) {
	WriteError(w, "404 Not Found")
}

// ServeHTTP calls the same method on the router
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	r.router.ServeHTTP(w, req)
}

// subPath returns the prefix of the router + the given path and eliminates
// duplicate slashes
func (r *Router) subPath(p string) string {
	pre := r.prefix

	if (pre == "/" || pre[:len(pre)-1] == "/") && p[:1] == "/" {
		pre = pre[:len(pre)-1]
	}

	return pre + p
}

// addRoute adds a route to the index and passes it over to the httprouter
func (r *Router) addRoute(m, p, t string, fn Handle) {

	path := r.subPath(p)

	// Add to index
	if len(t) > 0 && m == "GET" {
		// TODO: Display total path including host
		r.index[t] = path
	}

	// Wrapper function to bypass the parameter problem
	wf := func(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
		fn(w, req, paramsFromHTTPRouter(p))
	}

	// TODO: Maybe let the subrouters do work too and only call them if
	// needed
	if r.parent != nil {
		// We have a subrouter, let the main router handle it
		r.parent.router.Handle(m, path, wf)
	} else {
		r.router.Handle(m, path, wf)
	}
}

// Get adds a GET route
func (r *Router) Get(path, title string, fn Handle) {
	r.addRoute("GET", path, title, fn)
}

// Get adds a POST route
func (r *Router) Post(path, title string, fn Handle) {
	r.addRoute("POST", path, title, fn)
}

// Get adds a DELETE route
func (r *Router) Delete(path, title string, fn Handle) {
	r.addRoute("DELETE", path, title, fn)
}

// Get adds a PUT route
func (r *Router) Put(path, title string, fn Handle) {
	r.addRoute("PUT", path, title, fn)
}

// Subrouter creates and returns a subrouter
func (r *Router) Subrouter(path string) *Router {
	sr := &Router{}
	sr.index = make(map[string]string)
	sr.prefix = r.subPath(path)

	// Init relationships
	r.children = append(r.children, sr)
	sr.parent = r

	return sr
}

// IndexHandler writes the index of all GET methods to the ResponseWriter
func (r *Router) IndexHandler(w http.ResponseWriter, _ *http.Request, _ Params) {
	WriteJSON(w, r.Index())
}

// Index returns a string map with the titles and urls of all GET routes
func (r *Router) Index() map[string]string {
	index := r.index

	// Recursion
	for _, sr := range r.children {
		si := sr.Index()

		for k, v := range si {
			index[k] = v
		}
	}

	// Sort
	sorted := make(map[string]string)
	var keys []string
	for k := range index {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys {
		sorted[k] = index[k]
	}

	return sorted
}
<commit_msg>Add getRoot to get top level router<commit_after>package goat

import (
	"net/http"
	"sort"

	"github.com/julienschmidt/httprouter"
)

// Router represents a router
type Router struct {
	// Tree structure
	parent   *Router
	children []*Router

	// The prefix, default: /
	prefix string

	// The index, maps titles to urls
	index map[string]string

	// The router
	router *httprouter.Router
}

// Handle describes the function that should be used by handlers
type Handle func(http.ResponseWriter, *http.Request, Params)

// notFoundHandler handles (as you already know) the 404 error
func (r *Router) notFoundHandler(w http.ResponseWriter, req *http.Request) {
	WriteError(w, "404 Not Found")
}

// ServeHTTP calls the same method on the router
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	r.router.ServeHTTP(w, req)
}

// subPath returns the prefix of the router + the given path and eliminates
// duplicate slashes
func (r *Router) subPath(p string) string {
	pre := r.prefix

	if (pre == "/" || pre[:len(pre)-1] == "/") && p[:1] == "/" {
		pre = pre[:len(pre)-1]
	}

	return pre + p
}

// addRoute adds a route to the index and passes it over to the httprouter
func (r *Router) addRoute(m, p, t string, fn Handle) {

	path := r.subPath(p)

	// Add to index
	if len(t) > 0 && m == "GET" {
		// TODO: Display total path including host
		r.index[t] = path
	}

	// Wrapper function to bypass the parameter problem
	wf := func(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
		fn(w, req, paramsFromHTTPRouter(p))
	}

	// TODO: Maybe let the subrouters do work too and only call them if
	// needed
	if r.parent != nil {
		// We have a subrouter, let the main router handle it
		r.getRoot().router.Handle(m, path, wf)
	} else {
		r.router.Handle(m, path, wf)
	}
}

// getRoot returns the root router
func (r *Router) getRoot() *Router {
	if r.parent != nil {
		return r.parent.getRoot()
	}

	return r
}

// Get adds a GET route
func (r *Router) Get(path, title string, fn Handle) {
	r.addRoute("GET", path, title, fn)
}

// Get adds a POST route
func (r *Router) Post(path, title string, fn Handle) {
	r.addRoute("POST", path, title, fn)
}

// Get adds a DELETE route
func (r *Router) Delete(path, title string, fn Handle) {
	r.addRoute("DELETE", path, title, fn)
}

// Get adds a PUT route
func (r *Router) Put(path, title string, fn Handle) {
	r.addRoute("PUT", path, title, fn)
}

// Subrouter creates and returns a subrouter
func (r *Router) Subrouter(path string) *Router {
	sr := &Router{}
	sr.index = make(map[string]string)
	sr.prefix = r.subPath(path)

	// Init relationships
	r.children = append(r.children, sr)
	sr.parent = r

	return sr
}

// IndexHandler writes the index of all GET methods to the ResponseWriter
func (r *Router) IndexHandler(w http.ResponseWriter, _ *http.Request, _ Params) {
	WriteJSON(w, r.Index())
}

// Index returns a string map with the titles and urls of all GET routes
func (r *Router) Index() map[string]string {
	index := r.index

	// Recursion
	for _, sr := range r.children {
		si := sr.Index()

		for k, v := range si {
			index[k] = v
		}
	}

	// Sort
	sorted := make(map[string]string)
	var keys []string
	for k := range index {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys {
		sorted[k] = index[k]
	}

	return sorted
}
<|endoftext|>


<commit_before>package main

import (
	"bosun.org/graphite"
	"encoding/json"
	"fmt"
	"github.com/Dieterbe/go-metrics"
	"github.com/raintank/raintank-metric/schema"
	"io/ioutil"
	"log"
	"math"
	"math/rand"
	"net"
	"net/http"
	"os"
	"strconv"
	"sync"
	"time"
	//"strings"
)

type Hit struct {
	Index  string                  `json:"_index"`
	Type   string                  `json:"_type"`
	Id     string                  `json:"_id"`
	Score  float64                 `json:"_score"`
	Source schema.MetricDefinition `json:"_source"`
}

type EsResult struct {
	Took     int
	TimedOut bool
	_shards  struct {
		total      int
		successful int
		failed     int
	}
	Hits struct {
		Total    int
		MaxScore int
		Hits     []Hit
	}
}

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

type stat struct {
	def       schema.MetricDefinition
	firstSeen int64
}

var targets map[string]stat
var targetKeys []string
var targetsLock sync.Mutex

func init() {
	targets = make(map[string]stat)
	targetKeys = make([]string, 0)
}

func main() {
	if len(os.Args) != 4 {
		log.Fatal("usage: graphite-watcher <elasticsearch-addr> <metrics-addr> <graphite-addr>")
	}
	addr, _ := net.ResolveTCPAddr("tcp", os.Args[2])
	go metrics.Graphite(metrics.DefaultRegistry, 10e9, "graphite-watcher.", addr)
	lag := metrics.NewHistogram(metrics.NewExpDecaySample(1028, 0.015))
	metrics.Register("lag", lag)
	numMetrics := metrics.NewGauge()
	metrics.Register("num_metrics", numMetrics)
	nullPoints := metrics.NewCounter()
	metrics.Register("null_points", nullPoints)

	// for a metric to exist in ES at t=Y, there must at least have been 1 point for that metric
	// at a time X where X < Y.  Hence, we can confidently say that if we see a metric at Y, we can
	// demand data to show up for that metric at >=Y
	// for our data check to be useful we need metrics to show up in ES soon after being in the pipeline,
	// which seems to be true (see nsqadmin)
	updateTargets := func() {
		getEsTick := time.NewTicker(time.Second * time.Duration(10))
		for range getEsTick.C {
			res, err := http.Get("http://" + os.Args[1] + "/metric/_search?q=*:*&size=10000000")
			perror(err)
			defer res.Body.Close()
			body, err := ioutil.ReadAll(res.Body)
			perror(err)
			var data EsResult
			tsUnix := time.Now().Unix()
			err = json.Unmarshal(body, &data)
			perror(err)
			amount := len(data.Hits.Hits)
			numMetrics.Update(int64(amount))
			for _, h := range data.Hits.Hits {
				//fmt.Println(h.Source.Name)
				targetsLock.Lock()
				if _, ok := targets[h.Source.Name]; !ok {
					targets[h.Source.Name] = stat{h.Source, tsUnix}
					targetKeys = append(targetKeys, h.Source.Name)
				}
				targetsLock.Unlock()
			}
		}
	}

	test := func(wg *sync.WaitGroup, curTs int64, met stat) {
		defer wg.Done()
		g := graphite.HostHeader{Host: "http://" + os.Args[3] + "/render", Header: http.Header{}}
		g.Header.Add("X-Org-Id", strconv.FormatInt(int64(met.def.OrgId), 10))
		g.Header.Set("User-Agent", "graphite-watcher")
		q := graphite.Request{Targets: []string{met.def.Name}}
		series, err := g.Query(&q)
		if err != nil {
			log.Println("ERROR querying graphite:", err)
			return
		}
		for _, serie := range series {
			if met.def.Name != serie.Target {
				fmt.Println("ERROR: name != target name:", met.def.Name, serie.Target)
			}

			lastTs := int64(0)
			oldestNull := int64(math.MaxInt64)
			if len(serie.Datapoints) == 0 {
				fmt.Println("ERROR: series for", met.def.Name, "contains no points!")
			}
			for _, p := range serie.Datapoints {
				ts, err := p[1].Int64()
				if err != nil {
					fmt.Println("ERROR: could not parse timestamp", p)
				}
				if ts <= lastTs {
					fmt.Println("ERROR: timestamp must be bigger than last", lastTs, ts)
				}
				if lastTs == 0 && (ts < curTs-24*3600-60 || ts > curTs-24*3600+60) {
					fmt.Println("ERROR: first point", p, "should have been about 24h ago")
				}
				if lastTs != 0 && ts != lastTs+int64(met.def.Interval) {
					fmt.Println("ERROR: point", p, " is not interval ", met.def.Interval, "apart from previous point")
				}
				_, err = p[0].Float64()
				if err != nil && ts > met.firstSeen {
					if ts < oldestNull {
						oldestNull = ts
					}
					if ts < curTs-30 {
						nullPoints.Inc(1)
						fmt.Println("ERROR: ", met.def.Name, " at", curTs, "seeing a null for ts", p[1])
					}
				} else {
					// we saw a valid point, so reset oldestNull.
					oldestNull = int64(math.MaxInt64)
				}
				lastTs = ts
			}
			if lastTs < curTs-int64(met.def.Interval) || lastTs > curTs+int64(met.def.Interval) {
				fmt.Println("ERROR: last point at ", lastTs, "is out of range")
			}
			// if there was no null, we treat the point after the last one we had as null
			if oldestNull == math.MaxInt64 {
				oldestNull = lastTs + int64(met.def.Interval)
			}
			// lag cannot be < 0
			if oldestNull > curTs {
				oldestNull = curTs
			}
			// lag is from first null after a range of data until now
			//fmt.Printf("%60s - lag %d\n", name, curTs-oldestNull)
			lag.Update(curTs - oldestNull)
		}
	}

	go updateTargets()

	tick := time.NewTicker(time.Millisecond * time.Duration(100))
	wg := &sync.WaitGroup{}
	for ts := range tick.C {
		var t stat
		targetsLock.Lock()
		if len(targetKeys) > 0 {
			key := targetKeys[rand.Intn(len(targets))]
			t = targets[key]
		}
		targetsLock.Unlock()
		if len(targetKeys) > 0 {
			wg.Add(1)
			go test(wg, ts.Unix(), t)
		}
	}
	wg.Wait()
}
<commit_msg>fix data race<commit_after>package main

import (
	"bosun.org/graphite"
	"encoding/json"
	"fmt"
	"github.com/Dieterbe/go-metrics"
	"github.com/raintank/raintank-metric/schema"
	"io/ioutil"
	"log"
	"math"
	"math/rand"
	"net"
	"net/http"
	"os"
	"strconv"
	"sync"
	"time"
	//"strings"
)

type Hit struct {
	Index  string                  `json:"_index"`
	Type   string                  `json:"_type"`
	Id     string                  `json:"_id"`
	Score  float64                 `json:"_score"`
	Source schema.MetricDefinition `json:"_source"`
}

type EsResult struct {
	Took     int
	TimedOut bool
	_shards  struct {
		total      int
		successful int
		failed     int
	}
	Hits struct {
		Total    int
		MaxScore int
		Hits     []Hit
	}
}

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

type stat struct {
	def       schema.MetricDefinition
	firstSeen int64
}

var targets map[string]stat
var targetKeys []string
var targetsLock sync.Mutex

func init() {
	targets = make(map[string]stat)
	targetKeys = make([]string, 0)
}

func main() {
	if len(os.Args) != 4 {
		log.Fatal("usage: graphite-watcher <elasticsearch-addr> <metrics-addr> <graphite-addr>")
	}
	addr, _ := net.ResolveTCPAddr("tcp", os.Args[2])
	go metrics.Graphite(metrics.DefaultRegistry, 10e9, "graphite-watcher.", addr)
	lag := metrics.NewHistogram(metrics.NewExpDecaySample(1028, 0.015))
	metrics.Register("lag", lag)
	numMetrics := metrics.NewGauge()
	metrics.Register("num_metrics", numMetrics)
	nullPoints := metrics.NewCounter()
	metrics.Register("null_points", nullPoints)

	// for a metric to exist in ES at t=Y, there must at least have been 1 point for that metric
	// at a time X where X < Y.  Hence, we can confidently say that if we see a metric at Y, we can
	// demand data to show up for that metric at >=Y
	// for our data check to be useful we need metrics to show up in ES soon after being in the pipeline,
	// which seems to be true (see nsqadmin)
	updateTargets := func() {
		getEsTick := time.NewTicker(time.Second * time.Duration(10))
		for range getEsTick.C {
			res, err := http.Get("http://" + os.Args[1] + "/metric/_search?q=*:*&size=10000000")
			perror(err)
			defer res.Body.Close()
			body, err := ioutil.ReadAll(res.Body)
			perror(err)
			var data EsResult
			tsUnix := time.Now().Unix()
			err = json.Unmarshal(body, &data)
			perror(err)
			amount := len(data.Hits.Hits)
			numMetrics.Update(int64(amount))
			for _, h := range data.Hits.Hits {
				//fmt.Println(h.Source.Name)
				targetsLock.Lock()
				if _, ok := targets[h.Source.Name]; !ok {
					targets[h.Source.Name] = stat{h.Source, tsUnix}
					targetKeys = append(targetKeys, h.Source.Name)
				}
				targetsLock.Unlock()
			}
		}
	}

	test := func(wg *sync.WaitGroup, curTs int64, met stat) {
		defer wg.Done()
		g := graphite.HostHeader{Host: "http://" + os.Args[3] + "/render", Header: http.Header{}}
		g.Header.Add("X-Org-Id", strconv.FormatInt(int64(met.def.OrgId), 10))
		g.Header.Set("User-Agent", "graphite-watcher")
		q := graphite.Request{Targets: []string{met.def.Name}}
		series, err := g.Query(&q)
		if err != nil {
			log.Println("ERROR querying graphite:", err)
			return
		}
		for _, serie := range series {
			if met.def.Name != serie.Target {
				fmt.Println("ERROR: name != target name:", met.def.Name, serie.Target)
			}

			lastTs := int64(0)
			oldestNull := int64(math.MaxInt64)
			if len(serie.Datapoints) == 0 {
				fmt.Println("ERROR: series for", met.def.Name, "contains no points!")
			}
			for _, p := range serie.Datapoints {
				ts, err := p[1].Int64()
				if err != nil {
					fmt.Println("ERROR: could not parse timestamp", p)
				}
				if ts <= lastTs {
					fmt.Println("ERROR: timestamp must be bigger than last", lastTs, ts)
				}
				if lastTs == 0 && (ts < curTs-24*3600-60 || ts > curTs-24*3600+60) {
					fmt.Println("ERROR: first point", p, "should have been about 24h ago")
				}
				if lastTs != 0 && ts != lastTs+int64(met.def.Interval) {
					fmt.Println("ERROR: point", p, " is not interval ", met.def.Interval, "apart from previous point")
				}
				_, err = p[0].Float64()
				if err != nil && ts > met.firstSeen {
					if ts < oldestNull {
						oldestNull = ts
					}
					if ts < curTs-30 {
						nullPoints.Inc(1)
						fmt.Println("ERROR: ", met.def.Name, " at", curTs, "seeing a null for ts", p[1])
					}
				} else {
					// we saw a valid point, so reset oldestNull.
					oldestNull = int64(math.MaxInt64)
				}
				lastTs = ts
			}
			if lastTs < curTs-int64(met.def.Interval) || lastTs > curTs+int64(met.def.Interval) {
				fmt.Println("ERROR: last point at ", lastTs, "is out of range")
			}
			// if there was no null, we treat the point after the last one we had as null
			if oldestNull == math.MaxInt64 {
				oldestNull = lastTs + int64(met.def.Interval)
			}
			// lag cannot be < 0
			if oldestNull > curTs {
				oldestNull = curTs
			}
			// lag is from first null after a range of data until now
			//fmt.Printf("%60s - lag %d\n", name, curTs-oldestNull)
			lag.Update(curTs - oldestNull)
		}
	}

	go updateTargets()

	tick := time.NewTicker(time.Millisecond * time.Duration(100))
	wg := &sync.WaitGroup{}
	for ts := range tick.C {
		targetsLock.Lock()
		if len(targetKeys) > 0 {
			key := targetKeys[rand.Intn(len(targets))]
			wg.Add(1)
			go test(wg, ts.Unix(), targets[key])
		}
		targetsLock.Unlock()
	}
	wg.Wait()
}
<|endoftext|>


<commit_before>package runnel

import (
	"unsafe"

	"code.google.com/p/go-uuid/uuid"
	"github.com/cheekybits/genny/generic"
)

type Typed generic.Type

type TypedStream struct {
	Name        string
	Id          string
	storage     *Storage
	IsAlive     bool
	tail        uint64 //TODO: move to header
	lastMessage uint64 //TODO: move to header
}

type TypedRef struct {
	stream *TypedStream
	offset uint64
}

func NewTypedStream(name, id string, storage *Storage) *TypedStream {
	if id == "" {
		id = uuid.New()
	}
	if storage == nil {
		storage = NewFileStorage(id, "")
	}
	ret := &TypedStream{
		Name:    name,
		fileId:  id,
		storage: storage,
	}
	return ret
}

// ==================== WRITER ===================

type TypedStreamWriter struct {
	// Input channel allowing multiple goroutines to
	// safely write to this writer simultaneously
	inChannel <-chan *Typed
	// The stream that this writer will write to
	parent *TypedStream
	// Whether this writer is alive
	isAlive bool
}

// Create a writer for the given stream
// The writer will continually listen for calls to
// Insert and write them to the underlying stream
// as long as the writer and the stream that it operates
// on are both alive
func (stream *TypedStream) Writer() *TypedStreamWriter {
	ret := &TypedStreamWriter{
		parent:    parent,
		inChannel: make(<-chan *Typed, 10),
	}
	go ret.writeLoop()
	ret.isAlive = true
	return ret
}

// As long as the writer is alive, pick up
// data from the input channel and write it
// to the stream
func (writer *TypedStreamWriter) writeLoop() {
	for writer.isAlive {
		datum := <-writer.inChannel
		writer.write(data)
	}
}

// Write the given data into the stream
// TODO: Prefix with header
// The data is written in 3 steps:
// 1. Allocate space by bumping tail
// 2. Write data into allocated space
// 3. Declare data is available by bumping lastMessage
func (writer *TypedStreamWriter) write(data *Typed) {
	if !writer.parent.IsAlive() || !writer.isAlive {
		// If the stream/writer isn't alive, there's no point
		return
	}
	storage := writer.parent.storage
	// Check to see if we need to resize
	if storage.Utilization() > 75 {
		// TODO: Work out how to handle multiple writers here, maybe through buffer swap
		storage.Resize(2 * storage.Capacity())
	}

	// TODO make this atomic
	// Get old tail
	offset := writer.parent.tail
	// Bump tail
	writer.parent.tail += uint64(unsafe.Sizeof(data))

	// Write data
	address := &writer.parent.storage.GetBytes(0, -1)[offset]
	pointer := (*Typed)(unsafe.Pointer(address))
	*pointer = *data

	// Declare data available
	writer.parent.lastMessage = offset
	writer.parent.storage.Header().EntryCount += 1
}

// Close the writer
func (writer *TypedStreamWriter) Close() {
	writer.isAlive = false
	close(writer.inChannel)
}

// =================== OUTPUT ===================

type TypedStreamReader struct {
	// Out channel to allow blocking reads
	outChannel chan<- Typed
	// The stream that this reader will read from
	parent *TypedStream
	// The position in the stream that this reader
	// will begin from
	base uint64
	// The progress this reader has made since
	// it started reading
	offset uint64
	// Whether this reader is alive
	isAlive bool
}

// Build a new stream reader which maintains its place in the stream
// and provides functionality for leaving the stream
// TODO: Allow filtered readers, or maybe do an intermediate stream?
func (stream *TypedStream) Reader(parent *TypedStream, base uint64) *TypedStreamReader {
	ret := &TypedStreamReader{
		parent:     stream,
		outChannel: make(chan<- Typed),
		base:       base,
		offset:     0,
	}
	go ret.readLoop()
	ret.isAlive = true
	return ret
}

// Loop endlessly to read the data from the stream
func (reader *TypedStreamReader) readLoop() {
	for reader.isAlive {
		if reader.parent.lastMessage > reader.base+reader.offset {
			// Advance he
		}
	}
}

// =================== FILTERS ==================

// =================== STREAMS ==================

/**
 * Output all values in the stream onto the channel
 */
func (s *TypedStream) Outlet(c chan Typed) {
	middle := make(chan *TypedRef)
	s.out.outChannels = append(s.out.outChannels, middle)
	go func() {
		var count uint64 = 0
		for s.IsAlive() {
			if s.header.EntryCount > count {
				datum := s.FindOne(count)
				// TODO: Update to public API
				c <- *s.out.resolve(datum)
				count++
			} else {
				<-middle
			}
		}
	}()
}

/**
 * Close out the stream
 */
func (s *TypedStream) Close() {
	s.IsAlive = false
	s.storage.Close()
}

// ==================== UTILS ===================
<commit_msg>Finished stream reader<commit_after>package runnel

import (
	"unsafe"

	"code.google.com/p/go-uuid/uuid"
	"github.com/cheekybits/genny/generic"
)

type Typed generic.Type

type TypedStream struct {
	Name        string
	Id          string
	storage     *Storage
	IsAlive     bool
	tail        uint64 //TODO: move to header
	lastMessage uint64 //TODO: move to header
}

type TypedRef struct {
	stream *TypedStream
	offset uint64
}

func NewTypedStream(name, id string, storage *Storage) *TypedStream {
	if id == "" {
		id = uuid.New()
	}
	if storage == nil {
		storage = NewFileStorage(id, "")
	}
	ret := &TypedStream{
		Name:    name,
		fileId:  id,
		storage: storage,
	}
	return ret
}

// ==================== WRITER ===================

type TypedStreamWriter struct {
	// Input channel allowing multiple goroutines to
	// safely write to this writer simultaneously
	inChannel <-chan *Typed
	// The stream that this writer will write to
	parent *TypedStream
	// Whether this writer is alive
	isAlive bool
}

// Create a writer for the given stream
// The writer will continually listen for calls to
// Insert and write them to the underlying stream
// as long as the writer and the stream that it operates
// on are both alive
func (stream *TypedStream) Writer() *TypedStreamWriter {
	ret := &TypedStreamWriter{
		parent:    parent,
		inChannel: make(<-chan *Typed, 10),
	}
	go ret.writeLoop()
	ret.isAlive = true
	return ret
}

// As long as the writer is alive, pick up
// data from the input channel and write it
// to the stream
func (writer *TypedStreamWriter) writeLoop() {
	for writer.isAlive {
		datum := <-writer.inChannel
		writer.write(data)
	}
}

// Write the given data into the stream
// TODO: Prefix with header
// The data is written in 3 steps:
// 1. Allocate space by bumping tail
// 2. Write data into allocated space
// 3. Declare data is available by bumping lastMessage
func (writer *TypedStreamWriter) write(data *Typed) {
	if !writer.parent.IsAlive() || !writer.isAlive {
		// If the stream/writer isn't alive, there's no point
		return
	}
	storage := writer.parent.storage
	// Check to see if we need to resize
	if storage.Utilization() > 75 {
		// TODO: Work out how to handle multiple writers here, maybe through buffer swap
		storage.Resize(2 * storage.Capacity())
	}

	// TODO make this atomic
	// Get old tail
	offset := writer.parent.tail
	// Bump tail
	writer.parent.tail += uint64(unsafe.Sizeof(data))

	// Write data
	address := &writer.parent.storage.GetBytes(0, -1)[offset]
	pointer := (*Typed)(unsafe.Pointer(address))
	*pointer = *data

	// Declare data available
	writer.parent.lastMessage = offset
	writer.parent.storage.Header().EntryCount += 1
}

// Close the writer
func (writer *TypedStreamWriter) Close() {
	writer.isAlive = false
	close(writer.inChannel)
}

// =================== OUTPUT ===================

type TypedStreamReader struct {
	// Out channel to allow blocking reads
	outChannel chan<- Typed
	// The stream that this reader will read from
	parent *TypedStream
	// The position in the stream that this reader
	// will begin from
	base uint64
	// The progress this reader has made since
	// it started reading
	offset uint64
	// Whether this reader is alive
	isAlive bool
}

// Build a new stream reader which maintains its place in the stream
// and provides functionality for leaving the stream
// TODO: Allow filtered readers, or maybe do an intermediate stream?
func (stream *TypedStream) Reader(parent *TypedStream, base uint64) *TypedStreamReader {
	ret := &TypedStreamReader{
		parent:     stream,
		outChannel: make(chan<- Typed),
		base:       base,
		offset:     0,
	}
	go ret.readLoop()
	ret.isAlive = true
	return ret
}

// Loop endlessly to read the data from the stream
func (reader *TypedStreamReader) readLoop() {
	for reader.isAlive {
		if reader.base+reader.offset <= reader.parent.lastMessage {
			// Advance the reader through the stream
    address := &writer.parent.storage.GetBytes(0, -1)[offset]
   	pointer := (*Typed)(unsafe.Pointer(address))
   	reader.outChannel<-*pointer
    if reader.base+reader.offset < reader.parent.lastMessage {
reader.offset+= uint64(unsafe.sizeOf(pointer))
}
		}
	}
}

// Read a single value from the stream (in a blocking fashion)
func (reader *TypedStreamReader) read() Typed {
  return <- reader.outChannel
}

// =================== FILTERS ==================

// =================== STREAMS ==================

/**
 * Close out the stream
 */
func (s *TypedStream) Close() {
	s.IsAlive = false
	s.storage.Close()
}

// ==================== UTILS ===================
<|endoftext|>


<commit_before>package factories

import (
	"github.com/cloudfoundry-incubator/runtime-schema/models"
	"github.com/nu7hatch/gouuid"
)

func GenerateGuid() string {
	guid, err := uuid.NewV4()
	if err != nil {
		panic("Failed to generate a GUID.  Craziness.")
	}

	return guid.String()
}

func BuildTaskWithRunAction(domain string, rootfs string, memoryMB int, diskMB int, path string, args []string) models.Task {
	return models.Task{
		Domain:   domain,
		TaskGuid: GenerateGuid(),
		RootFS:   rootfs,
		MemoryMB: memoryMB,
		DiskMB:   diskMB,
		Action: &models.RunAction{
			Path: path,
			Args: args,
		},
	}
}
<commit_msg>Update ltc Godeps<commit_after><|endoftext|>


<commit_before>package matchers_test

import (
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/sclevine/agouti"
	. "github.com/sclevine/agouti/matchers"
	"github.com/sclevine/agouti/matchers/internal/mocks"
)

var _ = Describe("Page Matchers", func() {
	var page *mocks.Page

	BeforeEach(func() {
		page = &mocks.Page{}
	})

	Describe("#HaveTitle", func() {
		It("should call the page#HaveTitle matcher", func() {
			page.TitleCall.ReturnTitle = "Some Title"
			Expect(page).To(HaveTitle("Some Title"))
			Expect(page).NotTo(HaveTitle("Some Other Title"))
		})
	})

	Describe("#HaveURL", func() {
		It("should call the page#HaveURL matcher", func() {
			page.URLCall.ReturnURL = "some/url"
			Expect(page).To(HaveURL("some/url"))
			Expect(page).NotTo(HaveURL("some/other/url"))
		})
	})

	Describe("#HavePopupText", func() {
		It("should call the page#HavePopupText matcher", func() {
			page.PopupTextCall.ReturnText = "some text"
			Expect(page).To(HavePopupText("some text"))
			Expect(page).NotTo(HavePopupText("some other text"))
		})
	})

	Describe("#HaveLoggedError", func() {
		It("should call the page#HaveLoggedError matcher", func() {
			page.ReadLogsCall.ReturnLogs = []agouti.Log{agouti.Log{"some log", "", "WARNING", time.Time{}}}
			Expect(page).To(HaveLoggedError("some log"))
			Expect(page).NotTo(HaveLoggedError("some other log"))
		})
	})

	Describe("#HaveLoggedInfo", func() {
		It("should call the page#HaveLoggedInfo matcher", func() {
			page.ReadLogsCall.ReturnLogs = []agouti.Log{agouti.Log{"some log", "", "INFO", time.Time{}}}
			Expect(page).To(HaveLoggedInfo("some log"))
			Expect(page).NotTo(HaveLoggedInfo("some other log"))
		})
	})
})
<commit_msg>Fix broken matchers test that was due to an outdated mock<commit_after>package matchers_test

import (
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/sclevine/agouti"
	. "github.com/sclevine/agouti/matchers"
	"github.com/sclevine/agouti/matchers/internal/mocks"
)

var _ = Describe("Page Matchers", func() {
	var page *mocks.Page

	BeforeEach(func() {
		page = &mocks.Page{}
	})

	Describe("#HaveTitle", func() {
		It("should call the page#HaveTitle matcher", func() {
			page.TitleCall.ReturnTitle = "Some Title"
			Expect(page).To(HaveTitle("Some Title"))
			Expect(page).NotTo(HaveTitle("Some Other Title"))
		})
	})

	Describe("#HaveURL", func() {
		It("should call the page#HaveURL matcher", func() {
			page.URLCall.ReturnURL = "some/url"
			Expect(page).To(HaveURL("some/url"))
			Expect(page).NotTo(HaveURL("some/other/url"))
		})
	})

	Describe("#HavePopupText", func() {
		It("should call the page#HavePopupText matcher", func() {
			page.PopupTextCall.ReturnText = "some text"
			Expect(page).To(HavePopupText("some text"))
			Expect(page).NotTo(HavePopupText("some other text"))
		})
	})

	Describe("#HaveLoggedError", func() {
		It("should call the page#HaveLoggedError matcher", func() {
			page.ReadAllLogsCall.ReturnLogs = []agouti.Log{agouti.Log{"some log", "", "WARNING", time.Time{}}}
			Expect(page).To(HaveLoggedError("some log"))
			Expect(page).NotTo(HaveLoggedError("some other log"))
		})
	})

	Describe("#HaveLoggedInfo", func() {
		It("should call the page#HaveLoggedInfo matcher", func() {
			page.ReadAllLogsCall.ReturnLogs = []agouti.Log{agouti.Log{"some log", "", "INFO", time.Time{}}}
			Expect(page).To(HaveLoggedInfo("some log"))
			Expect(page).NotTo(HaveLoggedInfo("some other log"))
		})
	})
})
<|endoftext|>