<commit_before>package phpobject

import (
	"errors"
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"
)

var patNumber, _ = regexp.Compile(`^-?[1-9][0-9]*$`)
var patVarName, _ = regexp.Compile(`^[[:alpha:]_]\w*$`)

type PValueType int

const (
	PTNil PValueType = iota
	PTBool
	PTLong
	PTDouble
	PTString
	PTArray
	PTObject
)

var pValueNames = [9]string{"nil", "boolean", "long", "double", "string", "array", "object"}

func (vt PValueType) String() string {
	return pValueNames[int(vt)]
}

type PValue interface {
	String() string
	Type() PValueType
	serialize(w io.Writer)
	//unserialize(r io.Reader)
}

type PNilType struct{}

func (nl *PNilType) String() string   { return "nil" }
func (nl *PNilType) Type() PValueType { return PTNil }
func (nl *PNilType) serialize(w io.Writer) {
	w.Write([]byte("N;"))
}

var PNil = PValue(&PNilType{})

type PBool bool

func (bl PBool) String() string {
	if bool(bl) {
		return "true"
	}
	return "false"
}
func (bl PBool) Type() PValueType { return PTBool }
func (bl PBool) serialize(w io.Writer) {
	if bl {
		w.Write([]byte("b:1;"))
	} else {
		w.Write([]byte("b:0;"))
	}
}

var PTrue = PBool(true)
var PFalse = PBool(false)

type PLong int

func (lt PLong) String() string   { return fmt.Sprint(int(lt)) }
func (lt PLong) Type() PValueType { return PTLong }
func (lt PLong) serialize(w io.Writer) {
	fmt.Fprintf(w, "i:%d;", lt)
}

type PDouble float64

func (dt PDouble) String() string   { return fmt.Sprint(float64(dt)) }
func (dt PDouble) Type() PValueType { return PTDouble }
func (dt PDouble) serialize(w io.Writer) {
	fmt.Fprintf(w, "d:%f;", dt)
}

type PString string

func (st PString) String() string   { return string(st) }
func (st PString) Type() PValueType { return PTString }
func (st PString) serialize(w io.Writer) {
	fmt.Fprintf(w, "s:%d:\"", len(st))
	fmt.Fprint(w, st)
	fmt.Fprint(w, "\";")
}

const (
	NumArray = 1
	KeyArray = 2
)

type PArray struct {
	array map[string]PValue
	//forceType int
}

func NewArray() *PArray {
	var at PArray
	at.array = make(map[string]PValue)
	return &at
}

func (tb *PArray) Iget(index int) (PValue, bool) {
	key := fmt.Sprintf("%d", index)
	v, o := tb.array[key]
	return v, o
}
func (tb *PArray) Get(key string) (PValue, bool) {
	v, o := tb.array[key]
	return v, o
}

func (tb *PArray) Iset(index int, value PValue) {
	key := fmt.Sprintf("%d", index)
	tb.array[key] = value
}
func (tb *PArray) Set(key string, value PValue) bool {
	tb.array[key] = value
	if key == "0" || patNumber.MatchString(key) {
		return true
	} else {
		return false
	}
}

func serializeKey(w io.Writer, key string) {
	if key == "0" || patNumber.MatchString(key) {
		fmt.Fprintf(w, "i:%s;", key)
	} else {
		fmt.Fprintf(w, "s:%d:\"", len(key))
		fmt.Fprint(w, key)
		fmt.Fprint(w, "\";")
	}
}

func (tb *PArray) String() string {
	slist := make([]string, len(tb.array)+2)
	slist[0] = fmt.Sprintf("Array(%d) [", len(tb.array))
	i := 1
	for k, v := range tb.array {
		slist[i] = fmt.Sprintf("%s : %s,", k, v.String())
		i++
	}
	slist[i] = "]"
	return strings.Join(slist, " ")
}
func (tb *PArray) Type() PValueType { return PTArray }

func (tb *PArray) serialize(w io.Writer) {
	fmt.Fprintf(w, "a:%d:{", len(tb.array))
	for k, v := range tb.array {
		serializeKey(w, k)
		v.serialize(w)
	}
	w.Write([]byte("}"))
}

const (
	PublicVar      = 0
	ProtectedVar   = 1
	PrivateVar     = 2
	BasePrivateVar = 4
	endVarType     = 5
)

type oValue struct {
	value   PValue
	varType int
}

type PObject struct {
	vars  map[string]oValue
	class string
}

func NewObject(class string) *PObject {
	var ot PObject
	ot.vars = make(map[string]oValue)
	ot.class = class
	return &ot
}

func (ot *PObject) SetVar(varname string, vtype int, value PValue) error {
	if vtype == BasePrivateVar {
		return errors.New("You should use SetBaseVar")
	}
	if vtype > BasePrivateVar || vtype < 0 {
		return errors.New("Error var type")
	}
	if !patVarName.MatchString(varname) {
		return errors.New("Error varname")
	}
	ot.vars[varname] = oValue{value, vtype}
	return nil
}

func (ot *PObject) SetPublicVar(varname string, value PValue) error {
	return ot.SetVar(varname, PublicVar, value)
}

func (ot *PObject) SetProtectedVar(varname string, value PValue) error {
	return ot.SetVar(varname, ProtectedVar, value)
}

func (ot *PObject) SetPrivateVar(varname string, value PValue) error {
	return ot.SetVar(varname, PrivateVar, value)
}

func (ot *PObject) SetBaseVar(clsname, varname string, value PValue) error {
	if !patVarName.MatchString(varname) {
		return errors.New("Error varname")
	}
	if !patVarName.MatchString(clsname) {
		return errors.New("Error class name")
	}
	key := fmt.Sprintf("\x00%s\x00%s", clsname, varname)
	ot.vars[key] = oValue{value, BasePrivateVar}
	return nil
}

func (ot *PObject) GetVar(varname string) (value PValue, vtype int, ok bool) {
	oval, ok := ot.vars[varname]
	return oval.value, oval.varType, ok
}

func (ot *PObject) GetBaseVar(clsname, varname string) (value PValue, ok bool) {
	key := fmt.Sprintf("\x00%s\x00%s", clsname, varname)
	oval, ok := ot.vars[key]
	return oval.value, ok
}

func (ot *PObject) String() string {
	slist := make([]string, len(ot.vars)+2)
	slist[0] = fmt.Sprintf("Object(%s:%d) {", ot.class, len(ot.vars))
	i := 1
	for k, v := range ot.vars {
		switch v.varType {
		case PublicVar:
			slist[i] = fmt.Sprintf("%s : %s,", k, v.value.String())
		case ProtectedVar:
			slist[i] = fmt.Sprintf("-%s : %s,", k, v.value.String())
		case PrivateVar:
			slist[i] = fmt.Sprintf("*%s : %s,", k, v.value.String())
		case BasePrivateVar:
			kk := strings.Replace(k, "\x00", "*", 2)
			slist[i] = fmt.Sprintf("%s : %s,", kk[1:], v.value.String())
		}
		i++
	}
	slist[i] = "}"
	return strings.Join(slist, " ")
}
func (ot *PObject) Type() PValueType { return PTObject }

func (ot *PObject) serialize(w io.Writer) {
	fmt.Fprintf(w, "O:%d:\"%s\"", len(ot.class), ot.class)
	fmt.Fprintf(w, ":%d:{", len(ot.vars))
	for k, v := range ot.vars {
		key := PString(k)
		switch v.varType {
		case ProtectedVar:
			key = PString("\x00*\x00" + k)
		case PrivateVar:
			key = PString(fmt.Sprintf("\x00%s\x00%s", ot.class, k))
			//case PublicVar, BasePrivateVar:
			//	key = k
		}
		key.serialize(w)
		v.value.serialize(w)
	}
	w.Write([]byte("}"))
}
<commit_msg>support Marshal<commit_after>package phpobject

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"regexp"
	"strings"
)

var patNumber, _ = regexp.Compile(`^-?[1-9][0-9]*$`)
var patVarName, _ = regexp.Compile(`^[[:alpha:]_]\w*$`)

type PValueType int

const (
	PTNil PValueType = iota
	PTBool
	PTLong
	PTDouble
	PTString
	PTArray
	PTObject
)

var pValueNames = [9]string{"nil", "boolean", "long", "double", "string", "array", "object"}

func (vt PValueType) String() string {
	return pValueNames[int(vt)]
}

type PValue interface {
	String() string
	Type() PValueType
	serialize(w io.Writer)
	Marshal() []byte
	//Unmarshal(bcode []byte) error
	//unserialize(r io.Reader) error
}

type PNilType struct{}

func (nl *PNilType) String() string   { return "nil" }
func (nl *PNilType) Type() PValueType { return PTNil }
func (nl *PNilType) Marshal() []byte  { return []byte("N;") }
func (nl *PNilType) serialize(w io.Writer) {
	w.Write([]byte("N;"))
}

var PNil = PValue(&PNilType{})

type PBool bool

func (bl PBool) String() string {
	if bool(bl) {
		return "true"
	}
	return "false"
}
func (bl PBool) Type() PValueType { return PTBool }
func (bl PBool) Marshal() []byte {
	if bl {
		return []byte("b:1;")
	} else {
		return []byte("b:0;")
	}
}
func (bl PBool) serialize(w io.Writer) {
	w.Write(bl.Marshal())
}

var PTrue = PBool(true)
var PFalse = PBool(false)

type PLong int

func (lt PLong) String() string   { return fmt.Sprint(int(lt)) }
func (lt PLong) Type() PValueType { return PTLong }
func (lt PLong) Marshal() []byte {
	return []byte(fmt.Sprintf("i:%d;", lt))
}
func (lt PLong) serialize(w io.Writer) {
	fmt.Fprintf(w, "i:%d;", lt)
}

type PDouble float64

func (dt PDouble) String() string   { return fmt.Sprint(float64(dt)) }
func (dt PDouble) Type() PValueType { return PTDouble }
func (dt PDouble) Marshal() []byte {
	return []byte(fmt.Sprintf("d:%f;", dt))
}
func (dt PDouble) serialize(w io.Writer) {
	fmt.Fprintf(w, "d:%f;", dt)
}

type PString string

func (st PString) String() string   { return string(st) }
func (st PString) Type() PValueType { return PTString }
func (st PString) Marshal() []byte {
	return []byte(fmt.Sprintf("s:%d:\"%s\";", len(st), st))
}
func (st PString) serialize(w io.Writer) {
	fmt.Fprintf(w, "s:%d:\"", len(st))
	fmt.Fprint(w, st)
	fmt.Fprint(w, "\";")
}

const (
	NumArray = 1
	KeyArray = 2
)

type PArray struct {
	array map[string]PValue
	//forceType int
}

func NewArray() *PArray {
	var at PArray
	at.array = make(map[string]PValue)
	return &at
}

func (tb *PArray) Iget(index int) (PValue, bool) {
	key := fmt.Sprintf("%d", index)
	v, o := tb.array[key]
	return v, o
}
func (tb *PArray) Get(key string) (PValue, bool) {
	v, o := tb.array[key]
	return v, o
}

func (tb *PArray) Iset(index int, value PValue) {
	key := fmt.Sprintf("%d", index)
	tb.array[key] = value
}
func (tb *PArray) Set(key string, value PValue) bool {
	tb.array[key] = value
	if key == "0" || patNumber.MatchString(key) {
		return true
	} else {
		return false
	}
}

func serializeKey(w io.Writer, key string) {
	if key == "0" || patNumber.MatchString(key) {
		fmt.Fprintf(w, "i:%s;", key)
	} else {
		fmt.Fprintf(w, "s:%d:\"", len(key))
		fmt.Fprint(w, key)
		fmt.Fprint(w, "\";")
	}
}

func (tb *PArray) String() string {
	slist := make([]string, len(tb.array)+2)
	slist[0] = fmt.Sprintf("Array(%d) [", len(tb.array))
	i := 1
	for k, v := range tb.array {
		slist[i] = fmt.Sprintf("%s : %s,", k, v.String())
		i++
	}
	slist[i] = "]"
	return strings.Join(slist, " ")
}
func (tb *PArray) Type() PValueType { return PTArray }
func (tb *PArray) Marshal() []byte {
	buf := bytes.NewBuffer(nil)
	tb.serialize(buf)
	return buf.Bytes()
}
func (tb *PArray) serialize(w io.Writer) {
	fmt.Fprintf(w, "a:%d:{", len(tb.array))
	for k, v := range tb.array {
		serializeKey(w, k)
		v.serialize(w)
	}
	w.Write([]byte("}"))
}

const (
	PublicVar      = 0
	ProtectedVar   = 1
	PrivateVar     = 2
	BasePrivateVar = 4
	endVarType     = 5
)

type oValue struct {
	value   PValue
	varType int
}

type PObject struct {
	vars  map[string]oValue
	class string
}

func NewObject(class string) *PObject {
	var ot PObject
	ot.vars = make(map[string]oValue)
	ot.class = class
	return &ot
}

func (ot *PObject) SetVar(varname string, vtype int, value PValue) error {
	if vtype == BasePrivateVar {
		return errors.New("You should use SetBaseVar")
	}
	if vtype > BasePrivateVar || vtype < 0 {
		return errors.New("Error var type")
	}
	if !patVarName.MatchString(varname) {
		return errors.New("Error varname")
	}
	ot.vars[varname] = oValue{value, vtype}
	return nil
}

func (ot *PObject) SetPublicVar(varname string, value PValue) error {
	return ot.SetVar(varname, PublicVar, value)
}

func (ot *PObject) SetProtectedVar(varname string, value PValue) error {
	return ot.SetVar(varname, ProtectedVar, value)
}

func (ot *PObject) SetPrivateVar(varname string, value PValue) error {
	return ot.SetVar(varname, PrivateVar, value)
}

func (ot *PObject) SetBaseVar(clsname, varname string, value PValue) error {
	if !patVarName.MatchString(varname) {
		return errors.New("Error varname")
	}
	if !patVarName.MatchString(clsname) {
		return errors.New("Error class name")
	}
	key := fmt.Sprintf("\x00%s\x00%s", clsname, varname)
	ot.vars[key] = oValue{value, BasePrivateVar}
	return nil
}

func (ot *PObject) GetVar(varname string) (value PValue, vtype int, ok bool) {
	oval, ok := ot.vars[varname]
	return oval.value, oval.varType, ok
}

func (ot *PObject) GetBaseVar(clsname, varname string) (value PValue, ok bool) {
	key := fmt.Sprintf("\x00%s\x00%s", clsname, varname)
	oval, ok := ot.vars[key]
	return oval.value, ok
}

func (ot *PObject) String() string {
	slist := make([]string, len(ot.vars)+2)
	slist[0] = fmt.Sprintf("Object(%s:%d) {", ot.class, len(ot.vars))
	i := 1
	for k, v := range ot.vars {
		switch v.varType {
		case PublicVar:
			slist[i] = fmt.Sprintf("%s : %s,", k, v.value.String())
		case ProtectedVar:
			slist[i] = fmt.Sprintf("-%s : %s,", k, v.value.String())
		case PrivateVar:
			slist[i] = fmt.Sprintf("*%s : %s,", k, v.value.String())
		case BasePrivateVar:
			kk := strings.Replace(k, "\x00", "*", 2)
			slist[i] = fmt.Sprintf("%s : %s,", kk[1:], v.value.String())
		}
		i++
	}
	slist[i] = "}"
	return strings.Join(slist, " ")
}
func (ot *PObject) Type() PValueType { return PTObject }
func (ot *PObject) Marshal() []byte {
	buf := bytes.NewBuffer(nil)
	ot.serialize(buf)
	return buf.Bytes()
}

func (ot *PObject) serialize(w io.Writer) {
	fmt.Fprintf(w, "O:%d:\"%s\"", len(ot.class), ot.class)
	fmt.Fprintf(w, ":%d:{", len(ot.vars))
	for k, v := range ot.vars {
		key := PString(k)
		switch v.varType {
		case ProtectedVar:
			key = PString("\x00*\x00" + k)
		case PrivateVar:
			key = PString(fmt.Sprintf("\x00%s\x00%s", ot.class, k))
			//case PublicVar, BasePrivateVar:
			//	key = k
		}
		key.serialize(w)
		v.value.serialize(w)
	}
	w.Write([]byte("}"))
}
<|endoftext|>


<commit_before>package jval

import (
	"github.com/thwd/jsem"
	"math"
	"math/rand"
	"regexp"
	"strconv"
	"time"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

type Error struct {
	Label   string      `json:"label"`
	Field   string      `json:"field"`
	Context interface{} `json:"context"`
}

// Error implements native "error" interface
func (e Error) Error() string {
	return e.Label
}

var NoErrors = []Error{}

type Validator interface {
	Validate(value *jsem.Value, field string) []Error
}

type lambda func(v *jsem.Value, f string) []Error

func (l lambda) Validate(v *jsem.Value, f string) []Error {
	return l(v, f)
}

type AnythingValidator struct{}

func Anything() Validator {
	return AnythingValidator{}
}

func (a AnythingValidator) Validate(v *jsem.Value, f string) []Error {
	return NoErrors
}

type StringValidator struct{}

func String() Validator {
	return StringValidator{}
}

func (a StringValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsString() {
		return NoErrors
	}
	return []Error{Error{"value_must_be_string", f, nil}}
}

type NumberValidator struct{}

func Number() Validator {
	return NumberValidator{}
}

func (a NumberValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsNumber() {
		return NoErrors
	}
	return []Error{Error{"value_must_be_number", f, nil}}
}

type BooleanValidator struct{}

func Boolean() Validator {
	return BooleanValidator{}
}

func (a BooleanValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsBoolean() {
		return NoErrors
	}
	return []Error{Error{"value_must_be_boolean", f, nil}}
}

type NullValidator struct{}

func Null() Validator {
	return NullValidator{}
}

func (a NullValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsNull() {
		return NoErrors
	}
	return []Error{Error{"value_must_be_null", f, nil}}
}

type NotNullValidator struct{}

func NotNull() Validator {
	return NotNullValidator{}
}

func (a NotNullValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsNull() {
		return []Error{Error{"value_must_not_be_null", f, nil}}
	}
	return NoErrors
}

type AndValidator []Validator

func And(vs ...Validator) Validator {
	return AndValidator(vs)
}

func (a AndValidator) Validate(v *jsem.Value, f string) []Error {
	for _, b := range a {
		es := b.Validate(v, f)
		if len(es) != 0 {
			return es
		}
	}
	return NoErrors
}
func (a AndValidator) Validators() []Validator {
	return []Validator(a)
}

type TupleValidator []Validator

func Tuple(vs ...Validator) Validator {
	return TupleValidator(vs)
}

func (a TupleValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Array(Anything()), Length(len(a)), lambda(func(v *jsem.Value, f string) []Error {
		for i, b := range a {
			u, _ := v.ArrayIndex(i)
			es := b.Validate(u, f+"."+strconv.Itoa(i))
			if len(es) != 0 {
				return es
			}
		}
		return NoErrors
	})).Validate(v, f)
}
func (a TupleValidator) Validators() []Validator {
	return []Validator(a)
}

type OrValidator []Validator

func Or(vs ...Validator) Validator {
	return OrValidator(vs)
}

func (b OrValidator) Validate(v *jsem.Value, f string) []Error {
	ae := make([]Error, 0, len(b))
	for _, a := range b {
		es := a.Validate(v, f)
		if len(es) == 0 {
			return NoErrors
		}
		ae = append(ae, es...)
	}
	return ae // TODO(thwd): merge errors into one?
}
func (a OrValidator) Validators() []Validator {
	return []Validator(a)
}

type ObjectValidator map[string]Validator

func Object(d map[string]Validator) Validator {
	return ObjectValidator(d)
}

func (d ObjectValidator) Validate(v *jsem.Value, f string) []Error {
	if !v.IsObject() {
		return []Error{Error{"value_must_be_object", f, nil}}
	}
	ae := make([]Error, 0, len(d))
	v.ObjectForEach(func(k string, u *jsem.Value) {
		if _, ok := d[k]; !ok {
			ae = append(ae, Error{"unexpected_object_key", f + "." + k, nil})
		}
	})
	for k, a := range d {
		u, _ := v.ObjectKey(k)
		ae = append(ae, a.Validate(u, f+"."+k)...)
	}
	return ae
}

func (a ObjectValidator) Structure() map[string]Validator {
	return (map[string]Validator)(a)
}

type MapValidator struct {
	e Validator
}

func Map(e Validator) Validator {
	return MapValidator{e}
}

func (a MapValidator) Validate(v *jsem.Value, f string) []Error {
	if !v.IsObject() {
		return []Error{Error{"value_must_be_object", f, nil}}
	}
	ae := make([]Error, 0, 8)
	v.ObjectForEach(func(k string, u *jsem.Value) {
		ae = append(ae, a.e.Validate(u, f+"."+k)...)
	})
	return ae
}
func (a MapValidator) Validator() Validator {
	return a.e
}

type ArrayValidator struct {
	e Validator
}

func Array(e Validator) Validator {
	return ArrayValidator{e}
}

func (a ArrayValidator) Validate(v *jsem.Value, f string) []Error {
	if !v.IsArray() {
		return []Error{Error{"value_must_be_array", f, nil}}
	}
	ae := make([]Error, 0, 8)
	v.ArrayForEach(func(i int, u *jsem.Value) {
		ae = append(ae, a.e.Validate(u, f+"."+strconv.Itoa(i))...)
	})
	return ae
}
func (a ArrayValidator) Validator() Validator {
	return a.e
}

type RegexValidator struct {
	r *regexp.Regexp
}

func Regex(r *regexp.Regexp) Validator {
	return RegexValidator{r}
}

func (a RegexValidator) Regex() *regexp.Regexp {
	return a.r
}

func (a RegexValidator) Validate(v *jsem.Value, f string) []Error {
	return And(String(), lambda(func(v *jsem.Value, f string) []Error {
		s, _ := v.String()
		if a.r.Match([]byte(s)) {
			return NoErrors
		}
		return []Error{
			Error{"value_must_match_regex", f, map[string]string{"regex": a.r.String()}},
		}
	})).Validate(v, f)
}

type RegexStringValidator struct{}

func RegexString() Validator {
	return RegexStringValidator{}
}

func (a RegexStringValidator) Validate(v *jsem.Value, f string) []Error {
	return And(String(), lambda(func(v *jsem.Value, f string) []Error {
		s, _ := v.String()
		_, e := regexp.Compile(s)
		if e != nil {
			return []Error{Error{"value_must_be_regex_string", f, nil}}
		}
		return NoErrors
	})).Validate(v, f)
}

type OptionalValidator struct {
	e Validator
}

func Optional(e Validator) Validator {
	return OptionalValidator{e}
}

func (a OptionalValidator) Validate(v *jsem.Value, f string) []Error {
	if v == nil {
		return NoErrors
	}
	return a.e.Validate(v, f)
}
func (a OptionalValidator) Validator() Validator {
	return a.e
}

type LengthBetweenValidator struct {
	x, y int
}

func LengthBetween(x, y int) Validator {
	if y < x {
		panic("LengthBetween: y < x")
	}
	return LengthBetweenValidator{x, y}
}

func (a LengthBetweenValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Or(String(), Array(Anything())), lambda(func(v *jsem.Value, f string) []Error {
		l := -1
		if v.IsArray() {
			l, _ = v.ArrayLength()
		} else {
			l, _ = v.StringLength()
		}
		if l < a.x || l > a.y {
			return []Error{
				Error{"value_must_have_length_between", f, map[string]int{"min": a.x, "max": a.y}},
			}
		}
		return NoErrors
	})).Validate(v, f)
}

func Length(x int) Validator {
	return LengthBetween(x, x)
}

type NumberBetweenValidator struct {
	x, y float64
}

func NumberBetween(x, y float64) Validator {
	if y < x {
		panic("NumberBetween: y < x")
	}
	return NumberBetweenValidator{x, y}
}

func (a NumberBetweenValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Number(), lambda(func(v *jsem.Value, f string) []Error {
		l, _ := v.Float64()
		if l < a.x || l > a.y {
			return []Error{
				Error{"value_must_have_value_between", f, map[string]float64{"min": a.x, "max": a.y}},
			}
		}
		return NoErrors
	})).Validate(v, f)
}

type WholeNumberValidator struct{}

func WholeNumber() Validator {
	return WholeNumberValidator{}
}

func (a WholeNumberValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Number(), lambda(func(v *jsem.Value, f string) []Error {
		n, _ := v.Float64()
		_, r := math.Modf(n)
		if r != 0 {
			return []Error{Error{"value_must_be_whole_number", f, nil}}
		}
		return NoErrors
	})).Validate(v, f)
}

type WholeNumberBetweenValidator struct {
	x, y int
}

func WholeNumberBetween(x, y int) Validator {
	if y < x {
		panic("WholeNumberBetween: y < x")
	}
	return WholeNumberBetweenValidator{x, y}
}

func (a WholeNumberBetweenValidator) Validate(v *jsem.Value, f string) []Error {
	return And(WholeNumber(), lambda(func(v *jsem.Value, f string) []Error {
		l, _ := v.Int()
		if l < a.x || l > a.y {
			return []Error{
				Error{"value_must_have_value_between", f, map[string]int{"min": a.x, "max": a.y}},
			}
		}
		return NoErrors
	})).Validate(v, f)
}

type ExactlyValidator struct {
	j *jsem.Value
}

func Exactly(j *jsem.Value) Validator {
	return ExactlyValidator{j}
}

func (a ExactlyValidator) Validate(v *jsem.Value, f string) []Error {
	if !v.Equals(a.j) {
		return []Error{Error{"value_not_matched_exactly", f, nil}}
	}
	return NoErrors
}

type CaseValidator []Validator

func Case(cs ...Validator) Validator {
	return CaseValidator(cs)
}

func (a CaseValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Tuple(WholeNumberBetween(0, len(a)-1), Anything()), lambda(func(v *jsem.Value, f string) []Error {
		cv, _ := v.ArrayIndex(0)
		vv, _ := v.ArrayIndex(1)
		ix, _ := cv.Int()
		return a[ix].Validate(vv, f+".1")
	})).Validate(v, f)
}

// here be dragons! use only if you know exactly what you're doing

type RecursiveValidator struct {
	v Validator
	l string
}

func (r *RecursiveValidator) Validator() Validator {
	return r.v
}

func (r *RecursiveValidator) Validate(v *jsem.Value, f string) []Error {
	return r.v.Validate(v, f)
}

func (r *RecursiveValidator) Define(v Validator) {
	r.v = v
}
<commit_msg>fixed path joining<commit_after>package jval

import (
	"github.com/thwd/jsem"
	"math"
	"math/rand"
	"regexp"
	"strconv"
	"time"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

type Error struct {
	Label   string      `json:"label"`
	Field   string      `json:"field"`
	Context interface{} `json:"context"`
}

// Error implements native "error" interface
func (e Error) Error() string {
	return e.Label
}

var NoErrors = []Error{}

type Validator interface {
	Validate(value *jsem.Value, field string) []Error
}

type lambda func(v *jsem.Value, f string) []Error

func (l lambda) Validate(v *jsem.Value, f string) []Error {
	return l(v, f)
}

type AnythingValidator struct{}

func Anything() Validator {
	return AnythingValidator{}
}

func (a AnythingValidator) Validate(v *jsem.Value, f string) []Error {
	return NoErrors
}

type StringValidator struct{}

func String() Validator {
	return StringValidator{}
}

func (a StringValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsString() {
		return NoErrors
	}
	return []Error{Error{"value_must_be_string", f, nil}}
}

type NumberValidator struct{}

func Number() Validator {
	return NumberValidator{}
}

func (a NumberValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsNumber() {
		return NoErrors
	}
	return []Error{Error{"value_must_be_number", f, nil}}
}

type BooleanValidator struct{}

func Boolean() Validator {
	return BooleanValidator{}
}

func (a BooleanValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsBoolean() {
		return NoErrors
	}
	return []Error{Error{"value_must_be_boolean", f, nil}}
}

type NullValidator struct{}

func Null() Validator {
	return NullValidator{}
}

func (a NullValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsNull() {
		return NoErrors
	}
	return []Error{Error{"value_must_be_null", f, nil}}
}

type NotNullValidator struct{}

func NotNull() Validator {
	return NotNullValidator{}
}

func (a NotNullValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsNull() {
		return []Error{Error{"value_must_not_be_null", f, nil}}
	}
	return NoErrors
}

type AndValidator []Validator

func And(vs ...Validator) Validator {
	return AndValidator(vs)
}

func (a AndValidator) Validate(v *jsem.Value, f string) []Error {
	for _, b := range a {
		es := b.Validate(v, f)
		if len(es) != 0 {
			return es
		}
	}
	return NoErrors
}
func (a AndValidator) Validators() []Validator {
	return []Validator(a)
}

type TupleValidator []Validator

func Tuple(vs ...Validator) Validator {
	return TupleValidator(vs)
}

func (a TupleValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Array(Anything()), Length(len(a)), lambda(func(v *jsem.Value, f string) []Error {
		for i, b := range a {
			u, _ := v.ArrayIndex(i)
			es := b.Validate(u, joinPaths(f, strconv.Itoa(i)))
			if len(es) != 0 {
				return es
			}
		}
		return NoErrors
	})).Validate(v, f)
}
func (a TupleValidator) Validators() []Validator {
	return []Validator(a)
}

type OrValidator []Validator

func Or(vs ...Validator) Validator {
	return OrValidator(vs)
}

func (b OrValidator) Validate(v *jsem.Value, f string) []Error {
	ae := make([]Error, 0, len(b))
	for _, a := range b {
		es := a.Validate(v, f)
		if len(es) == 0 {
			return NoErrors
		}
		ae = append(ae, es...)
	}
	return ae // TODO(thwd): merge errors into one?
}
func (a OrValidator) Validators() []Validator {
	return []Validator(a)
}

type ObjectValidator map[string]Validator

func Object(d map[string]Validator) Validator {
	return ObjectValidator(d)
}

func (d ObjectValidator) Validate(v *jsem.Value, f string) []Error {
	if !v.IsObject() {
		return []Error{Error{"value_must_be_object", f, nil}}
	}
	ae := make([]Error, 0, len(d))
	v.ObjectForEach(func(k string, u *jsem.Value) {
		if _, ok := d[k]; !ok {
			ae = append(ae, Error{"unexpected_object_key", joinPaths(f, k), nil})
		}
	})
	for k, a := range d {
		u, _ := v.ObjectKey(k)
		ae = append(ae, a.Validate(u, joinPaths(f, k))...)
	}
	return ae
}

func (a ObjectValidator) Structure() map[string]Validator {
	return (map[string]Validator)(a)
}

type MapValidator struct {
	e Validator
}

func Map(e Validator) Validator {
	return MapValidator{e}
}

func (a MapValidator) Validate(v *jsem.Value, f string) []Error {
	if !v.IsObject() {
		return []Error{Error{"value_must_be_object", f, nil}}
	}
	ae := make([]Error, 0, 8)
	v.ObjectForEach(func(k string, u *jsem.Value) {
		ae = append(ae, a.e.Validate(u, joinPaths(f, k))...)
	})
	return ae
}
func (a MapValidator) Validator() Validator {
	return a.e
}

type ArrayValidator struct {
	e Validator
}

func Array(e Validator) Validator {
	return ArrayValidator{e}
}

func (a ArrayValidator) Validate(v *jsem.Value, f string) []Error {
	if !v.IsArray() {
		return []Error{Error{"value_must_be_array", f, nil}}
	}
	ae := make([]Error, 0, 8)
	v.ArrayForEach(func(i int, u *jsem.Value) {
		ae = append(ae, a.e.Validate(u, joinPaths(f, strconv.Itoa(i)))...)
	})
	return ae
}
func (a ArrayValidator) Validator() Validator {
	return a.e
}

type RegexValidator struct {
	r *regexp.Regexp
}

func Regex(r *regexp.Regexp) Validator {
	return RegexValidator{r}
}

func (a RegexValidator) Regex() *regexp.Regexp {
	return a.r
}

func (a RegexValidator) Validate(v *jsem.Value, f string) []Error {
	return And(String(), lambda(func(v *jsem.Value, f string) []Error {
		s, _ := v.String()
		if a.r.Match([]byte(s)) {
			return NoErrors
		}
		return []Error{
			Error{"value_must_match_regex", f, map[string]string{"regex": a.r.String()}},
		}
	})).Validate(v, f)
}

type RegexStringValidator struct{}

func RegexString() Validator {
	return RegexStringValidator{}
}

func (a RegexStringValidator) Validate(v *jsem.Value, f string) []Error {
	return And(String(), lambda(func(v *jsem.Value, f string) []Error {
		s, _ := v.String()
		_, e := regexp.Compile(s)
		if e != nil {
			return []Error{Error{"value_must_be_regex_string", f, nil}}
		}
		return NoErrors
	})).Validate(v, f)
}

type OptionalValidator struct {
	e Validator
}

func Optional(e Validator) Validator {
	return OptionalValidator{e}
}

func (a OptionalValidator) Validate(v *jsem.Value, f string) []Error {
	if v == nil {
		return NoErrors
	}
	return a.e.Validate(v, f)
}
func (a OptionalValidator) Validator() Validator {
	return a.e
}

type LengthBetweenValidator struct {
	x, y int
}

func LengthBetween(x, y int) Validator {
	if y < x {
		panic("LengthBetween: y < x")
	}
	return LengthBetweenValidator{x, y}
}

func (a LengthBetweenValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Or(String(), Array(Anything())), lambda(func(v *jsem.Value, f string) []Error {
		l := -1
		if v.IsArray() {
			l, _ = v.ArrayLength()
		} else {
			l, _ = v.StringLength()
		}
		if l < a.x || l > a.y {
			return []Error{
				Error{"value_must_have_length_between", f, map[string]int{"min": a.x, "max": a.y}},
			}
		}
		return NoErrors
	})).Validate(v, f)
}

func Length(x int) Validator {
	return LengthBetween(x, x)
}

type NumberBetweenValidator struct {
	x, y float64
}

func NumberBetween(x, y float64) Validator {
	if y < x {
		panic("NumberBetween: y < x")
	}
	return NumberBetweenValidator{x, y}
}

func (a NumberBetweenValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Number(), lambda(func(v *jsem.Value, f string) []Error {
		l, _ := v.Float64()
		if l < a.x || l > a.y {
			return []Error{
				Error{"value_must_have_value_between", f, map[string]float64{"min": a.x, "max": a.y}},
			}
		}
		return NoErrors
	})).Validate(v, f)
}

type WholeNumberValidator struct{}

func WholeNumber() Validator {
	return WholeNumberValidator{}
}

func (a WholeNumberValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Number(), lambda(func(v *jsem.Value, f string) []Error {
		n, _ := v.Float64()
		_, r := math.Modf(n)
		if r != 0 {
			return []Error{Error{"value_must_be_whole_number", f, nil}}
		}
		return NoErrors
	})).Validate(v, f)
}

type WholeNumberBetweenValidator struct {
	x, y int
}

func WholeNumberBetween(x, y int) Validator {
	if y < x {
		panic("WholeNumberBetween: y < x")
	}
	return WholeNumberBetweenValidator{x, y}
}

func (a WholeNumberBetweenValidator) Validate(v *jsem.Value, f string) []Error {
	return And(WholeNumber(), lambda(func(v *jsem.Value, f string) []Error {
		l, _ := v.Int()
		if l < a.x || l > a.y {
			return []Error{
				Error{"value_must_have_value_between", f, map[string]int{"min": a.x, "max": a.y}},
			}
		}
		return NoErrors
	})).Validate(v, f)
}

type ExactlyValidator struct {
	j *jsem.Value
}

func Exactly(j *jsem.Value) Validator {
	return ExactlyValidator{j}
}

func (a ExactlyValidator) Validate(v *jsem.Value, f string) []Error {
	if !v.Equals(a.j) {
		return []Error{Error{"value_not_matched_exactly", f, nil}}
	}
	return NoErrors
}

type CaseValidator []Validator

func Case(cs ...Validator) Validator {
	return CaseValidator(cs)
}

func (a CaseValidator) Validators() []Validator {
	return []Validator(a)
}

func (a CaseValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Tuple(WholeNumberBetween(0, len(a)-1), Anything()), lambda(func(v *jsem.Value, f string) []Error {
		cv, _ := v.ArrayIndex(0)
		vv, _ := v.ArrayIndex(1)
		ix, _ := cv.Int()
		return a[ix].Validate(vv, f+".1")
	})).Validate(v, f)
}

// here be dragons! use only if you know exactly what you're doing

type RecursiveValidator struct {
	v Validator
	l string
}

func (r *RecursiveValidator) Validator() Validator {
	return r.v
}

func (r *RecursiveValidator) Validate(v *jsem.Value, f string) []Error {
	return r.v.Validate(v, f)
}

func (r *RecursiveValidator) Define(v Validator) {
	r.v = v
}

func joinPaths(a, b string) string {
	if a == "" {
		return b
	}
	return a + "." + b
}
<|endoftext|>


<commit_before>package lars

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

	"github.com/go-playground/form"
)

// HTTP Constant Terms and Variables
const (
	// CONNECT HTTP method
	CONNECT = "CONNECT"
	// DELETE HTTP method
	DELETE = "DELETE"
	// GET HTTP method
	GET = "GET"
	// HEAD HTTP method
	HEAD = "HEAD"
	// OPTIONS HTTP method
	OPTIONS = "OPTIONS"
	// PATCH HTTP method
	PATCH = "PATCH"
	// POST HTTP method
	POST = "POST"
	// PUT HTTP method
	PUT = "PUT"
	// TRACE HTTP method
	TRACE = "TRACE"

	//-------------
	// Media types
	//-------------

	ApplicationJSON                  = "application/json"
	ApplicationJSONCharsetUTF8       = ApplicationJSON + "; " + CharsetUTF8
	ApplicationJavaScript            = "application/javascript"
	ApplicationJavaScriptCharsetUTF8 = ApplicationJavaScript + "; " + CharsetUTF8
	ApplicationXML                   = "application/xml"
	ApplicationXMLCharsetUTF8        = ApplicationXML + "; " + CharsetUTF8
	ApplicationForm                  = "application/x-www-form-urlencoded"
	ApplicationProtobuf              = "application/protobuf"
	ApplicationMsgpack               = "application/msgpack"
	TextHTML                         = "text/html"
	TextHTMLCharsetUTF8              = TextHTML + "; " + CharsetUTF8
	TextPlain                        = "text/plain"
	TextPlainCharsetUTF8             = TextPlain + "; " + CharsetUTF8
	MultipartForm                    = "multipart/form-data"
	OctetStream                      = "application/octet-stream"

	//---------
	// Charset
	//---------

	CharsetUTF8 = "charset=utf-8"

	//---------
	// Headers
	//---------

	AcceptedLanguage   = "Accept-Language"
	AcceptEncoding     = "Accept-Encoding"
	Authorization      = "Authorization"
	ContentDisposition = "Content-Disposition"
	ContentEncoding    = "Content-Encoding"
	ContentLength      = "Content-Length"
	ContentType        = "Content-Type"
	Location           = "Location"
	Upgrade            = "Upgrade"
	Vary               = "Vary"
	WWWAuthenticate    = "WWW-Authenticate"
	XForwardedFor      = "X-Forwarded-For"
	XRealIP            = "X-Real-Ip"
	Allow              = "Allow"
	Origin             = "Origin"

	Gzip = "gzip"

	WildcardParam = "*wildcard"

	basePath = "/"
	blank    = ""

	slashByte = '/'
	paramByte = ':'
	wildByte  = '*'
)

// Handler is the type used in registering handlers.
// NOTE: these handlers may get wrapped by the HandlerFunc
// type internally.
type Handler interface{}

// HandlerFunc is the internal handler type used for middleware and handlers
type HandlerFunc func(Context)

// HandlersChain is an array of HanderFunc handlers to run
type HandlersChain []HandlerFunc

// ContextFunc is the function to run when creating a new context
type ContextFunc func(l *LARS) Context

// CustomHandlerFunc wraped by HandlerFunc and called where you can type cast both Context and Handler
// and call Handler
type CustomHandlerFunc func(Context, Handler)

// customHandlers is a map of your registered custom CustomHandlerFunc's
// used in determining how to wrap them.
type customHandlers map[reflect.Type]CustomHandlerFunc

// LARS is the main routing instance
type LARS struct {
	routeGroup
	trees map[string]*node

	// function that gets called to create the context object... is total overridable using RegisterContext
	contextFunc ContextFunc

	pool sync.Pool

	http404 HandlersChain // 404 Not Found
	http405 HandlersChain // 405 Method Not Allowed

	automaticOPTIONS HandlersChain
	notFound         HandlersChain

	customHandlersFuncs customHandlers

	// mostParams used to keep track of the most amount of
	// params in any URL and this will set the default capacity
	// of eachContext Params
	mostParams uint8

	// Enables automatic redirection if the current route can't be matched but a
	// handler for the path with (without) the trailing slash exists.
	// For example if /foo/ is requested but a route only exists for /foo, the
	// client is redirected to /foo with http status code 301 for GET requests
	// and 307 for all other request methods.
	redirectTrailingSlash bool

	// If enabled, the router checks if another method is allowed for the
	// current route, if the current request can not be routed.
	// If this is the case, the request is answered with 'Method Not Allowed'
	// and HTTP status code 405.
	// If no other Method is allowed, the request is delegated to the NotFound
	// handler.
	handleMethodNotAllowed bool

	// if enabled automatically handles OPTION requests; manually configured OPTION
	// handlers take precidence. default true
	automaticallyHandleOPTIONS bool
}

// RouteMap contains a single routes full path
// and other information
type RouteMap struct {
	Depth   int    `json:"depth"`
	Path    string `json:"path"`
	Method  string `json:"method"`
	Handler string `json:"handler"`
}

var (
	default404Handler = func(c Context) {
		http.Error(c.Response(), http.StatusText(http.StatusNotFound), http.StatusNotFound)
	}

	methodNotAllowedHandler = func(c Context) {
		c.Response().WriteHeader(http.StatusMethodNotAllowed)
	}

	automaticOPTIONSHandler = func(c Context) {
		c.Response().WriteHeader(http.StatusOK)
	}

	formDecoder     *form.Decoder
	formDecoderInit sync.Once
)

// New Creates and returns a new lars instance
func New() *LARS {

	l := &LARS{
		routeGroup: routeGroup{
			middleware: make(HandlersChain, 0),
		},
		trees: make(map[string]*node),
		contextFunc: func(l *LARS) Context {
			return NewContext(l)
		},
		mostParams:                 0,
		http404:                    []HandlerFunc{default404Handler},
		http405:                    []HandlerFunc{methodNotAllowedHandler},
		redirectTrailingSlash:      true,
		handleMethodNotAllowed:     false,
		automaticallyHandleOPTIONS: false,
	}

	l.routeGroup.lars = l
	l.pool.New = func() interface{} {

		c := l.contextFunc(l)
		b := c.BaseContext()
		b.parent = c

		return b
	}

	return l
}

func initFormDecoder() {
	formDecoderInit.Do(func() {
		formDecoder = form.NewDecoder()
	})
}

// BuiltInFormDecoder returns the built in form decoder github.com/go-playground/form
// in order for custom type to be registered.
func (l *LARS) BuiltInFormDecoder() *form.Decoder {

	initFormDecoder()

	return formDecoder
}

// RegisterCustomHandler registers a custom handler that gets wrapped by HandlerFunc
func (l *LARS) RegisterCustomHandler(customType interface{}, fn CustomHandlerFunc) {

	if l.customHandlersFuncs == nil {
		l.customHandlersFuncs = make(customHandlers)
	}

	t := reflect.TypeOf(customType)

	if _, ok := l.customHandlersFuncs[t]; ok {
		panic(fmt.Sprint("Custom Type + CustomHandlerFunc already declared: ", t))
	}

	l.customHandlersFuncs[t] = fn
}

// RegisterContext registers a custom Context function for creation
// and resetting of a global object passed per http request
func (l *LARS) RegisterContext(fn ContextFunc) {
	l.contextFunc = fn
}

// Register404 alows for overriding of the not found handler function.
// NOTE: this is run after not finding a route even after redirecting with the trailing slash
func (l *LARS) Register404(notFound ...Handler) {

	chain := make(HandlersChain, len(notFound))

	for i, h := range notFound {
		chain[i] = l.wrapHandler(h)
	}

	l.http404 = chain
}

// SetAutomaticallyHandleOPTIONS tells lars whether to
// automatically handle OPTION requests; manually configured
// OPTION handlers take precedence. default true
func (l *LARS) SetAutomaticallyHandleOPTIONS(set bool) {
	l.automaticallyHandleOPTIONS = set
}

// SetRedirectTrailingSlash tells lars whether to try
// and fix a URL by trying to find it
// lowercase -> with or without slash -> 404
func (l *LARS) SetRedirectTrailingSlash(set bool) {
	l.redirectTrailingSlash = set
}

// SetHandle405MethodNotAllowed tells lars whether to
// handle the http 405 Method Not Allowed status code
func (l *LARS) SetHandle405MethodNotAllowed(set bool) {
	l.handleMethodNotAllowed = set
}

// Serve returns an http.Handler to be used.
func (l *LARS) Serve() http.Handler {

	// reserved for any logic that needs to happen before serving starts.
	// i.e. although this router does not use priority to determine route order
	// could add sorting of tree nodes here....

	l.notFound = make(HandlersChain, len(l.middleware)+len(l.http404))
	copy(l.notFound, l.middleware)
	copy(l.notFound[len(l.middleware):], l.http404)

	if l.automaticallyHandleOPTIONS {
		l.automaticOPTIONS = make(HandlersChain, len(l.middleware)+1)
		copy(l.automaticOPTIONS, l.middleware)
		copy(l.automaticOPTIONS[len(l.middleware):], []HandlerFunc{automaticOPTIONSHandler})
	}

	return http.HandlerFunc(l.serveHTTP)
}

// Conforms to the http.Handler interface.
func (l *LARS) serveHTTP(w http.ResponseWriter, r *http.Request) {

	c := l.pool.Get().(*Ctx)

	c.parent.RequestStart(w, r)

	if root := l.trees[r.Method]; root != nil {

		if c.handlers, c.params, c.handlerName = root.find(r.URL.Path, c.params); c.handlers == nil {

			c.params = c.params[0:0]

			if l.redirectTrailingSlash && len(r.URL.Path) > 1 {

				// find again all lowercase
				orig := r.URL.Path
				lc := strings.ToLower(orig)

				if lc != r.URL.Path {

					if c.handlers, _, _ = root.find(lc, c.params); c.handlers != nil {
						r.URL.Path = lc
						c.handlers = l.redirect(r.Method, r.URL.String())
						r.URL.Path = orig
						goto END
					}
				}

				if lc[len(lc)-1:] == basePath {
					lc = lc[:len(lc)-1]
				} else {
					lc = lc + basePath
				}

				if c.handlers, _, _ = root.find(lc, c.params); c.handlers != nil {
					r.URL.Path = lc
					c.handlers = l.redirect(r.Method, r.URL.String())
					r.URL.Path = orig
					goto END
				}
			}

		} else {
			goto END
		}
	}

	if l.automaticallyHandleOPTIONS && r.Method == OPTIONS {
		l.getOptions(c)
		goto END
	}

	if l.handleMethodNotAllowed {

		if l.checkMethodNotAllowed(c) {
			goto END
		}
	}

	// not found
	c.handlers = l.notFound

END:

	c.parent.Next()
	c.parent.RequestEnd()

	l.pool.Put(c)
}

func (l *LARS) getOptions(c *Ctx) {

	if c.request.URL.Path == "*" { // check server-wide OPTIONS

		for m := range l.trees {

			if m == OPTIONS {
				continue
			}

			c.response.Header().Add(Allow, m)
		}

	} else {
		for m, tree := range l.trees {

			if m == c.request.Method || m == OPTIONS {
				continue
			}

			if c.handlers, _, _ = tree.find(c.request.URL.Path, c.params); c.handlers != nil {
				c.response.Header().Add(Allow, m)
			}
		}

	}

	c.response.Header().Add(Allow, OPTIONS)
	c.handlers = l.automaticOPTIONS

	return
}

func (l *LARS) checkMethodNotAllowed(c *Ctx) (found bool) {

	for m, tree := range l.trees {

		if m != c.request.Method {
			if c.handlers, _, _ = tree.find(c.request.URL.Path, c.params); c.handlers != nil {
				// add methods
				c.response.Header().Add(Allow, m)
				found = true
			}
		}
	}

	if found {
		c.handlers = l.http405
	}

	return
}
<commit_msg>correct typo<commit_after>package lars

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

	"github.com/go-playground/form"
)

// HTTP Constant Terms and Variables
const (
	// CONNECT HTTP method
	CONNECT = "CONNECT"
	// DELETE HTTP method
	DELETE = "DELETE"
	// GET HTTP method
	GET = "GET"
	// HEAD HTTP method
	HEAD = "HEAD"
	// OPTIONS HTTP method
	OPTIONS = "OPTIONS"
	// PATCH HTTP method
	PATCH = "PATCH"
	// POST HTTP method
	POST = "POST"
	// PUT HTTP method
	PUT = "PUT"
	// TRACE HTTP method
	TRACE = "TRACE"

	//-------------
	// Media types
	//-------------

	ApplicationJSON                  = "application/json"
	ApplicationJSONCharsetUTF8       = ApplicationJSON + "; " + CharsetUTF8
	ApplicationJavaScript            = "application/javascript"
	ApplicationJavaScriptCharsetUTF8 = ApplicationJavaScript + "; " + CharsetUTF8
	ApplicationXML                   = "application/xml"
	ApplicationXMLCharsetUTF8        = ApplicationXML + "; " + CharsetUTF8
	ApplicationForm                  = "application/x-www-form-urlencoded"
	ApplicationProtobuf              = "application/protobuf"
	ApplicationMsgpack               = "application/msgpack"
	TextHTML                         = "text/html"
	TextHTMLCharsetUTF8              = TextHTML + "; " + CharsetUTF8
	TextPlain                        = "text/plain"
	TextPlainCharsetUTF8             = TextPlain + "; " + CharsetUTF8
	MultipartForm                    = "multipart/form-data"
	OctetStream                      = "application/octet-stream"

	//---------
	// Charset
	//---------

	CharsetUTF8 = "charset=utf-8"

	//---------
	// Headers
	//---------

	AcceptedLanguage   = "Accept-Language"
	AcceptEncoding     = "Accept-Encoding"
	Authorization      = "Authorization"
	ContentDisposition = "Content-Disposition"
	ContentEncoding    = "Content-Encoding"
	ContentLength      = "Content-Length"
	ContentType        = "Content-Type"
	Location           = "Location"
	Upgrade            = "Upgrade"
	Vary               = "Vary"
	WWWAuthenticate    = "WWW-Authenticate"
	XForwardedFor      = "X-Forwarded-For"
	XRealIP            = "X-Real-Ip"
	Allow              = "Allow"
	Origin             = "Origin"

	Gzip = "gzip"

	WildcardParam = "*wildcard"

	basePath = "/"
	blank    = ""

	slashByte = '/'
	paramByte = ':'
	wildByte  = '*'
)

// Handler is the type used in registering handlers.
// NOTE: these handlers may get wrapped by the HandlerFunc
// type internally.
type Handler interface{}

// HandlerFunc is the internal handler type used for middleware and handlers
type HandlerFunc func(Context)

// HandlersChain is an array of HanderFunc handlers to run
type HandlersChain []HandlerFunc

// ContextFunc is the function to run when creating a new context
type ContextFunc func(l *LARS) Context

// CustomHandlerFunc wraped by HandlerFunc and called where you can type cast both Context and Handler
// and call Handler
type CustomHandlerFunc func(Context, Handler)

// customHandlers is a map of your registered custom CustomHandlerFunc's
// used in determining how to wrap them.
type customHandlers map[reflect.Type]CustomHandlerFunc

// LARS is the main routing instance
type LARS struct {
	routeGroup
	trees map[string]*node

	// function that gets called to create the context object... is total overridable using RegisterContext
	contextFunc ContextFunc

	pool sync.Pool

	http404 HandlersChain // 404 Not Found
	http405 HandlersChain // 405 Method Not Allowed

	automaticOPTIONS HandlersChain
	notFound         HandlersChain

	customHandlersFuncs customHandlers

	// mostParams used to keep track of the most amount of
	// params in any URL and this will set the default capacity
	// of eachContext Params
	mostParams uint8

	// Enables automatic redirection if the current route can't be matched but a
	// handler for the path with (without) the trailing slash exists.
	// For example if /foo/ is requested but a route only exists for /foo, the
	// client is redirected to /foo with http status code 301 for GET requests
	// and 307 for all other request methods.
	redirectTrailingSlash bool

	// If enabled, the router checks if another method is allowed for the
	// current route, if the current request can not be routed.
	// If this is the case, the request is answered with 'Method Not Allowed'
	// and HTTP status code 405.
	// If no other Method is allowed, the request is delegated to the NotFound
	// handler.
	handleMethodNotAllowed bool

	// if enabled automatically handles OPTION requests; manually configured OPTION
	// handlers take presidence. default true
	automaticallyHandleOPTIONS bool
}

// RouteMap contains a single routes full path
// and other information
type RouteMap struct {
	Depth   int    `json:"depth"`
	Path    string `json:"path"`
	Method  string `json:"method"`
	Handler string `json:"handler"`
}

var (
	default404Handler = func(c Context) {
		http.Error(c.Response(), http.StatusText(http.StatusNotFound), http.StatusNotFound)
	}

	methodNotAllowedHandler = func(c Context) {
		c.Response().WriteHeader(http.StatusMethodNotAllowed)
	}

	automaticOPTIONSHandler = func(c Context) {
		c.Response().WriteHeader(http.StatusOK)
	}

	formDecoder     *form.Decoder
	formDecoderInit sync.Once
)

// New Creates and returns a new lars instance
func New() *LARS {

	l := &LARS{
		routeGroup: routeGroup{
			middleware: make(HandlersChain, 0),
		},
		trees: make(map[string]*node),
		contextFunc: func(l *LARS) Context {
			return NewContext(l)
		},
		mostParams:                 0,
		http404:                    []HandlerFunc{default404Handler},
		http405:                    []HandlerFunc{methodNotAllowedHandler},
		redirectTrailingSlash:      true,
		handleMethodNotAllowed:     false,
		automaticallyHandleOPTIONS: false,
	}

	l.routeGroup.lars = l
	l.pool.New = func() interface{} {

		c := l.contextFunc(l)
		b := c.BaseContext()
		b.parent = c

		return b
	}

	return l
}

func initFormDecoder() {
	formDecoderInit.Do(func() {
		formDecoder = form.NewDecoder()
	})
}

// BuiltInFormDecoder returns the built in form decoder github.com/go-playground/form
// in order for custom type to be registered.
func (l *LARS) BuiltInFormDecoder() *form.Decoder {

	initFormDecoder()

	return formDecoder
}

// RegisterCustomHandler registers a custom handler that gets wrapped by HandlerFunc
func (l *LARS) RegisterCustomHandler(customType interface{}, fn CustomHandlerFunc) {

	if l.customHandlersFuncs == nil {
		l.customHandlersFuncs = make(customHandlers)
	}

	t := reflect.TypeOf(customType)

	if _, ok := l.customHandlersFuncs[t]; ok {
		panic(fmt.Sprint("Custom Type + CustomHandlerFunc already declared: ", t))
	}

	l.customHandlersFuncs[t] = fn
}

// RegisterContext registers a custom Context function for creation
// and resetting of a global object passed per http request
func (l *LARS) RegisterContext(fn ContextFunc) {
	l.contextFunc = fn
}

// Register404 alows for overriding of the not found handler function.
// NOTE: this is run after not finding a route even after redirecting with the trailing slash
func (l *LARS) Register404(notFound ...Handler) {

	chain := make(HandlersChain, len(notFound))

	for i, h := range notFound {
		chain[i] = l.wrapHandler(h)
	}

	l.http404 = chain
}

// SetAutomaticallyHandleOPTIONS tells lars whether to
// automatically handle OPTION requests; manually configured
// OPTION handlers take precedence. default true
func (l *LARS) SetAutomaticallyHandleOPTIONS(set bool) {
	l.automaticallyHandleOPTIONS = set
}

// SetRedirectTrailingSlash tells lars whether to try
// and fix a URL by trying to find it
// lowercase -> with or without slash -> 404
func (l *LARS) SetRedirectTrailingSlash(set bool) {
	l.redirectTrailingSlash = set
}

// SetHandle405MethodNotAllowed tells lars whether to
// handle the http 405 Method Not Allowed status code
func (l *LARS) SetHandle405MethodNotAllowed(set bool) {
	l.handleMethodNotAllowed = set
}

// Serve returns an http.Handler to be used.
func (l *LARS) Serve() http.Handler {

	// reserved for any logic that needs to happen before serving starts.
	// i.e. although this router does not use priority to determine route order
	// could add sorting of tree nodes here....

	l.notFound = make(HandlersChain, len(l.middleware)+len(l.http404))
	copy(l.notFound, l.middleware)
	copy(l.notFound[len(l.middleware):], l.http404)

	if l.automaticallyHandleOPTIONS {
		l.automaticOPTIONS = make(HandlersChain, len(l.middleware)+1)
		copy(l.automaticOPTIONS, l.middleware)
		copy(l.automaticOPTIONS[len(l.middleware):], []HandlerFunc{automaticOPTIONSHandler})
	}

	return http.HandlerFunc(l.serveHTTP)
}

// Conforms to the http.Handler interface.
func (l *LARS) serveHTTP(w http.ResponseWriter, r *http.Request) {

	c := l.pool.Get().(*Ctx)

	c.parent.RequestStart(w, r)

	if root := l.trees[r.Method]; root != nil {

		if c.handlers, c.params, c.handlerName = root.find(r.URL.Path, c.params); c.handlers == nil {

			c.params = c.params[0:0]

			if l.redirectTrailingSlash && len(r.URL.Path) > 1 {

				// find again all lowercase
				orig := r.URL.Path
				lc := strings.ToLower(orig)

				if lc != r.URL.Path {

					if c.handlers, _, _ = root.find(lc, c.params); c.handlers != nil {
						r.URL.Path = lc
						c.handlers = l.redirect(r.Method, r.URL.String())
						r.URL.Path = orig
						goto END
					}
				}

				if lc[len(lc)-1:] == basePath {
					lc = lc[:len(lc)-1]
				} else {
					lc = lc + basePath
				}

				if c.handlers, _, _ = root.find(lc, c.params); c.handlers != nil {
					r.URL.Path = lc
					c.handlers = l.redirect(r.Method, r.URL.String())
					r.URL.Path = orig
					goto END
				}
			}

		} else {
			goto END
		}
	}

	if l.automaticallyHandleOPTIONS && r.Method == OPTIONS {
		l.getOptions(c)
		goto END
	}

	if l.handleMethodNotAllowed {

		if l.checkMethodNotAllowed(c) {
			goto END
		}
	}

	// not found
	c.handlers = l.notFound

END:

	c.parent.Next()
	c.parent.RequestEnd()

	l.pool.Put(c)
}

func (l *LARS) getOptions(c *Ctx) {

	if c.request.URL.Path == "*" { // check server-wide OPTIONS

		for m := range l.trees {

			if m == OPTIONS {
				continue
			}

			c.response.Header().Add(Allow, m)
		}

	} else {
		for m, tree := range l.trees {

			if m == c.request.Method || m == OPTIONS {
				continue
			}

			if c.handlers, _, _ = tree.find(c.request.URL.Path, c.params); c.handlers != nil {
				c.response.Header().Add(Allow, m)
			}
		}

	}

	c.response.Header().Add(Allow, OPTIONS)
	c.handlers = l.automaticOPTIONS

	return
}

func (l *LARS) checkMethodNotAllowed(c *Ctx) (found bool) {

	for m, tree := range l.trees {

		if m != c.request.Method {
			if c.handlers, _, _ = tree.find(c.request.URL.Path, c.params); c.handlers != nil {
				// add methods
				c.response.Header().Add(Allow, m)
				found = true
			}
		}
	}

	if found {
		c.handlers = l.http405
	}

	return
}
<|endoftext|>


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

package buildcfg

import (
	"fmt"
	"reflect"
	"strings"

	"internal/goexperiment"
)

// ExperimentFlags represents a set of GOEXPERIMENT flags relative to a baseline
// (platform-default) experiment configuration.
type ExperimentFlags struct {
	goexperiment.Flags
	baseline goexperiment.Flags
}

// Experiment contains the toolchain experiments enabled for the
// current build.
//
// (This is not necessarily the set of experiments the compiler itself
// was built with.)
//
// experimentBaseline specifies the experiment flags that are enabled by
// default in the current toolchain. This is, in effect, the "control"
// configuration and any variation from this is an experiment.
var Experiment ExperimentFlags = func() ExperimentFlags {
	flags, err := ParseGOEXPERIMENT(GOOS, GOARCH, envOr("GOEXPERIMENT", defaultGOEXPERIMENT))
	if err != nil {
		Error = err
		return ExperimentFlags{}
	}
	return *flags
}()

// DefaultGOEXPERIMENT is the embedded default GOEXPERIMENT string.
// It is not guaranteed to be canonical.
const DefaultGOEXPERIMENT = defaultGOEXPERIMENT

// FramePointerEnabled enables the use of platform conventions for
// saving frame pointers.
//
// This used to be an experiment, but now it's always enabled on
// platforms that support it.
//
// Note: must agree with runtime.framepointer_enabled.
var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64"

// ParseGOEXPERIMENT parses a (GOOS, GOARCH, GOEXPERIMENT)
// configuration tuple and returns the enabled and baseline experiment
// flag sets.
//
// TODO(mdempsky): Move to internal/goexperiment.
func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) {
	// regabiSupported is set to true on platforms where register ABI is
	// supported and enabled by default.
	// regabiAlwaysOn is set to true on platforms where register ABI is
	// always on.
	var regabiSupported, regabiAlwaysOn bool
	switch goarch {
	case "amd64", "arm64", "ppc64le", "ppc64":
		regabiAlwaysOn = true
		regabiSupported = true
	case "riscv64":
		regabiSupported = true
	}

	baseline := goexperiment.Flags{
		RegabiWrappers: regabiSupported,
		RegabiArgs:     regabiSupported,
		Unified:        true,
	}

	// Start with the statically enabled set of experiments.
	flags := &ExperimentFlags{
		Flags:    baseline,
		baseline: baseline,
	}

	// Pick up any changes to the baseline configuration from the
	// GOEXPERIMENT environment. This can be set at make.bash time
	// and overridden at build time.
	if goexp != "" {
		// Create a map of known experiment names.
		names := make(map[string]func(bool))
		rv := reflect.ValueOf(&flags.Flags).Elem()
		rt := rv.Type()
		for i := 0; i < rt.NumField(); i++ {
			field := rv.Field(i)
			names[strings.ToLower(rt.Field(i).Name)] = field.SetBool
		}

		// "regabi" is an alias for all working regabi
		// subexperiments, and not an experiment itself. Doing
		// this as an alias make both "regabi" and "noregabi"
		// do the right thing.
		names["regabi"] = func(v bool) {
			flags.RegabiWrappers = v
			flags.RegabiArgs = v
		}

		// Parse names.
		for _, f := range strings.Split(goexp, ",") {
			if f == "" {
				continue
			}
			if f == "none" {
				// GOEXPERIMENT=none disables all experiment flags.
				// This is used by cmd/dist, which doesn't know how
				// to build with any experiment flags.
				flags.Flags = goexperiment.Flags{}
				continue
			}
			val := true
			if strings.HasPrefix(f, "no") {
				f, val = f[2:], false
			}
			set, ok := names[f]
			if !ok {
				return nil, fmt.Errorf("unknown GOEXPERIMENT %s", f)
			}
			set(val)
		}
	}

	if regabiAlwaysOn {
		flags.RegabiWrappers = true
		flags.RegabiArgs = true
	}
	// regabi is only supported on amd64, arm64, riscv64, ppc64 and ppc64le.
	if !regabiSupported {
		flags.RegabiWrappers = false
		flags.RegabiArgs = false
	}
	// Check regabi dependencies.
	if flags.RegabiArgs && !flags.RegabiWrappers {
		return nil, fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers")
	}
	return flags, nil
}

// String returns the canonical GOEXPERIMENT string to enable this experiment
// configuration. (Experiments in the same state as in the baseline are elided.)
func (exp *ExperimentFlags) String() string {
	return strings.Join(expList(&exp.Flags, &exp.baseline, false), ",")
}

// expList returns the list of lower-cased experiment names for
// experiments that differ from base. base may be nil to indicate no
// experiments. If all is true, then include all experiment flags,
// regardless of base.
func expList(exp, base *goexperiment.Flags, all bool) []string {
	var list []string
	rv := reflect.ValueOf(exp).Elem()
	var rBase reflect.Value
	if base != nil {
		rBase = reflect.ValueOf(base).Elem()
	}
	rt := rv.Type()
	for i := 0; i < rt.NumField(); i++ {
		name := strings.ToLower(rt.Field(i).Name)
		val := rv.Field(i).Bool()
		baseVal := false
		if base != nil {
			baseVal = rBase.Field(i).Bool()
		}
		if all || val != baseVal {
			if val {
				list = append(list, name)
			} else {
				list = append(list, "no"+name)
			}
		}
	}
	return list
}

// Enabled returns a list of enabled experiments, as
// lower-cased experiment names.
func (exp *ExperimentFlags) Enabled() []string {
	return expList(&exp.Flags, nil, false)
}

// All returns a list of all experiment settings.
// Disabled experiments appear in the list prefixed by "no".
func (exp *ExperimentFlags) All() []string {
	return expList(&exp.Flags, nil, true)
}
<commit_msg>internal/buildcfg: enabled CoverageRedesign GOEXPERIMENT by default<commit_after>// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package buildcfg

import (
	"fmt"
	"reflect"
	"strings"

	"internal/goexperiment"
)

// ExperimentFlags represents a set of GOEXPERIMENT flags relative to a baseline
// (platform-default) experiment configuration.
type ExperimentFlags struct {
	goexperiment.Flags
	baseline goexperiment.Flags
}

// Experiment contains the toolchain experiments enabled for the
// current build.
//
// (This is not necessarily the set of experiments the compiler itself
// was built with.)
//
// experimentBaseline specifies the experiment flags that are enabled by
// default in the current toolchain. This is, in effect, the "control"
// configuration and any variation from this is an experiment.
var Experiment ExperimentFlags = func() ExperimentFlags {
	flags, err := ParseGOEXPERIMENT(GOOS, GOARCH, envOr("GOEXPERIMENT", defaultGOEXPERIMENT))
	if err != nil {
		Error = err
		return ExperimentFlags{}
	}
	return *flags
}()

// DefaultGOEXPERIMENT is the embedded default GOEXPERIMENT string.
// It is not guaranteed to be canonical.
const DefaultGOEXPERIMENT = defaultGOEXPERIMENT

// FramePointerEnabled enables the use of platform conventions for
// saving frame pointers.
//
// This used to be an experiment, but now it's always enabled on
// platforms that support it.
//
// Note: must agree with runtime.framepointer_enabled.
var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64"

// ParseGOEXPERIMENT parses a (GOOS, GOARCH, GOEXPERIMENT)
// configuration tuple and returns the enabled and baseline experiment
// flag sets.
//
// TODO(mdempsky): Move to internal/goexperiment.
func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) {
	// regabiSupported is set to true on platforms where register ABI is
	// supported and enabled by default.
	// regabiAlwaysOn is set to true on platforms where register ABI is
	// always on.
	var regabiSupported, regabiAlwaysOn bool
	switch goarch {
	case "amd64", "arm64", "ppc64le", "ppc64":
		regabiAlwaysOn = true
		regabiSupported = true
	case "riscv64":
		regabiSupported = true
	}

	baseline := goexperiment.Flags{
		RegabiWrappers:   regabiSupported,
		RegabiArgs:       regabiSupported,
		Unified:          true,
		CoverageRedesign: true,
	}

	// Start with the statically enabled set of experiments.
	flags := &ExperimentFlags{
		Flags:    baseline,
		baseline: baseline,
	}

	// Pick up any changes to the baseline configuration from the
	// GOEXPERIMENT environment. This can be set at make.bash time
	// and overridden at build time.
	if goexp != "" {
		// Create a map of known experiment names.
		names := make(map[string]func(bool))
		rv := reflect.ValueOf(&flags.Flags).Elem()
		rt := rv.Type()
		for i := 0; i < rt.NumField(); i++ {
			field := rv.Field(i)
			names[strings.ToLower(rt.Field(i).Name)] = field.SetBool
		}

		// "regabi" is an alias for all working regabi
		// subexperiments, and not an experiment itself. Doing
		// this as an alias make both "regabi" and "noregabi"
		// do the right thing.
		names["regabi"] = func(v bool) {
			flags.RegabiWrappers = v
			flags.RegabiArgs = v
		}

		// Parse names.
		for _, f := range strings.Split(goexp, ",") {
			if f == "" {
				continue
			}
			if f == "none" {
				// GOEXPERIMENT=none disables all experiment flags.
				// This is used by cmd/dist, which doesn't know how
				// to build with any experiment flags.
				flags.Flags = goexperiment.Flags{}
				continue
			}
			val := true
			if strings.HasPrefix(f, "no") {
				f, val = f[2:], false
			}
			set, ok := names[f]
			if !ok {
				return nil, fmt.Errorf("unknown GOEXPERIMENT %s", f)
			}
			set(val)
		}
	}

	if regabiAlwaysOn {
		flags.RegabiWrappers = true
		flags.RegabiArgs = true
	}
	// regabi is only supported on amd64, arm64, riscv64, ppc64 and ppc64le.
	if !regabiSupported {
		flags.RegabiWrappers = false
		flags.RegabiArgs = false
	}
	// Check regabi dependencies.
	if flags.RegabiArgs && !flags.RegabiWrappers {
		return nil, fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers")
	}
	return flags, nil
}

// String returns the canonical GOEXPERIMENT string to enable this experiment
// configuration. (Experiments in the same state as in the baseline are elided.)
func (exp *ExperimentFlags) String() string {
	return strings.Join(expList(&exp.Flags, &exp.baseline, false), ",")
}

// expList returns the list of lower-cased experiment names for
// experiments that differ from base. base may be nil to indicate no
// experiments. If all is true, then include all experiment flags,
// regardless of base.
func expList(exp, base *goexperiment.Flags, all bool) []string {
	var list []string
	rv := reflect.ValueOf(exp).Elem()
	var rBase reflect.Value
	if base != nil {
		rBase = reflect.ValueOf(base).Elem()
	}
	rt := rv.Type()
	for i := 0; i < rt.NumField(); i++ {
		name := strings.ToLower(rt.Field(i).Name)
		val := rv.Field(i).Bool()
		baseVal := false
		if base != nil {
			baseVal = rBase.Field(i).Bool()
		}
		if all || val != baseVal {
			if val {
				list = append(list, name)
			} else {
				list = append(list, "no"+name)
			}
		}
	}
	return list
}

// Enabled returns a list of enabled experiments, as
// lower-cased experiment names.
func (exp *ExperimentFlags) Enabled() []string {
	return expList(&exp.Flags, nil, false)
}

// All returns a list of all experiment settings.
// Disabled experiments appear in the list prefixed by "no".
func (exp *ExperimentFlags) All() []string {
	return expList(&exp.Flags, nil, true)
}
<|endoftext|>


<commit_before>package rtnetlink

import (
	"errors"
	"fmt"
	"net"

	"github.com/mdlayher/netlink"
	"github.com/mdlayher/netlink/nlenc"
)

var (
	// errInvalidLinkMessage is returned when a LinkMessage is malformed.
	errInvalidLinkMessage = errors.New("rtnetlink LinkMessage is invalid or too short")

	// errInvalidLinkMessageAttr is returned when link attributes are malformed.
	errInvalidLinkMessageAttr = errors.New("rtnetlink LinkMessage has a wrong attribute data length")
)

var _ Message = &LinkMessage{}

// A LinkMessage is a route netlink link message.
type LinkMessage struct {
	// Always set to AF_UNSPEC (0)
	Family uint16

	// Device Type
	Type uint16

	// Unique interface index, using a nonzero value with
	// NewLink will instruct the kernel to create a
	// device with the given index (kernel 3.7+ required)
	Index uint32

	// Contains device flags, see netdevice(7)
	Flags uint32

	// Change Flags, reserved for future use and should
	// always be 0xffffffff
	Change uint32

	// Attributes List
	Attributes LinkAttributes
}

const linkMessageLength = 16

// MarshalBinary marshals a LinkMessage into a byte slice.
func (m *LinkMessage) MarshalBinary() ([]byte, error) {
	b := make([]byte, linkMessageLength)

	b[0] = 0 //Family
	b[1] = 0 //reserved
	nlenc.PutUint16(b[2:4], m.Type)
	nlenc.PutUint32(b[4:8], m.Index)
	nlenc.PutUint32(b[8:12], m.Flags)
	nlenc.PutUint32(b[12:16], 0) //Change, reserved

	a, err := m.Attributes.MarshalBinary()
	if err != nil {
		return nil, err
	}

	return append(b, a...), nil
}

// UnmarshalBinary unmarshals the contents of a byte slice into a LinkMessage.
func (m *LinkMessage) UnmarshalBinary(b []byte) error {
	l := len(b)
	if l < 16 {
		return errInvalidLinkMessage
	}

	m.Family = nlenc.Uint16(b[0:2])
	m.Type = nlenc.Uint16(b[2:4])
	m.Index = nlenc.Uint32(b[4:8])
	m.Flags = nlenc.Uint32(b[8:12])
	m.Change = nlenc.Uint32(b[12:16])

	if l > 16 {
		m.Attributes = LinkAttributes{}
		err := m.Attributes.UnmarshalBinary(b[16:])
		if err != nil {
			return err
		}
	}

	return nil
}

// rtMessage is an empty method to sattisfy the Message interface.
func (*LinkMessage) rtMessage() {}

// LinkService is used to retrieve rtnetlink family information.
type LinkService struct {
	c *Conn
}

// Constants used to request information from rtnetlink links.
const (
	rtmNewLink = 16
	rtmDelLink = 17
	rtmGetLink = 18
	rtmSetLink = 19
)

// New creates a new interface using the LinkMessage information.
func (l *LinkService) New(req *LinkMessage) error {
	flags := netlink.HeaderFlagsRequest
	_, err := l.c.Send(req, rtmSetLink, flags)
	if err != nil {
		return err
	}

	return nil
}

// Delete removes an interface by index.
func (l *LinkService) Delete(index uint32) error {
	req := &LinkMessage{
		Index: index,
	}

	flags := netlink.HeaderFlagsRequest
	_, err := l.c.Send(req, rtmDelLink, flags)
	if err != nil {
		return err
	}

	return nil
}

// Get retrieves interface information by index.
func (l *LinkService) Get(index uint32) (LinkMessage, error) {
	req := &LinkMessage{
		Index: index,
	}

	flags := netlink.HeaderFlagsRequest | netlink.HeaderFlagsDumpFiltered
	msg, err := l.c.Execute(req, rtmGetLink, flags)
	if err != nil {
		return LinkMessage{}, err
	}

	if len(msg) > 1 {
		return LinkMessage{}, fmt.Errorf("too many matches")
	}

	link := (msg[0]).(*LinkMessage)
	return *link, nil
}

// Set sets interface attributes according to the LinkMessage information.
func (l *LinkService) Set(req *LinkMessage) error {
	flags := netlink.HeaderFlagsRequest
	_, err := l.c.Send(req, rtmSetLink, flags)
	if err != nil {
		return err
	}

	return nil
}

// List retrieves all interfaces.
func (l *LinkService) List() ([]LinkMessage, error) {
	req := &LinkMessage{}

	flags := netlink.HeaderFlagsRequest | netlink.HeaderFlagsDump
	msgs, err := l.c.Execute(req, rtmGetLink, flags)
	if err != nil {
		return nil, err
	}

	links := make([]LinkMessage, 0, len(msgs))
	for _, m := range msgs {
		link := (m).(*LinkMessage)
		links = append(links, *link)
	}

	return links, nil
}

// LinkAttributes contains all attributes for an interface.
type LinkAttributes struct {
	Address   net.HardwareAddr // Interface L2 address
	Broadcast net.HardwareAddr // L2 broadcast address
	Name      string           // Device name
	MTU       uint32           // MTU of the device
	Type      uint32           // Link type
	QueueDisc string           // Queueing discipline
	Stats     *LinkStats       // Interface Statistics
}

// Attribute IDs mapped to specific LinkAttribute fields.
const (
	iflaUnspec uint16 = iota
	iflaAddress
	iflaBroadcast
	iflaIfname
	iflaMTU
	iflaLink
	iflaQdisc
	iflaStats
)

// UnmarshalBinary unmarshals the contents of a byte slice into a LinkMessage.
func (a *LinkAttributes) UnmarshalBinary(b []byte) error {
	attrs, err := netlink.UnmarshalAttributes(b)
	if err != nil {
		return err
	}

	for _, attr := range attrs {
		switch attr.Type {
		case iflaUnspec:
			//unused attribute
		case iflaAddress:
			if len(attr.Data) != 6 {
				return errInvalidLinkMessageAttr
			}
			a.Address = attr.Data
		case iflaBroadcast:
			if len(attr.Data) != 6 {
				return errInvalidLinkMessageAttr
			}
			a.Broadcast = attr.Data
		case iflaIfname:
			a.Name = nlenc.String(attr.Data)
		case iflaMTU:
			if len(attr.Data) != 4 {
				return errInvalidLinkMessageAttr
			}
			a.MTU = nlenc.Uint32(attr.Data)
		case iflaLink:
			if len(attr.Data) != 4 {
				return errInvalidLinkMessageAttr
			}
			a.Type = nlenc.Uint32(attr.Data)
		case iflaQdisc:
			a.QueueDisc = nlenc.String(attr.Data)
		case iflaStats:
			a.Stats = &LinkStats{}
			err := a.Stats.UnmarshalBinary(attr.Data)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func uint16tobyte(v uint16) []byte {
	b := make([]byte, 2)
	nlenc.PutUint16(b, v)
	return b
}

func uint32tobyte(v uint32) []byte {
	b := make([]byte, 4)
	nlenc.PutUint32(b, v)
	return b
}

// MarshalBinary marshals a LinkAttributes into a byte slice.
func (a *LinkAttributes) MarshalBinary() ([]byte, error) {
	return netlink.MarshalAttributes([]netlink.Attribute{
		{
			Type: iflaUnspec,
			Data: uint16tobyte(0),
		},
		{
			Type: iflaAddress,
			Data: a.Address,
		},
		{
			Type: iflaBroadcast,
			Data: a.Broadcast,
		},
		{
			Type: iflaIfname,
			Data: nlenc.Bytes(a.Name),
		},
		{
			Type: iflaMTU,
			Data: uint32tobyte(a.MTU),
		},
		{
			Type: iflaLink,
			Data: uint32tobyte(a.Type),
		},
		{
			Type: iflaQdisc,
			Data: nlenc.Bytes(a.QueueDisc),
		},
		/*
			{
				Type: iflaStats,
				Data: nlenc.Bytes(name),
			},
		*/
	})
}

//LinkStats contains packet statistics
type LinkStats struct {
	RXPackets  uint32 // total packets received
	TXPackets  uint32 // total packets transmitted
	RXBytes    uint32 // total bytes received
	TXBytes    uint32 // total bytes transmitted
	RXErrors   uint32 // bad packets received
	TXErrors   uint32 // packet transmit problems
	RXDropped  uint32 // no space in linux buffers
	TXDropped  uint32 // no space available in linux
	Multicast  uint32 // multicast packets received
	Collisions uint32

	// detailed rx_errors:
	RXLengthErrors uint32
	RXOverErrors   uint32 // receiver ring buff overflow
	RXCRCErrors    uint32 // recved pkt with crc error
	RXFrameErrors  uint32 // recv'd frame alignment error
	RXFIFOErrors   uint32 // recv'r fifo overrun
	RXMissedErrors uint32 // receiver missed packet

	// detailed tx_errors
	TXAbortedErrors   uint32
	TXCarrierErrors   uint32
	TXFIFOErrors      uint32
	TXHeartbeatErrors uint32
	TXWindowErrors    uint32

	// for cslip etc
	RXCompressed uint32
	TXCompressed uint32

	RXNoHandler uint32 // dropped, no handler found
}

// UnmarshalBinary unmarshals the contents of a byte slice into a LinkMessage.
func (a *LinkStats) UnmarshalBinary(b []byte) error {
	if len(b) != 96 && len(b) != 104 {
		return fmt.Errorf("incorrect size, want: 96 or 104, got: %d", len(b))
	}

	a.RXPackets = nlenc.Uint32(b[0:4])
	a.TXPackets = nlenc.Uint32(b[4:8])
	a.RXBytes = nlenc.Uint32(b[8:12])
	a.TXBytes = nlenc.Uint32(b[12:16])
	a.RXErrors = nlenc.Uint32(b[16:20])
	a.TXErrors = nlenc.Uint32(b[20:24])
	a.RXDropped = nlenc.Uint32(b[24:28])
	a.TXDropped = nlenc.Uint32(b[28:32])
	a.Multicast = nlenc.Uint32(b[32:36])
	a.Collisions = nlenc.Uint32(b[36:40])

	a.RXLengthErrors = nlenc.Uint32(b[40:44])
	a.RXOverErrors = nlenc.Uint32(b[44:48])
	a.RXCRCErrors = nlenc.Uint32(b[48:52])
	a.RXFrameErrors = nlenc.Uint32(b[52:56])
	a.RXFIFOErrors = nlenc.Uint32(b[56:60])
	a.RXMissedErrors = nlenc.Uint32(b[60:64])

	a.TXAbortedErrors = nlenc.Uint32(b[68:72])
	a.TXCarrierErrors = nlenc.Uint32(b[76:80])
	a.TXFIFOErrors = nlenc.Uint32(b[80:84])
	a.TXHeartbeatErrors = nlenc.Uint32(b[84:88])
	a.TXWindowErrors = nlenc.Uint32(b[88:92])

	if len(b) == 96 {
		a.RXNoHandler = nlenc.Uint32(b[92:96])
	}

	if len(b) == 104 {
		a.RXCompressed = nlenc.Uint32(b[92:96])
		a.TXCompressed = nlenc.Uint32(b[96:100])
		a.RXNoHandler = nlenc.Uint32(b[100:104])
	}

	return nil
}
<commit_msg>link: additional slice length check<commit_after>package rtnetlink

import (
	"errors"
	"fmt"
	"net"

	"github.com/mdlayher/netlink"
	"github.com/mdlayher/netlink/nlenc"
)

var (
	// errInvalidLinkMessage is returned when a LinkMessage is malformed.
	errInvalidLinkMessage = errors.New("rtnetlink LinkMessage is invalid or too short")

	// errInvalidLinkMessageAttr is returned when link attributes are malformed.
	errInvalidLinkMessageAttr = errors.New("rtnetlink LinkMessage has a wrong attribute data length")
)

var _ Message = &LinkMessage{}

// A LinkMessage is a route netlink link message.
type LinkMessage struct {
	// Always set to AF_UNSPEC (0)
	Family uint16

	// Device Type
	Type uint16

	// Unique interface index, using a nonzero value with
	// NewLink will instruct the kernel to create a
	// device with the given index (kernel 3.7+ required)
	Index uint32

	// Contains device flags, see netdevice(7)
	Flags uint32

	// Change Flags, reserved for future use and should
	// always be 0xffffffff
	Change uint32

	// Attributes List
	Attributes LinkAttributes
}

const linkMessageLength = 16

// MarshalBinary marshals a LinkMessage into a byte slice.
func (m *LinkMessage) MarshalBinary() ([]byte, error) {
	b := make([]byte, linkMessageLength)

	b[0] = 0 //Family
	b[1] = 0 //reserved
	nlenc.PutUint16(b[2:4], m.Type)
	nlenc.PutUint32(b[4:8], m.Index)
	nlenc.PutUint32(b[8:12], m.Flags)
	nlenc.PutUint32(b[12:16], 0) //Change, reserved

	a, err := m.Attributes.MarshalBinary()
	if err != nil {
		return nil, err
	}

	return append(b, a...), nil
}

// UnmarshalBinary unmarshals the contents of a byte slice into a LinkMessage.
func (m *LinkMessage) UnmarshalBinary(b []byte) error {
	l := len(b)
	if l < 16 {
		return errInvalidLinkMessage
	}

	m.Family = nlenc.Uint16(b[0:2])
	m.Type = nlenc.Uint16(b[2:4])
	m.Index = nlenc.Uint32(b[4:8])
	m.Flags = nlenc.Uint32(b[8:12])
	m.Change = nlenc.Uint32(b[12:16])

	if l > 16 {
		m.Attributes = LinkAttributes{}
		err := m.Attributes.UnmarshalBinary(b[16:])
		if err != nil {
			return err
		}
	}

	return nil
}

// rtMessage is an empty method to sattisfy the Message interface.
func (*LinkMessage) rtMessage() {}

// LinkService is used to retrieve rtnetlink family information.
type LinkService struct {
	c *Conn
}

// Constants used to request information from rtnetlink links.
const (
	rtmNewLink = 16
	rtmDelLink = 17
	rtmGetLink = 18
	rtmSetLink = 19
)

// New creates a new interface using the LinkMessage information.
func (l *LinkService) New(req *LinkMessage) error {
	flags := netlink.HeaderFlagsRequest
	_, err := l.c.Send(req, rtmSetLink, flags)
	if err != nil {
		return err
	}

	return nil
}

// Delete removes an interface by index.
func (l *LinkService) Delete(index uint32) error {
	req := &LinkMessage{
		Index: index,
	}

	flags := netlink.HeaderFlagsRequest
	_, err := l.c.Send(req, rtmDelLink, flags)
	if err != nil {
		return err
	}

	return nil
}

// Get retrieves interface information by index.
func (l *LinkService) Get(index uint32) (LinkMessage, error) {
	req := &LinkMessage{
		Index: index,
	}

	flags := netlink.HeaderFlagsRequest | netlink.HeaderFlagsDumpFiltered
	msg, err := l.c.Execute(req, rtmGetLink, flags)
	if err != nil {
		return LinkMessage{}, err
	}

	if len(msg) != 1 {
		return LinkMessage{}, fmt.Errorf("too many/little matches, expected 1")
	}

	link := (msg[0]).(*LinkMessage)
	return *link, nil
}

// Set sets interface attributes according to the LinkMessage information.
func (l *LinkService) Set(req *LinkMessage) error {
	flags := netlink.HeaderFlagsRequest
	_, err := l.c.Send(req, rtmSetLink, flags)
	if err != nil {
		return err
	}

	return nil
}

// List retrieves all interfaces.
func (l *LinkService) List() ([]LinkMessage, error) {
	req := &LinkMessage{}

	flags := netlink.HeaderFlagsRequest | netlink.HeaderFlagsDump
	msgs, err := l.c.Execute(req, rtmGetLink, flags)
	if err != nil {
		return nil, err
	}

	links := make([]LinkMessage, 0, len(msgs))
	for _, m := range msgs {
		link := (m).(*LinkMessage)
		links = append(links, *link)
	}

	return links, nil
}

// LinkAttributes contains all attributes for an interface.
type LinkAttributes struct {
	Address   net.HardwareAddr // Interface L2 address
	Broadcast net.HardwareAddr // L2 broadcast address
	Name      string           // Device name
	MTU       uint32           // MTU of the device
	Type      uint32           // Link type
	QueueDisc string           // Queueing discipline
	Stats     *LinkStats       // Interface Statistics
}

// Attribute IDs mapped to specific LinkAttribute fields.
const (
	iflaUnspec uint16 = iota
	iflaAddress
	iflaBroadcast
	iflaIfname
	iflaMTU
	iflaLink
	iflaQdisc
	iflaStats
)

// UnmarshalBinary unmarshals the contents of a byte slice into a LinkMessage.
func (a *LinkAttributes) UnmarshalBinary(b []byte) error {
	attrs, err := netlink.UnmarshalAttributes(b)
	if err != nil {
		return err
	}

	for _, attr := range attrs {
		switch attr.Type {
		case iflaUnspec:
			//unused attribute
		case iflaAddress:
			if len(attr.Data) != 6 {
				return errInvalidLinkMessageAttr
			}
			a.Address = attr.Data
		case iflaBroadcast:
			if len(attr.Data) != 6 {
				return errInvalidLinkMessageAttr
			}
			a.Broadcast = attr.Data
		case iflaIfname:
			a.Name = nlenc.String(attr.Data)
		case iflaMTU:
			if len(attr.Data) != 4 {
				return errInvalidLinkMessageAttr
			}
			a.MTU = nlenc.Uint32(attr.Data)
		case iflaLink:
			if len(attr.Data) != 4 {
				return errInvalidLinkMessageAttr
			}
			a.Type = nlenc.Uint32(attr.Data)
		case iflaQdisc:
			a.QueueDisc = nlenc.String(attr.Data)
		case iflaStats:
			a.Stats = &LinkStats{}
			err := a.Stats.UnmarshalBinary(attr.Data)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func uint16tobyte(v uint16) []byte {
	b := make([]byte, 2)
	nlenc.PutUint16(b, v)
	return b
}

func uint32tobyte(v uint32) []byte {
	b := make([]byte, 4)
	nlenc.PutUint32(b, v)
	return b
}

// MarshalBinary marshals a LinkAttributes into a byte slice.
func (a *LinkAttributes) MarshalBinary() ([]byte, error) {
	return netlink.MarshalAttributes([]netlink.Attribute{
		{
			Type: iflaUnspec,
			Data: uint16tobyte(0),
		},
		{
			Type: iflaAddress,
			Data: a.Address,
		},
		{
			Type: iflaBroadcast,
			Data: a.Broadcast,
		},
		{
			Type: iflaIfname,
			Data: nlenc.Bytes(a.Name),
		},
		{
			Type: iflaMTU,
			Data: uint32tobyte(a.MTU),
		},
		{
			Type: iflaLink,
			Data: uint32tobyte(a.Type),
		},
		{
			Type: iflaQdisc,
			Data: nlenc.Bytes(a.QueueDisc),
		},
		/*
			{
				Type: iflaStats,
				Data: nlenc.Bytes(name),
			},
		*/
	})
}

//LinkStats contains packet statistics
type LinkStats struct {
	RXPackets  uint32 // total packets received
	TXPackets  uint32 // total packets transmitted
	RXBytes    uint32 // total bytes received
	TXBytes    uint32 // total bytes transmitted
	RXErrors   uint32 // bad packets received
	TXErrors   uint32 // packet transmit problems
	RXDropped  uint32 // no space in linux buffers
	TXDropped  uint32 // no space available in linux
	Multicast  uint32 // multicast packets received
	Collisions uint32

	// detailed rx_errors:
	RXLengthErrors uint32
	RXOverErrors   uint32 // receiver ring buff overflow
	RXCRCErrors    uint32 // recved pkt with crc error
	RXFrameErrors  uint32 // recv'd frame alignment error
	RXFIFOErrors   uint32 // recv'r fifo overrun
	RXMissedErrors uint32 // receiver missed packet

	// detailed tx_errors
	TXAbortedErrors   uint32
	TXCarrierErrors   uint32
	TXFIFOErrors      uint32
	TXHeartbeatErrors uint32
	TXWindowErrors    uint32

	// for cslip etc
	RXCompressed uint32
	TXCompressed uint32

	RXNoHandler uint32 // dropped, no handler found
}

// UnmarshalBinary unmarshals the contents of a byte slice into a LinkMessage.
func (a *LinkStats) UnmarshalBinary(b []byte) error {
	if len(b) != 96 && len(b) != 104 {
		return fmt.Errorf("incorrect size, want: 96 or 104, got: %d", len(b))
	}

	a.RXPackets = nlenc.Uint32(b[0:4])
	a.TXPackets = nlenc.Uint32(b[4:8])
	a.RXBytes = nlenc.Uint32(b[8:12])
	a.TXBytes = nlenc.Uint32(b[12:16])
	a.RXErrors = nlenc.Uint32(b[16:20])
	a.TXErrors = nlenc.Uint32(b[20:24])
	a.RXDropped = nlenc.Uint32(b[24:28])
	a.TXDropped = nlenc.Uint32(b[28:32])
	a.Multicast = nlenc.Uint32(b[32:36])
	a.Collisions = nlenc.Uint32(b[36:40])

	a.RXLengthErrors = nlenc.Uint32(b[40:44])
	a.RXOverErrors = nlenc.Uint32(b[44:48])
	a.RXCRCErrors = nlenc.Uint32(b[48:52])
	a.RXFrameErrors = nlenc.Uint32(b[52:56])
	a.RXFIFOErrors = nlenc.Uint32(b[56:60])
	a.RXMissedErrors = nlenc.Uint32(b[60:64])

	a.TXAbortedErrors = nlenc.Uint32(b[68:72])
	a.TXCarrierErrors = nlenc.Uint32(b[76:80])
	a.TXFIFOErrors = nlenc.Uint32(b[80:84])
	a.TXHeartbeatErrors = nlenc.Uint32(b[84:88])
	a.TXWindowErrors = nlenc.Uint32(b[88:92])

	if len(b) == 96 {
		a.RXNoHandler = nlenc.Uint32(b[92:96])
	}

	if len(b) == 104 {
		a.RXCompressed = nlenc.Uint32(b[92:96])
		a.TXCompressed = nlenc.Uint32(b[96:100])
		a.RXNoHandler = nlenc.Uint32(b[100:104])
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strings"
	"unicode"
)

// Atom, List, Sexper
type Atom struct{ name string }
type List struct{ car, cdr Sexper }
type Sexper interface {
	Atomp() bool
	Listp() bool
	String() string
}

// Atom is a Sexper
func (this Atom) Atomp() bool    { return true }
func (this Atom) Listp() bool    { return false }
func (this Atom) String() string { return this.name }

// List is a Sexper
func (this List) Atomp() bool { return false }
func (this List) Listp() bool { return true }
func (this List) String() string {
	scar, scdr := this.car.String(), this.cdr.String()
	switch {
	case scdr[0] == '(':
		return strings.Join([]string{"(", scar, " ", scdr[1 : len(scdr)-1], ")"}, "")
	case scdr == "nil":
		return strings.Join([]string{"(", scar, ")"}, "")
	default:
		return strings.Join([]string{"(", scar, " . ", scdr, ")"}, "")
	}
}

// like in.ReadRune() but ignore all leading whitespace.
func ReadChar(in io.RuneScanner) (r rune, size int, err error) {
	r, size, err = in.ReadRune()
	for unicode.IsSpace(r) && err == nil {
		r, size, err = in.ReadRune()
	}
	return
}

// sexp reader
func ReadAtom(in io.RuneScanner) Sexper {
	ch, _, _ := ReadChar(in)
	chstr := string(ch)

	buffer := make([]rune, 128)
	var i int
	for i = 0; !unicode.IsSpace(ch) && chstr != "(" && chstr != ")"; i++ {
		buffer[i] = ch
		ch, _, _ = in.ReadRune()
		chstr = string(ch)
	}
	if chstr == "(" || chstr == ")" {
		in.UnreadRune()
	}
	return Atom{string(buffer[0:i])}
}

func ReadList(in io.RuneScanner) Sexper {
	ch, _, _ := ReadChar(in)
	chstr := string(ch)

	switch chstr {
	case ")":
		return Atom{"nil"}
	default:
		in.UnreadRune()
		scar := ReadSexp(in)
		scdr := ReadList(in)
		return List{scar, scdr}
	}
}

func ReadSexp(in io.RuneScanner) Sexper {
	ch, _, _ := ReadChar(in)

	switch string(ch) {
	case "(":
		return ReadList(in)
	case ")":
		panic("unexcepted ')' found")
	default:
		in.UnreadRune()
		return ReadAtom(in)
	}
}

func main() {
	atomn := Atom{"nil"}
	atom1 := Atom{"atom1"}
	atom2 := Atom{"atom2"}
	atom3 := Atom{"atom3"}
	list1 := List{Sexper(&atom1), Sexper(&atom2)}
	list2 := List{Sexper(&atom3), Sexper(&atomn)}
	list3 := List{Sexper(&list1), Sexper(&list2)}
	list4 := List{Sexper(&list2), Sexper(&list3)}
	fmt.Println(list1, list2, list3, list4)
	in := bufio.NewReader(os.Stdin)
	for {
		sexp := ReadSexp(in)
		fmt.Println("==================== sexp:", sexp)
	}
}
<commit_msg>Lisp in go interpreter: add `.', `'' syntax reader and seven basic functions.<commit_after>package main

import (
	// "bufio"
	"fmt"
	"io"
	// "os"
	"strings"
	"unicode"
)

// Atom, List, Sexper
type Atom struct{ name string }
type List struct{ car, cdr Sexper }
type Sexper interface {
	Atomp() bool
	Listp() bool
	String() string
}

// Atom is a Sexper
func (this Atom) Atomp() bool    { return true }
func (this Atom) Listp() bool    { return false }
func (this Atom) String() string { return this.name }

// List is a Sexper
func (this List) Atomp() bool { return false }
func (this List) Listp() bool { return true }
func (this List) String() string {
	scar, scdr := this.car.String(), this.cdr.String()
	switch {
	case scdr[0] == '(':
		return strings.Join([]string{"(", scar, " ", scdr[1 : len(scdr)-1], ")"}, "")
	case scdr == "nil":
		return strings.Join([]string{"(", scar, ")"}, "")
	default:
		return strings.Join([]string{"(", scar, " . ", scdr, ")"}, "")
	}
}

var (
	NIL = Atom{"nil"}
	TEE = Atom{"t"}
)

// like in.ReadRune() but ignore all leading whitespace.
func ReadChar(in io.RuneScanner) (r rune, size int, err error) {
	r, size, err = in.ReadRune()
	for unicode.IsSpace(r) && err == nil {
		r, size, err = in.ReadRune()
	}
	return
}

// sexp reader
func ReadAtom(in io.RuneScanner) Sexper {
	ch, _, _ := ReadChar(in)
	chstr := string(ch)

	buffer := make([]rune, 128)
	var i int
	for i = 0; !unicode.IsSpace(ch) && chstr != "(" && chstr != ")"; i++ {
		buffer[i] = ch
		ch, _, _ = in.ReadRune()
		chstr = string(ch)
	}
	if chstr == "(" || chstr == ")" {
		in.UnreadRune()
	}
	return Atom{string(buffer[0:i])}
}

func ReadList(in io.RuneScanner) Sexper {
	ch, _, _ := ReadChar(in)
	chstr := string(ch)

	switch chstr {
	case ")":
		return NIL
	case ".":
		return ReadAtom(in)
	default:
		in.UnreadRune()
		scar := ReadSexp(in)
		scdr := ReadList(in)
		return List{scar, scdr}
	}
}

func ReadSexp(in io.RuneScanner) Sexper {
	ch, _, _ := ReadChar(in)

	switch string(ch) {
	case "(":
		return ReadList(in)
	case "'":
		return fn_list(Atom{"quote"}, ReadSexp(in))
	case ")":
		panic("unexcepted ')' found")
	default:
		in.UnreadRune()
		return ReadAtom(in)
	}
}

func ReadFrom(str string) Sexper {
	return ReadSexp(strings.NewReader(str))
}

// seven basic functions
func fn_quote(sexp Sexper) Sexper {
	return sexp
}

func fn_atom(sexp Sexper) Sexper {
	if sexp.Atomp() {
		return TEE
	} else {
		return NIL
	}
}

func fn_eq(s1, s2 Sexper) Sexper {
	if s1 == s2 {
		return TEE
	} else {
		return NIL
	}
}

func fn_car(sexp Sexper) Sexper {
	return sexp.(List).car
}

func fn_cdr(sexp Sexper) Sexper {
	return sexp.(List).cdr
}

func fn_cons(s1, s2 Sexper) Sexper {
	return List{s1, s2}
}

func fn_list(s1, s2 Sexper) Sexper {
	return List{s1, List{s2, NIL}}
}

func fn_cond(ss ...Sexper) Sexper {
	for _, list := range ss {
		if fn_car(list) != NIL {
			return fn_car(fn_cdr(list))
		}
	}
	return NIL
}

func main() {
	atomn := NIL
	atom1 := Atom{"atom1"}
	atom2 := Atom{"atom2"}
	atom3 := Atom{"atom3"}
	list1 := List{Sexper(&atom1), Sexper(&atom2)}
	list2 := List{Sexper(&atom3), Sexper(&atomn)}
	list3 := List{Sexper(&list1), Sexper(&list2)}
	list4 := List{Sexper(&list2), Sexper(&list3)}
	fmt.Println(list1, list2, list3, list4)
	hello_world := ReadFrom("'(hello . world)")
	fmt.Println(hello_world, fn_car(hello_world), fn_cdr(hello_world))
	fmt.Println(fn_cons(Atom{"hello"}, fn_cons(Atom{"emacs"}, NIL)))

	// env := List{fn_cons(Atom{"hello"}, Atom{"world"}), NIL}
	// fmt.Println(env.Get(Atom{"hello"}))
	// fmt.Println(env.Set(Atom{"os"}, Atom{"mac"}))
	// fmt.Println(env.Set(Atom{"os"}, Atom{"mac"}).Get(Atom{"os"}))
}
<|endoftext|>


<commit_before>// +build linux

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"text/tabwriter"
	"time"

	"encoding/json"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
)

const formatOptions = `table or json`

// containerState represents the platform agnostic pieces relating to a
// running container's status and state
type containerState struct {
	// ID is the container ID
	ID string `json:"id"`
	// InitProcessPid is the init process id in the parent namespace
	InitProcessPid int `json:"pid"`
	// Status is the current status of the container, running, paused, ...
	Status string `json:"status"`
	// Bundle is the path on the filesystem to the bundle
	Bundle string `json:"bundle"`
	// Created is the unix timestamp for the creation time of the container in UTC
	Created time.Time `json:"created"`
}

var listCommand = cli.Command{
	Name:  "list",
	Usage: "lists containers started by runc with the given root",
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "format, f",
			Value: "",
			Usage: `select one of: ` + formatOptions + `.

The default format is table.  The following will output the list of containers
in json format:

    # runc list -f json`,
		},
	},
	Action: func(context *cli.Context) {
		s, err := getContainers(context)
		if err != nil {
			logrus.Fatal(err)
		}

		switch context.String("format") {
		case "", "table":
			w := tabwriter.NewWriter(os.Stdout, 12, 1, 3, ' ', 0)
			fmt.Fprint(w, "ID\tPID\tSTATUS\tBUNDLE\tCREATED\n")
			for _, item := range s {
				fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\n",
					item.ID,
					item.InitProcessPid,
					item.Status,
					item.Bundle,
					item.Created.Format(time.RFC3339Nano))
			}
			if err := w.Flush(); err != nil {
				logrus.Fatal(err)
			}
		case "json":
			data, err := json.Marshal(s)
			if err != nil {
				logrus.Fatal(err)
			}
			os.Stdout.Write(data)

		default:
			logrus.Fatal("invalid format option")
		}
	},
}

func getContainers(context *cli.Context) ([]containerState, error) {
	factory, err := loadFactory(context)
	if err != nil {
		return nil, err
	}
	root := context.GlobalString("root")
	absRoot, err := filepath.Abs(root)
	if err != nil {
		return nil, err
	}
	list, err := ioutil.ReadDir(absRoot)
	if err != nil {
		logrus.Fatal(err)
	}

	var s []containerState
	for _, item := range list {
		if item.IsDir() {
			container, err := factory.Load(item.Name())
			if err != nil {
				return nil, err
			}
			containerStatus, err := container.Status()
			if err != nil {
				return nil, err
			}
			state, err := container.State()
			if err != nil {
				return nil, err
			}
			s = append(s, containerState{
				ID:             state.BaseState.ID,
				InitProcessPid: state.BaseState.InitProcessPid,
				Status:         containerStatus.String(),
				Bundle:         searchLabels(state.Config.Labels, "bundle"),
				Created:        state.BaseState.Created})
		}
	}
	return s, nil
}

func searchLabels(labels []string, query string) string {
	for _, l := range labels {
		parts := strings.SplitN(l, "=", 2)
		if len(parts) < 2 {
			continue
		}
		if parts[0] == query {
			return parts[1]
		}
	}
	return ""
}
<commit_msg>Changing from logrus to fatal in list<commit_after>// +build linux

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"text/tabwriter"
	"time"

	"encoding/json"

	"github.com/codegangsta/cli"
)

const formatOptions = `table or json`

// containerState represents the platform agnostic pieces relating to a
// running container's status and state
type containerState struct {
	// ID is the container ID
	ID string `json:"id"`
	// InitProcessPid is the init process id in the parent namespace
	InitProcessPid int `json:"pid"`
	// Status is the current status of the container, running, paused, ...
	Status string `json:"status"`
	// Bundle is the path on the filesystem to the bundle
	Bundle string `json:"bundle"`
	// Created is the unix timestamp for the creation time of the container in UTC
	Created time.Time `json:"created"`
}

var listCommand = cli.Command{
	Name:  "list",
	Usage: "lists containers started by runc with the given root",
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "format, f",
			Value: "",
			Usage: `select one of: ` + formatOptions + `.

The default format is table.  The following will output the list of containers
in json format:

    # runc list -f json`,
		},
	},
	Action: func(context *cli.Context) {
		s, err := getContainers(context)
		if err != nil {
			fatal(err)
		}

		switch context.String("format") {
		case "", "table":
			w := tabwriter.NewWriter(os.Stdout, 12, 1, 3, ' ', 0)
			fmt.Fprint(w, "ID\tPID\tSTATUS\tBUNDLE\tCREATED\n")
			for _, item := range s {
				fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\n",
					item.ID,
					item.InitProcessPid,
					item.Status,
					item.Bundle,
					item.Created.Format(time.RFC3339Nano))
			}
			if err := w.Flush(); err != nil {
				fatal(err)
			}
		case "json":
			data, err := json.Marshal(s)
			if err != nil {
				fatal(err)
			}
			os.Stdout.Write(data)

		default:
			fatalf("invalid format option")
		}
	},
}

func getContainers(context *cli.Context) ([]containerState, error) {
	factory, err := loadFactory(context)
	if err != nil {
		return nil, err
	}
	root := context.GlobalString("root")
	absRoot, err := filepath.Abs(root)
	if err != nil {
		return nil, err
	}
	list, err := ioutil.ReadDir(absRoot)
	if err != nil {
		fatal(err)
	}

	var s []containerState
	for _, item := range list {
		if item.IsDir() {
			container, err := factory.Load(item.Name())
			if err != nil {
				return nil, err
			}
			containerStatus, err := container.Status()
			if err != nil {
				return nil, err
			}
			state, err := container.State()
			if err != nil {
				return nil, err
			}
			s = append(s, containerState{
				ID:             state.BaseState.ID,
				InitProcessPid: state.BaseState.InitProcessPid,
				Status:         containerStatus.String(),
				Bundle:         searchLabels(state.Config.Labels, "bundle"),
				Created:        state.BaseState.Created})
		}
	}
	return s, nil
}

func searchLabels(labels []string, query string) string {
	for _, l := range labels {
		parts := strings.SplitN(l, "=", 2)
		if len(parts) < 2 {
			continue
		}
		if parts[0] == query {
			return parts[1]
		}
	}
	return ""
}
<|endoftext|>