<commit_before>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// The version package implements version parsing.
// It also acts as guardian of the current client Juju version number.
package version

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"

	"labix.org/v2/mgo/bson"
)

// The presence and format of this constant is very important.
// The debian/rules build recipe uses this value for the version
// number of the release package.
const version = "1.11.3"

// CurrentNumber returns the version number.
func CurrentNumber() Number {
	return MustParse(version)
}

// CurrentSeries returns the current Ubuntu release name.
func CurrentSeries() string {
	return readSeries("/etc/lsb-release")
}

// CurrentArch returns the architecture of the machine.
func CurrentArch() string {
	return ubuntuArch(runtime.GOARCH)
}

// Current gives the current version of the system.  If the file
// "FORCE-VERSION" is present in the same directory as the running
// binary, it will override this.
var Current = Binary{
	Number: CurrentNumber(),
	Series: CurrentSeries(),
	Arch:   CurrentArch(),
}

func init() {
	toolsDir := filepath.Dir(os.Args[0])
	v, err := ioutil.ReadFile(filepath.Join(toolsDir, "FORCE-VERSION"))
	if err != nil {
		if os.IsNotExist(err) {
			return
		}
		panic(fmt.Errorf("version: cannot read forced version: %v", err))
	}
	Current.Number = MustParse(strings.TrimSpace(string(v)))
}

// Number represents a juju version.  When bugs are fixed the patch
// number is incremented; when new features are added the minor number
// is incremented and patch is reset; and when compatibility is broken
// the major version is incremented and minor and patch are reset.  The
// build number is automatically assigned and has no well defined
// sequence.  If the build number is greater than zero or the minor
// version is odd, it indicates that the release is still in
// development.
type Number struct {
	Major int
	Minor int
	Patch int
	Build int
}

// Zero is occasionally convenient and readable.
// Please don't change its value.
var Zero = Number{}

// Binary specifies a binary version of juju.
type Binary struct {
	Number
	Series string
	Arch   string
}

func (v Binary) String() string {
	return fmt.Sprintf("%v-%s-%s", v.Number, v.Series, v.Arch)
}

// GetBSON turns v into a bson.Getter so it can be saved directly
// on a MongoDB database with mgo.
func (v Binary) GetBSON() (interface{}, error) {
	return v.String(), nil
}

// SetBSON turns v into a bson.Setter so it can be loaded directly
// from a MongoDB database with mgo.
func (vp *Binary) SetBSON(raw bson.Raw) error {
	var s string
	err := raw.Unmarshal(&s)
	if err != nil {
		return err
	}
	v, err := ParseBinary(s)
	if err != nil {
		return err
	}
	*vp = v
	return nil
}

var (
	binaryPat = regexp.MustCompile(`^(\d{1,9})\.(\d{1,9})\.(\d{1,9})(\.\d{1,9})?-([^-]+)-([^-]+)$`)
	numberPat = regexp.MustCompile(`^(\d{1,9})\.(\d{1,9})\.(\d{1,9})(\.\d{1,9})?$`)
)

// MustParse parses a version and panics if it does
// not parse correctly.
func MustParse(s string) Number {
	v, err := Parse(s)
	if err != nil {
		panic(err)
	}
	return v
}

// MustParseBinary parses a binary version and panics if it does
// not parse correctly.
func MustParseBinary(s string) Binary {
	v, err := ParseBinary(s)
	if err != nil {
		panic(err)
	}
	return v
}

// ParseBinary parses a binary version of the form "1.2.3-series-arch".
func ParseBinary(s string) (Binary, error) {
	m := binaryPat.FindStringSubmatch(s)
	if m == nil {
		return Binary{}, fmt.Errorf("invalid binary version %q", s)
	}
	var v Binary
	v.Major = atoi(m[1])
	v.Minor = atoi(m[2])
	v.Patch = atoi(m[3])
	if m[4] != "" {
		v.Build = atoi(m[4][1:])
	}
	v.Series = m[5]
	v.Arch = m[6]
	return v, nil
}

// Parse parses the version, which is of the form 1.2.3
// giving the major, minor and release versions
// respectively.
func Parse(s string) (Number, error) {
	m := numberPat.FindStringSubmatch(s)
	if m == nil {
		return Number{}, fmt.Errorf("invalid version %q", s)
	}
	var v Number
	v.Major = atoi(m[1])
	v.Minor = atoi(m[2])
	v.Patch = atoi(m[3])
	if m[4] != "" {
		v.Build = atoi(m[4][1:])
	}
	return v, nil
}

// atoi is the same as strconv.Atoi but assumes that
// the string has been verified to be a valid integer.
func atoi(s string) int {
	n, err := strconv.Atoi(s)
	if err != nil {
		panic(err)
	}
	return n
}

func (v Number) String() string {
	s := fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
	if v.Build > 0 {
		s += fmt.Sprintf(".%d", v.Build)
	}
	return s
}

// Less returns whether v is semantically earlier in the
// version sequence than w.
func (v Number) Less(w Number) bool {
	switch {
	case v.Major != w.Major:
		return v.Major < w.Major
	case v.Minor != w.Minor:
		return v.Minor < w.Minor
	case v.Patch != w.Patch:
		return v.Patch < w.Patch
	case v.Build != w.Build:
		return v.Build < w.Build
	}
	return false
}

// GetBSON turns v into a bson.Getter so it can be saved directly
// on a MongoDB database with mgo.
func (v Number) GetBSON() (interface{}, error) {
	return v.String(), nil
}

// SetBSON turns v into a bson.Setter so it can be loaded directly
// from a MongoDB database with mgo.
func (vp *Number) SetBSON(raw bson.Raw) error {
	var s string
	err := raw.Unmarshal(&s)
	if err != nil {
		return err
	}
	v, err := Parse(s)
	if err != nil {
		return err
	}
	*vp = v
	return nil
}

func isOdd(x int) bool {
	return x%2 != 0
}

// IsDev returns whether the version represents a development
// version. A version with an odd-numbered minor component or
// a nonzero build component is considered to be a development
// version.
func (v Number) IsDev() bool {
	return isOdd(v.Minor) || v.Build > 0
}

func readSeries(releaseFile string) string {
	data, err := ioutil.ReadFile(releaseFile)
	if err != nil {
		return "unknown"
	}
	for _, line := range strings.Split(string(data), "\n") {
		const p = "DISTRIB_CODENAME="
		if strings.HasPrefix(line, p) {
			return strings.Trim(line[len(p):], "\t '\"")
		}
	}
	return "unknown"
}

func ubuntuArch(arch string) string {
	if arch == "386" {
		arch = "i386"
	}
	return arch
}
<commit_msg>Set development version to 1.11.4<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// The version package implements version parsing.
// It also acts as guardian of the current client Juju version number.
package version

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"

	"labix.org/v2/mgo/bson"
)

// The presence and format of this constant is very important.
// The debian/rules build recipe uses this value for the version
// number of the release package.
const version = "1.11.4"

// CurrentNumber returns the version number.
func CurrentNumber() Number {
	return MustParse(version)
}

// CurrentSeries returns the current Ubuntu release name.
func CurrentSeries() string {
	return readSeries("/etc/lsb-release")
}

// CurrentArch returns the architecture of the machine.
func CurrentArch() string {
	return ubuntuArch(runtime.GOARCH)
}

// Current gives the current version of the system.  If the file
// "FORCE-VERSION" is present in the same directory as the running
// binary, it will override this.
var Current = Binary{
	Number: CurrentNumber(),
	Series: CurrentSeries(),
	Arch:   CurrentArch(),
}

func init() {
	toolsDir := filepath.Dir(os.Args[0])
	v, err := ioutil.ReadFile(filepath.Join(toolsDir, "FORCE-VERSION"))
	if err != nil {
		if os.IsNotExist(err) {
			return
		}
		panic(fmt.Errorf("version: cannot read forced version: %v", err))
	}
	Current.Number = MustParse(strings.TrimSpace(string(v)))
}

// Number represents a juju version.  When bugs are fixed the patch
// number is incremented; when new features are added the minor number
// is incremented and patch is reset; and when compatibility is broken
// the major version is incremented and minor and patch are reset.  The
// build number is automatically assigned and has no well defined
// sequence.  If the build number is greater than zero or the minor
// version is odd, it indicates that the release is still in
// development.
type Number struct {
	Major int
	Minor int
	Patch int
	Build int
}

// Zero is occasionally convenient and readable.
// Please don't change its value.
var Zero = Number{}

// Binary specifies a binary version of juju.
type Binary struct {
	Number
	Series string
	Arch   string
}

func (v Binary) String() string {
	return fmt.Sprintf("%v-%s-%s", v.Number, v.Series, v.Arch)
}

// GetBSON turns v into a bson.Getter so it can be saved directly
// on a MongoDB database with mgo.
func (v Binary) GetBSON() (interface{}, error) {
	return v.String(), nil
}

// SetBSON turns v into a bson.Setter so it can be loaded directly
// from a MongoDB database with mgo.
func (vp *Binary) SetBSON(raw bson.Raw) error {
	var s string
	err := raw.Unmarshal(&s)
	if err != nil {
		return err
	}
	v, err := ParseBinary(s)
	if err != nil {
		return err
	}
	*vp = v
	return nil
}

var (
	binaryPat = regexp.MustCompile(`^(\d{1,9})\.(\d{1,9})\.(\d{1,9})(\.\d{1,9})?-([^-]+)-([^-]+)$`)
	numberPat = regexp.MustCompile(`^(\d{1,9})\.(\d{1,9})\.(\d{1,9})(\.\d{1,9})?$`)
)

// MustParse parses a version and panics if it does
// not parse correctly.
func MustParse(s string) Number {
	v, err := Parse(s)
	if err != nil {
		panic(err)
	}
	return v
}

// MustParseBinary parses a binary version and panics if it does
// not parse correctly.
func MustParseBinary(s string) Binary {
	v, err := ParseBinary(s)
	if err != nil {
		panic(err)
	}
	return v
}

// ParseBinary parses a binary version of the form "1.2.3-series-arch".
func ParseBinary(s string) (Binary, error) {
	m := binaryPat.FindStringSubmatch(s)
	if m == nil {
		return Binary{}, fmt.Errorf("invalid binary version %q", s)
	}
	var v Binary
	v.Major = atoi(m[1])
	v.Minor = atoi(m[2])
	v.Patch = atoi(m[3])
	if m[4] != "" {
		v.Build = atoi(m[4][1:])
	}
	v.Series = m[5]
	v.Arch = m[6]
	return v, nil
}

// Parse parses the version, which is of the form 1.2.3
// giving the major, minor and release versions
// respectively.
func Parse(s string) (Number, error) {
	m := numberPat.FindStringSubmatch(s)
	if m == nil {
		return Number{}, fmt.Errorf("invalid version %q", s)
	}
	var v Number
	v.Major = atoi(m[1])
	v.Minor = atoi(m[2])
	v.Patch = atoi(m[3])
	if m[4] != "" {
		v.Build = atoi(m[4][1:])
	}
	return v, nil
}

// atoi is the same as strconv.Atoi but assumes that
// the string has been verified to be a valid integer.
func atoi(s string) int {
	n, err := strconv.Atoi(s)
	if err != nil {
		panic(err)
	}
	return n
}

func (v Number) String() string {
	s := fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
	if v.Build > 0 {
		s += fmt.Sprintf(".%d", v.Build)
	}
	return s
}

// Less returns whether v is semantically earlier in the
// version sequence than w.
func (v Number) Less(w Number) bool {
	switch {
	case v.Major != w.Major:
		return v.Major < w.Major
	case v.Minor != w.Minor:
		return v.Minor < w.Minor
	case v.Patch != w.Patch:
		return v.Patch < w.Patch
	case v.Build != w.Build:
		return v.Build < w.Build
	}
	return false
}

// GetBSON turns v into a bson.Getter so it can be saved directly
// on a MongoDB database with mgo.
func (v Number) GetBSON() (interface{}, error) {
	return v.String(), nil
}

// SetBSON turns v into a bson.Setter so it can be loaded directly
// from a MongoDB database with mgo.
func (vp *Number) SetBSON(raw bson.Raw) error {
	var s string
	err := raw.Unmarshal(&s)
	if err != nil {
		return err
	}
	v, err := Parse(s)
	if err != nil {
		return err
	}
	*vp = v
	return nil
}

func isOdd(x int) bool {
	return x%2 != 0
}

// IsDev returns whether the version represents a development
// version. A version with an odd-numbered minor component or
// a nonzero build component is considered to be a development
// version.
func (v Number) IsDev() bool {
	return isOdd(v.Minor) || v.Build > 0
}

func readSeries(releaseFile string) string {
	data, err := ioutil.ReadFile(releaseFile)
	if err != nil {
		return "unknown"
	}
	for _, line := range strings.Split(string(data), "\n") {
		const p = "DISTRIB_CODENAME="
		if strings.HasPrefix(line, p) {
			return strings.Trim(line[len(p):], "\t '\"")
		}
	}
	return "unknown"
}

func ubuntuArch(arch string) string {
	if arch == "386" {
		arch = "i386"
	}
	return arch
}
<|endoftext|>


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

package main

import (
	"fmt"
	"strings"

	"github.com/danryan/hal"
)

var whoisHandler = hear(`whois ([A-Za-z0-9\-_\{\}\[\]\\]+)`, "whois (name)", "Find out who a user is", func(res *hal.Response) error {
	name := res.Match[1]
	key := strings.ToUpper(name)
	val, err := res.Robot.Store.Get(key)
	if err != nil {
		res.Send(fmt.Sprintf("%s is no one to me\n", name))
		return err
	}
	return res.Send(fmt.Sprintf("%s is %s", name, string(val)))
})

var isHandler = hear(`(.+) is (.+)`, "(name) is (role)", "Tell Ash who the user is", func(res *hal.Response) error {
	name := res.Match[1]
	key := strings.ToUpper(name)
	role := res.Match[2]

	storedRoles, err := res.Robot.Store.Get(key)
	roleToStore := role
	if len(storedRoles) > 0 {
		roleToStore = roleToStore + ", " + string(storedRoles)
	}

	err = res.Robot.Store.Set(key, []byte(roleToStore))
	if err != nil {
		res.Send("There's something wrong")
		return err
	}
	return res.Send(fmt.Sprintf("Got it. %s is %s\n", name, role))
})

var whoamHandler = hear(`who am (?i)I`, "who am I", "Ask ash who you are", func(res *hal.Response) error {
	name := res.UserName()
	key := strings.ToUpper(name)
	val, err := res.Robot.Store.Get(key)
	if err != nil {
		return res.Send(fmt.Sprintf("%s?  I have no memory of who you are.", name))
	}

	return res.Send(fmt.Sprintf("%s, you are %s", name, string(val)))
})
<commit_msg>Do not append roles in whois command<commit_after>// Copyright 2014-2015 Chadev. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"strings"

	"github.com/danryan/hal"
)

var whoisHandler = hear(`whois ([A-Za-z0-9\-_\{\}\[\]\\]+)`, "whois (name)", "Find out who a user is", func(res *hal.Response) error {
	name := res.Match[1]
	key := strings.ToUpper(name)
	val, err := res.Robot.Store.Get(key)
	if err != nil {
		res.Send(fmt.Sprintf("%s is no one to me\n", name))
		return err
	}
	return res.Send(fmt.Sprintf("%s is %s", name, string(val)))
})

var isHandler = hear(`(.+) is (.+)`, "(name) is (role)", "Tell Ash who the user is", func(res *hal.Response) error {
	name := res.Match[1]
	key := strings.ToUpper(name)
	role := res.Match[2]

	err := res.Robot.Store.Set(key, []byte(role))
	if err != nil {
		res.Send("There's something wrong")
		return err
	}
	return res.Send(fmt.Sprintf("Got it. %s is %s\n", name, role))
})

var whoamHandler = hear(`who am (?i)I`, "who am I", "Ask ash who you are", func(res *hal.Response) error {
	name := res.UserName()
	key := strings.ToUpper(name)
	val, err := res.Robot.Store.Get(key)
	if err != nil {
		return res.Send(fmt.Sprintf("%s?  I have no memory of who you are.", name))
	}

	return res.Send(fmt.Sprintf("%s, you are %s", name, string(val)))
})
<|endoftext|>


<commit_before>package browser

import (
	"net/url"
	"strings"

	"github.com/PuerkitoBio/goquery"
	"github.com/gomaps/surf/errors"
)

// Submittable represents an element that may be submitted, such as a form.
type Submittable interface {
	Method() string
	Action() string
	Input(name, value string) error
	Click(button string) error
	Submit() error
	Dom() *goquery.Selection
}

// Form is the default form element.
type Form struct {
	bow       Browsable
	selection *goquery.Selection
	method    string
	action    string
	fields    url.Values
	buttons   url.Values
}

// NewForm creates and returns a *Form type.
func NewForm(bow Browsable, s *goquery.Selection) *Form {
	fields, buttons := serializeForm(s)
	method, action := formAttributes(bow, s)

	return &Form{
		bow:       bow,
		selection: s,
		method:    method,
		action:    action,
		fields:    fields,
		buttons:   buttons,
	}
}

// Method returns the form method, eg "GET" or "POST".
func (f *Form) Method() string {
	return f.method
}

// Action returns the form action URL.
// The URL will always be absolute.
func (f *Form) Action() string {
	return f.action
}

// Input sets the value of a form field.
func (f *Form) Input(name, value string) error {
	if _, ok := f.fields[name]; ok {
		f.fields.Set(name, value)
		return nil
	}
	return errors.NewElementNotFound(
		"No input found with name '%s'.", name)
}

// Submit submits the form.
// Clicks the first button in the form, or submits the form without using
// any button when the form does not contain any buttons.
func (f *Form) Submit() error {
	if len(f.buttons) > 0 {
		for name := range f.buttons {
			return f.Click(name)
		}
	}
	return f.send("", "")
}

// Click submits the form by clicking the button with the given name.
func (f *Form) Click(button string) error {
	if _, ok := f.buttons[button]; !ok {
		return errors.NewInvalidFormValue(
			"Form does not contain a button with the name '%s'.", button)
	}
	return f.send(button, f.buttons[button][0])
}

// Dom returns the inner *goquery.Selection.
func (f *Form) Dom() *goquery.Selection {
	return f.selection
}

// send submits the form.
func (f *Form) send(buttonName, buttonValue string) error {
	method, ok := f.selection.Attr("method")
	if !ok {
		method = "GET"
	}
	action, ok := f.selection.Attr("action")
	if !ok {
		action = f.bow.Url().String()
	}
	aurl, err := url.Parse(action)
	if err != nil {
		return err
	}
	aurl = f.bow.ResolveUrl(aurl)

	values := make(url.Values, len(f.fields)+1)
	for name, vals := range f.fields {
		values[name] = vals
	}
	if buttonName != "" {
		values.Set(buttonName, buttonValue)
	}

	if strings.ToUpper(method) == "GET" {
		return f.bow.OpenForm(aurl.String(), values)
	} else {
		enctype, _ := f.selection.Attr("enctype")
		if enctype == "multipart/form-data" {
			return f.bow.PostMultipart(aurl.String(), values)
		}
		return f.bow.PostForm(aurl.String(), values)
	}

	return nil
}

// Serialize converts the form fields into a url.Values type.
// Returns two url.Value types. The first is the form field values, and the
// second is the form button values.
func serializeForm(sel *goquery.Selection) (url.Values, url.Values) {
	input := sel.Find("input,button")
	if input.Length() == 0 {
		return url.Values{}, url.Values{}
	}

	fields := make(url.Values)
	buttons := make(url.Values)
	input.Each(func(_ int, s *goquery.Selection) {
		name, ok := s.Attr("name")
		if ok {
			typ, ok := s.Attr("type")
			if ok {
				if typ == "submit" {
					val, ok := s.Attr("value")
					if ok {
						buttons.Add(name, val)
					} else {
						buttons.Add(name, "")
					}
				} else {
					val, ok := s.Attr("value")
					if !ok {
						val = ""
					}
					fields.Add(name, val)
				}
			}
		}
	})

	return fields, buttons
}

func formAttributes(bow Browsable, s *goquery.Selection) (string, string) {
	method, ok := s.Attr("method")
	if !ok {
		method = "GET"
	}
	action, ok := s.Attr("action")
	if !ok {
		action = bow.Url().String()
	}
	aurl, err := url.Parse(action)
	if err != nil {
		return "", ""
	}
	aurl = bow.ResolveUrl(aurl)

	return strings.ToUpper(method), aurl.String()
}
<commit_msg>- Added support for select form fields<commit_after>package browser

import (
	"net/url"
	"strings"

	"github.com/PuerkitoBio/goquery"
	"github.com/gomaps/surf/errors"
)

// Submittable represents an element that may be submitted, such as a form.
type Submittable interface {
	Method() string
	Action() string
	Input(name, value string) error
	Click(button string) error
	Submit() error
	Dom() *goquery.Selection
}

// Form is the default form element.
type Form struct {
	bow       Browsable
	selection *goquery.Selection
	method    string
	action    string
	fields    url.Values
	buttons   url.Values
}

// NewForm creates and returns a *Form type.
func NewForm(bow Browsable, s *goquery.Selection) *Form {
	fields, buttons := serializeForm(s)
	method, action := formAttributes(bow, s)

	//fmt.Println(fields)

	return &Form{
		bow:       bow,
		selection: s,
		method:    method,
		action:    action,
		fields:    fields,
		buttons:   buttons,
	}
}

// Method returns the form method, eg "GET" or "POST".
func (f *Form) Method() string {
	return f.method
}

// Action returns the form action URL.
// The URL will always be absolute.
func (f *Form) Action() string {
	return f.action
}

// Input sets the value of a form field.
func (f *Form) Input(name, value string) error {
	if _, ok := f.fields[name]; ok {
		f.fields.Set(name, value)
		return nil
	}
	return errors.NewElementNotFound(
		"No input found with name '%s'.", name)
}

// Submit submits the form.
// Clicks the first button in the form, or submits the form without using
// any button when the form does not contain any buttons.
func (f *Form) Submit() error {
	if len(f.buttons) > 0 {
		for name := range f.buttons {
			return f.Click(name)
		}
	}
	return f.send("", "")
}

// Click submits the form by clicking the button with the given name.
func (f *Form) Click(button string) error {
	if _, ok := f.buttons[button]; !ok {
		return errors.NewInvalidFormValue(
			"Form does not contain a button with the name '%s'.", button)
	}
	return f.send(button, f.buttons[button][0])
}

// Dom returns the inner *goquery.Selection.
func (f *Form) Dom() *goquery.Selection {
	return f.selection
}

// send submits the form.
func (f *Form) send(buttonName, buttonValue string) error {
	method, ok := f.selection.Attr("method")
	if !ok {
		method = "GET"
	}
	action, ok := f.selection.Attr("action")
	if !ok {
		action = f.bow.Url().String()
	}
	aurl, err := url.Parse(action)
	if err != nil {
		return err
	}
	aurl = f.bow.ResolveUrl(aurl)

	values := make(url.Values, len(f.fields)+1)
	for name, vals := range f.fields {
		values[name] = vals
	}
	if buttonName != "" {
		values.Set(buttonName, buttonValue)
	}

	//fmt.Println(values)
	if strings.ToUpper(method) == "GET" {
		return f.bow.OpenForm(aurl.String(), values)
	} else {
		enctype, _ := f.selection.Attr("enctype")
		if enctype == "multipart/form-data" {
			return f.bow.PostMultipart(aurl.String(), values)
		}
		return f.bow.PostForm(aurl.String(), values)
	}

	return nil
}

// Serialize converts the form fields into a url.Values type.
// Returns two url.Value types. The first is the form field values, and the
// second is the form button values.
func serializeForm(sel *goquery.Selection) (url.Values, url.Values) {
	input := sel.Find("input,button,select")
	if input.Length() == 0 {
		return url.Values{}, url.Values{}
	}

	fields := make(url.Values)
	buttons := make(url.Values)
	input.Each(func(_ int, s *goquery.Selection) {
		name, ok := s.Attr("name")
		if ok {
			typ, ok := s.Attr("type")
			if ok {
				if typ == "submit" {
					val, ok := s.Attr("value")
					if ok {
						buttons.Add(name, val)
					} else {
						buttons.Add(name, "")
					}
				} else {
					val, ok := s.Attr("value")
					if !ok {
						val = ""
					}
					fields.Add(name, val)
				}
			} else {
				//fmt.Println(name)
				// Handle select (dropdown) inputs
				s.Find("option").Each(func(i int, s *goquery.Selection) {
					val, ok := s.Attr("selected")
					if ok {
						if val == "selected" {
							val, ok := s.Attr("value")
							if ok {
								//fmt.Println(val)
								fields.Add(name, val)
							}
						}
					}
				})
			}
		}
	})

	return fields, buttons
}

func formAttributes(bow Browsable, s *goquery.Selection) (string, string) {
	method, ok := s.Attr("method")
	if !ok {
		method = "GET"
	}
	action, ok := s.Attr("action")
	if !ok {
		action = bow.Url().String()
	}
	aurl, err := url.Parse(action)
	if err != nil {
		return "", ""
	}
	aurl = bow.ResolveUrl(aurl)

	return strings.ToUpper(method), aurl.String()
}
<|endoftext|>


<commit_before>// Copyright 2009 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.

// The exvar package provides a standardized interface to public variables,
// such as operation counters in servers.
package exvar

import (
	"fmt";
	"sync";
)

// Global state.
var (
	mutex sync.Mutex;
	intVars = make(map[string] int);
	mapVars = make(map[string] map[string] int);
	// TODO(dsymonds):
	// - string-valued vars
	// - docstrings
	// - dynamic lookup vars (via chan)
)

// Increment adds inc to the var called name.
func Increment(name string, inc int) {
	mutex.Lock();
	defer mutex.Unlock();

	if x, ok := intVars[name]; ok {
		intVars[name] += inc
	} else {
		intVars[name] = inc
	}
}

// Set sets the var called name to value.
func Set(name string, value int) {
	intVars[name] = value
}

// Get retrieves an integer-valued var called name.
func Get(name string) (x int, ok bool) {
	x, ok = intVars[name];
	return
}

// TODO(dsymonds): Functions for map-valued vars.

// String produces a string of all the vars in textual format.
func String() string {
	mutex.Lock();
	defer mutex.Unlock();

	s := "";
	for name, value := range intVars {
		s += fmt.Sprintln(name, value)
	}
	return s
}
<commit_msg>Use the mutex in exvar.Set since map access is not atomic.<commit_after>// Copyright 2009 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.

// The exvar package provides a standardized interface to public variables,
// such as operation counters in servers.
package exvar

import (
	"fmt";
	"sync";
)

// Global state.
var (
	mutex sync.Mutex;
	intVars = make(map[string] int);
	mapVars = make(map[string] map[string] int);
	// TODO(dsymonds):
	// - string-valued vars
	// - docstrings
	// - dynamic lookup vars (via chan)
)

// Increment adds inc to the var called name.
func Increment(name string, inc int) {
	mutex.Lock();
	defer mutex.Unlock();

	if x, ok := intVars[name]; ok {
		intVars[name] += inc
	} else {
		intVars[name] = inc
	}
}

// Set sets the var called name to value.
func Set(name string, value int) {
	mutex.Lock();
	defer mutex.Unlock();

	intVars[name] = value
}

// Get retrieves an integer-valued var called name.
func Get(name string) (x int, ok bool) {
	x, ok = intVars[name];
	return
}

// TODO(dsymonds): Functions for map-valued vars.

// String produces a string of all the vars in textual format.
func String() string {
	mutex.Lock();
	defer mutex.Unlock();

	s := "";
	for name, value := range intVars {
		s += fmt.Sprintln(name, value)
	}
	return s
}
<|endoftext|>


<commit_before>package onapp

import (
	"container/list"
	"encoding/json"
	"errors"
	"github.com/alexzorin/onapp/log"
	"strconv"
)

// sort.Sort'ing over this type will
// sort by UserId
type VirtualMachines []VirtualMachine

// The OnApp Virtual Machine as according to /virtual_machines.json
type VirtualMachine struct {
	client         *Client
	Id             int                    `json:"id"`
	Label          string                 `json:"label"`
	Booted         bool                   `json:"booted"`
	Hostname       string                 `json:"hostname"`
	HV             int                    `json:"hypervisor_id"`
	Cpus           int                    `json:"cpus"`
	CpuShares      int                    `json:"cpu_shares"`
	Memory         int                    `json:"memory"`
	Template       string                 `json:"template_label"`
	User           int                    `json:"user_id"`
	Locked         bool                   `json:"locked"`
	RootPassword   string                 `json:"initial_root_password"`
	IpAddressesRaw []map[string]IpAddress `json:"ip_addresses"`
	VncPassword    string                 `json:"remote_access_password"`
}

// IP address of a virtual machine as represented by /virtual_machines/:id.json
type IpAddress struct {
	Address        string `json:"address"`
	Gateway        string `json:"gateway"`
	Broadcast      string `json:"broadcast"`
	NetworkAddress string `json:"network_address"`
	Netmask        string `json:"netmask"`
}

// Remote Access Session
type RemoteAccessSession struct {
	Port int `json:"port"`
}

func (c *Client) GetRemoteAccessSession(id int) (RemoteAccessSession, error) {
	data, err, _ := c.getReq("/virtual_machines/", strconv.Itoa(id), "/console.json")
	if err != nil {
		return RemoteAccessSession{}, err
	}
	var out map[string]RemoteAccessSession
	err = json.Unmarshal(data, &out)
	if err != nil {
		return RemoteAccessSession{}, err
	} else {
		return out["remote_access_session"], err
	}
}

// Fetches a list of Virtual Machines from the dashboard server
func (c *Client) GetVirtualMachines() (VirtualMachines, error) {
	data, err, _ := c.getReq("virtual_machines.json")
	if err != nil {
		return nil, err
	}
	var out []map[string]VirtualMachine
	err = json.Unmarshal(data, &out)
	if err != nil {
		return nil, err
	}
	vms := make([]VirtualMachine, len(out))
	for i := range vms {
		vms[i] = out[i]["virtual_machine"]
		vms[i].client = c
	}
	return vms, nil
}

// Fetches an individual Virtual Machine from the dashboard server
func (c *Client) GetVirtualMachine(id int) (VirtualMachine, error) {
	data, err, _ := c.getReq("virtual_machines/", strconv.Itoa(id), ".json")
	if err != nil {
		return VirtualMachine{}, err
	}
	var out map[string]VirtualMachine
	err = json.Unmarshal(data, &out)
	if err != nil {
		return VirtualMachine{}, err
	}
	vm := out["virtual_machine"]
	vm.client = c
	return vm, nil
}

func (c *Client) VirtualMachineStartup(id int) error {
	_, err, rc := c.postReq("", "virtual_machines/", strconv.Itoa(id), "/startup.json")
	if rc == 422 {
		return errors.New("HTTP 422 - VM can't currently be booted")
	}
	return err
}

func (c *Client) VirtualMachineShutdown(id int) error {
	_, err, rc := c.postReq("", "virtual_machines/", strconv.Itoa(id), "/shutdown.json")
	if rc == 422 {
		return errors.New("HTTP 422 - VM can't currently be shut down")
	}
	return err
}

func (c *Client) VirtualMachineReboot(id int) error {
	_, err, rc := c.postReq("", "virtual_machines/", strconv.Itoa(id), "/reboot.json")
	if rc == 422 {
		return errors.New("HTTP 422 - VM can't currently be rebooted")
	}
	return err
}

func (c *Client) VirtualMachineGetTransactions(vmId int) (Transactions, error) {
	return c.getTransactions(vmId)
}

func (c *Client) VirtualMachineGetLatestTransaction(vmId int, statuses ...string) (Transaction, error) {
	txns, err := c.VirtualMachineGetTransactions(vmId)
	if err != nil {
		return Transaction{}, err
	}
	if len(statuses) == 0 && len(txns) > 0 {
		return txns[0], nil
	}
	for _, t := range txns {
		if len(statuses) > 0 {
			for _, s := range statuses {
				if t.Status == s {
					return t, nil
				}
			}
		}
	}
	return Transaction{}, nil
}

func (vm *VirtualMachine) Startup() error {
	return vm.client.VirtualMachineStartup(vm.Id)
}

func (vm *VirtualMachine) Shutdown() error {
	return vm.client.VirtualMachineShutdown(vm.Id)
}

func (vm *VirtualMachine) Reboot() error {
	return vm.client.VirtualMachineReboot(vm.Id)
}

func (vm *VirtualMachine) GetTransactions() (Transactions, error) {
	return vm.client.VirtualMachineGetTransactions(vm.Id)
}

func (vm *VirtualMachine) GetRunningTransaction() (Transaction, error) {
	return vm.client.VirtualMachineGetLatestTransaction(vm.Id, "running")
}

func (vm *VirtualMachine) GetIpAddresses() ([]IpAddress, error) {
	var addrs []IpAddress
	for _, v := range vm.IpAddressesRaw {
		addrs = append(addrs, v["ip_address"])
	}
	return addrs, nil
}

func (vm *VirtualMachine) GetIpAddress() IpAddress {
	ips, err := vm.GetIpAddresses()
	if err != nil {
		return IpAddress{}
	} else {
		if len(ips) > 0 {
			return ips[0]
		} else {
			return IpAddress{}
		}
	}
}

func (vm *VirtualMachine) BootedString() string {
	if vm.Locked {
		return "Locked"
	} else if vm.Booted {
		return "Booted"
	} else {
		return "Offline"
	}
}

func (vm *VirtualMachine) BootedStringColored() string {
	if vm.Locked {
		return log.ColorString("LOCKED", log.YELLOW)
	} else if vm.Booted {
		return log.ColorString("Booted", log.GREEN)
	} else {
		return log.ColorString("Offline", log.RED)
	}
}

func (vm *VirtualMachine) GetRemoteAccessSession() (*RemoteAccessSession, error) {
	ras, err := vm.client.GetRemoteAccessSession(vm.Id)
	if err != nil {
		return &RemoteAccessSession{}, err
	}
	newVm, err := vm.client.GetVirtualMachine(vm.Id)
	if err != nil {
		return &RemoteAccessSession{}, err
	}
	*vm = newVm
	return &ras, nil
}

func (vms VirtualMachines) AsList() list.List {
	var l list.List
	for _, v := range vms {
		l.PushBack(v)
	}
	return l
}

func (vms VirtualMachines) Swap(i, j int) {
	vms[i], vms[j] = vms[j], vms[i]
}

func (vms VirtualMachines) Len() int {
	return len(vms)
}

func (vms VirtualMachines) Less(i, j int) bool {
	return vms[i].User < vms[j].User
}
<commit_msg>Vm.GetIpAddress prefers non-reserved addresses<commit_after>package onapp

import (
	"container/list"
	"encoding/json"
	"errors"
	"strconv"

	"github.com/alexzorin/onapp/log"
	"github.com/alexzorin/rfc5735"
)

// sort.Sort'ing over this type will
// sort by UserId
type VirtualMachines []VirtualMachine

// The OnApp Virtual Machine as according to /virtual_machines.json
type VirtualMachine struct {
	client         *Client
	Id             int                    `json:"id"`
	Label          string                 `json:"label"`
	Booted         bool                   `json:"booted"`
	Hostname       string                 `json:"hostname"`
	HV             int                    `json:"hypervisor_id"`
	Cpus           int                    `json:"cpus"`
	CpuShares      int                    `json:"cpu_shares"`
	Memory         int                    `json:"memory"`
	Template       string                 `json:"template_label"`
	User           int                    `json:"user_id"`
	Locked         bool                   `json:"locked"`
	RootPassword   string                 `json:"initial_root_password"`
	IpAddressesRaw []map[string]IpAddress `json:"ip_addresses"`
	VncPassword    string                 `json:"remote_access_password"`
}

// IP address of a virtual machine as represented by /virtual_machines/:id.json
type IpAddress struct {
	Address        string `json:"address"`
	Gateway        string `json:"gateway"`
	Broadcast      string `json:"broadcast"`
	NetworkAddress string `json:"network_address"`
	Netmask        string `json:"netmask"`
}

// Remote Access Session
type RemoteAccessSession struct {
	Port int `json:"port"`
}

func (c *Client) GetRemoteAccessSession(id int) (RemoteAccessSession, error) {
	data, err, _ := c.getReq("/virtual_machines/", strconv.Itoa(id), "/console.json")
	if err != nil {
		return RemoteAccessSession{}, err
	}
	var out map[string]RemoteAccessSession
	err = json.Unmarshal(data, &out)
	if err != nil {
		return RemoteAccessSession{}, err
	} else {
		return out["remote_access_session"], err
	}
}

// Fetches a list of Virtual Machines from the dashboard server
func (c *Client) GetVirtualMachines() (VirtualMachines, error) {
	data, err, _ := c.getReq("virtual_machines.json")
	if err != nil {
		return nil, err
	}
	var out []map[string]VirtualMachine
	err = json.Unmarshal(data, &out)
	if err != nil {
		return nil, err
	}
	vms := make([]VirtualMachine, len(out))
	for i := range vms {
		vms[i] = out[i]["virtual_machine"]
		vms[i].client = c
	}
	return vms, nil
}

// Fetches an individual Virtual Machine from the dashboard server
func (c *Client) GetVirtualMachine(id int) (VirtualMachine, error) {
	data, err, _ := c.getReq("virtual_machines/", strconv.Itoa(id), ".json")
	if err != nil {
		return VirtualMachine{}, err
	}
	var out map[string]VirtualMachine
	err = json.Unmarshal(data, &out)
	if err != nil {
		return VirtualMachine{}, err
	}
	vm := out["virtual_machine"]
	vm.client = c
	return vm, nil
}

func (c *Client) VirtualMachineStartup(id int) error {
	_, err, rc := c.postReq("", "virtual_machines/", strconv.Itoa(id), "/startup.json")
	if rc == 422 {
		return errors.New("HTTP 422 - VM can't currently be booted")
	}
	return err
}

func (c *Client) VirtualMachineShutdown(id int) error {
	_, err, rc := c.postReq("", "virtual_machines/", strconv.Itoa(id), "/shutdown.json")
	if rc == 422 {
		return errors.New("HTTP 422 - VM can't currently be shut down")
	}
	return err
}

func (c *Client) VirtualMachineReboot(id int) error {
	_, err, rc := c.postReq("", "virtual_machines/", strconv.Itoa(id), "/reboot.json")
	if rc == 422 {
		return errors.New("HTTP 422 - VM can't currently be rebooted")
	}
	return err
}

func (c *Client) VirtualMachineGetTransactions(vmId int) (Transactions, error) {
	return c.getTransactions(vmId)
}

func (c *Client) VirtualMachineGetLatestTransaction(vmId int, statuses ...string) (Transaction, error) {
	txns, err := c.VirtualMachineGetTransactions(vmId)
	if err != nil {
		return Transaction{}, err
	}
	if len(statuses) == 0 && len(txns) > 0 {
		return txns[0], nil
	}
	for _, t := range txns {
		if len(statuses) > 0 {
			for _, s := range statuses {
				if t.Status == s {
					return t, nil
				}
			}
		}
	}
	return Transaction{}, nil
}

func (vm *VirtualMachine) Startup() error {
	return vm.client.VirtualMachineStartup(vm.Id)
}

func (vm *VirtualMachine) Shutdown() error {
	return vm.client.VirtualMachineShutdown(vm.Id)
}

func (vm *VirtualMachine) Reboot() error {
	return vm.client.VirtualMachineReboot(vm.Id)
}

func (vm *VirtualMachine) GetTransactions() (Transactions, error) {
	return vm.client.VirtualMachineGetTransactions(vm.Id)
}

func (vm *VirtualMachine) GetRunningTransaction() (Transaction, error) {
	return vm.client.VirtualMachineGetLatestTransaction(vm.Id, "running")
}

func (vm *VirtualMachine) GetIpAddresses() ([]IpAddress, error) {
	var addrs []IpAddress
	for _, v := range vm.IpAddressesRaw {
		addrs = append(addrs, v["ip_address"])
	}
	return addrs, nil
}

// This prefers non-rfc5735 addresses
// if possible
func (vm *VirtualMachine) GetIpAddress() IpAddress {
	var out IpAddress
	ips, err := vm.GetIpAddresses()
	if err != nil {
		return IpAddress{}
	} else {
		if len(ips) > 0 {
			out = ips[0]
		}
		if len(ips) > 1 {
			for _, i := range ips {
				if !rfc5735.IsReservedString(i.Address) {
					out = i
					break
				}
			}
		}
	}
	return out
}

func (vm *VirtualMachine) BootedString() string {
	if vm.Locked {
		return "Locked"
	} else if vm.Booted {
		return "Booted"
	} else {
		return "Offline"
	}
}

func (vm *VirtualMachine) BootedStringColored() string {
	if vm.Locked {
		return log.ColorString("LOCKED", log.YELLOW)
	} else if vm.Booted {
		return log.ColorString("Booted", log.GREEN)
	} else {
		return log.ColorString("Offline", log.RED)
	}
}

func (vm *VirtualMachine) GetRemoteAccessSession() (*RemoteAccessSession, error) {
	ras, err := vm.client.GetRemoteAccessSession(vm.Id)
	if err != nil {
		return &RemoteAccessSession{}, err
	}
	newVm, err := vm.client.GetVirtualMachine(vm.Id)
	if err != nil {
		return &RemoteAccessSession{}, err
	}
	*vm = newVm
	return &ras, nil
}

func (vms VirtualMachines) AsList() list.List {
	var l list.List
	for _, v := range vms {
		l.PushBack(v)
	}
	return l
}

func (vms VirtualMachines) Swap(i, j int) {
	vms[i], vms[j] = vms[j], vms[i]
}

func (vms VirtualMachines) Len() int {
	return len(vms)
}

func (vms VirtualMachines) Less(i, j int) bool {
	return vms[i].User < vms[j].User
}
<|endoftext|>


<commit_before>// Copyright 2011 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package vm

import (
	"strings"
	"testing"

	"github.com/go-test/deep"
)

var testCodeGenPrograms = []struct {
	name   string
	source string
	prog   []instr // expected bytecode
}{
	// Composite literals require too many explicit conversions.
	{"simple line counter",
		"counter line_count\n/$/ { line_count++\n }\n",
		[]instr{
			instr{match, 0},
			instr{jnm, 7},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"count a",
		"counter a_count\n/a$/ { a_count++\n }\n",
		[]instr{
			instr{match, 0},
			instr{jnm, 7},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"strptime and capref",
		"counter foo\n" +
			"/(.*)/ { strptime($1, \"2006-01-02T15:04:05\")\n" +
			"foo++\n}\n",
		[]instr{
			instr{match, 0},
			instr{jnm, 11},
			instr{setmatched, false},
			instr{push, 0},
			instr{capref, 0},
			instr{str, 0},
			instr{strptime, 2},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"strptime and named capref",
		"counter foo\n" +
			"/(?P<date>.*)/ { strptime($date, \"2006-01-02T15:04:05\")\n" +
			"foo++\n }\n",
		[]instr{
			instr{match, 0},
			instr{jnm, 11},
			instr{setmatched, false},
			instr{push, 0},
			instr{capref, 1},
			instr{str, 0},
			instr{strptime, 2},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"inc by and set",
		"counter foo\ncounter bar\n" +
			"/([0-9]+)/ {\n" +
			"foo += $1\n" +
			"bar = $1\n" +
			"}\n",
		[]instr{
			instr{match, 0},
			instr{jnm, 17},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{mload, 0},
			instr{dload, 0},
			instr{push, 0},
			instr{capref, 0},
			instr{iadd, nil},
			instr{iset, nil},
			instr{mload, 1},
			instr{dload, 0},
			instr{push, 0},
			instr{capref, 0},
			instr{iset, nil},
			instr{setmatched, true}}},
	{"cond expr gt",
		"counter foo\n" +
			"1 > 0 {\n" +
			"  foo++\n" +
			"}\n",
		[]instr{
			instr{push, 1},
			instr{push, 0},
			instr{cmp, 1},
			instr{jnm, 9},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"cond expr lt",
		"counter foo\n" +
			"1 < 0 {\n" +
			"  foo++\n" +
			"}\n",
		[]instr{
			instr{push, 1},
			instr{push, 0},
			instr{cmp, -1},
			instr{jnm, 9},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"cond expr eq",
		"counter foo\n" +
			"1 == 0 {\n" +
			"  foo++\n" +
			"}\n",
		[]instr{
			instr{push, 1},
			instr{push, 0},
			instr{cmp, 0},
			instr{jnm, 9},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"cond expr le",
		"counter foo\n" +
			"1 <= 0 {\n" +
			"  foo++\n" +
			"}\n",
		[]instr{
			instr{push, 1},
			instr{push, 0},
			instr{cmp, 1},
			instr{jm, 9},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"cond expr ge",
		"counter foo\n" +
			"1 >= 0 {\n" +
			"  foo++\n" +
			"}\n",
		[]instr{
			instr{push, 1},
			instr{push, 0},
			instr{cmp, -1},
			instr{jm, 9},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"cond expr ne",
		"counter foo\n" +
			"1 != 0 {\n" +
			"  foo++\n" +
			"}\n",
		[]instr{
			instr{push, 1},
			instr{push, 0},
			instr{cmp, 0},
			instr{jm, 9},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"nested cond",
		"counter foo\n" +
			"/(.*)/ {\n" +
			"  $1 <= 1 {\n" +
			"    foo++\n" +
			"  }\n" +
			"}\n",
		[]instr{
			instr{match, 0},
			instr{jnm, 14},
			instr{setmatched, false},
			instr{push, 0},
			instr{capref, 0},
			instr{push, 1},
			instr{cmp, 1},
			instr{jm, 13},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true},
			instr{setmatched, true}}},
	{"deco",
		"counter foo\n" +
			"counter bar\n" +
			"def fooWrap {\n" +
			"  /.*/ {\n" +
			"    foo++\n" +
			"    next\n" +
			"  }\n" +
			"}\n" +
			"" +
			"@fooWrap { bar++\n }\n",
		[]instr{
			instr{match, 0},
			instr{jnm, 10},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{mload, 1},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"length",
		"len(\"foo\") > 0 {\n" +
			"}\n",
		[]instr{
			instr{str, 0},
			instr{length, 1},
			instr{push, 0},
			instr{cmp, 1},
			instr{jnm, 7},
			instr{setmatched, false},
			instr{setmatched, true}}},
	{"bitwise", `
1 & 7 ^ 15 | 8
~ 16 << 2
1 >> 20
`,
		[]instr{
			instr{push, 1},
			instr{push, 7},
			instr{and, nil},
			instr{push, 15},
			instr{xor, nil},
			instr{push, 8},
			instr{or, nil},
			instr{push, 16},
			instr{not, nil},
			instr{push, 2},
			instr{shl, nil},
			instr{push, 1},
			instr{push, 20},
			instr{shr, nil}}},
	{"pow", `
counter a
counter b
a ** b
`,
		[]instr{
			instr{mload, 0},
			instr{dload, 0},
			instr{mload, 1},
			instr{dload, 0},
			instr{ipow, nil}}},
	{"indexed expr", `
counter a by b
a["string"]++
`,
		[]instr{
			instr{str, 0},
			instr{mload, 0},
			instr{dload, 1},
			instr{inc, nil}}},
	{"strtol", `
strtol("deadbeef", 16)
`,
		[]instr{
			instr{str, 0},
			instr{push, 16},
			instr{strtol, 2}}},
	{"float", `
20.0
`,
		[]instr{
			instr{push, 20.0}}},
	{"otherwise", `
counter a
otherwise {
	a++
}
`,
		[]instr{
			instr{otherwise, nil},
			instr{jnm, 7},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"cond else",
		`counter foo
counter bar
1 > 0 {
  foo++
} else {
  bar++
}`,
		[]instr{
			instr{push, 1},
			instr{push, 0},
			instr{cmp, 1},
			instr{jnm, 10},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true},
			instr{jmp, 13},
			instr{mload, 1},
			instr{dload, 0},
			instr{inc, nil},
		},
	},
	{"mod",
		`
3 % 1
`,
		[]instr{
			instr{push, 3},
			instr{push, 1},
			instr{imod, nil},
		},
	},
	{"del", `
counter a by b
del a["string"]
`,
		[]instr{
			instr{str, 0},
			instr{mload, 0},
			instr{del, 1}},
	},
}

func TestCodegen(t *testing.T) {
	for _, tc := range testCodeGenPrograms {
		t.Logf("Starting %s", tc.name)
		ast, err := Parse(tc.name, strings.NewReader(tc.source))
		if err != nil {
			t.Fatalf("Unexpected parse failure in %q: %s", tc.name, err)
		}
		err = Check(ast)
		if err != nil {
			t.Fatalf("Unexpected check failure in %q: %s", tc.name, err)
		}
		obj, err := CodeGen(tc.name, ast)
		if err != nil {
			t.Errorf("Compile errors for %q:\n%q", tc.name, err)
			continue
		}
		if diff := deep.Equal(tc.prog, obj.prog); diff != nil {
			t.Errorf("%s: VM prog doesn't match.\n%s", tc.name, diff)
		}
	}
}
<commit_msg>Fix formatting for a test error.<commit_after>// Copyright 2011 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package vm

import (
	"strings"
	"testing"

	"github.com/go-test/deep"
)

var testCodeGenPrograms = []struct {
	name   string
	source string
	prog   []instr // expected bytecode
}{
	// Composite literals require too many explicit conversions.
	{"simple line counter",
		"counter line_count\n/$/ { line_count++\n }\n",
		[]instr{
			instr{match, 0},
			instr{jnm, 7},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"count a",
		"counter a_count\n/a$/ { a_count++\n }\n",
		[]instr{
			instr{match, 0},
			instr{jnm, 7},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"strptime and capref",
		"counter foo\n" +
			"/(.*)/ { strptime($1, \"2006-01-02T15:04:05\")\n" +
			"foo++\n}\n",
		[]instr{
			instr{match, 0},
			instr{jnm, 11},
			instr{setmatched, false},
			instr{push, 0},
			instr{capref, 0},
			instr{str, 0},
			instr{strptime, 2},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"strptime and named capref",
		"counter foo\n" +
			"/(?P<date>.*)/ { strptime($date, \"2006-01-02T15:04:05\")\n" +
			"foo++\n }\n",
		[]instr{
			instr{match, 0},
			instr{jnm, 11},
			instr{setmatched, false},
			instr{push, 0},
			instr{capref, 1},
			instr{str, 0},
			instr{strptime, 2},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"inc by and set",
		"counter foo\ncounter bar\n" +
			"/([0-9]+)/ {\n" +
			"foo += $1\n" +
			"bar = $1\n" +
			"}\n",
		[]instr{
			instr{match, 0},
			instr{jnm, 17},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{mload, 0},
			instr{dload, 0},
			instr{push, 0},
			instr{capref, 0},
			instr{iadd, nil},
			instr{iset, nil},
			instr{mload, 1},
			instr{dload, 0},
			instr{push, 0},
			instr{capref, 0},
			instr{iset, nil},
			instr{setmatched, true}}},
	{"cond expr gt",
		"counter foo\n" +
			"1 > 0 {\n" +
			"  foo++\n" +
			"}\n",
		[]instr{
			instr{push, 1},
			instr{push, 0},
			instr{cmp, 1},
			instr{jnm, 9},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"cond expr lt",
		"counter foo\n" +
			"1 < 0 {\n" +
			"  foo++\n" +
			"}\n",
		[]instr{
			instr{push, 1},
			instr{push, 0},
			instr{cmp, -1},
			instr{jnm, 9},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"cond expr eq",
		"counter foo\n" +
			"1 == 0 {\n" +
			"  foo++\n" +
			"}\n",
		[]instr{
			instr{push, 1},
			instr{push, 0},
			instr{cmp, 0},
			instr{jnm, 9},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"cond expr le",
		"counter foo\n" +
			"1 <= 0 {\n" +
			"  foo++\n" +
			"}\n",
		[]instr{
			instr{push, 1},
			instr{push, 0},
			instr{cmp, 1},
			instr{jm, 9},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"cond expr ge",
		"counter foo\n" +
			"1 >= 0 {\n" +
			"  foo++\n" +
			"}\n",
		[]instr{
			instr{push, 1},
			instr{push, 0},
			instr{cmp, -1},
			instr{jm, 9},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"cond expr ne",
		"counter foo\n" +
			"1 != 0 {\n" +
			"  foo++\n" +
			"}\n",
		[]instr{
			instr{push, 1},
			instr{push, 0},
			instr{cmp, 0},
			instr{jm, 9},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"nested cond",
		"counter foo\n" +
			"/(.*)/ {\n" +
			"  $1 <= 1 {\n" +
			"    foo++\n" +
			"  }\n" +
			"}\n",
		[]instr{
			instr{match, 0},
			instr{jnm, 14},
			instr{setmatched, false},
			instr{push, 0},
			instr{capref, 0},
			instr{push, 1},
			instr{cmp, 1},
			instr{jm, 13},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true},
			instr{setmatched, true}}},
	{"deco",
		"counter foo\n" +
			"counter bar\n" +
			"def fooWrap {\n" +
			"  /.*/ {\n" +
			"    foo++\n" +
			"    next\n" +
			"  }\n" +
			"}\n" +
			"" +
			"@fooWrap { bar++\n }\n",
		[]instr{
			instr{match, 0},
			instr{jnm, 10},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{mload, 1},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"length",
		"len(\"foo\") > 0 {\n" +
			"}\n",
		[]instr{
			instr{str, 0},
			instr{length, 1},
			instr{push, 0},
			instr{cmp, 1},
			instr{jnm, 7},
			instr{setmatched, false},
			instr{setmatched, true}}},
	{"bitwise", `
1 & 7 ^ 15 | 8
~ 16 << 2
1 >> 20
`,
		[]instr{
			instr{push, 1},
			instr{push, 7},
			instr{and, nil},
			instr{push, 15},
			instr{xor, nil},
			instr{push, 8},
			instr{or, nil},
			instr{push, 16},
			instr{not, nil},
			instr{push, 2},
			instr{shl, nil},
			instr{push, 1},
			instr{push, 20},
			instr{shr, nil}}},
	{"pow", `
counter a
counter b
a ** b
`,
		[]instr{
			instr{mload, 0},
			instr{dload, 0},
			instr{mload, 1},
			instr{dload, 0},
			instr{ipow, nil}}},
	{"indexed expr", `
counter a by b
a["string"]++
`,
		[]instr{
			instr{str, 0},
			instr{mload, 0},
			instr{dload, 1},
			instr{inc, nil}}},
	{"strtol", `
strtol("deadbeef", 16)
`,
		[]instr{
			instr{str, 0},
			instr{push, 16},
			instr{strtol, 2}}},
	{"float", `
20.0
`,
		[]instr{
			instr{push, 20.0}}},
	{"otherwise", `
counter a
otherwise {
	a++
}
`,
		[]instr{
			instr{otherwise, nil},
			instr{jnm, 7},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true}}},
	{"cond else",
		`counter foo
counter bar
1 > 0 {
  foo++
} else {
  bar++
}`,
		[]instr{
			instr{push, 1},
			instr{push, 0},
			instr{cmp, 1},
			instr{jnm, 10},
			instr{setmatched, false},
			instr{mload, 0},
			instr{dload, 0},
			instr{inc, nil},
			instr{setmatched, true},
			instr{jmp, 13},
			instr{mload, 1},
			instr{dload, 0},
			instr{inc, nil},
		},
	},
	{"mod",
		`
3 % 1
`,
		[]instr{
			instr{push, 3},
			instr{push, 1},
			instr{imod, nil},
		},
	},
	{"del", `
counter a by b
del a["string"]
`,
		[]instr{
			instr{str, 0},
			instr{mload, 0},
			instr{del, 1}},
	},
}

func TestCodegen(t *testing.T) {
	for _, tc := range testCodeGenPrograms {
		t.Logf("Starting %s", tc.name)
		ast, err := Parse(tc.name, strings.NewReader(tc.source))
		if err != nil {
			t.Fatalf("Unexpected parse failure in %q: %s", tc.name, err)
		}
		err = Check(ast)
		if err != nil {
			t.Fatalf("Unexpected check failure in %q: %s", tc.name, err)
		}
		obj, err := CodeGen(tc.name, ast)
		if err != nil {
			t.Errorf("Compile errors for %q:\n%s", tc.name, err)
			continue
		}
		if diff := deep.Equal(tc.prog, obj.prog); diff != nil {
			t.Errorf("%s: VM prog doesn't match.\n%s", tc.name, diff)
		}
	}
}
<|endoftext|>


<commit_before>package vnc

import (
	"bytes"
	"encoding/base64"
	"image"
	"image/color"
	"image/png"

	"github.com/golang/glog"
	vnclib "github.com/kward/go-vnc"
	"github.com/kward/venue/venuelib"
)

// Framebuffer maintains a local copy of the remote VNC image.
type Framebuffer struct {
	Width, Height int
	fb            *image.RGBA
}

// NewFramebuffer returns a new Framebuffer object.
func NewFramebuffer(w, h int) *Framebuffer {
	return &Framebuffer{
		w, h,
		image.NewRGBA(image.Rectangle{image.Point{0, 0}, image.Point{w, h}}),
	}
}

// Paint accepts a Rectangle and Color data, and paints the framebuffer with it.
func (f *Framebuffer) Paint(r vnclib.Rectangle, colors []vnclib.Color) {
	if glog.V(3) {
		glog.Info(venuelib.FnName())
	}
	// TODO(kward): Implement double or triple buffering to reduce paint
	// interference.
	for x := 0; x < int(r.Width); x++ {
		for y := 0; y < int(r.Height); y++ {
			c := colors[x+y*int(r.Width)]
			f.fb.SetRGBA(x+int(r.X), y+int(r.Y), color.RGBA{uint8(c.R), uint8(c.G), uint8(c.B), 255})
		}
	}
}

// PNG converts the framebuffer into a base64 encoded PNG string.
func (f *Framebuffer) PNG() (string, error) {
	if glog.V(3) {
		glog.Info(venuelib.FnName())
	}
	var buf bytes.Buffer
	err := png.Encode(&buf, f.fb)
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(buf.Bytes()), nil
}
<commit_msg>increased logging level for Paint()<commit_after>package vnc

import (
	"bytes"
	"encoding/base64"
	"image"
	"image/color"
	"image/png"

	"github.com/golang/glog"
	vnclib "github.com/kward/go-vnc"
	"github.com/kward/venue/venuelib"
)

// Framebuffer maintains a local copy of the remote VNC image.
type Framebuffer struct {
	Width, Height int
	fb            *image.RGBA
}

// NewFramebuffer returns a new Framebuffer object.
func NewFramebuffer(w, h int) *Framebuffer {
	return &Framebuffer{
		w, h,
		image.NewRGBA(image.Rectangle{image.Point{0, 0}, image.Point{w, h}}),
	}
}

// Paint accepts a Rectangle and Color data, and paints the framebuffer with it.
func (f *Framebuffer) Paint(r vnclib.Rectangle, colors []vnclib.Color) {
	if glog.V(4) {
		glog.Info(venuelib.FnName())
	}
	// TODO(kward): Implement double or triple buffering to reduce paint
	// interference.
	for x := 0; x < int(r.Width); x++ {
		for y := 0; y < int(r.Height); y++ {
			c := colors[x+y*int(r.Width)]
			f.fb.SetRGBA(x+int(r.X), y+int(r.Y), color.RGBA{uint8(c.R), uint8(c.G), uint8(c.B), 255})
		}
	}
}

// PNG converts the framebuffer into a base64 encoded PNG string.
func (f *Framebuffer) PNG() (string, error) {
	if glog.V(3) {
		glog.Info(venuelib.FnName())
	}
	var buf bytes.Buffer
	err := png.Encode(&buf, f.fb)
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(buf.Bytes()), nil
}
<|endoftext|>


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

package stomp

import (
	//	"fmt"
	//	"os"
	"testing"
	"time"
)

// Test Ack errors
func TestAckErrors(t *testing.T) {

	n, _ := openConn(t)
	conn_headers := check11(TEST_HEADERS)
	c, _ := Connect(n, conn_headers)

	h := Headers{}
	// No subscription
	e := c.Ack(h)
	if c.protocol >= SPL_11 {
		if e == nil {
			t.Errorf("ACK -1- expected [nil], got error: %[v]\n", e)
		}
		if e != EREQSUBACK {
			t.Errorf("ACK -1- expected error [%v], got [%v]\n", EREQSUBACK, e)
		}
	} else {
		if e == nil {
			t.Errorf("ACK -2- expected [nil], got error: %[v]\n", e)
		}
		if e != EREQMIDACK {
			t.Errorf("ACK -2- expected error [%v], got [%v]\n", EREQMIDACK, e)
		}
	}
	h = Headers{"subscription", "my-sub-id"}
	// No message id
	e = c.Ack(h)
	if e == nil {
		t.Errorf("ACK -3- expected [nil], got error: %[v]\n", e)
	}
	if e != EREQMIDACK {
		t.Errorf("ACK -3- expected error [%v], got [%v]\n", EREQMIDACK, e)
	}

	//
	_ = c.Disconnect(h)
	_ = closeConn(t, n)

}

// Test Ack Same Connection
func TestAckSameConn(t *testing.T) {

	n, _ := openConn(t)
	conn_headers := check11(TEST_HEADERS)
	c, _ := Connect(n, conn_headers)

	// Basic headers
	h := Headers{"destination", TEST_TDESTPREF + "acksc1-" + c.protocol}
	m := "acksc1 message 1"
	si := TEST_TDESTPREF + "acksc1.protocol-" + c.protocol
	// Subscribe Headers
	sh := h.Add("ack", "client")
	sh = sh.Add("id", si) // Always use an 'id'
	// Unsubscribe headers
	uh := h.Add("id", si)
	// Subscribe
	s, e := c.Subscribe(sh)
	if e != nil {
		t.Errorf("SUBSCRIBE expected [nil], got: [%v]\n", e)
	}
	hn := h.Add("current-time", time.Now().String())
	e = c.Send(hn, m)
	if e != nil {
		t.Errorf("SEND expected [nil], got: [%v]\n", e)
	}
	// Receive
	r := <-s
	if r.Error != nil {
		t.Errorf("read error:  expected [nil], got: [%v]\n", r.Error)
	}
	if m != r.Message.BodyString() {
		t.Errorf("message error: expected: [%v], got: [%v]\n", m, r.Message.BodyString())
	}

	// Ack headers
	a := Headers{"message-id", r.Message.Headers.Value("message-id")}
	a = a.Add("subscription", si) // Always use subscription
	// Ack
	e = c.Ack(a)
	if e != nil {
		t.Errorf("ACK expected [nil], got: [%v]\n", e)
	}

	// Error check: Apollo bug ?
	select {
	case r = <-s:
		t.Errorf("RECEIVE not expected, got: [%v]\n", r)
	default:
	}

	// Unsubscribe
	e = c.Unsubscribe(uh)
	if e != nil {
		t.Errorf("UNSUBSCRIBE expected [nil], got: [%v]\n", e)
	}
	//
	checkReceived(t, c, "tasc1")
	_ = c.Disconnect(h)
	_ = closeConn(t, n)

}

// Test Ack Different Connection
func TestAckDiffConn(t *testing.T) {

	n, _ := openConn(t)
	conn_headers := check11(TEST_HEADERS)
	c, _ := Connect(n, conn_headers)

	// Basic headers
	h := Headers{"destination", TEST_TDESTPREF + "ackdc1-" + c.protocol}
	m := "ackdc1 message 1"
	si := TEST_TDESTPREF + "ackdc1.protocol-" + c.protocol
	// Send
	hn := h.Add("current-time", time.Now().String())
	e := c.Send(hn, m)
	if e != nil {
		t.Errorf("SEND expected [nil], got: [%v]\n", e)
	}
	// Disconnect
	_ = c.Disconnect(h)
	_ = closeConn(t, n)

	// Restart
	n, _ = openConn(t)
	c, _ = Connect(n, conn_headers)

	// Subscribe Headers
	sh := h.Add("ack", "client")
	sh = sh.Add("id", si) // Always use an 'id'
	// Unsubscribe headers
	uh := h.Add("id", si)

	// Subscribe
	s, e := c.Subscribe(sh)
	if e != nil {
		t.Errorf("SUBSCRIBE expected [nil], got: [%v]\n", e)
	}
	// Receive
	r := <-s
	if r.Error != nil {
		t.Errorf("read error:  expected [nil], got: [%v]\n", r.Error)
	}
	if m != r.Message.BodyString() {
		t.Errorf("message error: expected: [%v], got: [%v]\n", m, r.Message.BodyString())
	}
	// Ack headers
	a := Headers{"message-id", r.Message.Headers.Value("message-id")}
	a = a.Add("subscription", si) // Always use subscription
	// Ack
	e = c.Ack(a)
	if e != nil {
		t.Errorf("ACK expected [nil], got: [%v]\n", e)
	}

	// Error check: Apollo bug ?
	select {
	case r = <-s:
		t.Errorf("RECEIVE not expected, got: [%v]\n", r)
	default:
	}

	// Unsubscribe
	e = c.Unsubscribe(uh)
	if e != nil {
		t.Errorf("UNSUBSCRIBE expected [nil], got: [%v]\n", e)
	}
	//
	_ = c.Disconnect(h)
	_ = closeConn(t, n)

}

// Test Nack errors
func TestNackErrors(t *testing.T) {

	n, _ := openConn(t)
	conn_headers := check11(TEST_HEADERS)
	c, _ := Connect(n, conn_headers)

	h := Headers{}
	// No subscription
	e := c.Nack(h)
	if c.protocol == SPL_10 {
		if e == nil {
			t.Errorf("NACK -1- expected error, got [nil]\n")
		}
		if e != EBADVER {
			t.Errorf("NACK expected error [%v], got [%v]\n", EBADVER, e)
		}
		_ = c.Disconnect(h)
		_ = closeConn(t, n)
		return
	}
	if e == nil {
		t.Errorf("NACK -2- expected error, got [nil]\n")
	}
	if e != EREQSUBNAK {
		t.Errorf("NACK expected error [%v], got [%v]\n", EREQSUBNAK, e)
	}
	h = Headers{"subscription", "my-sub-id"}
	// No message id
	e = c.Nack(h)
	if e == nil {
		t.Errorf("NACK -3- expected error, got [nil]\n")
	}
	if e != EREQMIDNAK {
		t.Errorf("NACK expected error [%v], got [%v]\n", EREQMIDNAK, e)
	}
	//
	_ = c.Disconnect(h)
	_ = closeConn(t, n)

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

package stomp

import (
	//	"fmt"
	//	"os"
	"testing"
	"time"
)

// Test Ack errors
func TestAckErrors(t *testing.T) {

	n, _ := openConn(t)
	conn_headers := check11(TEST_HEADERS)
	c, _ := Connect(n, conn_headers)

	h := Headers{}
	// No subscription
	e := c.Ack(h)
	if c.protocol >= SPL_11 {
		if e == nil {
			t.Errorf("ACK -1- expected [nil], got error: %[v]\n", e)
		}
		if e != EREQSUBACK {
			t.Errorf("ACK -1- expected error [%v], got [%v]\n", EREQSUBACK, e)
		}
	} else {
		if e == nil {
			t.Errorf("ACK -2- expected [nil], got error: %[v]\n", e)
		}
		if e != EREQMIDACK {
			t.Errorf("ACK -2- expected error [%v], got [%v]\n", EREQMIDACK, e)
		}
	}
	h = Headers{"subscription", "my-sub-id"}
	// No message id
	e = c.Ack(h)
	if e == nil {
		t.Errorf("ACK -3- expected [nil], got error: %[v]\n", e)
	}
	if e != EREQMIDACK {
		t.Errorf("ACK -3- expected error [%v], got [%v]\n", EREQMIDACK, e)
	}

	//
	_ = c.Disconnect(h)
	_ = closeConn(t, n)

}

// Test Ack Same Connection
func TestAckSameConn(t *testing.T) {

	n, _ := openConn(t)
	conn_headers := check11(TEST_HEADERS)
	c, _ := Connect(n, conn_headers)

	// Basic headers
	h := Headers{"destination", TEST_TDESTPREF + "acksc1-" + c.protocol}
	m := "acksc1 message 1"
	si := TEST_TDESTPREF + "acksc1.protocol-" + c.protocol
	// Subscribe Headers
	sh := h.Add("ack", "client")
	sh = sh.Add("id", si) // Always use an 'id'
	// Unsubscribe headers
	uh := h.Add("id", si)
	// Subscribe
	s, e := c.Subscribe(sh)
	if e != nil {
		t.Errorf("SUBSCRIBE expected [nil], got: [%v]\n", e)
	}
	hn := h.Add("current-time", time.Now().String())
	e = c.Send(hn, m)
	if e != nil {
		t.Errorf("SEND expected [nil], got: [%v]\n", e)
	}
	// Receive
	r := <-s
	if r.Error != nil {
		t.Errorf("read error:  expected [nil], got: [%v]\n", r.Error)
	}
	if m != r.Message.BodyString() {
		t.Errorf("message error: expected: [%v], got: [%v]\n", m, r.Message.BodyString())
	}

	// Ack headers
	a := Headers{"message-id", r.Message.Headers.Value("message-id")}
	a = a.Add("subscription", si) // Always use subscription
	// Ack
	e = c.Ack(a)
	if e != nil {
		t.Errorf("ACK expected [nil], got: [%v]\n", e)
	}

	time.Sleep(1e9 / 20) // 50ms. See Apollo Jira issue APLO-88
	// See Apollo Jira issue APLO-88
	select {
	case r = <-s:
		t.Errorf("RECEIVE not expected, got: [%v]\n", r)
	default:
	}

	// Unsubscribe
	e = c.Unsubscribe(uh)
	if e != nil {
		t.Errorf("UNSUBSCRIBE expected [nil], got: [%v]\n", e)
	}
	//
	checkReceived(t, c, "tasc1")
	_ = c.Disconnect(h)
	_ = closeConn(t, n)

}

// Test Ack Different Connection
func TestAckDiffConn(t *testing.T) {

	n, _ := openConn(t)
	conn_headers := check11(TEST_HEADERS)
	c, _ := Connect(n, conn_headers)

	// Basic headers
	h := Headers{"destination", TEST_TDESTPREF + "ackdc1-" + c.protocol}
	m := "ackdc1 message 1"
	si := TEST_TDESTPREF + "ackdc1.protocol-" + c.protocol
	// Send
	hn := h.Add("current-time", time.Now().String())
	e := c.Send(hn, m)
	if e != nil {
		t.Errorf("SEND expected [nil], got: [%v]\n", e)
	}
	// Disconnect
	_ = c.Disconnect(h)
	_ = closeConn(t, n)

	// Restart
	n, _ = openConn(t)
	c, _ = Connect(n, conn_headers)

	// Subscribe Headers
	sh := h.Add("ack", "client")
	sh = sh.Add("id", si) // Always use an 'id'
	// Unsubscribe headers
	uh := h.Add("id", si)

	// Subscribe
	s, e := c.Subscribe(sh)
	if e != nil {
		t.Errorf("SUBSCRIBE expected [nil], got: [%v]\n", e)
	}
	// Receive
	r := <-s
	if r.Error != nil {
		t.Errorf("read error:  expected [nil], got: [%v]\n", r.Error)
	}
	if m != r.Message.BodyString() {
		t.Errorf("message error: expected: [%v], got: [%v]\n", m, r.Message.BodyString())
	}
	// Ack headers
	a := Headers{"message-id", r.Message.Headers.Value("message-id")}
	a = a.Add("subscription", si) // Always use subscription
	// Ack
	e = c.Ack(a)
	if e != nil {
		t.Errorf("ACK expected [nil], got: [%v]\n", e)
	}

	time.Sleep(1e9 / 20) // 50ms. See Apollo Jira issue APLO-88
	// See Apollo Jira issue APLO-88
	select {
	case r = <-s:
		t.Errorf("RECEIVE not expected, got: [%v]\n", r)
	default:
	}

	// Unsubscribe
	e = c.Unsubscribe(uh)
	if e != nil {
		t.Errorf("UNSUBSCRIBE expected [nil], got: [%v]\n", e)
	}
	//
	_ = c.Disconnect(h)
	_ = closeConn(t, n)

}

// Test Nack errors
func TestNackErrors(t *testing.T) {

	n, _ := openConn(t)
	conn_headers := check11(TEST_HEADERS)
	c, _ := Connect(n, conn_headers)

	h := Headers{}
	// No subscription
	e := c.Nack(h)
	if c.protocol == SPL_10 {
		if e == nil {
			t.Errorf("NACK -1- expected error, got [nil]\n")
		}
		if e != EBADVER {
			t.Errorf("NACK expected error [%v], got [%v]\n", EBADVER, e)
		}
		_ = c.Disconnect(h)
		_ = closeConn(t, n)
		return
	}
	if e == nil {
		t.Errorf("NACK -2- expected error, got [nil]\n")
	}
	if e != EREQSUBNAK {
		t.Errorf("NACK expected error [%v], got [%v]\n", EREQSUBNAK, e)
	}
	h = Headers{"subscription", "my-sub-id"}
	// No message id
	e = c.Nack(h)
	if e == nil {
		t.Errorf("NACK -3- expected error, got [nil]\n")
	}
	if e != EREQMIDNAK {
		t.Errorf("NACK expected error [%v], got [%v]\n", EREQMIDNAK, e)
	}
	//
	_ = c.Disconnect(h)
	_ = closeConn(t, n)

}
<|endoftext|>


<commit_before>// Package world provide a Go interface to WORLD - a high-quality speech analysis, modification and synthesis system.
package world

// Functions in this file ara simple ports to original cpp functions.
// Go-like interfaces will be found in go_interface.go

// #cgo pkg-config: world
// #include <world/dio.h>
// #include <world/stonemask.h>
// #include <world/platinum.h>
// #include <world/synthesis.h>
// #include <world/star.h>
// #include <world/cheaptrick.h>
// #include <world/aperiodicity.h>
// #include <world/synthesisfromaperiodicity.h>
// #include <world/tandem_ap.h>
// #include <world/synthesis_ap.h>
// #include <stdlib.h>
import "C"

const (
	byteSizeOfFloat64 = 8
)

type DioOption struct {
	F0Floor          float64
	F0Ceil           float64
	ChannelsInOctave float64
	FramePeriod      float64 // [ms]
	Speed            int     // (1,2, ..., 12)
}

func NewDioOption() DioOption {
	opt := DioOption{
		F0Floor:          80.0,
		F0Ceil:           800.0,
		FramePeriod:      5,
		ChannelsInOctave: 4.0,
		Speed:            2,
	}
	return opt
}

func Dio(x []float64, fs int, option DioOption) ([]float64, []float64) {
	numFrames := GetSamplesForDIO(fs, len(x), option.FramePeriod)
	timeAxis := make([]float64, numFrames)
	f0 := make([]float64, numFrames)

	// Create C interface of DioOption
	var opt C.DioOption
	opt.frame_period = C.double(option.FramePeriod)
	opt.f0_floor = C.double(option.F0Floor)
	opt.f0_ceil = C.double(option.F0Ceil)
	opt.channels_in_octave = C.double(option.ChannelsInOctave)
	opt.speed = C.int(option.Speed)

	// Perform DIO
	C.Dio((*C.double)(&x[0]),
		C.int(len(x)),
		C.int(fs),
		opt,
		(*C.double)(&timeAxis[0]),
		(*C.double)(&f0[0]))

	return timeAxis, f0
}

func GetSamplesForDIO(fs, x_length int, framePeriod float64) int {
	return int(C.GetSamplesForDIO(C.int(fs), C.int(x_length), C.double(framePeriod)))
}

func StoneMask(x []float64, fs int, timeAxis, f0 []float64) []float64 {
	refinedF0 := make([]float64, len(f0))

	// Perform StoneMask
	C.StoneMask((*C.double)(&x[0]),
		C.int(len(x)),
		C.int(fs),
		(*C.double)(&timeAxis[0]),
		(*C.double)(&f0[0]),
		C.int(len(f0)),
		(*C.double)(&refinedF0[0]))

	return refinedF0
}

// will be deprecated
func Star(x []float64, fs int, timeAxis, f0 []float64) [][]float64 {
	FFTSize := C.size_t(C.GetFFTSizeForStar(C.int(fs)))
	numFreqBins := C.size_t(FFTSize/2 + 1)

	spectrogram := make([][]float64, len(f0))
	for i := range spectrogram {
		spectrogram[i] = make([]float64, numFreqBins)
	}

	spectrogramUsedInC := Make2DCArrayAlternative(spectrogram)

	// Perform star
	C.Star((*C.double)(&x[0]),
		C.int(len(x)),
		C.int(fs),
		(*C.double)(&timeAxis[0]),
		(*C.double)(&f0[0]),
		C.int(len(f0)),
		(**C.double)(&spectrogramUsedInC[0]))

	return spectrogram
}

// will be deprecated
func GetFFTSizeForStar(fs int) int {
	return int(C.GetFFTSizeForStar(C.int(fs)))
}

func CheapTrick(x []float64, fs int, timeAxis, f0 []float64) [][]float64 {
	FFTSize := C.size_t(C.GetFFTSizeForCheapTrick(C.int(fs)))
	numFreqBins := C.size_t(FFTSize/2 + 1)

	spectrogram := make([][]float64, len(f0))
	for i := range spectrogram {
		spectrogram[i] = make([]float64, numFreqBins)
	}

	spectrogramUsedInC := Make2DCArrayAlternative(spectrogram)

	// Perform star
	C.CheapTrick((*C.double)(&x[0]),
		C.int(len(x)),
		C.int(fs),
		(*C.double)(&timeAxis[0]),
		(*C.double)(&f0[0]),
		C.int(len(f0)),
		(**C.double)(&spectrogramUsedInC[0]))

	return spectrogram
}

func GetFFTSizeForCheapTrick(fs int) int {
	return int(C.GetFFTSizeForCheapTrick(C.int(fs)))
}

func Platinum(x []float64, fs int, timeAxis, f0 []float64, spectrogram [][]float64) [][]float64 {
	FFTSize := C.size_t(C.GetFFTSizeForStar(C.int(fs)))
	numFreqBins := C.size_t(FFTSize + 1)

	residualSpectrogram := make([][]float64, len(f0))
	for i := range residualSpectrogram {
		residualSpectrogram[i] = make([]float64, numFreqBins)
	}

	spectrogramUsedInC := Make2DCArrayAlternative(spectrogram)
	residualSpectrogramUsedInC := Make2DCArrayAlternative(residualSpectrogram)

	C.Platinum((*C.double)(&x[0]),
		C.int(len(x)),
		C.int(fs),
		(*C.double)(&timeAxis[0]),
		(*C.double)(&f0[0]),
		C.int(len(f0)),
		(**C.double)(&spectrogramUsedInC[0]),
		C.int(FFTSize),
		(**C.double)(&residualSpectrogramUsedInC[0]))

	return residualSpectrogram
}

func Synthesis(f0 []float64, spectrogram, residualSpectrogram [][]float64, framePeriod float64, fs, length int) []float64 {
	FFTSize := C.size_t(C.GetFFTSizeForStar(C.int(fs)))

	spectrogramUsedInC := Make2DCArrayAlternative(spectrogram)
	residualSpectrogramUsedInC := Make2DCArrayAlternative(residualSpectrogram)

	synthesized := make([]float64, length)
	C.Synthesis((*C.double)(&f0[0]),
		C.int(len(f0)),
		(**C.double)(&spectrogramUsedInC[0]),
		(**C.double)(&residualSpectrogramUsedInC[0]),
		C.int(FFTSize),
		C.double(framePeriod),
		C.int(fs),
		C.int(len(synthesized)),
		(*C.double)(&synthesized[0]))

	return synthesized
}

func AperiodicityRatio(x []float64, fs int, f0, timeAxis []float64) [][]float64 {
	FFTSize := C.size_t(C.GetFFTSizeForStar(C.int(fs)))
	numBins := C.size_t(FFTSize/2 + 1)

	aperiodicity := make([][]float64, len(f0))
	for i := range aperiodicity {
		aperiodicity[i] = make([]float64, numBins)
	}
	aperiodicityUsedInC := Make2DCArrayAlternative(aperiodicity)

	// Peform aperiodicity analysis
	C.AperiodicityRatio((*C.double)(&x[0]),
		C.int(len(x)),
		C.int(fs),
		(*C.double)(&f0[0]),
		C.int(len(f0)),
		(*C.double)(&timeAxis[0]),
		C.int(FFTSize),
		(**C.double)(&aperiodicityUsedInC[0]))

	return aperiodicity
}

func SynthesisFromAperiodicity(f0 []float64, spectrogram, aperiodicity [][]float64, framePeriod float64, fs, length int) []float64 {
	FFTSize := C.size_t(C.GetFFTSizeForStar(C.int(fs)))

	spectrogramUsedInC := Make2DCArrayAlternative(spectrogram)
	aperiodicityUsedInC := Make2DCArrayAlternative(aperiodicity)

	synthesized := make([]float64, length)
	C.SynthesisFromAperiodicity((*C.double)(&f0[0]),
		C.int(len(f0)),
		(**C.double)(&spectrogramUsedInC[0]),
		(**C.double)(&aperiodicityUsedInC[0]),
		C.int(FFTSize),
		C.double(framePeriod),
		C.int(fs),
		C.int(len(synthesized)),
		(*C.double)(&synthesized[0]))

	return synthesized
}
<commit_msg>Remove deprecated headers in v.0.1.4<commit_after>// Package world provide a Go interface to WORLD - a high-quality speech analysis, modification and synthesis system.
package world

// Functions in this file ara simple ports to original cpp functions.
// Go-like interfaces will be found in go_interface.go

// #cgo pkg-config: world
// #include <world/dio.h>
// #include <world/stonemask.h>
// #include <world/platinum.h>
// #include <world/synthesis.h>
// #include <world/star.h>
// #include <world/cheaptrick.h>
// #include <world/aperiodicity.h>
// #include <world/synthesisfromaperiodicity.h>
// #include <stdlib.h>
import "C"

const (
	byteSizeOfFloat64 = 8
)

type DioOption struct {
	F0Floor          float64
	F0Ceil           float64
	ChannelsInOctave float64
	FramePeriod      float64 // [ms]
	Speed            int     // (1,2, ..., 12)
}

func NewDioOption() DioOption {
	opt := DioOption{
		F0Floor:          80.0,
		F0Ceil:           800.0,
		FramePeriod:      5,
		ChannelsInOctave: 4.0,
		Speed:            2,
	}
	return opt
}

func Dio(x []float64, fs int, option DioOption) ([]float64, []float64) {
	numFrames := GetSamplesForDIO(fs, len(x), option.FramePeriod)
	timeAxis := make([]float64, numFrames)
	f0 := make([]float64, numFrames)

	// Create C interface of DioOption
	var opt C.DioOption
	opt.frame_period = C.double(option.FramePeriod)
	opt.f0_floor = C.double(option.F0Floor)
	opt.f0_ceil = C.double(option.F0Ceil)
	opt.channels_in_octave = C.double(option.ChannelsInOctave)
	opt.speed = C.int(option.Speed)

	// Perform DIO
	C.Dio((*C.double)(&x[0]),
		C.int(len(x)),
		C.int(fs),
		opt,
		(*C.double)(&timeAxis[0]),
		(*C.double)(&f0[0]))

	return timeAxis, f0
}

func GetSamplesForDIO(fs, x_length int, framePeriod float64) int {
	return int(C.GetSamplesForDIO(C.int(fs), C.int(x_length), C.double(framePeriod)))
}

func StoneMask(x []float64, fs int, timeAxis, f0 []float64) []float64 {
	refinedF0 := make([]float64, len(f0))

	// Perform StoneMask
	C.StoneMask((*C.double)(&x[0]),
		C.int(len(x)),
		C.int(fs),
		(*C.double)(&timeAxis[0]),
		(*C.double)(&f0[0]),
		C.int(len(f0)),
		(*C.double)(&refinedF0[0]))

	return refinedF0
}

// will be deprecated
func Star(x []float64, fs int, timeAxis, f0 []float64) [][]float64 {
	FFTSize := C.size_t(C.GetFFTSizeForStar(C.int(fs)))
	numFreqBins := C.size_t(FFTSize/2 + 1)

	spectrogram := make([][]float64, len(f0))
	for i := range spectrogram {
		spectrogram[i] = make([]float64, numFreqBins)
	}

	spectrogramUsedInC := Make2DCArrayAlternative(spectrogram)

	// Perform star
	C.Star((*C.double)(&x[0]),
		C.int(len(x)),
		C.int(fs),
		(*C.double)(&timeAxis[0]),
		(*C.double)(&f0[0]),
		C.int(len(f0)),
		(**C.double)(&spectrogramUsedInC[0]))

	return spectrogram
}

// will be deprecated
func GetFFTSizeForStar(fs int) int {
	return int(C.GetFFTSizeForStar(C.int(fs)))
}

func CheapTrick(x []float64, fs int, timeAxis, f0 []float64) [][]float64 {
	FFTSize := C.size_t(C.GetFFTSizeForCheapTrick(C.int(fs)))
	numFreqBins := C.size_t(FFTSize/2 + 1)

	spectrogram := make([][]float64, len(f0))
	for i := range spectrogram {
		spectrogram[i] = make([]float64, numFreqBins)
	}

	spectrogramUsedInC := Make2DCArrayAlternative(spectrogram)

	// Perform star
	C.CheapTrick((*C.double)(&x[0]),
		C.int(len(x)),
		C.int(fs),
		(*C.double)(&timeAxis[0]),
		(*C.double)(&f0[0]),
		C.int(len(f0)),
		(**C.double)(&spectrogramUsedInC[0]))

	return spectrogram
}

func GetFFTSizeForCheapTrick(fs int) int {
	return int(C.GetFFTSizeForCheapTrick(C.int(fs)))
}

func Platinum(x []float64, fs int, timeAxis, f0 []float64, spectrogram [][]float64) [][]float64 {
	FFTSize := C.size_t(C.GetFFTSizeForStar(C.int(fs)))
	numFreqBins := C.size_t(FFTSize + 1)

	residualSpectrogram := make([][]float64, len(f0))
	for i := range residualSpectrogram {
		residualSpectrogram[i] = make([]float64, numFreqBins)
	}

	spectrogramUsedInC := Make2DCArrayAlternative(spectrogram)
	residualSpectrogramUsedInC := Make2DCArrayAlternative(residualSpectrogram)

	C.Platinum((*C.double)(&x[0]),
		C.int(len(x)),
		C.int(fs),
		(*C.double)(&timeAxis[0]),
		(*C.double)(&f0[0]),
		C.int(len(f0)),
		(**C.double)(&spectrogramUsedInC[0]),
		C.int(FFTSize),
		(**C.double)(&residualSpectrogramUsedInC[0]))

	return residualSpectrogram
}

func Synthesis(f0 []float64, spectrogram, residualSpectrogram [][]float64, framePeriod float64, fs, length int) []float64 {
	FFTSize := C.size_t(C.GetFFTSizeForStar(C.int(fs)))

	spectrogramUsedInC := Make2DCArrayAlternative(spectrogram)
	residualSpectrogramUsedInC := Make2DCArrayAlternative(residualSpectrogram)

	synthesized := make([]float64, length)
	C.Synthesis((*C.double)(&f0[0]),
		C.int(len(f0)),
		(**C.double)(&spectrogramUsedInC[0]),
		(**C.double)(&residualSpectrogramUsedInC[0]),
		C.int(FFTSize),
		C.double(framePeriod),
		C.int(fs),
		C.int(len(synthesized)),
		(*C.double)(&synthesized[0]))

	return synthesized
}

func AperiodicityRatio(x []float64, fs int, f0, timeAxis []float64) [][]float64 {
	FFTSize := C.size_t(C.GetFFTSizeForStar(C.int(fs)))
	numBins := C.size_t(FFTSize/2 + 1)

	aperiodicity := make([][]float64, len(f0))
	for i := range aperiodicity {
		aperiodicity[i] = make([]float64, numBins)
	}
	aperiodicityUsedInC := Make2DCArrayAlternative(aperiodicity)

	// Peform aperiodicity analysis
	C.AperiodicityRatio((*C.double)(&x[0]),
		C.int(len(x)),
		C.int(fs),
		(*C.double)(&f0[0]),
		C.int(len(f0)),
		(*C.double)(&timeAxis[0]),
		C.int(FFTSize),
		(**C.double)(&aperiodicityUsedInC[0]))

	return aperiodicity
}

func SynthesisFromAperiodicity(f0 []float64, spectrogram, aperiodicity [][]float64, framePeriod float64, fs, length int) []float64 {
	FFTSize := C.size_t(C.GetFFTSizeForStar(C.int(fs)))

	spectrogramUsedInC := Make2DCArrayAlternative(spectrogram)
	aperiodicityUsedInC := Make2DCArrayAlternative(aperiodicity)

	synthesized := make([]float64, length)
	C.SynthesisFromAperiodicity((*C.double)(&f0[0]),
		C.int(len(f0)),
		(**C.double)(&spectrogramUsedInC[0]),
		(**C.double)(&aperiodicityUsedInC[0]),
		C.int(FFTSize),
		C.double(framePeriod),
		C.int(fs),
		C.int(len(synthesized)),
		(*C.double)(&synthesized[0]))

	return synthesized
}
<|endoftext|>


<commit_before>package xtcp

import (
	"bytes"
	"errors"
	"io"
	"net"
	"sync"
	"sync/atomic"
	"time"

	log "github.com/xfxdev/xlog"
)

const (
	connStateNormal int32 = iota
	connStateStopping
	connStateStopped
)

var (
	errSendToClosedConn = errors.New("send to closed conn")
	errSendListFull     = errors.New("send list full")

	bufferPool1K = &sync.Pool{
		New: func() interface{} {
			return make([]byte, 1<<10)
		},
	}
	bufferPool2K = &sync.Pool{
		New: func() interface{} {
			return make([]byte, 2<<10)
		},
	}
	bufferPool4K = &sync.Pool{
		New: func() interface{} {
			return make([]byte, 4<<10)
		},
	}
	bufferPoolBig = &sync.Pool{}
)

func getBufferFromPool(targetSize int) []byte {
	var buf []byte
	if targetSize <= 1<<10 {
		buf = bufferPool1K.Get().([]byte)
	} else if targetSize <= 2<<10 {
		buf = bufferPool2K.Get().([]byte)
	} else if targetSize <= 4<<10 {
		buf = bufferPool4K.Get().([]byte)
	} else {
		itr := bufferPoolBig.Get()
		if itr != nil {
			buf = itr.([]byte)
		} else {
			buf = make([]byte, targetSize)
		}
	}
	buf = buf[:targetSize]
	return buf
}

func putBufferToPool(buf []byte) {
	cap := cap(buf)
	if cap <= 1<<10 {
		bufferPool1K.Put(buf)
	} else if cap <= 2<<10 {
		bufferPool2K.Put(buf)
	} else if cap <= 4<<10 {
		bufferPool4K.Put(buf)
	} else {
		bufferPoolBig.Put(buf)
	}
}

// A Conn represents the server side of an tcp connection.
type Conn struct {
	sync.Mutex
	Opts        *Options
	RawConn     net.Conn
	UserData    interface{}
	sendBufList chan []byte
	closed      chan struct{}
	state       int32
	wg          sync.WaitGroup
	once        sync.Once
	SendDropped uint32
	sendBytes   uint64
	recvBytes   uint64
	dropped     uint32
}

// NewConn return new conn.
func NewConn(opts *Options) *Conn {
	if opts.RecvBufSize <= 0 {
		log.Warnf("Invalid Opts.RecvBufSize : %v, use DefaultRecvBufSize instead", opts.RecvBufSize)
		opts.RecvBufSize = DefaultRecvBufSize
	}
	if opts.SendBufListLen <= 0 {
		log.Warnf("Invalid Opts.SendBufListLen : %v, use DefaultRecvBufSize instead", opts.SendBufListLen)
		opts.SendBufListLen = DefaultSendBufListLen
	}
	c := &Conn{
		Opts:        opts,
		sendBufList: make(chan []byte, opts.SendBufListLen),
		closed:      make(chan struct{}),
		state:       connStateNormal,
	}

	return c
}

func (c *Conn) String() string {
	return c.RawConn.LocalAddr().String() + " -> " + c.RawConn.RemoteAddr().String()
}

// SendBytes return the total send bytes.
func (c *Conn) SendBytes() uint64 {
	return atomic.LoadUint64(&c.sendBytes)
}

// RecvBytes return the total receive bytes.
func (c *Conn) RecvBytes() uint64 {
	return atomic.LoadUint64(&c.recvBytes)
}

// DroppedPacket return the total dropped packet.
func (c *Conn) DroppedPacket() uint32 {
	return atomic.LoadUint32(&c.dropped)
}

// Stop stops the conn.
func (c *Conn) Stop(mode StopMode) {
	c.once.Do(func() {
		if mode == StopImmediately {
			atomic.StoreInt32(&c.state, connStateStopped)
			c.RawConn.Close()
			//close(c.sendBufList) // leave channel open, because other goroutine maybe use it in Send.
			close(c.closed)
		} else {
			atomic.StoreInt32(&c.state, connStateStopping)
			// c.RawConn.Close() 	// will close in sendLoop
			// close(c.sendBufList)
			close(c.closed)
			if mode == StopGracefullyAndWait {
				c.wg.Wait()
			}
		}
	})
}

// IsStoped return true if Conn is stopped, otherwise return false.
func (c *Conn) IsStoped() bool {
	return atomic.LoadInt32(&c.state) != connStateNormal
}

func (c *Conn) serve() {
	tcpConn := c.RawConn.(*net.TCPConn)
	tcpConn.SetNoDelay(c.Opts.NoDelay)
	tcpConn.SetKeepAlive(c.Opts.KeepAlive)
	if c.Opts.KeepAlivePeriod != 0 {
		tcpConn.SetKeepAlivePeriod(c.Opts.KeepAlivePeriod)
	}

	if c.Opts.AsyncWrite {
		c.wg.Add(2)
		go c.sendLoop()
	} else {
		c.wg.Add(1)
	}
	c.recvLoop()

	c.Opts.Handler.OnClose(c)
}

func (c *Conn) recvLoop() {
	var tempDelay time.Duration
	tempBuf := make([]byte, c.Opts.RecvBufSize)
	recvBuf := bytes.NewBuffer(make([]byte, 0, c.Opts.RecvBufSize))
	maxDelay := 1 * time.Second

	defer func() {
		log.Debug("XTCP - Conn recv loop exit : ", c.RawConn.RemoteAddr())
		c.wg.Done()
	}()

	for {
		if c.Opts.ReadDeadline != 0 {
			c.RawConn.SetReadDeadline(time.Now().Add(c.Opts.ReadDeadline))
		}

		n, err := c.RawConn.Read(tempBuf)
		if err != nil {
			if nerr, ok := err.(net.Error); ok {
				if nerr.Timeout() {
					// timeout
				} else if nerr.Temporary() {
					if tempDelay == 0 {
						tempDelay = 5 * time.Millisecond
					} else {
						tempDelay *= 2
					}
					if tempDelay > maxDelay {
						tempDelay = maxDelay
					}
					log.Errorf("XTCP - Conn[%v] recv error : %v; retrying in %v", c.RawConn.RemoteAddr(), err, tempDelay)
					time.Sleep(tempDelay)
					continue
				}
			}

			if !c.IsStoped() {
				if err != io.EOF {
					log.Errorf("XTCP - Conn[%v] recv error : %v", c.RawConn.RemoteAddr(), err)
				}
				c.Stop(StopImmediately)
			}

			return
		}

		recvBuf.Write(tempBuf[:n])
		atomic.AddUint64(&c.recvBytes, uint64(n))
		tempDelay = 0

		for recvBuf.Len() > 0 {
			p, pl, err := c.Opts.Protocol.Unpack(recvBuf.Bytes())
			if err != nil {
				c.Opts.Handler.OnUnpackErr(c, recvBuf.Bytes(), err)
			}

			if pl > 0 {
				_ = recvBuf.Next(pl)
			}

			if p != nil {
				c.Opts.Handler.OnRecv(c, p)
			} else {
				break
			}
		}
	}
}

func (c *Conn) sendBuf(buf []byte) (int, error) {
	sended := 0
	var tempDelay time.Duration
	maxDelay := 1 * time.Second
	for sended < len(buf) {
		if c.Opts.WriteDeadline != 0 {
			c.RawConn.SetWriteDeadline(time.Now().Add(c.Opts.WriteDeadline))
		}
		wn, err := c.RawConn.Write(buf[sended:])
		if wn > 0 {
			sended += wn
			atomic.AddUint64(&c.sendBytes, uint64(wn))
		}

		if err != nil {
			if nerr, ok := err.(net.Error); ok {
				if nerr.Timeout() {
					// timeout
				} else if nerr.Temporary() {
					if tempDelay == 0 {
						tempDelay = 5 * time.Millisecond
					} else {
						tempDelay *= 2
					}
					if tempDelay > maxDelay {
						tempDelay = maxDelay
					}
					log.Errorf("XTCP - Conn[%v] Send error: %v; retrying in %v", c.RawConn.RemoteAddr(), err, tempDelay)
					time.Sleep(tempDelay)
					continue
				}
			}

			if !c.IsStoped() {
				log.Errorf("XTCP - Conn[%v] Send error : %v", c.RawConn.RemoteAddr(), err)
				c.Stop(StopImmediately)
			}
			return sended, err
		}
		tempDelay = 0
	}
	return sended, nil
}

func (c *Conn) sendLoop() {
	defer func() {
		log.Debug("XTCP - Conn send loop exit : ", c.RawConn.RemoteAddr())
		c.wg.Done()
	}()
	for {
		if atomic.LoadInt32(&c.state) == connStateStopped {
			return
		}

		select {
		case buf, ok := <-c.sendBufList:
			if !ok {
				return
			}
			_, err := c.sendBuf(buf)
			if err != nil {
				return
			}
			putBufferToPool(buf)
		case <-c.closed:
			if atomic.LoadInt32(&c.state) == connStateStopping {
				if len(c.sendBufList) == 0 {
					atomic.SwapInt32(&c.state, connStateStopped)
					c.RawConn.Close()
					return
				}
			}
		}
	}
}

func (c *Conn) sendByteBuffer(buffer *bytes.Buffer) (int, error) {
	select {
	case c.sendBufList <- buffer:
		return buffer.Len(), nil
	default:
		putBufferToPool(buffer)
		atomic.AddUint32(&c.dropped, 1)
		return 0, errSendListFull
	}
}

// Send use for send data, can be call in any goroutines.
func (c *Conn) Send(buf []byte) (int, error) {
	if atomic.LoadInt32(&c.state) != connStateNormal {
		return 0, errSendToClosedConn
	}
	bufLen := len(buf)
	if bufLen <= 0 {
		return 0, nil
	}

	if c.Opts.AsyncWrite {
		buffer := getBufferFromPool(len(buf))
		copy(buffer, buf)
		select {
		case c.sendBufList <- buffer:
			return bufLen, nil
		default:
			atomic.AddUint32(&c.dropped, 1)
			return 0, errSendListFull
		}
	} else {
		c.Lock() // Ensure entirety of buf is written together
		n, err := c.sendBuf(buf)
		c.Unlock()
		return n, err
	}
}

// SendPacket use for send packet, can be call in any goroutines.
func (c *Conn) SendPacket(p Packet) (int, error) {
	if atomic.LoadInt32(&c.state) != connStateNormal {
		return 0, errSendToClosedConn
	}

	needSize := c.Opts.Protocol.PackSize(p)
	if needSize <= 0 {
		return 0, nil
	}

	buffer := getBufferFromPool(needSize)
	_, err := c.Opts.Protocol.PackTo(p, buffer)
	if err != nil {
		putBufferToPool(buffer)
		return 0, err
	}
	return c.sendByteBuffer(buffer)
}

// DialAndServe connects to the addr and serve.
func (c *Conn) DialAndServe(addr string) error {
	rawConn, err := net.Dial("tcp", addr)
	if err != nil {
		return err
	}

	c.RawConn = rawConn

	c.Opts.Handler.OnConnect(c)

	c.serve()

	return nil
}
<commit_msg>upgrade<commit_after>package xtcp

import (
	"bytes"
	"errors"
	"io"
	"net"
	"sync"
	"sync/atomic"
	"time"

	log "github.com/xfxdev/xlog"
)

const (
	connStateNormal int32 = iota
	connStateStopping
	connStateStopped
)

var (
	errSendToClosedConn = errors.New("send to closed conn")
	errSendListFull     = errors.New("send list full")

	bufferPool1K = &sync.Pool{
		New: func() interface{} {
			return make([]byte, 1<<10)
		},
	}
	bufferPool2K = &sync.Pool{
		New: func() interface{} {
			return make([]byte, 2<<10)
		},
	}
	bufferPool4K = &sync.Pool{
		New: func() interface{} {
			return make([]byte, 4<<10)
		},
	}
	bufferPoolBig = &sync.Pool{}
)

func getBufferFromPool(targetSize int) []byte {
	var buf []byte
	if targetSize <= 1<<10 {
		buf = bufferPool1K.Get().([]byte)
	} else if targetSize <= 2<<10 {
		buf = bufferPool2K.Get().([]byte)
	} else if targetSize <= 4<<10 {
		buf = bufferPool4K.Get().([]byte)
	} else {
		itr := bufferPoolBig.Get()
		if itr != nil {
			buf = itr.([]byte)
		} else {
			buf = make([]byte, targetSize)
		}
	}
	buf = buf[:targetSize]
	return buf
}

func putBufferToPool(buf []byte) {
	cap := cap(buf)
	if cap <= 1<<10 {
		bufferPool1K.Put(buf)
	} else if cap <= 2<<10 {
		bufferPool2K.Put(buf)
	} else if cap <= 4<<10 {
		bufferPool4K.Put(buf)
	} else {
		bufferPoolBig.Put(buf)
	}
}

// A Conn represents the server side of an tcp connection.
type Conn struct {
	sync.Mutex
	Opts        *Options
	RawConn     net.Conn
	UserData    interface{}
	sendBufList chan []byte
	closed      chan struct{}
	state       int32
	wg          sync.WaitGroup
	once        sync.Once
	SendDropped uint32
	sendBytes   uint64
	recvBytes   uint64
	dropped     uint32
}

// NewConn return new conn.
func NewConn(opts *Options) *Conn {
	if opts.RecvBufSize <= 0 {
		log.Warnf("Invalid Opts.RecvBufSize : %v, use DefaultRecvBufSize instead", opts.RecvBufSize)
		opts.RecvBufSize = DefaultRecvBufSize
	}
	if opts.SendBufListLen <= 0 {
		log.Warnf("Invalid Opts.SendBufListLen : %v, use DefaultRecvBufSize instead", opts.SendBufListLen)
		opts.SendBufListLen = DefaultSendBufListLen
	}
	c := &Conn{
		Opts:        opts,
		sendBufList: make(chan []byte, opts.SendBufListLen),
		closed:      make(chan struct{}),
		state:       connStateNormal,
	}

	return c
}

func (c *Conn) String() string {
	return c.RawConn.LocalAddr().String() + " -> " + c.RawConn.RemoteAddr().String()
}

// SendBytes return the total send bytes.
func (c *Conn) SendBytes() uint64 {
	return atomic.LoadUint64(&c.sendBytes)
}

// RecvBytes return the total receive bytes.
func (c *Conn) RecvBytes() uint64 {
	return atomic.LoadUint64(&c.recvBytes)
}

// DroppedPacket return the total dropped packet.
func (c *Conn) DroppedPacket() uint32 {
	return atomic.LoadUint32(&c.dropped)
}

// Stop stops the conn.
func (c *Conn) Stop(mode StopMode) {
	c.once.Do(func() {
		if mode == StopImmediately {
			atomic.StoreInt32(&c.state, connStateStopped)
			c.RawConn.Close()
			//close(c.sendBufList) // leave channel open, because other goroutine maybe use it in Send.
			close(c.closed)
		} else {
			atomic.StoreInt32(&c.state, connStateStopping)
			// c.RawConn.Close() 	// will close in sendLoop
			// close(c.sendBufList)
			close(c.closed)
			if mode == StopGracefullyAndWait {
				c.wg.Wait()
			}
		}
	})
}

// IsStoped return true if Conn is stopped, otherwise return false.
func (c *Conn) IsStoped() bool {
	return atomic.LoadInt32(&c.state) != connStateNormal
}

func (c *Conn) serve() {
	tcpConn := c.RawConn.(*net.TCPConn)
	tcpConn.SetNoDelay(c.Opts.NoDelay)
	tcpConn.SetKeepAlive(c.Opts.KeepAlive)
	if c.Opts.KeepAlivePeriod != 0 {
		tcpConn.SetKeepAlivePeriod(c.Opts.KeepAlivePeriod)
	}

	if c.Opts.AsyncWrite {
		c.wg.Add(2)
		go c.sendLoop()
	} else {
		c.wg.Add(1)
	}
	c.recvLoop()

	c.Opts.Handler.OnClose(c)
}

func (c *Conn) recvLoop() {
	var tempDelay time.Duration
	tempBuf := make([]byte, c.Opts.RecvBufSize)
	recvBuf := bytes.NewBuffer(make([]byte, 0, c.Opts.RecvBufSize))
	maxDelay := 1 * time.Second

	defer func() {
		log.Debug("XTCP - Conn recv loop exit : ", c.RawConn.RemoteAddr())
		c.wg.Done()
	}()

	for {
		if c.Opts.ReadDeadline != 0 {
			c.RawConn.SetReadDeadline(time.Now().Add(c.Opts.ReadDeadline))
		}

		n, err := c.RawConn.Read(tempBuf)
		if err != nil {
			if nerr, ok := err.(net.Error); ok {
				if nerr.Timeout() {
					// timeout
				} else if nerr.Temporary() {
					if tempDelay == 0 {
						tempDelay = 5 * time.Millisecond
					} else {
						tempDelay *= 2
					}
					if tempDelay > maxDelay {
						tempDelay = maxDelay
					}
					log.Errorf("XTCP - Conn[%v] recv error : %v; retrying in %v", c.RawConn.RemoteAddr(), err, tempDelay)
					time.Sleep(tempDelay)
					continue
				}
			}

			if !c.IsStoped() {
				if err != io.EOF {
					log.Errorf("XTCP - Conn[%v] recv error : %v", c.RawConn.RemoteAddr(), err)
				}
				c.Stop(StopImmediately)
			}

			return
		}

		recvBuf.Write(tempBuf[:n])
		atomic.AddUint64(&c.recvBytes, uint64(n))
		tempDelay = 0

		for recvBuf.Len() > 0 {
			p, pl, err := c.Opts.Protocol.Unpack(recvBuf.Bytes())
			if err != nil {
				c.Opts.Handler.OnUnpackErr(c, recvBuf.Bytes(), err)
			}

			if pl > 0 {
				_ = recvBuf.Next(pl)
			}

			if p != nil {
				c.Opts.Handler.OnRecv(c, p)
			} else {
				break
			}
		}
	}
}

func (c *Conn) sendBuf(buf []byte) (int, error) {
	sended := 0
	var tempDelay time.Duration
	maxDelay := 1 * time.Second
	for sended < len(buf) {
		if c.Opts.WriteDeadline != 0 {
			c.RawConn.SetWriteDeadline(time.Now().Add(c.Opts.WriteDeadline))
		}
		wn, err := c.RawConn.Write(buf[sended:])
		if wn > 0 {
			sended += wn
			atomic.AddUint64(&c.sendBytes, uint64(wn))
		}

		if err != nil {
			if nerr, ok := err.(net.Error); ok {
				if nerr.Timeout() {
					// timeout
				} else if nerr.Temporary() {
					if tempDelay == 0 {
						tempDelay = 5 * time.Millisecond
					} else {
						tempDelay *= 2
					}
					if tempDelay > maxDelay {
						tempDelay = maxDelay
					}
					log.Errorf("XTCP - Conn[%v] Send error: %v; retrying in %v", c.RawConn.RemoteAddr(), err, tempDelay)
					time.Sleep(tempDelay)
					continue
				}
			}

			if !c.IsStoped() {
				log.Errorf("XTCP - Conn[%v] Send error : %v", c.RawConn.RemoteAddr(), err)
				c.Stop(StopImmediately)
			}
			return sended, err
		}
		tempDelay = 0
	}
	return sended, nil
}

func (c *Conn) sendLoop() {
	defer func() {
		log.Debug("XTCP - Conn send loop exit : ", c.RawConn.RemoteAddr())
		c.wg.Done()
	}()
	for {
		if atomic.LoadInt32(&c.state) == connStateStopped {
			return
		}

		select {
		case buf, ok := <-c.sendBufList:
			if !ok {
				return
			}
			_, err := c.sendBuf(buf)
			if err != nil {
				return
			}
			putBufferToPool(buf)
		case <-c.closed:
			if atomic.LoadInt32(&c.state) == connStateStopping {
				if len(c.sendBufList) == 0 {
					atomic.SwapInt32(&c.state, connStateStopped)
					c.RawConn.Close()
					return
				}
			}
		}
	}
}

// Send use for send data, can be call in any goroutines.
func (c *Conn) Send(buf []byte) (int, error) {
	if atomic.LoadInt32(&c.state) != connStateNormal {
		return 0, errSendToClosedConn
	}
	bufLen := len(buf)
	if bufLen <= 0 {
		return 0, nil
	}

	if c.Opts.AsyncWrite {
		buffer := getBufferFromPool(len(buf))
		copy(buffer, buf)
		select {
		case c.sendBufList <- buffer:
			return bufLen, nil
		default:
			atomic.AddUint32(&c.dropped, 1)
			return 0, errSendListFull
		}
	} else {
		c.Lock() // Ensure entirety of buf is written together
		n, err := c.sendBuf(buf)
		c.Unlock()
		return n, err
	}
}

// SendPacket use for send packet, can be call in any goroutines.
func (c *Conn) SendPacket(p Packet) (int, error) {
	if atomic.LoadInt32(&c.state) != connStateNormal {
		return 0, errSendToClosedConn
	}
	buf, err := c.Opts.Protocol.Pack(p)
	if err != nil {
		return 0, err
	}

	return c.Send(buf)
}

// DialAndServe connects to the addr and serve.
func (c *Conn) DialAndServe(addr string) error {
	rawConn, err := net.Dial("tcp", addr)
	if err != nil {
		return err
	}

	c.RawConn = rawConn

	c.Opts.Handler.OnConnect(c)

	c.serve()

	return nil
}
<|endoftext|>