<commit_before>package lifted

// Assign numbers to all things of type Name

import (
	"fmt"
	"os"
)

func (d *Domain) AssignNums(s *Symtab) os.Error {
	if err := d.numberTypes(s); err != nil {
		return err
	}
	s.typeObjs = make([][]int, len(s.typeNames))
	if err := numberConsts(s, d.Constants); err != nil {
		return err
	}
	if err := d.numberPreds(s); err != nil {
		return err
	}
	for _, a := range d.Actions {
		if err := a.AssignNums(s); err != nil {
			return err
		}
	}
	return nil
}

func (d *Domain) numberTypes(s *Symtab) os.Error {
	for i, _ := range d.Types {
		d.Types[i].Name.numberType(s)
	}
	for i, _ := range d.Types {
		t := d.Types[i]
		for j, _ := range t.Type {
			if found := t.Type[j].numberType(s); !found {
				return undefType(&t.Type[j])
			}
		}
	}
	return nil
}

func (d *Domain) numberPreds(s *Symtab) os.Error {
	for i, _ := range d.Predicates {
		p := &d.Predicates[i]
		p.Name.numberPred(s)
		for j, _ := range p.Parameters {
			parm := p.Parameters[j]
			for k, _ := range parm.Type {
				if found := parm.Type[k].numberType(s); !found {
					return undefType(&parm.Type[k])
				}
			}
		}
	}
	return nil
}

func numberConsts(s *Symtab, consts []TypedName) os.Error {
	for i, _ := range consts {
		c := &consts[i]
		first := c.Name.numberConst(s)
		cnum := c.Name.Num
		for j, _ := range c.Type {
			if found := c.Type[j].numberType(s); !found {
				return undefType(&c.Type[j])
			}
			// If this is the 1st decl of this object
			// then add it to the table of all objects
			// of the given type
			if !first {
				tnum := c.Type[j].Num
				s.typeObjs[tnum] = append(s.typeObjs[tnum], cnum)
			}
		}
	}
	return nil
}

func (a *Action) AssignNums(s *Symtab) os.Error {
	var f *numFrame
	for i, _ := range a.Parameters {
		p := &a.Parameters[i]
		f = p.Name.numberVar(s, f)
		for j, _ := range p.Type {
			if found := p.Type[j].numberType(s); !found {
				return undefType(&p.Type[j])
			}
		}
	}
	if err := a.Precondition.AssignNums(s, f); err != nil {
		return err
	}
	return a.Effect.AssignNums(s, f)
}

func (p *Problem) AssignNums(s *Symtab) os.Error {
	numberConsts(s, p.Objects)
	for _, init := range p.Init {
		if err := init.AssignNums(s, nil); err != nil {
			return err
		}
	}
	return p.Goal.AssignNums(s, nil)
}

func (l *Literal) AssignNums(s *Symtab, f *numFrame) os.Error {
	for i, t := range l.Parameters {
		name := &l.Parameters[i].Name
		switch t.Kind {
		case TermVariable:
			if fnxt := name.numberVar(s, f); fnxt == f {
				break
			}
			return fmt.Errorf("%s: Unbound variable %s\n", name.Loc, name.Str)
		case TermConstant:
			name.numberConst(s)
		}
	}
	l.Name.numberPred(s)
	return nil
}

func (e *ExprBinary) AssignNums(s *Symtab, f *numFrame) os.Error {
	if err := e.Left.AssignNums(s, f); err != nil {
		return err
	}
	return e.Right.AssignNums(s, f)
}

func (ExprTrue) AssignNums(*Symtab, *numFrame) os.Error {
	return nil
}

func (ExprFalse) AssignNums(*Symtab, *numFrame) os.Error {
	return nil
}

func (e *ExprAnd) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*ExprBinary)(e).AssignNums(s, f)
}

func (e *ExprOr) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*ExprBinary)(e).AssignNums(s, f)
}

func (e *ExprNot) AssignNums(s *Symtab, f *numFrame) os.Error {
	return e.Expr.AssignNums(s, f)
}

func (e *ExprQuant) AssignNums(s *Symtab, f *numFrame) os.Error {
	f = e.Variable.Name.numberVar(s, f)
	for i, _ := range e.Variable.Type {
		if found := e.Variable.Type[i].numberType(s); !found {
			return undefType(&e.Variable.Type[i])
		}
	}
	return e.Expr.AssignNums(s, f)
}

func (e *ExprForall) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*ExprQuant)(e).AssignNums(s, f)
}

func (e *ExprExists) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*ExprQuant)(e).AssignNums(s, f)
}

func (e *ExprLiteral) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*Literal)(e).AssignNums(s, f)
}

func (e *EffectUnary) AssignNums(s *Symtab, f *numFrame) os.Error {
	return e.Effect.AssignNums(s, f)
}

func (EffectNone) AssignNums(*Symtab, *numFrame) os.Error {
	return nil
}

func (e *EffectAnd) AssignNums(s *Symtab, f *numFrame) os.Error {
	if err := e.Left.AssignNums(s, f); err != nil {
		return err
	}
	return e.Right.AssignNums(s, f)
}

func (e *EffectForall) AssignNums(s *Symtab, f *numFrame) os.Error {
	f = e.Variable.Name.numberVar(s, f)
	for i, _ := range e.Variable.Type {
		if found := e.Variable.Type[i].numberType(s); !found {
			return undefType(&e.Variable.Type[i])
		}
	}
	return e.Effect.AssignNums(s, f)
}

func (e *EffectWhen) AssignNums(s *Symtab, f *numFrame) os.Error {
	if err := e.Condition.AssignNums(s, f); err != nil {
		return err
	}
	return e.Effect.AssignNums(s, f)
}

func (e *EffectLiteral) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*Literal)(e).AssignNums(s, f)
}

func (e *EffectAssign) AssignNums(*Symtab, *numFrame) os.Error {
	return nil
}

func (i *InitLiteral) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*Literal)(i).AssignNums(s, f)
}

func (i *InitEq) AssignNums(s *Symtab, f *numFrame) os.Error {
	return nil
}

func (name *Name) numberType(s *Symtab) bool {
	if n, ok := s.typeNums[name.Str]; ok {
		name.Num = n
		return true
	}
	name.Num = len(s.typeNames)
	s.typeNums[name.Str] = name.Num
	s.typeNames = append(s.typeNames, name.Str)
	return false
}

func (name *Name) numberConst(s *Symtab) bool {
	if n, ok := s.constNums[name.Str]; ok {
		name.Num = n
		return true
	}
	name.Num = len(s.constNames)
	s.constNums[name.Str] = name.Num
	s.constNames = append(s.constNames, name.Str)
	return false
}

func (name *Name) numberPred(s *Symtab) bool {
	if n, ok := s.predNums[name.Str]; ok {
		name.Num = n
		return true
	}
	name.Num = len(s.predNames)
	s.predNums[name.Str] = name.Num
	s.predNames = append(s.predNames, name.Str)
	return false
}

func (name *Name) numberVar(s *Symtab, f *numFrame) *numFrame {
	if n, ok := f.lookup(name.Str); ok {
		name.Num = n
		return f
	}
	n := len(s.varNames)
	name.Num = n
	s.varNames = append(s.varNames, name.Str)
	return &numFrame{name: name.Str, num: n, up: f}
}

type numFrame struct {
	name string
	num  int
	up   *numFrame
}

func (f *numFrame) lookup(name string) (int, bool) {
	if f == nil {
		return 0, false
	}
	if f.name == name {
		return f.num, true
	}
	return f.up.lookup(name)
}

func undefType(n *Name) os.Error {
	return fmt.Errorf("%s: Undefined type %s\n", n.Loc, n.Str)
}<commit_msg>Error if things are used but not declared.<commit_after>package lifted

// Assign numbers to all things of type Name

import (
	"fmt"
	"os"
)

func (d *Domain) AssignNums(s *Symtab) os.Error {
	if err := d.numberTypes(s); err != nil {
		return err
	}
	s.typeObjs = make([][]int, len(s.typeNames))
	if err := numberConsts(s, d.Constants); err != nil {
		return err
	}
	if err := d.numberPreds(s); err != nil {
		return err
	}
	for _, a := range d.Actions {
		if err := a.AssignNums(s); err != nil {
			return err
		}
	}
	return nil
}

func (d *Domain) numberTypes(s *Symtab) os.Error {
	for i, _ := range d.Types {
		d.Types[i].Name.numberType(s)
	}
	for i, _ := range d.Types {
		t := d.Types[i]
		for j, _ := range t.Type {
			if found := t.Type[j].numberType(s); !found {
				return undeclType(&t.Type[j])
			}
		}
	}
	return nil
}

func (d *Domain) numberPreds(s *Symtab) os.Error {
	for i, _ := range d.Predicates {
		p := &d.Predicates[i]
		p.Name.numberPred(s)
		for j, _ := range p.Parameters {
			parm := p.Parameters[j]
			for k, _ := range parm.Type {
				if found := parm.Type[k].numberType(s); !found {
					return undeclType(&parm.Type[k])
				}
			}
		}
	}
	return nil
}

func numberConsts(s *Symtab, consts []TypedName) os.Error {
	for i, _ := range consts {
		c := &consts[i]
		first := c.Name.numberConst(s)
		cnum := c.Name.Num
		for j, _ := range c.Type {
			if found := c.Type[j].numberType(s); !found {
				return undeclType(&c.Type[j])
			}
			// If this is the 1st decl of this object
			// then add it to the table of all objects
			// of the given type
			if !first {
				tnum := c.Type[j].Num
				s.typeObjs[tnum] = append(s.typeObjs[tnum], cnum)
			}
		}
	}
	return nil
}

func (a *Action) AssignNums(s *Symtab) os.Error {
	var f *numFrame
	for i, _ := range a.Parameters {
		p := &a.Parameters[i]
		f = p.Name.numberVar(s, f)
		for j, _ := range p.Type {
			if found := p.Type[j].numberType(s); !found {
				return undeclType(&p.Type[j])
			}
		}
	}
	if err := a.Precondition.AssignNums(s, f); err != nil {
		return err
	}
	return a.Effect.AssignNums(s, f)
}

func (p *Problem) AssignNums(s *Symtab) os.Error {
	numberConsts(s, p.Objects)
	for _, init := range p.Init {
		if err := init.AssignNums(s, nil); err != nil {
			return err
		}
	}
	return p.Goal.AssignNums(s, nil)
}

func (l *Literal) AssignNums(s *Symtab, f *numFrame) os.Error {
	for i, t := range l.Parameters {
		name := &l.Parameters[i].Name
		switch t.Kind {
		case TermVariable:
			if fnxt := name.numberVar(s, f); fnxt != f {
				return undeclVar(name)
			}
		case TermConstant:
			if found := name.numberConst(s); !found {
				return undeclConst(name)
			}
		}
	}
	if found := l.Name.numberPred(s); !found {
		return undeclPred(&l.Name)
	}
	return nil
}

func (e *ExprBinary) AssignNums(s *Symtab, f *numFrame) os.Error {
	if err := e.Left.AssignNums(s, f); err != nil {
		return err
	}
	return e.Right.AssignNums(s, f)
}

func (ExprTrue) AssignNums(*Symtab, *numFrame) os.Error {
	return nil
}

func (ExprFalse) AssignNums(*Symtab, *numFrame) os.Error {
	return nil
}

func (e *ExprAnd) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*ExprBinary)(e).AssignNums(s, f)
}

func (e *ExprOr) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*ExprBinary)(e).AssignNums(s, f)
}

func (e *ExprNot) AssignNums(s *Symtab, f *numFrame) os.Error {
	return e.Expr.AssignNums(s, f)
}

func (e *ExprQuant) AssignNums(s *Symtab, f *numFrame) os.Error {
	f = e.Variable.Name.numberVar(s, f)
	for i, _ := range e.Variable.Type {
		if found := e.Variable.Type[i].numberType(s); !found {
			return undeclType(&e.Variable.Type[i])
		}
	}
	return e.Expr.AssignNums(s, f)
}

func (e *ExprForall) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*ExprQuant)(e).AssignNums(s, f)
}

func (e *ExprExists) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*ExprQuant)(e).AssignNums(s, f)
}

func (e *ExprLiteral) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*Literal)(e).AssignNums(s, f)
}

func (e *EffectUnary) AssignNums(s *Symtab, f *numFrame) os.Error {
	return e.Effect.AssignNums(s, f)
}

func (EffectNone) AssignNums(*Symtab, *numFrame) os.Error {
	return nil
}

func (e *EffectAnd) AssignNums(s *Symtab, f *numFrame) os.Error {
	if err := e.Left.AssignNums(s, f); err != nil {
		return err
	}
	return e.Right.AssignNums(s, f)
}

func (e *EffectForall) AssignNums(s *Symtab, f *numFrame) os.Error {
	f = e.Variable.Name.numberVar(s, f)
	for i, _ := range e.Variable.Type {
		if found := e.Variable.Type[i].numberType(s); !found {
			return undeclType(&e.Variable.Type[i])
		}
	}
	return e.Effect.AssignNums(s, f)
}

func (e *EffectWhen) AssignNums(s *Symtab, f *numFrame) os.Error {
	if err := e.Condition.AssignNums(s, f); err != nil {
		return err
	}
	return e.Effect.AssignNums(s, f)
}

func (e *EffectLiteral) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*Literal)(e).AssignNums(s, f)
}

func (e *EffectAssign) AssignNums(*Symtab, *numFrame) os.Error {
	return nil
}

func (i *InitLiteral) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*Literal)(i).AssignNums(s, f)
}

func (i *InitEq) AssignNums(s *Symtab, f *numFrame) os.Error {
	return nil
}

func (name *Name) numberType(s *Symtab) bool {
	if n, ok := s.typeNums[name.Str]; ok {
		name.Num = n
		return true
	}
	name.Num = len(s.typeNames)
	s.typeNums[name.Str] = name.Num
	s.typeNames = append(s.typeNames, name.Str)
	return false
}

func (name *Name) numberConst(s *Symtab) bool {
	if n, ok := s.constNums[name.Str]; ok {
		name.Num = n
		return true
	}
	name.Num = len(s.constNames)
	s.constNums[name.Str] = name.Num
	s.constNames = append(s.constNames, name.Str)
	return false
}

func (name *Name) numberPred(s *Symtab) bool {
	if n, ok := s.predNums[name.Str]; ok {
		name.Num = n
		return true
	}
	name.Num = len(s.predNames)
	s.predNums[name.Str] = name.Num
	s.predNames = append(s.predNames, name.Str)
	return false
}

func (name *Name) numberVar(s *Symtab, f *numFrame) *numFrame {
	if n, ok := f.lookup(name.Str); ok {
		name.Num = n
		return f
	}
	n := len(s.varNames)
	name.Num = n
	s.varNames = append(s.varNames, name.Str)
	return &numFrame{name: name.Str, num: n, up: f}
}

type numFrame struct {
	name string
	num  int
	up   *numFrame
}

func (f *numFrame) lookup(name string) (int, bool) {
	if f == nil {
		return 0, false
	}
	if f.name == name {
		return f.num, true
	}
	return f.up.lookup(name)
}

func undeclType(n *Name) os.Error {
	return fmt.Errorf("%s: Undeclared type %s\n", n.Loc, n.Str)
}

func undeclConst(n *Name) os.Error {
	return fmt.Errorf("%s: Undeclared constant %s\n", n.Loc, n.Str)
}

func undeclPred(n *Name) os.Error {
	return fmt.Errorf("%s: Undeclared predicate %s\n", n.Loc, n.Str)
}

func undeclVar(n *Name) os.Error {
	return fmt.Errorf("%s: Unbound variable %s\n", n.Loc, n.Str)
}<|endoftext|>


<commit_before>package tunnel

import (
	"encoding/xml"
	"fmt"
	"github.com/oopschen/xtunnel/iputil"
	"github.com/oopschen/xtunnel/sys"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"regexp"
	"strings"
	"time"
)

const (
	maxTunnelNum = 5
)

var (
	ipListPattern *regexp.Regexp
)

func init() {
	pattern, err := regexp.Compile(`<span\s+style\s*=\s*"\s*float:\s*right;\s*color:\s*darkgray\s*"\s*>\s*([^\s<]+)\s*</span>`)

	if nil != err {
		sys.Logger.Printf("init ip list pattern for he\n")

	}

	ipListPattern = pattern
}

type HEBroker struct {
	config *sys.Config
	client *http.Client
}

func (broker *HEBroker) Init(cfg *sys.Config) bool {
	if 1 > len(cfg.Username) || 1 > len(cfg.Userpasswd) {
		sys.Logger.Printf("username(%s) or userpwd(%s) must not bu nil\n", cfg.Username, cfg.Userpasswd)
		return false

	}

	broker.config = cfg

	// set up client
	var (
		transport   http.RoundTripper
		cookiJarIns http.CookieJar
	)

	transport = &http.Transport{
		DisableKeepAlives:  false,
		DisableCompression: false,
	}

	cookiJarIns, err := cookiejar.New(nil)
	if nil != err {
		sys.Logger.Printf("init cookie jar %s\n", err)
		return false
	}

	broker.client = &http.Client{
		Timeout:   10 * time.Second,
		Transport: transport,
		Jar:       cookiJarIns,
	}
	return true
}

func (broker *HEBroker) GetMeta() *sys.Meta {
	/*
	   get current ip
	   login
	   get all tunnels
	   if exists:
	     update cfg
	     parse meta
	   else:
	     visit create page
	     get meta
	*/
	curIP := iputil.GetLocalAddress()
	if 1 > len(curIP) {
		sys.Logger.Printf("Get local address\n")
		return nil

	}

	// find tunnels
	if !broker.login() {
		sys.Logger.Printf("Login as %s fail\n", broker.config.Username)
		return nil

	}

	sys.Logger.Printf("Login success\n")

	var (
		tunnels         []*sys.Meta
		foundMeta, meta *sys.Meta
	)

	tunnels = broker.findAllTunnels()
	// set up metas
	meta = &sys.Meta{}
	meta.IPv4Server = broker.getBestIP()
	meta.IPv4Client = curIP

	if "" == meta.IPv4Server {
		sys.Logger.Printf("Get Best Server fail\n")
		return nil

	}
	sys.Logger.Printf("Choose %s for tunnel %s\n", meta.IPv4Server, meta.IPv4Client)

	// find matched tunnel
	if nil != tunnels {
		sys.Logger.Printf("%d Tunnels found\n", len(tunnels))
		for _, m := range tunnels {
			if meta.IPv4Server == m.IPv4Server {
				foundMeta = m
				break
			}

		}

	} else {
		sys.Logger.Printf("No Tunnels found\n")

	}

	// create or update tunnel
	if nil == foundMeta {
		// max tunnel number reached
		if maxTunnelNum <= len(tunnels) {
			sys.Logger.Printf("max tunnel number(%d) reached, please delete some tunnels at https://tunnelbroker.net", maxTunnelNum)
			return nil

		} else if !broker.createTunnel(meta) {
			sys.Logger.Printf("Create Tunnel fail\n")
			// create tunnel
			return nil

		}

	} else if meta.ID = foundMeta.ID; !broker.updateTunnel(meta) {
		sys.Logger.Printf("Update Tunnel fail \n")
		// update
		return nil

	}

	sys.Logger.Printf("Fetch tunnel info success\n")
	return meta
}

func (broker *HEBroker) Destroy() bool {
	return true
}

// internal methods
func (broker *HEBroker) login() bool {
	var (
		loginUrl = "https://tunnelbroker.net/login.php"
		postData = url.Values{}
	)

	// post
	postData.Add("f_user", broker.config.Username)
	postData.Add("f_pass", broker.config.Userpasswd)
	postData.Add("Login", "Login")
	postData.Add("redir", "/")

	resp := broker.doHttpPost(loginUrl, postData)
	if nil == resp {
		return false

	}

	// check cookie
	cookieUrl, err := url.Parse(loginUrl)
	if nil != err {
		sys.Logger.Printf("Login fail cookie url: %s\n", err)
		return false

	}
	cookies := broker.client.Jar.Cookies(cookieUrl)
	if nil == cookies {
		sys.Logger.Printf("Login fail no cookie found\n")
		return false
	}

	foundHETB := false
	for _, c := range cookies {
		if "hetb" == strings.ToLower(c.Name) {
			foundHETB = true
			break

		}

	}

	if !foundHETB {
		sys.Logger.Printf("Login fail no cookie \"HETB\"found\n")
		return false

	}

	// check logout keyword in main page
	bodyBytes, err := ioutil.ReadAll(resp.Body)
	if nil != err {
		sys.Logger.Printf("Login redirect read body: %s, status: %s\n", err, resp.Status)
		return false

	}

	body := string(bodyBytes)

	if strings.Contains(body, "Logout") && strings.Contains(body, "Main Page") {
		return true

	}

	sys.Logger.Printf("Login redirect page do not contain keywords: status=%s, body=%s\n", resp.Status, body)
	return false
}

func (broker *HEBroker) findAllTunnels() []*sys.Meta {
	tunnelURL := fmt.Sprintf("https://%s:%s@tunnelbroker.net/tunnelInfo.php", broker.config.Username, broker.config.Userpasswd)
	resp := broker.doHttpGet(tunnelURL)
	if nil == resp {
		return nil

	}

	defer resp.Body.Close()

	return parseTunnels(resp.Body)
}

func (broker *HEBroker) updateTunnel(meta *sys.Meta) bool {
	/*
		update client server ip only
	*/
	updateURL := fmt.Sprintf("https://tunnelbroker.net/tunnel_detail.php?tid=%s&ajax=true", meta.ID)
	postData := url.Values{}
	postData.Add("ipv4z", meta.IPv4Client)

	resp := broker.doHttpPost(updateURL, postData)
	if nil == resp {
		return false

	}

	defer resp.Body.Close()

	// check result
	body, err := ioutil.ReadAll(resp.Body)
	if nil != err {
		sys.Logger.Printf("update tunnel read body %s\n", err)
		return false
	}

	if "" == string(body) {
		return true

	}

	sys.Logger.Printf("update tunnel fail: %s\n", string(body))
	return false
}

func (broker *HEBroker) createTunnel(meta *sys.Meta) bool {
	/*
		create tunnel
		parse server router info
	*/
	createUrl := "https://tunnelbroker.net/new_tunnel.php"
	postData := url.Values{}
	postData.Add("ipv4z", meta.IPv4Client)
	postData.Add("tserv", meta.IPv4Server)
	postData.Add("normaltunnel", "Create Tunnel")

	resp := broker.doHttpPost(createUrl, postData)
	if nil == resp {
		return false

	}

	defer resp.Body.Close()

	// parse tunnel
	metas := broker.findAllTunnels()
	if nil == meta {
		sys.Logger.Printf("Create tunnel: empty tunnels\n")
		return false

	}

	for _, m := range metas {
		if m.IPv4Server == meta.IPv4Server {
			meta.ID = m.ID
			meta.IPv6Client = m.IPv6Client
			meta.Router6 = m.Router6
			return true

		}

	}

	sys.Logger.Printf("Create tunnel: can not find created tunnel(%#v)\n", meta)
	return false
}

/**
*
get best meta info

*
test by icmp

* @return the ip ping the fastest
 */
func (broker *HEBroker) getBestIP() string {
	/*
		visit https://tunnelbroker.net/new_tunnel.php
		parse ips
	*/
	ipUrl := "https://tunnelbroker.net/new_tunnel.php"

	// do request
	resp := broker.doHttpGet(ipUrl)

	if nil == resp {
		return ""
	}

	defer resp.Body.Close()

	// read body
	body, err := ioutil.ReadAll(resp.Body)
	if nil != err {
		sys.Logger.Printf("Read best server body %s\n", err)
		return ""
	}

	// parse ips
	ips := ipListPattern.FindAllStringSubmatch(string(body), -1)
	if nil == ips {
		sys.Logger.Printf("Find best server body %s\n", string(body))
		return ""

	}

	ipSlice := make([]string, len(ips))
	for inx, ipMatches := range ips {
		ipSlice[inx] = ipMatches[1]

	}

	return iputil.GetBestIP(ipSlice)

}

func (b *HEBroker) doHttpGet(reqUrl string) *http.Response {
	resp, err := b.client.Get(reqUrl)

	if nil != err {
		sys.Logger.Printf("Get request %s: %s\n", reqUrl, err)
		return nil
	}
	return resp
}

func (b *HEBroker) doHttpPost(reqUrl string, vals url.Values) *http.Response {
	resp, err := b.client.PostForm(reqUrl, vals)

	if nil != err {
		sys.Logger.Printf("Post request %s: %s\n", reqUrl, err)
		return nil
	}

	return resp
}

func parseTunnels(xmlText io.Reader) []*sys.Meta {
	var (
		metas      []*sys.Meta
		xmlDecoder = xml.NewDecoder(xmlText)
	)

	for {
		token, err := xmlDecoder.Token()
		// end
		if nil == token && io.EOF == err {
			break
		}

		switch tokenType := token.(type) {
		case xml.StartElement:
			if "tunnel" == strings.ToLower(tokenType.Name.Local) {
				tunnel := &sys.Meta{}
				err := xmlDecoder.DecodeElement(&tunnel, &tokenType)
				if nil != err {
					sys.Logger.Printf("parse xml fail: %s, %v\n", err, xmlText)
					return nil

				}

				if nil == metas {
					metas = make([]*sys.Meta, 1)
					metas[0] = tunnel

				} else {
					metas = append(metas, tunnel)

				}

			}

		}

	}

	return metas
}
<commit_msg>add best server log && keepalive false<commit_after>package tunnel

import (
	"encoding/xml"
	"fmt"
	"github.com/oopschen/xtunnel/iputil"
	"github.com/oopschen/xtunnel/sys"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"regexp"
	"strings"
	"time"
)

const (
	maxTunnelNum = 5
)

var (
	ipListPattern *regexp.Regexp
)

func init() {
	pattern, err := regexp.Compile(`<span\s+style\s*=\s*"\s*float:\s*right;\s*color:\s*darkgray\s*"\s*>\s*([^\s<]+)\s*</span>`)

	if nil != err {
		sys.Logger.Printf("init ip list pattern for he\n")

	}

	ipListPattern = pattern
}

type HEBroker struct {
	config *sys.Config
	client *http.Client
}

func (broker *HEBroker) Init(cfg *sys.Config) bool {
	if 1 > len(cfg.Username) || 1 > len(cfg.Userpasswd) {
		sys.Logger.Printf("username(%s) or userpwd(%s) must not bu nil\n", cfg.Username, cfg.Userpasswd)
		return false

	}

	broker.config = cfg

	// set up client
	var (
		transport   http.RoundTripper
		cookiJarIns http.CookieJar
	)

	transport = &http.Transport{
		DisableKeepAlives:  true,
		DisableCompression: false,
	}

	cookiJarIns, err := cookiejar.New(nil)
	if nil != err {
		sys.Logger.Printf("init cookie jar %s\n", err)
		return false
	}

	broker.client = &http.Client{
		Timeout:   10 * time.Second,
		Transport: transport,
		Jar:       cookiJarIns,
	}
	return true
}

func (broker *HEBroker) GetMeta() *sys.Meta {
	/*
	   get current ip
	   login
	   get all tunnels
	   if exists:
	     update cfg
	     parse meta
	   else:
	     visit create page
	     get meta
	*/
	curIP := iputil.GetLocalAddress()
	if 1 > len(curIP) {
		sys.Logger.Printf("Get local address\n")
		return nil

	}

	// find tunnels
	if !broker.login() {
		sys.Logger.Printf("Login as %s fail\n", broker.config.Username)
		return nil

	}

	sys.Logger.Printf("Login success\n")

	var (
		tunnels         []*sys.Meta
		foundMeta, meta *sys.Meta
	)

	tunnels = broker.findAllTunnels()
	// set up metas
	meta = &sys.Meta{}
	meta.IPv4Server = broker.getBestIP()
	meta.IPv4Client = curIP

	if "" == meta.IPv4Server {
		sys.Logger.Printf("Get Best Server fail\n")
		return nil

	}
	sys.Logger.Printf("Choose %s for tunnel %s\n", meta.IPv4Server, meta.IPv4Client)

	// find matched tunnel
	if nil != tunnels {
		sys.Logger.Printf("%d Tunnels found\n", len(tunnels))
		for _, m := range tunnels {
			if meta.IPv4Server == m.IPv4Server {
				foundMeta = m
				break
			}

		}

	} else {
		sys.Logger.Printf("No Tunnels found\n")

	}

	// create or update tunnel
	if nil == foundMeta {
		// max tunnel number reached
		if maxTunnelNum <= len(tunnels) {
			sys.Logger.Printf("max tunnel number(%d) reached, please delete some tunnels at https://tunnelbroker.net", maxTunnelNum)
			return nil

		} else if !broker.createTunnel(meta) {
			sys.Logger.Printf("Create Tunnel fail\n")
			// create tunnel
			return nil

		}

	} else if meta.ID = foundMeta.ID; !broker.updateTunnel(meta) {
		sys.Logger.Printf("Update Tunnel fail \n")
		// update
		return nil

	}

	sys.Logger.Printf("Fetch tunnel info success\n")
	return meta
}

func (broker *HEBroker) Destroy() bool {
	return true
}

// internal methods
func (broker *HEBroker) login() bool {
	var (
		loginUrl = "https://tunnelbroker.net/login.php"
		postData = url.Values{}
	)

	// post
	postData.Add("f_user", broker.config.Username)
	postData.Add("f_pass", broker.config.Userpasswd)
	postData.Add("Login", "Login")
	postData.Add("redir", "/")

	resp := broker.doHttpPost(loginUrl, postData)
	if nil == resp {
		return false

	}

	// check cookie
	cookieUrl, err := url.Parse(loginUrl)
	if nil != err {
		sys.Logger.Printf("Login fail cookie url: %s\n", err)
		return false

	}
	cookies := broker.client.Jar.Cookies(cookieUrl)
	if nil == cookies {
		sys.Logger.Printf("Login fail no cookie found\n")
		return false
	}

	foundHETB := false
	for _, c := range cookies {
		if "hetb" == strings.ToLower(c.Name) {
			foundHETB = true
			break

		}

	}

	if !foundHETB {
		sys.Logger.Printf("Login fail no cookie \"HETB\"found\n")
		return false

	}

	// check logout keyword in main page
	bodyBytes, err := ioutil.ReadAll(resp.Body)
	if nil != err {
		sys.Logger.Printf("Login redirect read body: %s, status: %s\n", err, resp.Status)
		return false

	}

	body := string(bodyBytes)

	if strings.Contains(body, "Logout") && strings.Contains(body, "Main Page") {
		return true

	}

	sys.Logger.Printf("Login redirect page do not contain keywords: status=%s, body=%s\n", resp.Status, body)
	return false
}

func (broker *HEBroker) findAllTunnels() []*sys.Meta {
	tunnelURL := fmt.Sprintf("https://%s:%s@tunnelbroker.net/tunnelInfo.php", broker.config.Username, broker.config.Userpasswd)
	resp := broker.doHttpGet(tunnelURL)
	if nil == resp {
		return nil

	}

	defer resp.Body.Close()

	return parseTunnels(resp.Body)
}

func (broker *HEBroker) updateTunnel(meta *sys.Meta) bool {
	/*
		update client server ip only
	*/
	updateURL := fmt.Sprintf("https://tunnelbroker.net/tunnel_detail.php?tid=%s&ajax=true", meta.ID)
	postData := url.Values{}
	postData.Add("ipv4z", meta.IPv4Client)

	resp := broker.doHttpPost(updateURL, postData)
	if nil == resp {
		return false

	}

	defer resp.Body.Close()

	// check result
	body, err := ioutil.ReadAll(resp.Body)
	if nil != err {
		sys.Logger.Printf("update tunnel read body %s\n", err)
		return false
	}

	if "" == string(body) {
		return true

	}

	sys.Logger.Printf("update tunnel fail: %s\n", string(body))
	return false
}

func (broker *HEBroker) createTunnel(meta *sys.Meta) bool {
	/*
		create tunnel
		parse server router info
	*/
	createUrl := "https://tunnelbroker.net/new_tunnel.php"
	postData := url.Values{}
	postData.Add("ipv4z", meta.IPv4Client)
	postData.Add("tserv", meta.IPv4Server)
	postData.Add("normaltunnel", "Create Tunnel")

	resp := broker.doHttpPost(createUrl, postData)
	if nil == resp {
		return false

	}

	defer resp.Body.Close()

	// parse tunnel
	metas := broker.findAllTunnels()
	if nil == meta {
		sys.Logger.Printf("Create tunnel: empty tunnels\n")
		return false

	}

	for _, m := range metas {
		if m.IPv4Server == meta.IPv4Server {
			meta.ID = m.ID
			meta.IPv6Client = m.IPv6Client
			meta.Router6 = m.Router6
			return true

		}

	}

	sys.Logger.Printf("Create tunnel: can not find created tunnel(%#v)\n", meta)
	return false
}

/**
*
get best meta info

*
test by icmp

* @return the ip ping the fastest
 */
func (broker *HEBroker) getBestIP() string {
	/*
		visit https://tunnelbroker.net/new_tunnel.php
		parse ips
	*/
	ipUrl := "https://tunnelbroker.net/new_tunnel.php"

	// do request
	resp := broker.doHttpGet(ipUrl)

	if nil == resp {
		return ""
	}

	defer resp.Body.Close()

	// read body
	body, err := ioutil.ReadAll(resp.Body)
	if nil != err {
		sys.Logger.Printf("Read best server body %s\n", err)
		return ""
	}

	// parse ips
	ips := ipListPattern.FindAllStringSubmatch(string(body), -1)
	if nil == ips {
		sys.Logger.Printf("Find best server body %s\n", string(body))
		return ""

	}

	ipSlice := make([]string, len(ips))
	for inx, ipMatches := range ips {
		ipSlice[inx] = ipMatches[1]

	}

	sys.Logger.Printf("Get best server ip: %s\n", strings.Join(ipSlice, ","))
	return iputil.GetBestIP(ipSlice)

}

func (b *HEBroker) doHttpGet(reqUrl string) *http.Response {
	resp, err := b.client.Get(reqUrl)

	if nil != err {
		sys.Logger.Printf("Get request %s: %s\n", reqUrl, err)
		return nil
	}
	return resp
}

func (b *HEBroker) doHttpPost(reqUrl string, vals url.Values) *http.Response {
	resp, err := b.client.PostForm(reqUrl, vals)

	if nil != err {
		sys.Logger.Printf("Post request %s: %s\n", reqUrl, err)
		return nil
	}

	return resp
}

func parseTunnels(xmlText io.Reader) []*sys.Meta {
	var (
		metas      []*sys.Meta
		xmlDecoder = xml.NewDecoder(xmlText)
	)

	for {
		token, err := xmlDecoder.Token()
		// end
		if nil == token && io.EOF == err {
			break
		}

		switch tokenType := token.(type) {
		case xml.StartElement:
			if "tunnel" == strings.ToLower(tokenType.Name.Local) {
				tunnel := &sys.Meta{}
				err := xmlDecoder.DecodeElement(&tunnel, &tokenType)
				if nil != err {
					sys.Logger.Printf("parse xml fail: %s, %v\n", err, xmlText)
					return nil

				}

				if nil == metas {
					metas = make([]*sys.Meta, 1)
					metas[0] = tunnel

				} else {
					metas = append(metas, tunnel)

				}

			}

		}

	}

	return metas
}
<|endoftext|>


<commit_before>package limiter

import (
	"time"
	"github.com/muesli/cache2go"
)

func LoginLocked()  {

}

// InspectLoginAttempt records and checks if a login limit has been reached
// Returns true if a limit has been reached, false otherwise and the remaining attetmps
func RecordLoginAttempt(username string, incomingIP string) (bool, int, time.Time) {
	loginCache := cache2go.Cache("loginAttempt")
	if !loginCache.Exists(incomingIP) {
		var entry loginAttempt
		entry.FailedAttempts = loginAttempts
		entry.IncomingIP = incomingIP
		entry.Username = username
		entry.Timestamp = time.Now()
		loginCache.Add(incomingIP, time.Duration(loginLockMinutes) * time.Minute, &entry)
		return false, loginAttempts, time.Now()
	} else {
		limitReached, remainingAttempts, timestamp := checkLoginLimitReached(incomingIP)
		return limitReached, remainingAttempts, timestamp
	}
}

// checkLoginLimitReached checks if the login limit has been reached and returns true, false otherwise
func checkLoginLimitReached(incomingIP string) (bool, int, time.Time) {
	loginCache := cache2go.Cache("loginAttempt")
	if entry, ok := loginCache.Value(incomingIP); ok == nil {
		if entry.Data().(*loginAttempt).FailedAttempts == 0 {
			return true, entry.Data().(*loginAttempt).FailedAttempts, entry.Data().(*loginAttempt).Timestamp
		} else {
			return false, decreaseAttempt(incomingIP), entry.Data().(*loginAttempt).Timestamp
		}
	}
	return false, -1, time.Now()
}

// increaseAttempt increases the failed login attempts by 1
func decreaseAttempt(incomingIP string) int {
	loginCache := cache2go.Cache("loginAttempt")

	if entry, ok := loginCache.Value(incomingIP); ok == nil {
		entry.Data().(*loginAttempt).FailedAttempts -= 1
		entry.Data().(*loginAttempt).Timestamp = time.Now()
		return entry.Data().(*loginAttempt).FailedAttempts
	} else {
		return -1
	}
}
<commit_msg>cleanup limiter<commit_after>package limiter

import (
	"time"
	"github.com/muesli/cache2go"
)

// InspectLoginAttempt records and checks if a login limit has been reached
// Returns true if a limit has been reached, false otherwise and the remaining attetmps
func RecordLoginAttempt(username string, incomingIP string) (bool, int, time.Time) {
	loginCache := cache2go.Cache("loginAttempt")
	if !loginCache.Exists(incomingIP) {
		var entry loginAttempt
		entry.FailedAttempts = loginAttempts
		entry.IncomingIP = incomingIP
		entry.Username = username
		entry.Timestamp = time.Now()
		loginCache.Add(incomingIP, time.Duration(loginLockMinutes) * time.Minute, &entry)
		return false, loginAttempts, time.Now()
	} else {
		limitReached, remainingAttempts, timestamp := checkLoginLimitReached(incomingIP)
		return limitReached, remainingAttempts, timestamp
	}
}

// checkLoginLimitReached checks if the login limit has been reached and returns true, false otherwise
func checkLoginLimitReached(incomingIP string) (bool, int, time.Time) {
	loginCache := cache2go.Cache("loginAttempt")
	if entry, ok := loginCache.Value(incomingIP); ok == nil {
		if entry.Data().(*loginAttempt).FailedAttempts == 0 {
			return true, entry.Data().(*loginAttempt).FailedAttempts, entry.Data().(*loginAttempt).Timestamp
		} else {
			return false, decreaseAttempt(incomingIP), entry.Data().(*loginAttempt).Timestamp
		}
	}
	return false, -1, time.Now()
}

// increaseAttempt increases the failed login attempts by 1
func decreaseAttempt(incomingIP string) int {
	loginCache := cache2go.Cache("loginAttempt")

	if entry, ok := loginCache.Value(incomingIP); ok == nil {
		entry.Data().(*loginAttempt).FailedAttempts -= 1
		entry.Data().(*loginAttempt).Timestamp = time.Now()
		return entry.Data().(*loginAttempt).FailedAttempts
	} else {
		return -1
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"os"
)

type Faces struct {
	xPos, zPos int
	count      int

	vertexes Vertexes
	faces    []Face

	sideCache *SideCache
}

func NewFaces(sideCache *SideCache) *Faces {
	return &Faces{0, 0, 0, make([]int16, (128+1)*(16+1)*(16+1)), make([]Face, 0, 8192), sideCache}
}

func (fs *Faces) ProcessBlock(xPos, zPos int, blocks []byte) {
	var enclosed = fs.sideCache.EncloseChunk(xPos, zPos, blocks) // 0.0s  0%
	fs.Clean(xPos, zPos)                                         // 0.1s  4%
	processBlocks(enclosed, fs)                                  // 1.7s 60%
	fs.Process()                                                 // 1.0s 35%

	if cacheSides {
		fs.sideCache.ProcessBlock(xPos, zPos, blocks)
	}

	fmt.Fprintf(os.Stderr, "(%3v,%3v) Faces: %v\n", xPos, zPos, len(fs.faces))
}

func (b *EnclosedChunk) IsBoundary(x, y, z int, blockId byte) bool {
	var (
		empty, air, water       = IsEmptyBlock(blockId)
		otherEmpty, otherAir, _ = IsEmptyBlock(b.Get(x, y, z))
	)

	return (empty && !air && otherAir) || (!empty && otherEmpty) || (water && otherAir)
}

func IsEmptyBlock(blockId byte) (isEmpty bool, isAir bool, isWater bool) {
	isEmpty = false
	isAir = false
	isWater = false
	switch {
	case blockId == 0: // Air
		isEmpty = true
		isAir = true
	case blockId == 9: // Water
		isEmpty = true
		isWater = true
	case hideStone && blockId == 1: // Stone
	case IsMeshBlockId(blockId): // Object that can't be represented as a voxel cube
		isEmpty = true
	}
	return
}

type AddFacer interface {
	AddFace(blockId byte, v1, v2, v3, v4 Vertex)
}

type Face struct {
	blockId byte
	indexes [4]int
}

func (fs *Faces) Clean(xPos, zPos int) {
	fs.xPos = xPos
	fs.zPos = zPos
	fs.vertexes.Clear()
	fs.faces = fs.faces[0:0]
}

func (fs *Faces) AddFace(blockId byte, v1, v2, v3, v4 Vertex) {
	var face = Face{blockId, [4]int{fs.vertexes.Use(v1), fs.vertexes.Use(v2), fs.vertexes.Use(v3), fs.vertexes.Use(v4)}}
	fs.faces = append(fs.faces, face)
}

func (fs *Faces) Process() {
	fs.vertexes.Number()
	var vc = int16(fs.vertexes.Print(out, fs.xPos, fs.zPos))

	var blockIds = make([]byte, 0, 16)
	for _, face := range fs.faces {
		var found = false
		for _, id := range blockIds {
			if id == face.blockId {
				found = true
				break
			}
		}

		if !found {
			blockIds = append(blockIds, face.blockId)
		}
	}

	for _, blockId := range blockIds {
		printMtl(out, blockId)
		for _, face := range fs.faces {
			if face.blockId == blockId {
				fmt.Fprintln(out, "f", fs.vertexes.Get(face.indexes[0])-vc-1, fs.vertexes.Get(face.indexes[1])-vc-1, fs.vertexes.Get(face.indexes[2])-vc-1, fs.vertexes.Get(face.indexes[3])-vc-1)
				faceCount++
			}
		}
	}
}

type Vertexes []int16

func (vs *Vertexes) Index(x, y, z int) int {
	return y + (z*129 + (x * 129 * 17))
}

func (vs *Vertexes) Use(v Vertex) int {
	var i = vs.Index(v.x, v.y, v.z)
	(*vs)[i]++
	return i
}

func (vs *Vertexes) Release(v Vertex) int {
	var i = vs.Index(v.x, v.y, v.z)
	(*vs)[i]--
	return i
}

func (vs *Vertexes) Get(i int) int16 {
	return (*vs)[i]
}

func (vs *Vertexes) Clear() {
	for i, _ := range *vs {
		(*vs)[i] = 0
	}
}

func (vs *Vertexes) Number() {
	var count int16 = 0
	for i, references := range *vs {
		if references != 0 {
			count++
			(*vs)[i] = count
		} else {
			(*vs)[i] = -1
		}
	}
}

func (vs *Vertexes) Print(writer io.Writer, xPos, zPos int) (count int) {
	count = 0
	for i := 0; i < len(*vs); i += 129 {
		var x, z = (i / 129) / 17, (i / 129) % 17

		var column = (*vs)[i : i+129]
		for y, offset := range column {
			if offset != -1 {
				count++
				fmt.Fprintf(writer, "v %.2f %.2f %.2f\n", float64(x+xPos*16)*0.05, float64(y-64)*0.05, float64(z+zPos*16)*0.05)
			}
		}
	}
	return
}

type Vertex struct {
	x, y, z int
}

type blockRun struct {
	blockId        byte
	v1, v2, v3, v4 Vertex
	dirty          bool
}

func (r *blockRun) AddFace(faces AddFacer) {
	if r.dirty {
		faces.AddFace(r.blockId, r.v1, r.v2, r.v3, r.v4)
		r.dirty = false
	}
}

func (r *blockRun) Update(faces AddFacer, nr *blockRun, flag bool) {
	if !blockFaces {
		if r.dirty {
			if nr.blockId == r.blockId {
				if flag {
					r.v3 = nr.v3
					r.v4 = nr.v4
				} else {
					r.v2 = nr.v2
					r.v3 = nr.v3
				}
			} else {
				r.AddFace(faces)
				*r = *nr
			}
		} else {
			*r = *nr
		}
	} else {
		nr.AddFace(faces)
		nr.dirty = false
	}
}

func processBlocks(enclosedChunk *EnclosedChunk, faces AddFacer) {
	for i := 0; i < len(enclosedChunk.blocks); i += 128 {
		var x, z = (i / 128) / 16, (i / 128) % 16

		var r1, r2, r3, r4 blockRun

		var column = BlockColumn(enclosedChunk.blocks[i : i+128])
		for y, blockId := range column {
			if y < yMin {
				continue
			}

			if enclosedChunk.IsBoundary(x, y-1, z, blockId) {
				faces.AddFace(blockId, Vertex{x, y, z}, Vertex{x + 1, y, z}, Vertex{x + 1, y, z + 1}, Vertex{x, y, z + 1})
			}

			if enclosedChunk.IsBoundary(x, y+1, z, blockId) {
				faces.AddFace(blockId, Vertex{x, y + 1, z}, Vertex{x, y + 1, z + 1}, Vertex{x + 1, y + 1, z + 1}, Vertex{x + 1, y + 1, z})
			}

			if enclosedChunk.IsBoundary(x-1, y, z, blockId) {
				r1.Update(faces, &blockRun{blockId, Vertex{x, y, z}, Vertex{x, y, z + 1}, Vertex{x, y + 1, z + 1}, Vertex{x, y + 1, z}, true}, true)
			} else {
				r1.AddFace(faces)
			}

			if enclosedChunk.IsBoundary(x+1, y, z, blockId) {
				r2.Update(faces, &blockRun{blockId, Vertex{x + 1, y, z}, Vertex{x + 1, y + 1, z}, Vertex{x + 1, y + 1, z + 1}, Vertex{x + 1, y, z + 1}, true}, false)
			} else {
				r2.AddFace(faces)
			}

			if enclosedChunk.IsBoundary(x, y, z-1, blockId) {
				r3.Update(faces, &blockRun{blockId, Vertex{x, y, z}, Vertex{x, y + 1, z}, Vertex{x + 1, y + 1, z}, Vertex{x + 1, y, z}, true}, false)
			} else {
				r3.AddFace(faces)
			}

			if enclosedChunk.IsBoundary(x, y, z+1, blockId) {
				r4.Update(faces, &blockRun{blockId, Vertex{x, y, z + 1}, Vertex{x + 1, y, z + 1}, Vertex{x + 1, y + 1, z + 1}, Vertex{x, y + 1, z + 1}, true}, true)
			} else {
				r4.AddFace(faces)
			}
		}

		r1.AddFace(faces)
		r2.AddFace(faces)
		r3.AddFace(faces)
		r4.AddFace(faces)
	}
}
<commit_msg>Simplify IsEmptyBlock() implementation<commit_after>package main

import (
	"fmt"
	"io"
	"os"
)

type Faces struct {
	xPos, zPos int
	count      int

	vertexes Vertexes
	faces    []Face

	sideCache *SideCache
}

func NewFaces(sideCache *SideCache) *Faces {
	return &Faces{0, 0, 0, make([]int16, (128+1)*(16+1)*(16+1)), make([]Face, 0, 8192), sideCache}
}

func (fs *Faces) ProcessBlock(xPos, zPos int, blocks []byte) {
	var enclosed = fs.sideCache.EncloseChunk(xPos, zPos, blocks) // 0.0s  0%
	fs.Clean(xPos, zPos)                                         // 0.1s  4%
	processBlocks(enclosed, fs)                                  // 1.7s 60%
	fs.Process()                                                 // 1.0s 35%

	if cacheSides {
		fs.sideCache.ProcessBlock(xPos, zPos, blocks)
	}

	fmt.Fprintf(os.Stderr, "(%3v,%3v) Faces: %v\n", xPos, zPos, len(fs.faces))
}

func (b *EnclosedChunk) IsBoundary(x, y, z int, blockId byte) bool {
	var (
		empty, air, water       = IsEmptyBlock(blockId)
		otherEmpty, otherAir, _ = IsEmptyBlock(b.Get(x, y, z))
	)

	return (empty && !air && otherAir) || (!empty && otherEmpty) || (water && otherAir)
}

func IsEmptyBlock(blockId byte) (isEmpty bool, isAir bool, isWater bool) {
	isAir = (blockId == 0)
	isWater = (blockId == 9)
	isEmpty = isAir || isWater || (hideStone && blockId == 1) || IsMeshBlockId(blockId)
	return
}

type AddFacer interface {
	AddFace(blockId byte, v1, v2, v3, v4 Vertex)
}

type Face struct {
	blockId byte
	indexes [4]int
}

func (fs *Faces) Clean(xPos, zPos int) {
	fs.xPos = xPos
	fs.zPos = zPos
	fs.vertexes.Clear()
	fs.faces = fs.faces[0:0]
}

func (fs *Faces) AddFace(blockId byte, v1, v2, v3, v4 Vertex) {
	var face = Face{blockId, [4]int{fs.vertexes.Use(v1), fs.vertexes.Use(v2), fs.vertexes.Use(v3), fs.vertexes.Use(v4)}}
	fs.faces = append(fs.faces, face)
}

func (fs *Faces) Process() {
	fs.vertexes.Number()
	var vc = int16(fs.vertexes.Print(out, fs.xPos, fs.zPos))

	var blockIds = make([]byte, 0, 16)
	for _, face := range fs.faces {
		var found = false
		for _, id := range blockIds {
			if id == face.blockId {
				found = true
				break
			}
		}

		if !found {
			blockIds = append(blockIds, face.blockId)
		}
	}

	for _, blockId := range blockIds {
		printMtl(out, blockId)
		for _, face := range fs.faces {
			if face.blockId == blockId {
				fmt.Fprintln(out, "f", fs.vertexes.Get(face.indexes[0])-vc-1, fs.vertexes.Get(face.indexes[1])-vc-1, fs.vertexes.Get(face.indexes[2])-vc-1, fs.vertexes.Get(face.indexes[3])-vc-1)
				faceCount++
			}
		}
	}
}

type Vertexes []int16

func (vs *Vertexes) Index(x, y, z int) int {
	return y + (z*129 + (x * 129 * 17))
}

func (vs *Vertexes) Use(v Vertex) int {
	var i = vs.Index(v.x, v.y, v.z)
	(*vs)[i]++
	return i
}

func (vs *Vertexes) Release(v Vertex) int {
	var i = vs.Index(v.x, v.y, v.z)
	(*vs)[i]--
	return i
}

func (vs *Vertexes) Get(i int) int16 {
	return (*vs)[i]
}

func (vs *Vertexes) Clear() {
	for i, _ := range *vs {
		(*vs)[i] = 0
	}
}

func (vs *Vertexes) Number() {
	var count int16 = 0
	for i, references := range *vs {
		if references != 0 {
			count++
			(*vs)[i] = count
		} else {
			(*vs)[i] = -1
		}
	}
}

func (vs *Vertexes) Print(writer io.Writer, xPos, zPos int) (count int) {
	count = 0
	for i := 0; i < len(*vs); i += 129 {
		var x, z = (i / 129) / 17, (i / 129) % 17

		var column = (*vs)[i : i+129]
		for y, offset := range column {
			if offset != -1 {
				count++
				fmt.Fprintf(writer, "v %.2f %.2f %.2f\n", float64(x+xPos*16)*0.05, float64(y-64)*0.05, float64(z+zPos*16)*0.05)
			}
		}
	}
	return
}

type Vertex struct {
	x, y, z int
}

type blockRun struct {
	blockId        byte
	v1, v2, v3, v4 Vertex
	dirty          bool
}

func (r *blockRun) AddFace(faces AddFacer) {
	if r.dirty {
		faces.AddFace(r.blockId, r.v1, r.v2, r.v3, r.v4)
		r.dirty = false
	}
}

func (r *blockRun) Update(faces AddFacer, nr *blockRun, flag bool) {
	if !blockFaces {
		if r.dirty {
			if nr.blockId == r.blockId {
				if flag {
					r.v3 = nr.v3
					r.v4 = nr.v4
				} else {
					r.v2 = nr.v2
					r.v3 = nr.v3
				}
			} else {
				r.AddFace(faces)
				*r = *nr
			}
		} else {
			*r = *nr
		}
	} else {
		nr.AddFace(faces)
		nr.dirty = false
	}
}

func processBlocks(enclosedChunk *EnclosedChunk, faces AddFacer) {
	for i := 0; i < len(enclosedChunk.blocks); i += 128 {
		var x, z = (i / 128) / 16, (i / 128) % 16

		var r1, r2, r3, r4 blockRun

		var column = BlockColumn(enclosedChunk.blocks[i : i+128])
		for y, blockId := range column {
			if y < yMin {
				continue
			}

			if enclosedChunk.IsBoundary(x, y-1, z, blockId) {
				faces.AddFace(blockId, Vertex{x, y, z}, Vertex{x + 1, y, z}, Vertex{x + 1, y, z + 1}, Vertex{x, y, z + 1})
			}

			if enclosedChunk.IsBoundary(x, y+1, z, blockId) {
				faces.AddFace(blockId, Vertex{x, y + 1, z}, Vertex{x, y + 1, z + 1}, Vertex{x + 1, y + 1, z + 1}, Vertex{x + 1, y + 1, z})
			}

			if enclosedChunk.IsBoundary(x-1, y, z, blockId) {
				r1.Update(faces, &blockRun{blockId, Vertex{x, y, z}, Vertex{x, y, z + 1}, Vertex{x, y + 1, z + 1}, Vertex{x, y + 1, z}, true}, true)
			} else {
				r1.AddFace(faces)
			}

			if enclosedChunk.IsBoundary(x+1, y, z, blockId) {
				r2.Update(faces, &blockRun{blockId, Vertex{x + 1, y, z}, Vertex{x + 1, y + 1, z}, Vertex{x + 1, y + 1, z + 1}, Vertex{x + 1, y, z + 1}, true}, false)
			} else {
				r2.AddFace(faces)
			}

			if enclosedChunk.IsBoundary(x, y, z-1, blockId) {
				r3.Update(faces, &blockRun{blockId, Vertex{x, y, z}, Vertex{x, y + 1, z}, Vertex{x + 1, y + 1, z}, Vertex{x + 1, y, z}, true}, false)
			} else {
				r3.AddFace(faces)
			}

			if enclosedChunk.IsBoundary(x, y, z+1, blockId) {
				r4.Update(faces, &blockRun{blockId, Vertex{x, y, z + 1}, Vertex{x + 1, y, z + 1}, Vertex{x + 1, y + 1, z + 1}, Vertex{x, y + 1, z + 1}, true}, true)
			} else {
				r4.AddFace(faces)
			}
		}

		r1.AddFace(faces)
		r2.AddFace(faces)
		r3.AddFace(faces)
		r4.AddFace(faces)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"net"
	"time"

	"github.com/myzhan/boomer"
)

//                            +------------+
//                            |            |
//                            |   locust   |
//                            |            |
//                            +------+-----+
//                                   ^
//                                   |  qps & timeout
//                                   |
//	+------------+        +------+-----+        +------------+
//	|            |request |            |request |            |
//	| udpcopy    +------->+ udp proxy  +------->+ backend    |
//	|            |        |            |        |            |
//	+------------+        +------------+        +------------+

// While requests from udpcopy passing through this udp server, it keeps track of qps and timeout.
// Also, it can multi-copy the original request for more stress.

// Known Issues:
// 1. Once locust start the test, it can't be stopped by locust. You should restart locust and this udp server if you
//    want to restart.

// See also:
// udpcopy: https://github.com/wangbin579/udpcopy

func sendReq(req []byte, addr string) {

	a, err := net.ResolveUDPAddr("udp", addr)

	conn, err := net.DialUDP("udp", nil, a)
	if err != nil {
		boomer.Events.Publish("request_failure", "udp-dial", NAME, 0.0, err.Error())
		return
	}

	conn.SetReadDeadline(time.Now().Add(backendTimeout))

	for n := 0; n < *number; n++ {

		startTime := boomer.Now()

		_, err = conn.Write(req)
		if err != nil {
			boomer.Events.Publish("request_failure", "udp-write", NAME, 0.0, err.Error())
			return
		}

		resp := make([]byte, *UDPBufferSize)
		respLength, err := conn.Read(resp)
		if err != nil {
			boomer.Events.Publish("request_failure", "udp-read", NAME, 0.0, "REQ_TIMEOUT")
			return
		}

		endTime := boomer.Now()

		boomer.Events.Publish("request_success", "udp-resp", NAME, float64(endTime-startTime), int64(respLength))
	}

}

func proxy() {

	listener, err := net.ListenUDP("udp", &net.UDPAddr{
		IP:   net.ParseIP(*proxyHost),
		Port: *proxyPort,
	})
	if err != nil {
		log.Fatal("error binding on port:", *proxyPort, err)
	}

	for {
		data := make([]byte, *UDPBufferSize)
		n, _, err := listener.ReadFromUDP(data)
		if err != nil {
			log.Println("error during read, current request is dropped.", err)
			continue
		}
		if *UDPBufferSize <= n {
			log.Printf("request size is larger than %d，please enlarge udp-buffer-size. current request is dropped.\n", *UDPBufferSize)
			continue
		}
		if !testStarted {
			// test is not started, drop current request.
			continue
		}
		go sendReq(data[:n], *backendAddr)
	}
}

func deadend() {

	testStarted = true

	for {
		time.Sleep(time.Second * 100)
	}
}

func main() {

	task := &boomer.Task{
		Name:   "udproxy",
		Weight: 10,
		Fn:     deadend,
	}

	go proxy()

	boomer.Run(task)
}

const NAME string = "udproxy"

var testStarted bool = false

var backendAddr *string
var backendTimeout time.Duration
var proxyHost *string
var proxyPort *int
var UDPBufferSize *int
var number *int

func init() {

	backendAddr = flag.String("backend-addr", "127.0.0.1:44444", "backend address")
	timeout := flag.Int("backend-timeout", 1000, "backend timeout(ms)")
	backendTimeout = time.Duration(*timeout) * time.Millisecond
	proxyHost = flag.String("proxy-host", "0.0.0.0", "proxy bind-host")
	proxyPort = flag.Int("proxy-port", 23333, "proxy bind-port")
	UDPBufferSize = flag.Int("udp-buffer-size", 10240, "udp recv buffer size")
	number = flag.Int("number", 1, "the number of replication for multi-copying")
	flag.Parse()

}
<commit_msg>FIX: use err.Error() instead of unclear REQ_TIMEOUT<commit_after>package main

import (
	"flag"
	"log"
	"net"
	"time"

	"github.com/myzhan/boomer"
)

//                            +------------+
//                            |            |
//                            |   locust   |
//                            |            |
//                            +------+-----+
//                                   ^
//                                   |  qps & timeout
//                                   |
//	+------------+        +------+-----+        +------------+
//	|            |request |            |request |            |
//	| udpcopy    +------->+ udp proxy  +------->+ backend    |
//	|            |        |            |        |            |
//	+------------+        +------------+        +------------+

// While requests from udpcopy passing through this udp server, it keeps track of qps and timeout.
// Also, it can multi-copy the original request for more stress.

// Known Issues:
// 1. Once locust start the test, it can't be stopped by locust. You should restart locust and this udp server if you
//    want to restart.

// See also:
// udpcopy: https://github.com/wangbin579/udpcopy

func sendReq(req []byte, addr string) {

	a, err := net.ResolveUDPAddr("udp", addr)

	conn, err := net.DialUDP("udp", nil, a)
	if err != nil {
		boomer.Events.Publish("request_failure", "udp-dial", NAME, 0.0, err.Error())
		return
	}

	conn.SetReadDeadline(time.Now().Add(backendTimeout))

	for n := 0; n < *number; n++ {

		startTime := boomer.Now()

		_, err = conn.Write(req)
		if err != nil {
			boomer.Events.Publish("request_failure", "udp-write", NAME, 0.0, err.Error())
			return
		}

		resp := make([]byte, *UDPBufferSize)
		respLength, err := conn.Read(resp)
		if err != nil {
			boomer.Events.Publish("request_failure", "udp-read", NAME, 0.0, err.Error())
			return
		}

		endTime := boomer.Now()

		boomer.Events.Publish("request_success", "udp-resp", NAME, float64(endTime-startTime), int64(respLength))
	}

}

func proxy() {

	listener, err := net.ListenUDP("udp", &net.UDPAddr{
		IP:   net.ParseIP(*proxyHost),
		Port: *proxyPort,
	})
	if err != nil {
		log.Fatal("error binding on port:", *proxyPort, err)
	}

	for {
		data := make([]byte, *UDPBufferSize)
		n, _, err := listener.ReadFromUDP(data)
		if err != nil {
			log.Println("error during read, current request is dropped.", err)
			continue
		}
		if *UDPBufferSize <= n {
			log.Printf("request size is larger than %d，please enlarge udp-buffer-size. current request is dropped.\n", *UDPBufferSize)
			continue
		}
		if !testStarted {
			// test is not started, drop current request.
			continue
		}
		go sendReq(data[:n], *backendAddr)
	}
}

func deadend() {

	testStarted = true

	for {
		time.Sleep(time.Second * 100)
	}
}

func main() {

	task := &boomer.Task{
		Name:   "udproxy",
		Weight: 10,
		Fn:     deadend,
	}

	go proxy()

	boomer.Run(task)
}

const NAME string = "udproxy"

var testStarted bool = false

var backendAddr *string
var backendTimeout time.Duration
var proxyHost *string
var proxyPort *int
var UDPBufferSize *int
var number *int

func init() {

	backendAddr = flag.String("backend-addr", "127.0.0.1:44444", "backend address")
	timeout := flag.Int("backend-timeout", 1000, "backend timeout(ms)")
	backendTimeout = time.Duration(*timeout) * time.Millisecond
	proxyHost = flag.String("proxy-host", "0.0.0.0", "proxy bind-host")
	proxyPort = flag.Int("proxy-port", 23333, "proxy bind-port")
	UDPBufferSize = flag.Int("udp-buffer-size", 10240, "udp recv buffer size")
	number = flag.Int("number", 1, "the number of replication for multi-copying")
	flag.Parse()

}
<|endoftext|>


<commit_before>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package natss
<commit_msg>update<commit_after>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package natss

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"

	natsd "github.com/nats-io/nats-server/v2/server"
	stand "github.com/nats-io/nats-streaming-server/server"
	stores "github.com/nats-io/nats-streaming-server/stores"
	"github.com/webx-top/echo"
)

func Start() {
	// This configure the NATS Server using natsd package
	nOpts := &natsd.Options{
		HTTPPort: 8222,
		Port:     4223,
	}
	/*/ Create the NATS Server
	  ns := natsd.New(nOpts)

	  // Start it as a go routine
	  go ns.Start()

	  // Wait for it to be able to accept connections
	  if !ns.ReadyForConnections(10 * time.Second) {
	    panic("not able to start")
	  }
	  // */

	// Get NATS Streaming Server default options
	sOpts := stand.GetDefaultOptions()
	sOpts.StoreType = stores.TypeFile
	sOpts.FilestoreDir = filepath.Join(echo.Wd(), `data`, `nats`)
	// Force the streaming server to setup its own signal handler
	sOpts.HandleSignals = true
	// override the NoSigs for NATS since Streaming has its own signal handler
	nOpts.NoSigs = true
	// Without this option set to true, the logger is not configured.
	sOpts.EnableLogging = true
	// This will invoke RunServerWithOpts but on Windows, may run it as a service.
	if _, err := stand.Run(sOpts, nOpts); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	runtime.Goexit()
}
<|endoftext|>


<commit_before>// Package profile provides a simple way to manage runtime/pprof
// profiling of your Go application.
package profile

import (
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"sync/atomic"
)

// memProfileRate holds the rate for the memory profile.
var memProfileRate = 4096

// started counts the number of times Start has been called
var started uint32

const (
	cpuMode = iota
	memMode
	blockMode
)

type profile struct {
	// quiet suppresses informational messages during profiling.
	quiet bool

	// noShutdownHook controls whether the profiling package should
	// hook SIGINT to write profiles cleanly.
	noShutdownHook bool

	// mode holds the type of profiling that will be made
	mode int

	// path holds the base path where various profiling files are  written.
	// If blank, the base path will be generated by ioutil.TempDir.
	path string

	// closers holds the cleanup functions that run after each profile
	closers []func()
}

// NoShutdownHook controls whether the profiling package should
// hook SIGINT to write profiles cleanly.
// Programs with more sophisticated signal handling should set
// this to true and ensure the Stop() function returned from Start()
// is called during shutdown.
func NoShutdownHook(p *profile) { p.noShutdownHook = true }

// Quiet suppresses informational messages during profiling.
func Quiet(p *profile) { p.quiet = true }

// CPUProfile controls if cpu profiling will be enabled. It disables any previous profiling settings.
func CPUProfile(p *profile) { p.mode = cpuMode }

// MemProfile controls if memory profiling will be enabled. It disables any previous profiling settings.
func MemProfile(p *profile) { p.mode = memMode }

// MemProfileRate controls if memory profiling will be enabled. Additionally, it takes a parameter which
// allows the setting of the memory profile rate.
func MemProfileRate(rate int) func(*profile) {
	return func(p *profile) {
		memProfileRate = rate
		p.mode = memMode
	}
}

// BlockProfile controls if block (contention) profiling will be enabled. It disables any previous profiling settings.
func BlockProfile(p *profile) { p.mode = blockMode }

// ProfilePath controls the base path where various profiling
// files are written. If blank, the base path will be generated
// by ioutil.TempDir.
func ProfilePath(path string) func(*profile) {
	return func(p *profile) {
		p.path = path
	}
}

// Stop stops the profile and flushes any unwritten data.
func (p *profile) Stop() {
	for _, c := range p.closers {
		c()
	}
}

// Start starts a new profiling session.
// The caller should call the Stop method on the value returned
// to cleanly stop profiling.
func Start(options ...func(*profile)) interface {
	Stop()
} {
	if !atomic.CompareAndSwapUint32(&started, 0, 1) {
		log.Fatal("profile: Start() already called")
	}

	var prof profile
	for _, option := range options {
		option(&prof)
	}

	path, err := func() (string, error) {
		if p := prof.path; p != "" {
			return p, os.MkdirAll(p, 0777)
		}
		return ioutil.TempDir("", "profile")
	}()

	if err != nil {
		log.Fatalf("profile: could not create initial output directory: %v", err)
	}

	switch prof.mode {
	case cpuMode:
		fn := filepath.Join(path, "cpu.pprof")
		f, err := os.Create(fn)
		if err != nil {
			log.Fatalf("profile: could not create cpu profile %q: %v", fn, err)
		}
		if !prof.quiet {
			log.Printf("profile: cpu profiling enabled, %s", fn)
		}
		pprof.StartCPUProfile(f)
		prof.closers = append(prof.closers, func() {
			pprof.StopCPUProfile()
			f.Close()
		})

	case memMode:
		fn := filepath.Join(path, "mem.pprof")
		f, err := os.Create(fn)
		if err != nil {
			log.Fatalf("profile: could not create memory profile %q: %v", fn, err)
		}
		old := runtime.MemProfileRate
		runtime.MemProfileRate = memProfileRate
		if !prof.quiet {
			log.Printf("profile: memory profiling enabled (rate %d), %s", memProfileRate, fn)
		}
		prof.closers = append(prof.closers, func() {
			pprof.Lookup("heap").WriteTo(f, 0)
			f.Close()
			runtime.MemProfileRate = old
		})

	case blockMode:
		fn := filepath.Join(path, "block.pprof")
		f, err := os.Create(fn)
		if err != nil {
			log.Fatalf("profile: could not create block profile %q: %v", fn, err)
		}
		runtime.SetBlockProfileRate(1)
		if !prof.quiet {
			log.Printf("profile: block profiling enabled, %s", fn)
		}
		prof.closers = append(prof.closers, func() {
			pprof.Lookup("block").WriteTo(f, 0)
			f.Close()
			runtime.SetBlockProfileRate(0)
		})
	}

	if !prof.noShutdownHook {
		go func() {
			c := make(chan os.Signal, 1)
			signal.Notify(c, os.Interrupt)
			<-c

			log.Println("profile: caught interrupt, stopping profiles")
			prof.Stop()

			os.Exit(0)
		}()
	}

	prof.closers = append(prof.closers, func() {
		atomic.SwapUint32(&started, 0)
	})

	return &prof
}
<commit_msg>profile: remove memProfileRate package var<commit_after>// Package profile provides a simple way to manage runtime/pprof
// profiling of your Go application.
package profile

import (
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"sync/atomic"
)

// started counts the number of times Start has been called
var started uint32

const (
	cpuMode = iota
	memMode
	blockMode
)

type profile struct {
	// quiet suppresses informational messages during profiling.
	quiet bool

	// noShutdownHook controls whether the profiling package should
	// hook SIGINT to write profiles cleanly.
	noShutdownHook bool

	// mode holds the type of profiling that will be made
	mode int

	// path holds the base path where various profiling files are  written.
	// If blank, the base path will be generated by ioutil.TempDir.

	// memProfileRate holds the rate for the memory profile.
	memProfileRate int

	path string

	// closers holds the cleanup functions that run after each profile
	closers []func()
}

// NoShutdownHook controls whether the profiling package should
// hook SIGINT to write profiles cleanly.
// Programs with more sophisticated signal handling should set
// this to true and ensure the Stop() function returned from Start()
// is called during shutdown.
func NoShutdownHook(p *profile) { p.noShutdownHook = true }

// Quiet suppresses informational messages during profiling.
func Quiet(p *profile) { p.quiet = true }

// CPUProfile controls if cpu profiling will be enabled. It disables any previous profiling settings.
func CPUProfile(p *profile) { p.mode = cpuMode }

// DefaultMemProfileRate is the default memory profiling rate.
// See also http://golang.org/pkg/runtime/#pkg-variables
const DefaultMemProfileRate = 4096

// MemProfile controls if memory profiling will be enabled. It disables any previous profiling settings.
func MemProfile(p *profile) {
	p.memProfileRate = DefaultMemProfileRate
	p.mode = memMode
}

// MemProfileRate controls if memory profiling will be enabled. Additionally, it takes a parameter which
// allows the setting of the memory profile rate.
func MemProfileRate(rate int) func(*profile) {
	return func(p *profile) {
		p.memProfileRate = rate
		p.mode = memMode
	}
}

// BlockProfile controls if block (contention) profiling will be enabled. It disables any previous profiling settings.
func BlockProfile(p *profile) { p.mode = blockMode }

// ProfilePath controls the base path where various profiling
// files are written. If blank, the base path will be generated
// by ioutil.TempDir.
func ProfilePath(path string) func(*profile) {
	return func(p *profile) {
		p.path = path
	}
}

// Stop stops the profile and flushes any unwritten data.
func (p *profile) Stop() {
	for _, c := range p.closers {
		c()
	}
}

// Start starts a new profiling session.
// The caller should call the Stop method on the value returned
// to cleanly stop profiling.
func Start(options ...func(*profile)) interface {
	Stop()
} {
	if !atomic.CompareAndSwapUint32(&started, 0, 1) {
		log.Fatal("profile: Start() already called")
	}

	var prof profile
	for _, option := range options {
		option(&prof)
	}

	path, err := func() (string, error) {
		if p := prof.path; p != "" {
			return p, os.MkdirAll(p, 0777)
		}
		return ioutil.TempDir("", "profile")
	}()

	if err != nil {
		log.Fatalf("profile: could not create initial output directory: %v", err)
	}

	switch prof.mode {
	case cpuMode:
		fn := filepath.Join(path, "cpu.pprof")
		f, err := os.Create(fn)
		if err != nil {
			log.Fatalf("profile: could not create cpu profile %q: %v", fn, err)
		}
		if !prof.quiet {
			log.Printf("profile: cpu profiling enabled, %s", fn)
		}
		pprof.StartCPUProfile(f)
		prof.closers = append(prof.closers, func() {
			pprof.StopCPUProfile()
			f.Close()
		})

	case memMode:
		fn := filepath.Join(path, "mem.pprof")
		f, err := os.Create(fn)
		if err != nil {
			log.Fatalf("profile: could not create memory profile %q: %v", fn, err)
		}
		old := runtime.MemProfileRate
		runtime.MemProfileRate = prof.memProfileRate
		if !prof.quiet {
			log.Printf("profile: memory profiling enabled (rate %d), %s", runtime.MemProfileRate, fn)
		}
		prof.closers = append(prof.closers, func() {
			pprof.Lookup("heap").WriteTo(f, 0)
			f.Close()
			runtime.MemProfileRate = old
		})

	case blockMode:
		fn := filepath.Join(path, "block.pprof")
		f, err := os.Create(fn)
		if err != nil {
			log.Fatalf("profile: could not create block profile %q: %v", fn, err)
		}
		runtime.SetBlockProfileRate(1)
		if !prof.quiet {
			log.Printf("profile: block profiling enabled, %s", fn)
		}
		prof.closers = append(prof.closers, func() {
			pprof.Lookup("block").WriteTo(f, 0)
			f.Close()
			runtime.SetBlockProfileRate(0)
		})
	}

	if !prof.noShutdownHook {
		go func() {
			c := make(chan os.Signal, 1)
			signal.Notify(c, os.Interrupt)
			<-c

			log.Println("profile: caught interrupt, stopping profiles")
			prof.Stop()

			os.Exit(0)
		}()
	}

	prof.closers = append(prof.closers, func() {
		atomic.SwapUint32(&started, 0)
	})

	return &prof
}
<|endoftext|>


<commit_before>package expreduce

import "bytes"
import "math/big"
import "sort"
import "fmt"
import "flag"
import "hash"

var dirtystrings = flag.Bool("dirtystrings", false, "")
var printevals = flag.Bool("printevals", false, "")

type Expression struct {
	Parts []Ex
	needsEval bool
	correctlyInstantiated bool
}

// Deprecated in favor of headExAssertion
func HeadAssertion(ex Ex, head string) (*Expression, bool) {
	expr, isExpr := ex.(*Expression)
	if isExpr {
		sym, isSym := expr.Parts[0].(*Symbol)
		if isSym {
			if sym.Name == head {
				return expr, true
			}
		}
	}
	return NewEmptyExpression(), false
}

func headExAssertion(ex Ex, head Ex, cl *CASLogger) (*Expression, bool) {
	expr, isExpr := ex.(*Expression)
	if isExpr {
		if IsSameQ(head, expr.Parts[0], cl) {
			return expr, true
		}
	}
	return NewEmptyExpression(), false
}

func tryReturnValue(e Ex) (Ex, bool) {
	asReturn, isReturn := HeadAssertion(e, "Return")
	if !isReturn {
		return nil, false
	}
	if len(asReturn.Parts) >= 2 {
		return asReturn.Parts[1], true
	}
	return &Symbol{"Null"}, true
}

// Is this causing issues by not creating a copy as we modify? Actually it is
// creating copies.
func (this *Expression) mergeSequences(es *EvalState, headStr string, shouldEval bool) {
	// TODO: I should not be attempting to merge the head if it happens to be
	// a Sequence type. This is very similar to the flatten function. Perhaps
	// it should be combined. This version is not recursive, and it does not
	// accept level depths. It is a specific case of Flatten.
	origLen := len(this.Parts)
	offset := 0
	for i := 0; i < origLen; i++ {
		j := i + offset
		e := this.Parts[j]
		seq, isseq := HeadAssertion(e, headStr)
		if shouldEval {
			for j := 1; j < len(seq.Parts); j++ {
				seq.Parts[j] = seq.Parts[j].Eval(es)
			}
		}
		if isseq {
			start := j
			end := j + 1
			if j == 0 {
				this.Parts = append(seq.Parts[1:], this.Parts[end:]...)
			} else if j == len(this.Parts)-1 {
				this.Parts = append(this.Parts[:start], seq.Parts[1:]...)
			} else {
				// All of these deep copies may not be needed.
				this.Parts = append(append(this.DeepCopy().(*Expression).Parts[:start], seq.DeepCopy().(*Expression).Parts[1:]...), this.DeepCopy().(*Expression).Parts[end:]...)
			}
			offset += len(seq.Parts[1:]) - 1
		}
	}
}

func (this *Expression) Eval(es *EvalState) Ex {
	lastExHash := uint64(0)
	currExHash := hashEx(this)
	var currEx Ex = this.DeepCopy()
	insideDefinition := false
	//needsEval := currEx.NeedsEval()
	//for currEx.NeedsEval() && currExHash != lastExHash {
	for currExHash != lastExHash {
		lastExHash = currExHash
		curr, isExpr := currEx.(*Expression)
		if *printevals {
			fmt.Printf("Evaluating %v.\n", curr)
		}
		// Transition to the right Eval() if this is no longer an Expression
		if !isExpr {
			toReturn := currEx.Eval(es)
			// Handle tracing
			if es.trace != nil && !es.IsFrozen() {
				toAppend := NewExpression([]Ex{
					&Symbol{"HoldForm"},
					toReturn.DeepCopy(),
				})

				//fmt.Printf("Beginning: appending %v\n", toAppend.StringForm("FullForm"))
				es.trace.Parts = append(
					es.trace.Parts,
					toAppend,
				)
			}
			return toReturn
		}

		if insideDefinition {
			retVal, isReturn := tryReturnValue(curr)
			if isReturn {
				return retVal
			}
		}

		// Start by evaluating each argument
		headSym, headIsSym := &Symbol{}, false
		attrs := Attributes{}
		if len(curr.Parts) > 0 {
			headSym, headIsSym = curr.Parts[0].(*Symbol)
		}
		if headIsSym {
			attrs = headSym.Attrs(&es.defined)
		}
		for i := range curr.Parts {
			if headIsSym && i == 1 && attrs.HoldFirst {
				continue
			}
			if headIsSym && i > 1 && attrs.HoldRest {
				continue
			}
			if headIsSym && attrs.HoldAll {
				continue
			}

			// Handle tracing
			traceBak := es.trace
			if es.trace != nil && !es.IsFrozen() {
				es.trace = NewExpression([]Ex{&Symbol{"List"}})
			}
			curr.Parts[i] = curr.Parts[i].Eval(es)
			if es.trace != nil && !es.IsFrozen() {
				if len(es.trace.Parts) > 2 {
					// The DeepCopy here doesn't seem to affect anything, but
					// should be good to have.
					//fmt.Printf("Argument eval: appending %v\n", es.trace.DeepCopy().StringForm("FullForm"))
					traceBak.Parts = append(traceBak.Parts, es.trace.DeepCopy())
				}
				es.trace = traceBak
			}
		}

		// Handle tracing
		if es.trace != nil && !es.IsFrozen() {
			toAppend := NewExpression([]Ex{
				&Symbol{"HoldForm"},
				currEx.DeepCopy(),
			})

			if !IsSameQ(es.trace.Parts[len(es.trace.Parts)-1], toAppend, &es.CASLogger) {
				//fmt.Printf("Beginning: appending %v\n", toAppend.StringForm("FullForm"))
				es.trace.Parts = append(
					es.trace.Parts,
					toAppend,
				)
			}
		}

		// If any of the parts are Sequence, merge them with parts
		if headIsSym {
			if !attrs.SequenceHold {
				curr.mergeSequences(es, "Sequence", false)
			}
		} else {
			curr.mergeSequences(es, "Sequence", false)
		}
		curr.mergeSequences(es, "Evaluate", true)

		pureFunction, isPureFunction := HeadAssertion(curr.Parts[0], "Function")
		if headIsSym {
			if attrs.Flat {
				curr.mergeSequences(es, headSym.Name, false)
			}
			if attrs.Orderless {
				sort.Sort(curr)
			}
			if attrs.Listable {
				changed := false
				currEx, changed = ThreadExpr(curr)
				if changed {
					currExHash = hashEx(currEx)
					continue
				}
			}
			headStr := headSym.Name

			legacyEvalFn, hasLegacyEvalFn := (func(*Expression, *EvalState) Ex)(nil), false
			if _, inDefined := es.defined[headStr]; inDefined {
				if es.defined[headStr].legacyEvalFn != nil {
					hasLegacyEvalFn = true
					legacyEvalFn = es.defined[headStr].legacyEvalFn
				}
			}
			unchanged := true
			if hasLegacyEvalFn {
				currEx = legacyEvalFn(curr, es)
				// TODO: I could potentially have the legacyevalfn return this.
				unchanged = IsSameQ(currEx, curr, &es.CASLogger)
			}
			if unchanged {
				theRes, isDefined, def := es.GetDef(headStr, curr)
				if isDefined {
					//fmt.Printf("%v, %v, %v\n", headStr, curr, theRes)
					es.Infof("Def: %v ▶ %v ▶ using %v ▶ from %s head", currEx, theRes, def, headStr)
					currEx = theRes
					insideDefinition = true
				}
			}
		} else if isPureFunction {
			currEx = pureFunction.EvalFunction(es, curr.Parts[1:])
		}
		currExHash = hashEx(currEx)
	}
	curr, isExpr := currEx.(*Expression)
	if isExpr {
		curr.needsEval = false
	}
	return currEx
}

func (this *Expression) EvalFunction(es *EvalState, args []Ex) Ex {
	if len(this.Parts) == 2 {
		toReturn := this.Parts[1].DeepCopy()
		for i, arg := range args {
			toReturn = ReplaceAll(toReturn,
				NewExpression([]Ex{
					&Symbol{"Rule"},
					NewExpression([]Ex{
						&Symbol{"Slot"},
						&Integer{big.NewInt(int64(i + 1))},
					}),

					arg,
				}),

				es, EmptyPD(), "Function")
		}
		return toReturn
	} else if len(this.Parts) == 3 {
		repSym, ok := this.Parts[1].(*Symbol)
		if !ok {
			return this
		}
		toReturn := this.Parts[2].DeepCopy()
		toReturn = ReplaceAll(toReturn,
			NewExpression([]Ex{
				&Symbol{"Rule"},
				repSym,
				args[0],
			}),

			es, EmptyPD(), "Function")
		return toReturn
	}
	return this
}

func (this *Expression) ReplaceAll(r *Expression, stopAtHead string, es *EvalState) Ex {
	es.Debugf("In Expression.ReplaceAll. First trying IsMatchQ(this, r.Parts[1], es).")
	es.Debugf("Rule r is: %s", r)

	matchq, matches := IsMatchQ(this, r.Parts[1], EmptyPD(), es)
	toreturn := ReplacePD(r.Parts[2].DeepCopy(), es, matches)
	if matchq {
		es.Debugf("After MatchQ, rule is: %s", r)
		es.Debugf("MatchQ succeeded. Returning r.Parts[2]: %s", r.Parts[2])
		return toreturn
	}

	thisSym, thisSymOk := this.Parts[0].(*Symbol)
	lhsExpr, lhsExprOk := r.Parts[1].(*Expression)
	if lhsExprOk {
		otherSym, otherSymOk := lhsExpr.Parts[0].(*Symbol)
		if thisSymOk && otherSymOk {
			if thisSym.Name == otherSym.Name {
				attrs := thisSym.Attrs(&es.defined)
				if attrs.Flat {
					FlatReplace(this, lhsExpr, r.Parts[2], attrs.Orderless, es)
				}
			}
		}
	}

	for i := range this.Parts {
		this.Parts[i] = ReplaceAll(this.Parts[i], r, es, EmptyPD(), stopAtHead)
	}
	return this
}

func (this *Expression) StringForm(form string) string {
	headAsSym, isHeadSym := this.Parts[0].(*Symbol)
	fullForm := false
	if isHeadSym && !fullForm {
		res, ok := "", false
		headStr := headAsSym.Name
		toStringFn, hasToStringFn := toStringFns[headStr]
		if hasToStringFn {
			ok, res = toStringFn(this, form)
		}
		if ok {
			if this.needsEval && *dirtystrings {
				return "~" + res + "~"
			}
			return res
		}
	}

	// Default printing format
	var buffer bytes.Buffer
	buffer.WriteString(this.Parts[0].String())
	buffer.WriteString("[")
	for i, e := range this.Parts {
		if i == 0 {
			continue
		}
		buffer.WriteString(e.StringForm(form))
		if i != len(this.Parts)-1 {
			buffer.WriteString(", ")
		}
	}
	buffer.WriteString("]")
	if this.needsEval && *dirtystrings {
		return "~" + buffer.String() + "~"
	}
	return buffer.String()
}

func (this *Expression) String() string {
	return this.StringForm("InputForm")
}

func (this *Expression) IsEqual(otherEx Ex, cl *CASLogger) string {
	other, ok := otherEx.(*Expression)
	if !ok {
		return "EQUAL_UNK"
	}

	if len(this.Parts) != len(other.Parts) {
		return "EQUAL_UNK"
	}
	for i := range this.Parts {
		res := this.Parts[i].IsEqual(other.Parts[i], cl)
		switch res {
		case "EQUAL_FALSE":
			return "EQUAL_UNK"
		case "EQUAL_TRUE":
		case "EQUAL_UNK":
			return "EQUAL_UNK"
		}
	}
	return "EQUAL_TRUE"
}

func (this *Expression) DeepCopy() Ex {
	var thiscopy = NewEmptyExpression()
	for i := range this.Parts {
		thiscopy.Parts = append(thiscopy.Parts, this.Parts[i].DeepCopy())
	}
	thiscopy.needsEval = this.needsEval
	thiscopy.correctlyInstantiated = this.correctlyInstantiated
	return thiscopy
}

// Implement the sort.Interface
func (this *Expression) Len() int {
	return len(this.Parts) - 1
}

func (this *Expression) Less(i, j int) bool {
	return ExOrder(this.Parts[i+1], this.Parts[j+1]) == 1
}

func (this *Expression) Swap(i, j int) {
	this.Parts[j+1], this.Parts[i+1] = this.Parts[i+1], this.Parts[j+1]
}

func (this *Expression) appendEx(e Ex) {
	this.Parts = append(this.Parts, e)
}

func (this *Expression) NeedsEval() bool {
	return this.needsEval
}

func (this *Expression) Hash(h *hash.Hash64) {
	(*h).Write([]byte{72, 5, 244, 86, 5, 210, 69, 30})
	for _, part := range this.Parts {
		part.Hash(h)
	}
}

func NewExpression(parts []Ex) *Expression {
	return &Expression{
		Parts: parts,
		needsEval: true,
		correctlyInstantiated: true,
	}
}

func NewEmptyExpression() *Expression {
	return &Expression{
		needsEval: true,
		correctlyInstantiated: true,
	}
}
<commit_msg>Huge speedup.<commit_after>package expreduce

import "bytes"
import "math/big"
import "sort"
import "fmt"
import "flag"
import "hash"

var dirtystrings = flag.Bool("dirtystrings", false, "")
var printevals = flag.Bool("printevals", false, "")

type Expression struct {
	Parts []Ex
	needsEval bool
	correctlyInstantiated bool
	cachedHash uint64
}

// Deprecated in favor of headExAssertion
func HeadAssertion(ex Ex, head string) (*Expression, bool) {
	expr, isExpr := ex.(*Expression)
	if isExpr {
		sym, isSym := expr.Parts[0].(*Symbol)
		if isSym {
			if sym.Name == head {
				return expr, true
			}
		}
	}
	return NewEmptyExpression(), false
}

func headExAssertion(ex Ex, head Ex, cl *CASLogger) (*Expression, bool) {
	expr, isExpr := ex.(*Expression)
	if isExpr {
		if IsSameQ(head, expr.Parts[0], cl) {
			return expr, true
		}
	}
	return NewEmptyExpression(), false
}

func tryReturnValue(e Ex) (Ex, bool) {
	asReturn, isReturn := HeadAssertion(e, "Return")
	if !isReturn {
		return nil, false
	}
	if len(asReturn.Parts) >= 2 {
		return asReturn.Parts[1], true
	}
	return &Symbol{"Null"}, true
}

// Is this causing issues by not creating a copy as we modify? Actually it is
// creating copies.
func (this *Expression) mergeSequences(es *EvalState, headStr string, shouldEval bool) {
	// TODO: I should not be attempting to merge the head if it happens to be
	// a Sequence type. This is very similar to the flatten function. Perhaps
	// it should be combined. This version is not recursive, and it does not
	// accept level depths. It is a specific case of Flatten.
	origLen := len(this.Parts)
	offset := 0
	for i := 0; i < origLen; i++ {
		j := i + offset
		e := this.Parts[j]
		seq, isseq := HeadAssertion(e, headStr)
		if shouldEval {
			for j := 1; j < len(seq.Parts); j++ {
				seq.Parts[j] = seq.Parts[j].Eval(es)
			}
		}
		if isseq {
			start := j
			end := j + 1
			if j == 0 {
				this.Parts = append(seq.Parts[1:], this.Parts[end:]...)
			} else if j == len(this.Parts)-1 {
				this.Parts = append(this.Parts[:start], seq.Parts[1:]...)
			} else {
				// All of these deep copies may not be needed.
				this.Parts = append(append(this.DeepCopy().(*Expression).Parts[:start], seq.DeepCopy().(*Expression).Parts[1:]...), this.DeepCopy().(*Expression).Parts[end:]...)
			}
			offset += len(seq.Parts[1:]) - 1
		}
	}
}

func (this *Expression) Eval(es *EvalState) Ex {
	lastExHash := uint64(0)
	currExHash := hashEx(this)
	if currExHash == this.cachedHash {
		return this
	}
	var currEx Ex = this.DeepCopy()
	insideDefinition := false
	//needsEval := currEx.NeedsEval()
	//for currEx.NeedsEval() && currExHash != lastExHash {
	for currExHash != lastExHash {
		//fmt.Printf("%v, %v\n", currExHash, lastExHash)
		lastExHash = currExHash
		curr, isExpr := currEx.(*Expression)
		// if isExpr && currExHash == curr.cachedHash {
		// 	return this
		// }
		if *printevals {
			fmt.Printf("Evaluating %v.\n", curr)
		}
		// Transition to the right Eval() if this is no longer an Expression
		if !isExpr {
			toReturn := currEx.Eval(es)
			// Handle tracing
			if es.trace != nil && !es.IsFrozen() {
				toAppend := NewExpression([]Ex{
					&Symbol{"HoldForm"},
					toReturn.DeepCopy(),
				})

				//fmt.Printf("Beginning: appending %v\n", toAppend.StringForm("FullForm"))
				es.trace.Parts = append(
					es.trace.Parts,
					toAppend,
				)
			}
			return toReturn
		}

		if insideDefinition {
			retVal, isReturn := tryReturnValue(curr)
			if isReturn {
				return retVal
			}
		}

		// Start by evaluating each argument
		headSym, headIsSym := &Symbol{}, false
		attrs := Attributes{}
		if len(curr.Parts) > 0 {
			headSym, headIsSym = curr.Parts[0].(*Symbol)
		}
		if headIsSym {
			attrs = headSym.Attrs(&es.defined)
		}
		for i := range curr.Parts {
			if headIsSym && i == 1 && attrs.HoldFirst {
				continue
			}
			if headIsSym && i > 1 && attrs.HoldRest {
				continue
			}
			if headIsSym && attrs.HoldAll {
				continue
			}

			// Handle tracing
			traceBak := es.trace
			if es.trace != nil && !es.IsFrozen() {
				es.trace = NewExpression([]Ex{&Symbol{"List"}})
			}
			curr.Parts[i] = curr.Parts[i].Eval(es)
			if es.trace != nil && !es.IsFrozen() {
				if len(es.trace.Parts) > 2 {
					// The DeepCopy here doesn't seem to affect anything, but
					// should be good to have.
					//fmt.Printf("Argument eval: appending %v\n", es.trace.DeepCopy().StringForm("FullForm"))
					traceBak.Parts = append(traceBak.Parts, es.trace.DeepCopy())
				}
				es.trace = traceBak
			}
		}

		// Handle tracing
		if es.trace != nil && !es.IsFrozen() {
			toAppend := NewExpression([]Ex{
				&Symbol{"HoldForm"},
				currEx.DeepCopy(),
			})

			if !IsSameQ(es.trace.Parts[len(es.trace.Parts)-1], toAppend, &es.CASLogger) {
				//fmt.Printf("Beginning: appending %v\n", toAppend.StringForm("FullForm"))
				es.trace.Parts = append(
					es.trace.Parts,
					toAppend,
				)
			}
		}

		// If any of the parts are Sequence, merge them with parts
		if headIsSym {
			if !attrs.SequenceHold {
				curr.mergeSequences(es, "Sequence", false)
			}
		} else {
			curr.mergeSequences(es, "Sequence", false)
		}
		curr.mergeSequences(es, "Evaluate", true)

		pureFunction, isPureFunction := HeadAssertion(curr.Parts[0], "Function")
		if headIsSym {
			if attrs.Flat {
				curr.mergeSequences(es, headSym.Name, false)
			}
			if attrs.Orderless {
				sort.Sort(curr)
			}
			if attrs.Listable {
				changed := false
				currEx, changed = ThreadExpr(curr)
				if changed {
					currExHash = hashEx(currEx)
					continue
				}
			}
			headStr := headSym.Name

			legacyEvalFn, hasLegacyEvalFn := (func(*Expression, *EvalState) Ex)(nil), false
			if _, inDefined := es.defined[headStr]; inDefined {
				if es.defined[headStr].legacyEvalFn != nil {
					hasLegacyEvalFn = true
					legacyEvalFn = es.defined[headStr].legacyEvalFn
				}
			}
			unchanged := true
			if hasLegacyEvalFn {
				currEx = legacyEvalFn(curr, es)
				// TODO: I could potentially have the legacyevalfn return this.
				unchanged = IsSameQ(currEx, curr, &es.CASLogger)
			}
			if unchanged {
				theRes, isDefined, def := es.GetDef(headStr, curr)
				if isDefined {
					//fmt.Printf("%v, %v, %v\n", headStr, curr, theRes)
					es.Infof("Def: %v ▶ %v ▶ using %v ▶ from %s head", currEx, theRes, def, headStr)
					currEx = theRes
					insideDefinition = true
				}
			}
		} else if isPureFunction {
			currEx = pureFunction.EvalFunction(es, curr.Parts[1:])
		}
		currExHash = hashEx(currEx)
	}
	curr, isExpr := currEx.(*Expression)
	if isExpr {
		curr.needsEval = false
		curr.cachedHash = currExHash
	}
	return currEx
}

func (this *Expression) EvalFunction(es *EvalState, args []Ex) Ex {
	if len(this.Parts) == 2 {
		toReturn := this.Parts[1].DeepCopy()
		for i, arg := range args {
			toReturn = ReplaceAll(toReturn,
				NewExpression([]Ex{
					&Symbol{"Rule"},
					NewExpression([]Ex{
						&Symbol{"Slot"},
						&Integer{big.NewInt(int64(i + 1))},
					}),

					arg,
				}),

				es, EmptyPD(), "Function")
		}
		return toReturn
	} else if len(this.Parts) == 3 {
		repSym, ok := this.Parts[1].(*Symbol)
		if !ok {
			return this
		}
		toReturn := this.Parts[2].DeepCopy()
		toReturn = ReplaceAll(toReturn,
			NewExpression([]Ex{
				&Symbol{"Rule"},
				repSym,
				args[0],
			}),

			es, EmptyPD(), "Function")
		return toReturn
	}
	return this
}

func (this *Expression) ReplaceAll(r *Expression, stopAtHead string, es *EvalState) Ex {
	es.Debugf("In Expression.ReplaceAll. First trying IsMatchQ(this, r.Parts[1], es).")
	es.Debugf("Rule r is: %s", r)

	matchq, matches := IsMatchQ(this, r.Parts[1], EmptyPD(), es)
	toreturn := ReplacePD(r.Parts[2].DeepCopy(), es, matches)
	if matchq {
		es.Debugf("After MatchQ, rule is: %s", r)
		es.Debugf("MatchQ succeeded. Returning r.Parts[2]: %s", r.Parts[2])
		return toreturn
	}

	thisSym, thisSymOk := this.Parts[0].(*Symbol)
	lhsExpr, lhsExprOk := r.Parts[1].(*Expression)
	if lhsExprOk {
		otherSym, otherSymOk := lhsExpr.Parts[0].(*Symbol)
		if thisSymOk && otherSymOk {
			if thisSym.Name == otherSym.Name {
				attrs := thisSym.Attrs(&es.defined)
				if attrs.Flat {
					FlatReplace(this, lhsExpr, r.Parts[2], attrs.Orderless, es)
				}
			}
		}
	}

	for i := range this.Parts {
		this.Parts[i] = ReplaceAll(this.Parts[i], r, es, EmptyPD(), stopAtHead)
	}
	return this
}

func (this *Expression) StringForm(form string) string {
	headAsSym, isHeadSym := this.Parts[0].(*Symbol)
	fullForm := false
	if isHeadSym && !fullForm {
		res, ok := "", false
		headStr := headAsSym.Name
		toStringFn, hasToStringFn := toStringFns[headStr]
		if hasToStringFn {
			ok, res = toStringFn(this, form)
		}
		if ok {
			if this.cachedHash != 0 && *dirtystrings {
				return "~" + res + "~"
			}
			return res
		}
	}

	// Default printing format
	var buffer bytes.Buffer
	buffer.WriteString(this.Parts[0].String())
	buffer.WriteString("[")
	for i, e := range this.Parts {
		if i == 0 {
			continue
		}
		buffer.WriteString(e.StringForm(form))
		if i != len(this.Parts)-1 {
			buffer.WriteString(", ")
		}
	}
	buffer.WriteString("]")
	if this.cachedHash != 0 && *dirtystrings {
		return "~" + buffer.String() + fmt.Sprintf("%v %v", this.cachedHash, hashEx(this)) + "~"
	}
	return buffer.String()
}

func (this *Expression) String() string {
	return this.StringForm("InputForm")
}

func (this *Expression) IsEqual(otherEx Ex, cl *CASLogger) string {
	other, ok := otherEx.(*Expression)
	if !ok {
		return "EQUAL_UNK"
	}

	if len(this.Parts) != len(other.Parts) {
		return "EQUAL_UNK"
	}
	for i := range this.Parts {
		res := this.Parts[i].IsEqual(other.Parts[i], cl)
		switch res {
		case "EQUAL_FALSE":
			return "EQUAL_UNK"
		case "EQUAL_TRUE":
		case "EQUAL_UNK":
			return "EQUAL_UNK"
		}
	}
	return "EQUAL_TRUE"
}

func (this *Expression) DeepCopy() Ex {
	var thiscopy = NewEmptyExpression()
	for i := range this.Parts {
		thiscopy.Parts = append(thiscopy.Parts, this.Parts[i].DeepCopy())
	}
	thiscopy.needsEval = this.needsEval
	thiscopy.correctlyInstantiated = this.correctlyInstantiated
	thiscopy.cachedHash = this.cachedHash
	return thiscopy
}

// Implement the sort.Interface
func (this *Expression) Len() int {
	return len(this.Parts) - 1
}

func (this *Expression) Less(i, j int) bool {
	return ExOrder(this.Parts[i+1], this.Parts[j+1]) == 1
}

func (this *Expression) Swap(i, j int) {
	this.Parts[j+1], this.Parts[i+1] = this.Parts[i+1], this.Parts[j+1]
}

func (this *Expression) appendEx(e Ex) {
	this.Parts = append(this.Parts, e)
}

func (this *Expression) NeedsEval() bool {
	return this.needsEval
}

func (this *Expression) Hash(h *hash.Hash64) {
	(*h).Write([]byte{72, 5, 244, 86, 5, 210, 69, 30})
	for _, part := range this.Parts {
		part.Hash(h)
	}
}

func NewExpression(parts []Ex) *Expression {
	return &Expression{
		Parts: parts,
		needsEval: true,
		correctlyInstantiated: true,
	}
}

func NewEmptyExpression() *Expression {
	return &Expression{
		needsEval: true,
		correctlyInstantiated: true,
	}
}
<|endoftext|>


<commit_before>// Icon View/Icon View Basics
//
// The GtkIconView widget is used to display and manipulate icons.
// It uses a GtkTreeModel for data storage, so the list store
// example might be helpful.
package iconview

import "gobject/gtk-3.0"
import "gobject/gobject-2.0"
import "gobject/gdkpixbuf-2.0"
import "io/ioutil"
import "strings"
import "path/filepath"
import "os"
import "./common"

var window *gtk.Window
var file_pixbuf *gdkpixbuf.Pixbuf
var folder_pixbuf *gdkpixbuf.Pixbuf
var parent string
var up_button *gtk.ToolItem

const (
	col_path = iota
	col_display_name
	col_pixbuf
	col_is_directory
)

const folder_icon_filename = "gnome-fs-directory.png"
const file_icon_filename = "gnome-fs-regular.png"

// Loads the images for the demo and returns whether the operation succeeded
func load_pixbufs() error {
	if file_pixbuf != nil {
		return nil // already loaded earlier
	}

	var err error
	file_pixbuf, err = gdkpixbuf.NewPixbufFromFile(common.FindFile(file_icon_filename))
	if err != nil {
		return err
	}

	folder_pixbuf, err = gdkpixbuf.NewPixbufFromFile(common.FindFile(folder_icon_filename))
	if err != nil {
		return err
	}

	return nil
}

func sort_func(model *gtk.TreeModel, a, b *gtk.TreeIter) int {
	var is_dir_a, is_dir_b bool
	var name_a, name_b string

	model.Get(a,
		col_is_directory, &is_dir_a,
		col_display_name, &name_a)

	model.Get(b,
		col_is_directory, &is_dir_b,
		col_display_name, &name_b)

	name_a = strings.ToLower(name_a)
	name_b = strings.ToLower(name_b)

	if !is_dir_a && is_dir_b {
		return 1
	} else if is_dir_a && !is_dir_b {
		return -1
	} else {
		switch {
		case name_a < name_b:
			return -1
		case name_a == name_b:
			return 0
		case name_a > name_b:
			return 1
		}
	}

	return 0
}

func create_store() *gtk.ListStore {
	store := gtk.NewListStore(
		gobject.String,
		gobject.String,
		gdkpixbuf.PixbufGetType(),
		gobject.Boolean)

	// Set sort column and function
	store.SetDefaultSortFunc(sort_func)
	store.SetSortColumnID(-1, gtk.SortTypeAscending)

	return store
}

func fill_store(store *gtk.ListStore) {
	store.Clear()

	entries, err := ioutil.ReadDir(parent)
	if err != nil {
		println(err.Error())
		return
	}

	for _, entry := range entries {
		display_name := entry.Name()
		if strings.HasPrefix(display_name, ".") {
			// We ignore hidden files that start with a '.'
			continue
		}

		path := filepath.Join(parent, display_name)
		is_dir := entry.IsDir()

		var pixbuf *gdkpixbuf.Pixbuf
		if is_dir {
			pixbuf = folder_pixbuf
		} else {
			pixbuf = file_pixbuf
		}

		store.Append(path, display_name, pixbuf, is_dir)
	}
}

func item_activated(icon_view *gtk.IconView, tree_path *gtk.TreePath) {
	store := gtk.ToListStore(icon_view.GetModel())
	iter, _ := store.GetIter(tree_path)

	var is_dir bool
	var path string
	store.Get(&iter,
		col_path, &path,
		col_is_directory, &is_dir)

	if !is_dir {
		return
	}

	// Replace parent with path and re-fill the model
	parent = path
	fill_store(store)

	// Sensitize the up button
	up_button.SetSensitive(true)
}

func up_clicked(store *gtk.ListStore) {
	parent = filepath.Dir(parent)
	fill_store(store)

	// Maybe de-sensitize the up button
	up_button.SetSensitive(parent != "/")
}

func home_clicked(store *gtk.ListStore) {
	parent = os.Getenv("HOME")
	fill_store(store)

	// Sensitize the up button
	up_button.SetSensitive(true)
}

func Do(mainwin *gtk.Window) *gtk.Window {
	if window == nil {
		window = gtk.NewWindow(gtk.WindowTypeToplevel)
		window.SetDefaultSize(650, 400)
		window.SetScreen(mainwin.GetScreen())
		window.SetTitle("GtkIconView demo")
		window.Connect("destroy", func() {
			window = nil
			up_button = nil
		})

		if err := load_pixbufs(); err != nil {
			dialog := gtk.NewMessageDialog(window, gtk.DialogFlagsDestroyWithParent,
				gtk.MessageTypeError, gtk.ButtonsTypeClose,
				"Failed to load an image: %s", err)
			dialog.Connect("response", func() { dialog.Destroy() })
			dialog.Show()
			goto done
		}

		vbox := gtk.NewBox(gtk.OrientationVertical, 0)
		window.Add(vbox)

		tool_bar := gtk.NewToolbar()
		vbox.PackStart(tool_bar, false, false, 0)

		up_button = gtk.ToToolItem(gtk.NewToolButtonFromStock(gtk.StockGoUp))
		up_button.SetIsImportant(true)
		up_button.SetSensitive(false)
		tool_bar.Insert(up_button, -1)

		home_button := gtk.NewToolButtonFromStock(gtk.StockHome)
		home_button.SetIsImportant(true)
		tool_bar.Insert(home_button, -1)

		sw := gtk.NewScrolledWindow(nil, nil)
		sw.SetShadowType(gtk.ShadowTypeEtchedIn)
		sw.SetPolicy(gtk.PolicyTypeAutomatic, gtk.PolicyTypeAutomatic)
		vbox.PackStart(sw, true, true, 0)

		// Create the store and fill it with the contents of '/'
		parent = "/"
		store := create_store()
		fill_store(store)

		icon_view := gtk.NewIconViewWithModel(store)
		icon_view.SetSelectionMode(gtk.SelectionModeMultiple)

		// Connect to the "clicked" signal of the "Up" tool button
		up_button.Connect("clicked", func() { up_clicked(store) })

		// Connect to the "clicked" signal of the "Home" tool button
		home_button.Connect("clicked", func() { home_clicked(store) })

		// We now set which model columns that correspond to the text
		// and pixbuf of each item
		icon_view.SetTextColumn(col_display_name)
		icon_view.SetPixbufColumn(col_pixbuf)

		// Connect to the "item-activated" signal
		icon_view.Connect("item-activated", item_activated)
		sw.Add(icon_view)

		icon_view.GrabFocus()
	}

done:
	if !window.GetVisible() {
		window.ShowAll()
	} else {
		window.Destroy()
	}
	return window
}<commit_msg>Use "os" dir reading facility instead of ioutil. We don't need to sort.<commit_after>// Icon View/Icon View Basics
//
// The GtkIconView widget is used to display and manipulate icons.
// It uses a GtkTreeModel for data storage, so the list store
// example might be helpful.
package iconview

import "gobject/gtk-3.0"
import "gobject/gobject-2.0"
import "gobject/gdkpixbuf-2.0"
import "strings"
import "path/filepath"
import "os"
import "./common"

var window *gtk.Window
var file_pixbuf *gdkpixbuf.Pixbuf
var folder_pixbuf *gdkpixbuf.Pixbuf
var parent string
var up_button *gtk.ToolItem

const (
	col_path = iota
	col_display_name
	col_pixbuf
	col_is_directory
)

const folder_icon_filename = "gnome-fs-directory.png"
const file_icon_filename = "gnome-fs-regular.png"

// Loads the images for the demo and returns whether the operation succeeded
func load_pixbufs() error {
	if file_pixbuf != nil {
		return nil // already loaded earlier
	}

	var err error
	file_pixbuf, err = gdkpixbuf.NewPixbufFromFile(common.FindFile(file_icon_filename))
	if err != nil {
		return err
	}

	folder_pixbuf, err = gdkpixbuf.NewPixbufFromFile(common.FindFile(folder_icon_filename))
	if err != nil {
		return err
	}

	return nil
}

func sort_func(model *gtk.TreeModel, a, b *gtk.TreeIter) int {
	var is_dir_a, is_dir_b bool
	var name_a, name_b string

	model.Get(a,
		col_is_directory, &is_dir_a,
		col_display_name, &name_a)

	model.Get(b,
		col_is_directory, &is_dir_b,
		col_display_name, &name_b)

	name_a = strings.ToLower(name_a)
	name_b = strings.ToLower(name_b)

	if !is_dir_a && is_dir_b {
		return 1
	} else if is_dir_a && !is_dir_b {
		return -1
	} else {
		switch {
		case name_a < name_b:
			return -1
		case name_a == name_b:
			return 0
		case name_a > name_b:
			return 1
		}
	}

	return 0
}

func create_store() *gtk.ListStore {
	store := gtk.NewListStore(
		gobject.String,
		gobject.String,
		gdkpixbuf.PixbufGetType(),
		gobject.Boolean)

	// Set sort column and function
	store.SetDefaultSortFunc(sort_func)
	store.SetSortColumnID(-1, gtk.SortTypeAscending)

	return store
}

func fill_store(store *gtk.ListStore) {
	store.Clear()

	dir, err := os.Open(parent)
	if err != nil {
		println(err.Error())
		return
	}
	defer dir.Close()

	entries, err := dir.Readdir(-1)
	if err != nil {
		return
	}

	for _, entry := range entries {
		display_name := entry.Name()
		if strings.HasPrefix(display_name, ".") {
			// We ignore hidden files that start with a '.'
			continue
		}

		path := filepath.Join(parent, display_name)
		is_dir := entry.IsDir()

		var pixbuf *gdkpixbuf.Pixbuf
		if is_dir {
			pixbuf = folder_pixbuf
		} else {
			pixbuf = file_pixbuf
		}

		store.Append(path, display_name, pixbuf, is_dir)
	}
}

func item_activated(icon_view *gtk.IconView, tree_path *gtk.TreePath) {
	store := gtk.ToListStore(icon_view.GetModel())
	iter, _ := store.GetIter(tree_path)

	var is_dir bool
	var path string
	store.Get(&iter,
		col_path, &path,
		col_is_directory, &is_dir)

	if !is_dir {
		return
	}

	// Replace parent with path and re-fill the model
	parent = path
	fill_store(store)

	// Sensitize the up button
	up_button.SetSensitive(true)
}

func up_clicked(store *gtk.ListStore) {
	parent = filepath.Dir(parent)
	fill_store(store)

	// Maybe de-sensitize the up button
	up_button.SetSensitive(parent != "/")
}

func home_clicked(store *gtk.ListStore) {
	parent = os.Getenv("HOME")
	fill_store(store)

	// Sensitize the up button
	up_button.SetSensitive(true)
}

func Do(mainwin *gtk.Window) *gtk.Window {
	if window == nil {
		window = gtk.NewWindow(gtk.WindowTypeToplevel)
		window.SetDefaultSize(650, 400)
		window.SetScreen(mainwin.GetScreen())
		window.SetTitle("GtkIconView demo")
		window.Connect("destroy", func() {
			window = nil
			up_button = nil
		})

		if err := load_pixbufs(); err != nil {
			dialog := gtk.NewMessageDialog(window, gtk.DialogFlagsDestroyWithParent,
				gtk.MessageTypeError, gtk.ButtonsTypeClose,
				"Failed to load an image: %s", err)
			dialog.Connect("response", func() { dialog.Destroy() })
			dialog.Show()
			goto done
		}

		vbox := gtk.NewBox(gtk.OrientationVertical, 0)
		window.Add(vbox)

		tool_bar := gtk.NewToolbar()
		vbox.PackStart(tool_bar, false, false, 0)

		up_button = gtk.ToToolItem(gtk.NewToolButtonFromStock(gtk.StockGoUp))
		up_button.SetIsImportant(true)
		up_button.SetSensitive(false)
		tool_bar.Insert(up_button, -1)

		home_button := gtk.NewToolButtonFromStock(gtk.StockHome)
		home_button.SetIsImportant(true)
		tool_bar.Insert(home_button, -1)

		sw := gtk.NewScrolledWindow(nil, nil)
		sw.SetShadowType(gtk.ShadowTypeEtchedIn)
		sw.SetPolicy(gtk.PolicyTypeAutomatic, gtk.PolicyTypeAutomatic)
		vbox.PackStart(sw, true, true, 0)

		// Create the store and fill it with the contents of '/'
		parent = "/"
		store := create_store()
		fill_store(store)

		icon_view := gtk.NewIconViewWithModel(store)
		icon_view.SetSelectionMode(gtk.SelectionModeMultiple)

		// Connect to the "clicked" signal of the "Up" tool button
		up_button.Connect("clicked", func() { up_clicked(store) })

		// Connect to the "clicked" signal of the "Home" tool button
		home_button.Connect("clicked", func() { home_clicked(store) })

		// We now set which model columns that correspond to the text
		// and pixbuf of each item
		icon_view.SetTextColumn(col_display_name)
		icon_view.SetPixbufColumn(col_pixbuf)

		// Connect to the "item-activated" signal
		icon_view.Connect("item-activated", item_activated)
		sw.Add(icon_view)

		icon_view.GrabFocus()
	}

done:
	if !window.GetVisible() {
		window.ShowAll()
	} else {
		window.Destroy()
	}
	return window
}<|endoftext|>