<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"github.com/spf13/cobra"

	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/interpolate"
	"github.com/ovh/cds/sdk/log"
)

func cmdTmpl(w *currentWorker) *cobra.Command {
	c := &cobra.Command{
		Use:   "tmpl",
		Short: "worker tmpl inputFile outputFile",
		Long: `

Inside a step script (https://ovh.github.io/cds/docs/actions/builtin-script/), you can add a replace CDS variables with the real value into a file:

	# create a file
	cat << EOF > myFile
	this a a line in the file, with a CDS variable {{.cds.version}}
	EOF

	# worker tmpl <input file> <output file>
	worker tmpl {{.cds.workspace}}/myFile {{.cds.workspace}}/outputFile

The file ` + "`outputFile`" + ` will contain the string:

	this a a line in the file, with a CDS variable 2

if it's the RUN n°2 of the current workflow.
		`,
		Run: tmplCmd(w),
	}
	return c
}

type tmplPath struct {
	Path        string `json:"path"`
	Destination string `json:"destination"`
}

func tmplCmd(w *currentWorker) func(cmd *cobra.Command, args []string) {
	return func(cmd *cobra.Command, args []string) {
		portS := os.Getenv(WorkerServerPort)
		if portS == "" {
			sdk.Exit("%s not found, are you running inside a CDS worker job?\n", WorkerServerPort)
		}

		port, errPort := strconv.Atoi(portS)
		if errPort != nil {
			sdk.Exit("cannot parse '%s' as a port number", portS)
		}

		if len(args) != 2 {
			sdk.Exit("Wrong usage: Example : worker tmpl filea fileb")
		}

		currentDir, err := os.Getwd()
		if err != nil {
			sdk.Exit("Internal error during Getwd command")
		}

		a := tmplPath{
			filepath.Join(currentDir, args[0]),
			filepath.Join(currentDir, args[1]),
		}

		data, errMarshal := json.Marshal(a)
		if errMarshal != nil {
			sdk.Exit("internal error (%s)\n", errMarshal)
		}

		req, errRequest := http.NewRequest("POST", fmt.Sprintf("http://127.0.0.1:%d/tmpl", port), bytes.NewReader(data))
		if errRequest != nil {
			sdk.Exit("cannot post worker tmpl (Request): %s\n", errRequest)
		}

		client := http.DefaultClient
		client.Timeout = 5 * time.Minute

		resp, errDo := client.Do(req)
		if errDo != nil {
			sdk.Exit("tmpl call failed: %v", errDo)
		}

		if resp.StatusCode >= 300 {
			body, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				sdk.Exit("tmpl failed: unable to read body %v\n", err)
			}
			cdsError := sdk.DecodeError(body)
			sdk.Exit("tmpl failed: %v\n", cdsError)
		}
	}
}

func (wk *currentWorker) tmplHandler(w http.ResponseWriter, r *http.Request) {
	// Get body
	data, errRead := ioutil.ReadAll(r.Body)
	if errRead != nil {
		newError := sdk.NewError(sdk.ErrWrongRequest, errRead)
		writeError(w, r, newError)
		return
	}

	var a tmplPath
	if err := json.Unmarshal(data, &a); err != nil {
		newError := sdk.NewError(sdk.ErrWrongRequest, err)
		writeError(w, r, newError)
		return
	}

	btes, err := ioutil.ReadFile(a.Path)
	if err != nil {
		newError := sdk.NewError(sdk.ErrWrongRequest, err)
		writeError(w, r, newError)
		return
	}

	tmpvars := map[string]string{}
	for _, v := range wk.currentJob.buildVariables {
		tmpvars[v.Name] = v.Value
	}
	for _, v := range wk.currentJob.params {
		tmpvars[v.Name] = v.Value
	}

	res, err := interpolate.Do(string(btes), tmpvars)
	if err != nil {
		log.Error("Unable to interpolate: %v", err)
		newError := sdk.NewError(sdk.ErrWrongRequest, err)
		writeError(w, r, newError)
		return
	}

	if err := ioutil.WriteFile(a.Destination, []byte(res), os.FileMode(0644)); err != nil {
		writeError(w, r, err)
		return
	}
}
<commit_msg>PR Comment. Don't add current dir if already on a absolute path<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/spf13/cobra"

	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/interpolate"
	"github.com/ovh/cds/sdk/log"
)

func cmdTmpl(w *currentWorker) *cobra.Command {
	c := &cobra.Command{
		Use:   "tmpl",
		Short: "worker tmpl inputFile outputFile",
		Long: `

Inside a step script (https://ovh.github.io/cds/docs/actions/builtin-script/), you can add a replace CDS variables with the real value into a file:

	# create a file
	cat << EOF > myFile
	this a a line in the file, with a CDS variable {{.cds.version}}
	EOF

	# worker tmpl <input file> <output file>
	worker tmpl {{.cds.workspace}}/myFile {{.cds.workspace}}/outputFile

The file ` + "`outputFile`" + ` will contain the string:

	this a a line in the file, with a CDS variable 2

if it's the RUN n°2 of the current workflow.
		`,
		Run: tmplCmd(w),
	}
	return c
}

type tmplPath struct {
	Path        string `json:"path"`
	Destination string `json:"destination"`
}

func tmplCmd(w *currentWorker) func(cmd *cobra.Command, args []string) {
	return func(cmd *cobra.Command, args []string) {
		portS := os.Getenv(WorkerServerPort)
		if portS == "" {
			sdk.Exit("%s not found, are you running inside a CDS worker job?\n", WorkerServerPort)
		}

		port, errPort := strconv.Atoi(portS)
		if errPort != nil {
			sdk.Exit("cannot parse '%s' as a port number", portS)
		}

		if len(args) != 2 {
			sdk.Exit("Wrong usage: Example : worker tmpl filea fileb")
		}

		currentDir, err := os.Getwd()
		if err != nil {
			sdk.Exit("Internal error during Getwd command")
		}

		a := tmplPath{
			getAbsoluteDir(args[0], currentDir),
			getAbsoluteDir(args[1], currentDir),
		}

		data, errMarshal := json.Marshal(a)
		if errMarshal != nil {
			sdk.Exit("internal error (%s)\n", errMarshal)
		}

		req, errRequest := http.NewRequest("POST", fmt.Sprintf("http://127.0.0.1:%d/tmpl", port), bytes.NewReader(data))
		if errRequest != nil {
			sdk.Exit("cannot post worker tmpl (Request): %s\n", errRequest)
		}

		client := http.DefaultClient
		client.Timeout = 5 * time.Minute

		resp, errDo := client.Do(req)
		if errDo != nil {
			sdk.Exit("tmpl call failed: %v", errDo)
		}

		if resp.StatusCode >= 300 {
			body, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				sdk.Exit("tmpl failed: unable to read body %v\n", err)
			}
			cdsError := sdk.DecodeError(body)
			sdk.Exit("tmpl failed: %v\n", cdsError)
		}
	}
}

func getAbsoluteDir(arg string, currentDir string) string {
	if strings.HasSuffix(arg, string(filepath.Separator)) {
		return arg
	} else {
		return filepath.Join(currentDir, arg)
	}
}

func (wk *currentWorker) tmplHandler(w http.ResponseWriter, r *http.Request) {
	// Get body
	data, errRead := ioutil.ReadAll(r.Body)
	if errRead != nil {
		newError := sdk.NewError(sdk.ErrWrongRequest, errRead)
		writeError(w, r, newError)
		return
	}

	var a tmplPath
	if err := json.Unmarshal(data, &a); err != nil {
		newError := sdk.NewError(sdk.ErrWrongRequest, err)
		writeError(w, r, newError)
		return
	}

	btes, err := ioutil.ReadFile(a.Path)
	if err != nil {
		newError := sdk.NewError(sdk.ErrWrongRequest, err)
		writeError(w, r, newError)
		return
	}

	tmpvars := map[string]string{}
	for _, v := range wk.currentJob.buildVariables {
		tmpvars[v.Name] = v.Value
	}
	for _, v := range wk.currentJob.params {
		tmpvars[v.Name] = v.Value
	}

	res, err := interpolate.Do(string(btes), tmpvars)
	if err != nil {
		log.Error("Unable to interpolate: %v", err)
		newError := sdk.NewError(sdk.ErrWrongRequest, err)
		writeError(w, r, newError)
		return
	}

	if err := ioutil.WriteFile(a.Destination, []byte(res), os.FileMode(0644)); err != nil {
		writeError(w, r, err)
		return
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/ethereum/eth-go/ethchain"
	"github.com/ethereum/eth-go/ethpub"
	"github.com/ethereum/eth-go/ethutil"
	"github.com/go-qml/qml"
)

type QmlApplication struct {
	win    *qml.Window
	engine *qml.Engine
	lib    *UiLib
	path   string
}

func NewQmlApplication(path string, lib *UiLib) *QmlApplication {
	engine := qml.NewEngine()
	return &QmlApplication{engine: engine, path: path, lib: lib}
}

func (app *QmlApplication) Create() error {
	component, err := app.engine.LoadFile(app.path)
	if err != nil {
		logger.Warnln(err)
	}
	app.win = component.CreateWindow(nil)

	return nil
}

func (app *QmlApplication) Destroy() {
	app.engine.Destroy()
}

func (app *QmlApplication) NewWatcher(quitChan chan bool) {
}

// Events
func (app *QmlApplication) NewBlock(block *ethchain.Block) {
	pblock := &ethpub.PBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())}
	app.win.Call("onNewBlockCb", pblock)
}

func (app *QmlApplication) ObjectChanged(stateObject *ethchain.StateObject) {
	app.win.Call("onObjectChangeCb", ethpub.NewPStateObject(stateObject))
}

func (app *QmlApplication) StorageChanged(storageObject *ethchain.StorageState) {
	app.win.Call("onStorageChangeCb", ethpub.NewPStorageState(storageObject))
}

// Getters
func (app *QmlApplication) Engine() *qml.Engine {
	return app.engine
}
func (app *QmlApplication) Window() *qml.Window {
	return app.win
}
<commit_msg>Added path check for Windows when loading external QML windows/components<commit_after>package main

import (
	"github.com/ethereum/eth-go/ethchain"
	"github.com/ethereum/eth-go/ethpub"
	"github.com/ethereum/eth-go/ethutil"
	"github.com/go-qml/qml"
	"runtime"
)

type QmlApplication struct {
	win    *qml.Window
	engine *qml.Engine
	lib    *UiLib
	path   string
}

func NewQmlApplication(path string, lib *UiLib) *QmlApplication {
	engine := qml.NewEngine()
	return &QmlApplication{engine: engine, path: path, lib: lib}
}

func (app *QmlApplication) Create() error {
	path := string(app.path)

	// For some reason for windows we get /c:/path/to/something, windows doesn't like the first slash but is fine with the others so we are removing it
	if string(app.path[0]) == "/" && runtime.GOOS == "windows" {
		path = app.path[1:]
	}

	component, err := app.engine.LoadFile(path)
	if err != nil {
		logger.Warnln(err)
	}
	app.win = component.CreateWindow(nil)

	return nil
}

func (app *QmlApplication) Destroy() {
	app.engine.Destroy()
}

func (app *QmlApplication) NewWatcher(quitChan chan bool) {
}

// Events
func (app *QmlApplication) NewBlock(block *ethchain.Block) {
	pblock := &ethpub.PBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())}
	app.win.Call("onNewBlockCb", pblock)
}

func (app *QmlApplication) ObjectChanged(stateObject *ethchain.StateObject) {
	app.win.Call("onObjectChangeCb", ethpub.NewPStateObject(stateObject))
}

func (app *QmlApplication) StorageChanged(storageObject *ethchain.StorageState) {
	app.win.Call("onStorageChangeCb", ethpub.NewPStorageState(storageObject))
}

// Getters
func (app *QmlApplication) Engine() *qml.Engine {
	return app.engine
}
func (app *QmlApplication) Window() *qml.Window {
	return app.win
}
<|endoftext|>


<commit_before>package coap

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"sort"
)

type COAPType uint8

const (
	Confirmable     = COAPType(0)
	NonConfirmable  = COAPType(1)
	Acknowledgement = COAPType(2)
	Reset           = COAPType(3)
)

const (
	GET       = 1
	POST      = 2
	PUT       = 3
	DELETE    = 4
	SUBSCRIBE = 5
)

const (
	Created               = 65
	Deleted               = 66
	Valid                 = 67
	Changed               = 68
	Content               = 69
	BadRequest            = 128
	Unauthorized          = 129
	BadOption             = 130
	Forbidden             = 131
	NotFound              = 132
	MethodNotAllowed      = 133
	NotAcceptable         = 134
	PreconditionFailed    = 140
	RequestEntityTooLarge = 141
	UnsupportedMediaType  = 143
	InternalServerError   = 160
	NotImplemented        = 161
	BadGateway            = 162
	ServiceUnavailable    = 163
	GatewayTimeout        = 164
	ProxyingNotSupported  = 165
)

var TooManyOptions = errors.New("Too many options")
var OptionTooLong = errors.New("Option is too long")

type OptionID uint8

const (
	ContentType   = OptionID(1)
	MaxAge        = OptionID(2)
	ProxyURI      = OptionID(3)
	ETag          = OptionID(4)
	URIHost       = OptionID(5)
	LocationPath  = OptionID(6)
	URIPort       = OptionID(7)
	LocationQuery = OptionID(8)
	URIPath       = OptionID(9)
	Token         = OptionID(11)
	Accept        = OptionID(12)
	IfMatch       = OptionID(13)
	UriQuery      = OptionID(15)
	IfNoneMatch   = OptionID(21)
)

type MediaType byte

const (
	TextPlain     = MediaType(0)  // text/plain;charset=utf-8
	AppLinkFormat = MediaType(40) // application/link-format
	AppXML        = MediaType(41) // application/xml
	AppOctets     = MediaType(42) // application/octet-stream
	AppExi        = MediaType(47) // application/exi
	AppJSON       = MediaType(50) // application/json
)

/*
   +-----+---+---+----------------+--------+---------+-------------+
   | No. | C | R | Name           | Format | Length  | Default     |
   +-----+---+---+----------------+--------+---------+-------------+
   |   1 | x |   | Content-Type   | uint   | 0-2 B   | (none)      |
   |   2 |   |   | Max-Age        | uint   | 0-4 B   | 60          |
   |   3 | x | x | Proxy-Uri      | string | 1-270 B | (none)      |
   |   4 |   | x | ETag           | opaque | 1-8 B   | (none)      |
   |   5 | x |   | Uri-Host       | string | 1-270 B | (see below) |
   |   6 |   | x | Location-Path  | string | 0-270 B | (none)      |
   |   7 | x |   | Uri-Port       | uint   | 0-2 B   | (see below) |
   |   8 |   | x | Location-Query | string | 0-270 B | (none)      |
   |   9 | x | x | Uri-Path       | string | 0-270 B | (none)      |
   |  11 | x |   | Token          | opaque | 1-8 B   | (empty)     |
   |  12 |   | x | Accept         | uint   | 0-2 B   | (none)      |
   |  13 | x | x | If-Match       | opaque | 0-8 B   | (none)      |
   |  15 | x | x | Uri-Query      | string | 0-270 B | (none)      |
   |  21 | x |   | If-None-Match  | empty  | 0 B     | (none)      |
   +-----+---+---+----------------+--------+---------+-------------+
*/

type Option struct {
	ID    OptionID
	Value interface{}
}

func encodeInt(v uint32) []byte {
	switch {
	case v == 0:
		return []byte{}
	case v < 256:
		return []byte{byte(v)}
	case v < 65536:
		rv := []byte{0, 0}
		binary.BigEndian.PutUint16(rv, uint16(v))
		return rv
	case v < 16777216:
		rv := []byte{0, 0, 0, 0}
		binary.BigEndian.PutUint32(rv, uint32(v))
		return rv[1:]
	default:
		rv := []byte{0, 0, 0, 0}
		binary.BigEndian.PutUint32(rv, uint32(v))
		return rv
	}
	panic("Has to be one of those")
}

func decodeInt(b []byte) uint32 {
	tmp := []byte{0, 0, 0, 0}
	copy(tmp[4-len(b):], b)
	return binary.BigEndian.Uint32(tmp)
}

func (o Option) toBytes() []byte {
	switch o.ID {
	case ContentType,
		MaxAge,
		URIPort,
		Accept:

		var v uint32
		switch i := o.Value.(type) {
		case int:
			v = uint32(i)
		case int32:
			v = uint32(i)
		case uint:
			v = uint32(i)
		case uint32:
			v = i
		default:
			panic(fmt.Errorf("Invalid type for option %x", o.ID))
		}
		return encodeInt(v)
	default:
		return o.Value.([]byte)
	}
	panic("Has to be one of those")
}

type Options []Option

func (o Options) Len() int {
	return len(o)
}

func (o Options) Less(i, j int) bool {
	return o[i].ID < o[j].ID
}

func (o Options) Swap(i, j int) {
	o[i], o[j] = o[j], o[i]
}

// A CoAP message.
type Message struct {
	Type      COAPType
	Code      uint8
	MessageID uint16

	Options Options

	Payload []byte
}

// Return True if this message is confirmable.
func (m Message) IsConfirmable() bool {
	return m.Type == Confirmable
}

// Get the Path set on this message if any.
//
// XXX: The path is expected to be a segment at a time, not the entire
// thing.
func (m Message) Path() string {
	for _, o := range m.Options {
		if o.ID == URIPath {
			return o.Value.(string)
		}
	}
	return ""
}

// Update or add a LocationPath attribute on this message.
//
// XXX: The path is expected to be a segment at a time, not the entire
// thing.
func (m *Message) SetPath(s string) {
	for _, o := range m.Options {
		if o.ID == URIPath {
			o.Value = []byte(s)
			return
		}
	}
	m.Options = append(m.Options, Option{LocationPath, []byte(s)})
}

func encodeMessage(r Message) ([]byte, error) {
	if len(r.Options) > 14 {
		return []byte{}, TooManyOptions
	}

	tmpbuf := []byte{0, 0}
	binary.BigEndian.PutUint16(tmpbuf, r.MessageID)

	/*
	     0                   1                   2                   3
	    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	   |Ver| T |  OC   |      Code     |          Message ID           |
	   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	   |   Options (if any) ...
	   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	   |   Payload (if any) ...
	   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	*/

	buf := bytes.Buffer{}
	buf.Write([]byte{
		(1 << 6) | (uint8(r.Type) << 4) | uint8(0xf&len(r.Options)),
		byte(r.Code),
		tmpbuf[0], tmpbuf[1],
	})

	/*
	     0   1   2   3   4   5   6   7
	   +---+---+---+---+---+---+---+---+
	   | Option Delta  |    Length     | for 0..14
	   +---+---+---+---+---+---+---+---+
	   |   Option Value ...
	   +---+---+---+---+---+---+---+---+
	                                               for 15..270:
	   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
	   | Option Delta  | 1   1   1   1 |          Length - 15          |
	   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
	   |   Option Value ...
	   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
	*/

	sort.Sort(&r.Options)

	prev := 0
	for _, o := range r.Options {
		b := o.toBytes()
		if len(b) > 15 {
			buf.Write([]byte{
				byte(int(o.ID)-prev)<<4 | 15,
				byte(len(b) - 15),
			})
		} else {
			buf.Write([]byte{byte(int(o.ID)-prev)<<4 | byte(len(b))})
		}
		if int(o.ID)-prev > 15 {
			return []byte{}, errors.New("Gap too large")
		}

		buf.Write(b)
		prev = int(o.ID)
	}

	buf.Write(r.Payload)

	return buf.Bytes(), nil
}

func parseMessage(data []byte) (rv Message, err error) {
	if len(data) < 8 {
		return rv, errors.New("Short packet")
	}

	if data[0]>>6 != 1 {
		return rv, errors.New("Invalid version")
	}

	rv.Type = COAPType((data[0] >> 4) & 0x3)
	opCount := int(data[0] & 0xf)
	if opCount > 14 {
		return rv, TooManyOptions
	}

	rv.Code = data[1]
	rv.MessageID = binary.BigEndian.Uint16(data[2:4])

	b := data[4:]
	prev := 0
	for i := 0; i < opCount && len(b) > 0; i++ {
		oid := OptionID(prev + int(b[0]>>4))
		l := int(b[0] & 0xf)
		b = b[1:]
		if l > 14 {
			l += int(b[0])
			b = b[1:]
		}
		if len(b) < l {
			return rv, errors.New("Truncated")
		}
		var opval interface{} = b[:l]
		switch oid {
		case ContentType,
			MaxAge,
			URIPort,
			Accept:
			opval = decodeInt(b[:l])
		case ProxyURI, URIHost, LocationPath, LocationQuery, URIPath, UriQuery:
			opval = string(b[:l])
		}

		option := Option{
			ID:    oid,
			Value: opval,
		}
		b = b[l:]
		prev = int(option.ID)

		rv.Options = append(rv.Options, option)
	}

	rv.Payload = b
	return rv, nil
}
<commit_msg>This was supposed to set URIPath<commit_after>package coap

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"sort"
)

type COAPType uint8

const (
	Confirmable     = COAPType(0)
	NonConfirmable  = COAPType(1)
	Acknowledgement = COAPType(2)
	Reset           = COAPType(3)
)

const (
	GET       = 1
	POST      = 2
	PUT       = 3
	DELETE    = 4
	SUBSCRIBE = 5
)

const (
	Created               = 65
	Deleted               = 66
	Valid                 = 67
	Changed               = 68
	Content               = 69
	BadRequest            = 128
	Unauthorized          = 129
	BadOption             = 130
	Forbidden             = 131
	NotFound              = 132
	MethodNotAllowed      = 133
	NotAcceptable         = 134
	PreconditionFailed    = 140
	RequestEntityTooLarge = 141
	UnsupportedMediaType  = 143
	InternalServerError   = 160
	NotImplemented        = 161
	BadGateway            = 162
	ServiceUnavailable    = 163
	GatewayTimeout        = 164
	ProxyingNotSupported  = 165
)

var TooManyOptions = errors.New("Too many options")
var OptionTooLong = errors.New("Option is too long")

type OptionID uint8

const (
	ContentType   = OptionID(1)
	MaxAge        = OptionID(2)
	ProxyURI      = OptionID(3)
	ETag          = OptionID(4)
	URIHost       = OptionID(5)
	LocationPath  = OptionID(6)
	URIPort       = OptionID(7)
	LocationQuery = OptionID(8)
	URIPath       = OptionID(9)
	Token         = OptionID(11)
	Accept        = OptionID(12)
	IfMatch       = OptionID(13)
	UriQuery      = OptionID(15)
	IfNoneMatch   = OptionID(21)
)

type MediaType byte

const (
	TextPlain     = MediaType(0)  // text/plain;charset=utf-8
	AppLinkFormat = MediaType(40) // application/link-format
	AppXML        = MediaType(41) // application/xml
	AppOctets     = MediaType(42) // application/octet-stream
	AppExi        = MediaType(47) // application/exi
	AppJSON       = MediaType(50) // application/json
)

/*
   +-----+---+---+----------------+--------+---------+-------------+
   | No. | C | R | Name           | Format | Length  | Default     |
   +-----+---+---+----------------+--------+---------+-------------+
   |   1 | x |   | Content-Type   | uint   | 0-2 B   | (none)      |
   |   2 |   |   | Max-Age        | uint   | 0-4 B   | 60          |
   |   3 | x | x | Proxy-Uri      | string | 1-270 B | (none)      |
   |   4 |   | x | ETag           | opaque | 1-8 B   | (none)      |
   |   5 | x |   | Uri-Host       | string | 1-270 B | (see below) |
   |   6 |   | x | Location-Path  | string | 0-270 B | (none)      |
   |   7 | x |   | Uri-Port       | uint   | 0-2 B   | (see below) |
   |   8 |   | x | Location-Query | string | 0-270 B | (none)      |
   |   9 | x | x | Uri-Path       | string | 0-270 B | (none)      |
   |  11 | x |   | Token          | opaque | 1-8 B   | (empty)     |
   |  12 |   | x | Accept         | uint   | 0-2 B   | (none)      |
   |  13 | x | x | If-Match       | opaque | 0-8 B   | (none)      |
   |  15 | x | x | Uri-Query      | string | 0-270 B | (none)      |
   |  21 | x |   | If-None-Match  | empty  | 0 B     | (none)      |
   +-----+---+---+----------------+--------+---------+-------------+
*/

type Option struct {
	ID    OptionID
	Value interface{}
}

func encodeInt(v uint32) []byte {
	switch {
	case v == 0:
		return []byte{}
	case v < 256:
		return []byte{byte(v)}
	case v < 65536:
		rv := []byte{0, 0}
		binary.BigEndian.PutUint16(rv, uint16(v))
		return rv
	case v < 16777216:
		rv := []byte{0, 0, 0, 0}
		binary.BigEndian.PutUint32(rv, uint32(v))
		return rv[1:]
	default:
		rv := []byte{0, 0, 0, 0}
		binary.BigEndian.PutUint32(rv, uint32(v))
		return rv
	}
	panic("Has to be one of those")
}

func decodeInt(b []byte) uint32 {
	tmp := []byte{0, 0, 0, 0}
	copy(tmp[4-len(b):], b)
	return binary.BigEndian.Uint32(tmp)
}

func (o Option) toBytes() []byte {
	switch o.ID {
	case ContentType,
		MaxAge,
		URIPort,
		Accept:

		var v uint32
		switch i := o.Value.(type) {
		case int:
			v = uint32(i)
		case int32:
			v = uint32(i)
		case uint:
			v = uint32(i)
		case uint32:
			v = i
		default:
			panic(fmt.Errorf("Invalid type for option %x", o.ID))
		}
		return encodeInt(v)
	default:
		return o.Value.([]byte)
	}
	panic("Has to be one of those")
}

type Options []Option

func (o Options) Len() int {
	return len(o)
}

func (o Options) Less(i, j int) bool {
	return o[i].ID < o[j].ID
}

func (o Options) Swap(i, j int) {
	o[i], o[j] = o[j], o[i]
}

// A CoAP message.
type Message struct {
	Type      COAPType
	Code      uint8
	MessageID uint16

	Options Options

	Payload []byte
}

// Return True if this message is confirmable.
func (m Message) IsConfirmable() bool {
	return m.Type == Confirmable
}

// Get the Path set on this message if any.
//
// XXX: The path is expected to be a segment at a time, not the entire
// thing.
func (m Message) Path() string {
	for _, o := range m.Options {
		if o.ID == URIPath {
			return o.Value.(string)
		}
	}
	return ""
}

// Update or add a LocationPath attribute on this message.
//
// XXX: The path is expected to be a segment at a time, not the entire
// thing.
func (m *Message) SetPath(s string) {
	for _, o := range m.Options {
		if o.ID == URIPath {
			o.Value = []byte(s)
			return
		}
	}
	m.Options = append(m.Options, Option{URIPath, []byte(s)})
}

func encodeMessage(r Message) ([]byte, error) {
	if len(r.Options) > 14 {
		return []byte{}, TooManyOptions
	}

	tmpbuf := []byte{0, 0}
	binary.BigEndian.PutUint16(tmpbuf, r.MessageID)

	/*
	     0                   1                   2                   3
	    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	   |Ver| T |  OC   |      Code     |          Message ID           |
	   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	   |   Options (if any) ...
	   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	   |   Payload (if any) ...
	   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	*/

	buf := bytes.Buffer{}
	buf.Write([]byte{
		(1 << 6) | (uint8(r.Type) << 4) | uint8(0xf&len(r.Options)),
		byte(r.Code),
		tmpbuf[0], tmpbuf[1],
	})

	/*
	     0   1   2   3   4   5   6   7
	   +---+---+---+---+---+---+---+---+
	   | Option Delta  |    Length     | for 0..14
	   +---+---+---+---+---+---+---+---+
	   |   Option Value ...
	   +---+---+---+---+---+---+---+---+
	                                               for 15..270:
	   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
	   | Option Delta  | 1   1   1   1 |          Length - 15          |
	   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
	   |   Option Value ...
	   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
	*/

	sort.Sort(&r.Options)

	prev := 0
	for _, o := range r.Options {
		b := o.toBytes()
		if len(b) > 15 {
			buf.Write([]byte{
				byte(int(o.ID)-prev)<<4 | 15,
				byte(len(b) - 15),
			})
		} else {
			buf.Write([]byte{byte(int(o.ID)-prev)<<4 | byte(len(b))})
		}
		if int(o.ID)-prev > 15 {
			return []byte{}, errors.New("Gap too large")
		}

		buf.Write(b)
		prev = int(o.ID)
	}

	buf.Write(r.Payload)

	return buf.Bytes(), nil
}

func parseMessage(data []byte) (rv Message, err error) {
	if len(data) < 8 {
		return rv, errors.New("Short packet")
	}

	if data[0]>>6 != 1 {
		return rv, errors.New("Invalid version")
	}

	rv.Type = COAPType((data[0] >> 4) & 0x3)
	opCount := int(data[0] & 0xf)
	if opCount > 14 {
		return rv, TooManyOptions
	}

	rv.Code = data[1]
	rv.MessageID = binary.BigEndian.Uint16(data[2:4])

	b := data[4:]
	prev := 0
	for i := 0; i < opCount && len(b) > 0; i++ {
		oid := OptionID(prev + int(b[0]>>4))
		l := int(b[0] & 0xf)
		b = b[1:]
		if l > 14 {
			l += int(b[0])
			b = b[1:]
		}
		if len(b) < l {
			return rv, errors.New("Truncated")
		}
		var opval interface{} = b[:l]
		switch oid {
		case ContentType,
			MaxAge,
			URIPort,
			Accept:
			opval = decodeInt(b[:l])
		case ProxyURI, URIHost, LocationPath, LocationQuery, URIPath, UriQuery:
			opval = string(b[:l])
		}

		option := Option{
			ID:    oid,
			Value: opval,
		}
		b = b[l:]
		prev = int(option.ID)

		rv.Options = append(rv.Options, option)
	}

	rv.Payload = b
	return rv, nil
}
<|endoftext|>


<commit_before>package speed

import (
	"fmt"
	"hash/fnv"
)

// MetricType is an enumerated type representing all valid types for a metric
type MetricType int32

// Possible values for a MetricType
const (
	NoSupportType       MetricType = iota
	Int32Type           MetricType = iota
	Uint32Type          MetricType = iota
	Int64Type           MetricType = iota
	Uint64Type          MetricType = iota
	FloatType           MetricType = iota
	DoubleType          MetricType = iota
	StringType          MetricType = iota
	AggregateType       MetricType = iota
	AggregateStaticType MetricType = iota
	EventType           MetricType = iota
	HighresEventType    MetricType = iota
	UnknownType         MetricType = iota
)

func (mt MetricType) String() string {
	switch mt {
	case NoSupportType:
		return "Type: No Support"
	case Int32Type:
		return "Type: Int32"
	case Int64Type:
		return "Type: Int64"
	case Uint32Type:
		return "Type: Uint32"
	case Uint64Type:
		return "Type: Uint64"
	case FloatType:
		return "Type: Float"
	case DoubleType:
		return "Type: Double"
	case StringType:
		return "Type: String"
	case AggregateType:
		return "Type: Aggregate"
	case AggregateStaticType:
		return "Type: Aggregate Static"
	case EventType:
		return "Type: Event"
	case HighresEventType:
		return "Type: Highres Event"
	case UnknownType:
		return "Type: Unknown"
	default:
		return "Type: Invalid"
	}
}

// MetricUnit is an enumerated type representing all possible values for a valid PCP unit
type MetricUnit int32

// SpaceUnit is an enumerated type representing all units for space
type SpaceUnit MetricUnit

// Possible values for SpaceUnit
const (
	ByteUnit     SpaceUnit = iota
	KilobyteUnit SpaceUnit = iota
	MegabyteUnit SpaceUnit = iota
	GigabyteUnit SpaceUnit = iota
	TerabyteUnit SpaceUnit = iota
	PetabyteUnit SpaceUnit = iota
	ExabyteUnit  SpaceUnit = iota
)

func (su SpaceUnit) String() string {
	switch su {
	case ByteUnit:
		return "Unit: Byte"
	case KilobyteUnit:
		return "Unit: Kilobyte"
	case MegabyteUnit:
		return "Unit: Megabyte"
	case GigabyteUnit:
		return "Unit: Gigabyte"
	case TerabyteUnit:
		return "Unit: Terabyte"
	case PetabyteUnit:
		return "Unit: Petabyte"
	case ExabyteUnit:
		return "Unit: Exabyte"
	default:
		return "Unit: Invalid SpaceUnit"
	}
}

// TimeUnit is an enumerated type representing all possible units for representing time
type TimeUnit MetricUnit

// Possible Values for TimeUnit
const (
	NanosecondUnit  TimeUnit = iota
	MicrosecondUnit TimeUnit = iota
	MillisecondUnit TimeUnit = iota
	SecondUnit      TimeUnit = iota
	MinuteUnit      TimeUnit = iota
	HourUnit        TimeUnit = iota
)

func (tu TimeUnit) String() string {
	switch tu {
	case NanosecondUnit:
		return "Unit: Nanosecond"
	case MicrosecondUnit:
		return "Unit: Microsecond"
	case MillisecondUnit:
		return "Unit: Millisecond"
	case SecondUnit:
		return "Unit: Second"
	case MinuteUnit:
		return "Unit: Minute"
	case HourUnit:
		return "Unit: Hour"
	default:
		return "Unit: Invalid TimeUnit"
	}
}

// CountUnit is a type representing a counted quantity
type CountUnit MetricUnit

// OneUnit represents the only CountUnit
const OneUnit CountUnit = iota

func (cu CountUnit) String() string {
	switch cu {
	case OneUnit:
		return "Unit: One"
	default:
		return "Unit: Invalid CounterUnit"
	}
}

// MetricSemantics represents an enumerated type representing the possible
// values for the semantics of a metric
type MetricSemantics int32

// Possible values for MetricSemantics
const (
	NoSemantics       MetricSemantics = iota
	CounterSemantics  MetricSemantics = iota
	InstantSemantics  MetricSemantics = iota
	DiscreteSemantics MetricSemantics = iota
)

func (ms MetricSemantics) String() string {
	switch ms {
	case NoSemantics:
		return "Semantics: None"
	case CounterSemantics:
		return "Semantics: Counter"
	case InstantSemantics:
		return "Semantics: Instant"
	case DiscreteSemantics:
		return "Semantics: Discrete"
	default:
		return "Semantics: Invalid"
	}
}

// Metric defines the general interface a type needs to implement to qualify
// as a valid PCP metric
type Metric interface {
	Val() interface{}           // gets the value of the metric
	Set(interface{}) error      // sets the value of the metric to a value, optionally returns an error on failure
	Type() MetricType           // gets the type of a metric
	Unit() MetricUnit           // gets the unit of a metric
	Semantics() MetricSemantics // gets the semantics for a metric
	Description() string        // gets the description of a metric
}

// generate a unique uint32 hash for a string
// NOTE: make sure this is as fast as possible
func getHash(s string) uint32 {
	h := fnv.New32a()
	h.Write([]byte(s))
	return h.Sum32()
}

// MetricDesc is a metric metadata wrapper
// each metric type can wrap its metadata by containing a MetricDesc type and only define its own
// specific properties assuming MetricDesc will handle the rest
//
// when writing, this type is supposed to map directly to the pmDesc struct as defined in PCP core
type MetricDesc struct {
	id                                uint32          // unique metric id
	name                              string          // the name
	indom                             InstanceDomain  // the instance domain
	t                                 MetricType      // the type of a metric
	sem                               MetricSemantics // the semantics
	u                                 MetricUnit      // the unit
	shortDescription, longDescription string
}

// NewMetricDesc creates a new Metric Description wrapper type
func NewMetricDesc(n string, i InstanceDomain, t MetricType, s MetricSemantics, u MetricUnit, short, long string) *MetricDesc {
	return &MetricDesc{
		getHash(n), n, i, t, s, u, short, long,
	}
}

func (md *MetricDesc) String() string {
	return fmt.Sprintf("%s{%v, %v, %v, %v}", md.name, md.indom, md.t, md.sem, md.u)
}
<commit_msg>metrics: initial PCPMetric implementation<commit_after>package speed

import (
	"fmt"
	"hash/fnv"
	"sync"
)

// MetricType is an enumerated type representing all valid types for a metric
type MetricType int32

// Possible values for a MetricType
const (
	NoSupportType       MetricType = iota
	Int32Type           MetricType = iota
	Uint32Type          MetricType = iota
	Int64Type           MetricType = iota
	Uint64Type          MetricType = iota
	FloatType           MetricType = iota
	DoubleType          MetricType = iota
	StringType          MetricType = iota
	AggregateType       MetricType = iota
	AggregateStaticType MetricType = iota
	EventType           MetricType = iota
	HighresEventType    MetricType = iota
	UnknownType         MetricType = iota
)

func (mt MetricType) String() string {
	switch mt {
	case NoSupportType:
		return "Type: No Support"
	case Int32Type:
		return "Type: Int32"
	case Int64Type:
		return "Type: Int64"
	case Uint32Type:
		return "Type: Uint32"
	case Uint64Type:
		return "Type: Uint64"
	case FloatType:
		return "Type: Float"
	case DoubleType:
		return "Type: Double"
	case StringType:
		return "Type: String"
	case AggregateType:
		return "Type: Aggregate"
	case AggregateStaticType:
		return "Type: Aggregate Static"
	case EventType:
		return "Type: Event"
	case HighresEventType:
		return "Type: Highres Event"
	case UnknownType:
		return "Type: Unknown"
	default:
		return "Type: Invalid"
	}
}

// MetricUnit is an enumerated type representing all possible values for a valid PCP unit
type MetricUnit int32

// SpaceUnit is an enumerated type representing all units for space
type SpaceUnit MetricUnit

// Possible values for SpaceUnit
const (
	ByteUnit     SpaceUnit = iota
	KilobyteUnit SpaceUnit = iota
	MegabyteUnit SpaceUnit = iota
	GigabyteUnit SpaceUnit = iota
	TerabyteUnit SpaceUnit = iota
	PetabyteUnit SpaceUnit = iota
	ExabyteUnit  SpaceUnit = iota
)

func (su SpaceUnit) String() string {
	switch su {
	case ByteUnit:
		return "Unit: Byte"
	case KilobyteUnit:
		return "Unit: Kilobyte"
	case MegabyteUnit:
		return "Unit: Megabyte"
	case GigabyteUnit:
		return "Unit: Gigabyte"
	case TerabyteUnit:
		return "Unit: Terabyte"
	case PetabyteUnit:
		return "Unit: Petabyte"
	case ExabyteUnit:
		return "Unit: Exabyte"
	default:
		return "Unit: Invalid SpaceUnit"
	}
}

// TimeUnit is an enumerated type representing all possible units for representing time
type TimeUnit MetricUnit

// Possible Values for TimeUnit
const (
	NanosecondUnit  TimeUnit = iota
	MicrosecondUnit TimeUnit = iota
	MillisecondUnit TimeUnit = iota
	SecondUnit      TimeUnit = iota
	MinuteUnit      TimeUnit = iota
	HourUnit        TimeUnit = iota
)

func (tu TimeUnit) String() string {
	switch tu {
	case NanosecondUnit:
		return "Unit: Nanosecond"
	case MicrosecondUnit:
		return "Unit: Microsecond"
	case MillisecondUnit:
		return "Unit: Millisecond"
	case SecondUnit:
		return "Unit: Second"
	case MinuteUnit:
		return "Unit: Minute"
	case HourUnit:
		return "Unit: Hour"
	default:
		return "Unit: Invalid TimeUnit"
	}
}

// CountUnit is a type representing a counted quantity
type CountUnit MetricUnit

// OneUnit represents the only CountUnit
const OneUnit CountUnit = iota

func (cu CountUnit) String() string {
	switch cu {
	case OneUnit:
		return "Unit: One"
	default:
		return "Unit: Invalid CounterUnit"
	}
}

// MetricSemantics represents an enumerated type representing the possible
// values for the semantics of a metric
type MetricSemantics int32

// Possible values for MetricSemantics
const (
	NoSemantics       MetricSemantics = iota
	CounterSemantics  MetricSemantics = iota
	InstantSemantics  MetricSemantics = iota
	DiscreteSemantics MetricSemantics = iota
)

func (ms MetricSemantics) String() string {
	switch ms {
	case NoSemantics:
		return "Semantics: None"
	case CounterSemantics:
		return "Semantics: Counter"
	case InstantSemantics:
		return "Semantics: Instant"
	case DiscreteSemantics:
		return "Semantics: Discrete"
	default:
		return "Semantics: Invalid"
	}
}

// Metric defines the general interface a type needs to implement to qualify
// as a valid PCP metric
type Metric interface {
	Val() interface{}           // gets the value of the metric
	Set(interface{}) error      // sets the value of the metric to a value, optionally returns an error on failure
	Type() MetricType           // gets the type of a metric
	Unit() MetricUnit           // gets the unit of a metric
	Semantics() MetricSemantics // gets the semantics for a metric
	Description() string        // gets the description of a metric
}

// generate a unique uint32 hash for a string
// NOTE: make sure this is as fast as possible
func getHash(s string) uint32 {
	h := fnv.New32a()
	h.Write([]byte(s))
	return h.Sum32()
}

// MetricDesc is a metric metadata wrapper
// each metric type can wrap its metadata by containing a MetricDesc type and only define its own
// specific properties assuming MetricDesc will handle the rest
//
// when writing, this type is supposed to map directly to the pmDesc struct as defined in PCP core
type MetricDesc struct {
	id                                uint32          // unique metric id
	name                              string          // the name
	indom                             InstanceDomain  // the instance domain
	t                                 MetricType      // the type of a metric
	sem                               MetricSemantics // the semantics
	u                                 MetricUnit      // the unit
	shortDescription, longDescription string
}

// NewMetricDesc creates a new Metric Description wrapper type
func NewMetricDesc(n string, i InstanceDomain, t MetricType, s MetricSemantics, u MetricUnit, short, long string) *MetricDesc {
	return &MetricDesc{
		getHash(n), n, i, t, s, u, short, long,
	}
}

func (md *MetricDesc) String() string {
	return fmt.Sprintf("%s{%v, %v, %v, %v}", md.name, md.indom, md.t, md.sem, md.u)
}

// PCPMetric defines a PCP compatible metric type that can be constructed by specifying values
// for type, semantics and unit
type PCPMetric struct {
	val  interface{} // all bets are off, store whatever you want
	desc *MetricDesc // the metadata associated with this metric
	mu   sync.Mutex  // mutex to control reads and writes of value to the metric
}

// NewPCPMetric creates a new instance of PCPMetric
func NewPCPMetric(val interface{}, name string, indom InstanceDomain, t MetricType, s MetricSemantics, u MetricUnit, short, long string) *PCPMetric {
	return &PCPMetric{
		val:  val,
		desc: NewMetricDesc(name, indom, t, s, u, short, long),
	}
}

// Val returns the current set value of PCPMetric
func (m *PCPMetric) Val() interface{} {
	m.mu.Lock()
	defer m.mu.Unlock()
	return m.val
}

// Set sets the current value of PCPMetric
func (m *PCPMetric) Set(val interface{}) error {
	if val != m.val {
		m.mu.Lock()
		defer m.mu.Unlock()
		m.val = val
	}
	return nil
}

// Semantics returns the current stored value for PCPMetric
func (m *PCPMetric) Semantics() MetricSemantics { return m.desc.sem }

// Unit returns the unit for PCPMetric
func (m *PCPMetric) Unit() MetricUnit { return m.desc.u }

// Type returns the type for PCPMetric
func (m *PCPMetric) Type() MetricType { return m.desc.t }

// Description returns the description for PCPMetric
func (m *PCPMetric) Description() string {
	sd := m.desc.shortDescription
	ld := m.desc.longDescription
	if len(ld) > 0 {
		return sd + "\n\n" + ld
	}
	return sd
}

func (m *PCPMetric) String() string {
	return fmt.Sprintf("Val: %v\n%v", m.val, m.Description())
}

// TODO: implement PCPCounterMetric, PCPGaugeMetric ...
<|endoftext|>


<commit_before>package binny

import (
	"bytes"
	"encoding"
	"fmt"
	"math"
	"math/big"
	"reflect"
	"testing"
	"unsafe"

	"log"

	"encoding/binary"
	"encoding/gob"
	"testing/quick"
)

type S struct {
	Str    string   `json:",omitempty"`
	Ignore string   `json:",omitempty" binny:"-"`
	I8     int8     `json:",omitempty"`
	U8     uint8    `json:",omitempty"`
	I16    int16    `json:",omitempty"`
	U16    uint16   `json:",omitempty"`
	I32    int32    `json:",omitempty"`
	U32    uint32   `json:",omitempty"`
	I64    int64    `json:",omitempty"`
	U64    uint64   `json:",omitempty"`
	F32    float32  `json:",omitempty"`
	F64    float64  `json:",omitempty"`
	Bi     *big.Int `json:",omitempty"`
	S      *S       `binny:"s"`
	Z      uint     `json:",omitempty"`
}

var le = binary.LittleEndian

type expValue struct {
	in []string
	b  []byte
}

type Len uint64

func Exp(in ...interface{}) (ev expValue) {
L:
	for _, v := range in {
		switch v := v.(type) {
		case Type:
			ev.b = append(ev.b, byte(v))
			ev.in = append(ev.in, v.String())
			continue L
		case string:
			ev.b = append(ev.b, autoUint(uint64(len(v)), true)...)
			ev.b = append(ev.b, v...)
			ev.in = append(ev.in, fmt.Sprintf("%q", v))
			continue L
		case int:
			ev.b = append(ev.b, byte(v))
			ev.in = append(ev.in, fmt.Sprintf("%v", v))
			continue L
		case int64:
			ev.b = append(ev.b, autoInt(v)...)
			ev.in = append(ev.in, fmt.Sprintf("%v", v))
			continue L
		case uint64:
			ev.b = append(ev.b, autoUint(v, false)...)
			ev.in = append(ev.in, fmt.Sprintf("%v", v))
			continue L
		case Len:
			ev.b = append(ev.b, autoUint(uint64(v), true)...)
			ev.in = append(ev.in, fmt.Sprintf("%v", v))
			continue L
		case int32:
			i := make([]byte, 10)
			ev.b = append(ev.b, i[:binary.PutVarint(i, int64(v))]...)
		case uint32:
			i := make([]byte, 10)
			ev.b = append(ev.b, i[:binary.PutUvarint(i, uint64(v))]...)
		case float32:
			i := make([]byte, 4)
			le.PutUint32(i, math.Float32bits(v))
			ev.b = append(ev.b, i...)
			ev.in = append(ev.in, fmt.Sprintf("%v", v))
			continue L
		case float64:
			i := make([]byte, 8)
			le.PutUint64(i, math.Float64bits(v))
			ev.b = append(ev.b, i...)
			ev.in = append(ev.in, fmt.Sprintf("%v", v))
			continue L
		case gob.GobEncoder:
			b, _ := v.GobEncode()
			ev.b = append(ev.b, autoUint(uint64(len(b)), true)...)
			ev.b = append(ev.b, b...)
		case encoding.BinaryMarshaler:
			b, _ := v.MarshalBinary()
			ev.b = append(ev.b, autoUint(uint64(len(b)), true)...)
			ev.b = append(ev.b, b...)
		default:
			panic(v)
		}
		ev.in = append(ev.in, fmt.Sprintf("%T(%+v)", v, v))
	}
	return
}

func autoUint(u uint64, ln bool) (v []byte) {
	switch {
	case u <= math.MaxUint8:
		v = []byte{byte(Uint8), byte(u)}
	case u <= math.MaxUint16:
		v = append([]byte{byte(Uint16)}, (*[2]byte)(unsafe.Pointer(&u))[:2:2]...)
	case u <= math.MaxUint32:
		v = append([]byte{byte(Uint32)}, (*[4]byte)(unsafe.Pointer(&u))[:4:4]...)
	default:
		v = append([]byte{byte(Uint64)}, (*[8]byte)(unsafe.Pointer(&u))[:8:8]...)
	}
	if ln {
		return v
	}
	return v[1:]

}

func autoInt(v int64) []byte {
	u := v
	if u < 0 {
		u = -u
	}
	if u <= math.MaxInt8 {
		return []byte{byte(u)}
	}
	if u <= math.MaxInt16 {
		return (*[8]byte)(unsafe.Pointer(&v))[:2:2]
	}
	if u <= math.MaxInt32 {
		return (*[8]byte)(unsafe.Pointer(&v))[:4:4]
	}
	return (*[8]byte)(unsafe.Pointer(&v))[:8:8]
}

var SLen = len(cachedTypeFields(reflect.TypeOf(S{})))

func init() {
	log.SetFlags(log.Lshortfile)
}

var benchVal = S{
	I8:     1,
	U16:    2,
	Str:    "hello",
	Ignore: "xczczcasdsa",
	S: &S{
		I32: 3,
		Str: "bye",
		S: &S{
			U64: math.MaxUint64,
			S: &S{
				F32: math.MaxFloat32,
				F64: math.MaxFloat64,
				U64: math.MaxUint64,
				Bi:  bigIntVal,
				Str: "w00t",
			},
		},
	},
}

type SAll struct {
	I    int
	U    uint
	I8   int8
	U8   uint8
	I16  int16
	U16  uint16
	I32  int32
	U32  uint32
	I64  int64
	U64  uint64
	F32  float32
	F64  float64
	C64  complex64
	C128 complex128
	S    string
	BS   []byte
	M    map[string]*SAll
}

func (s *SAll) NotEq(t *testing.T, o *SAll) (errored bool) {
	if s == nil && o == nil || o == s {
		return false
	}
	if s == nil || o == nil {
		t.Logf("s == nil || o == nil\n%+v\n%+v", s, o)
		return true
	}
	if s.I != o.I {
		t.Logf("I wanted %v, got %v.", s.I, o.I)
		errored = true
	}

	if s.U != o.U {
		t.Logf("U wanted %v, got %v.", s.U, o.U)
		errored = true
	}

	if s.I8 != o.I8 {
		t.Logf("I8 wanted %v, got %v.", s.I8, o.I8)
		errored = true
	}

	if s.U8 != o.U8 {
		t.Logf("U8 wanted %v, got %v.", s.U8, o.U8)
		errored = true
	}

	if s.I16 != o.I16 {
		t.Logf("I16 wanted %v, got %v.", s.I16, o.I16)
		errored = true
	}

	if s.U16 != o.U16 {
		t.Logf("U16 wanted %v, got %v.", s.U16, o.U16)
		errored = true
	}

	if s.I32 != o.I32 {
		t.Logf("I32 wanted %v, got %v.", s.I32, o.I32)
		errored = true
	}

	if s.U32 != o.U32 {
		t.Logf("U32 wanted %v, got %v.", s.U32, o.U32)
		errored = true
	}

	if s.I64 != o.I64 {
		t.Logf("I64 wanted %v, got %v.", s.I64, o.I64)
		errored = true
	}

	if s.U64 != o.U64 {
		t.Logf("U64 wanted %v, got %v.", s.U64, o.U64)
		errored = true
	}

	if s.F32 != o.F32 {
		t.Logf("F32 wanted %v, got %v.", s.F32, o.F32)
		errored = true
	}

	if s.F64 != o.F64 {
		t.Logf("F64 wanted %v, got %v.", s.F64, o.F64)
		errored = true
	}

	if s.C64 != o.C64 {
		t.Logf("C64 wanted %v, got %v.", s.C64, o.C64)
		errored = true
	}

	if s.C128 != o.C128 {
		t.Logf("C128 wanted %v, got %v.", s.C128, o.C128)
		errored = true
	}

	if s.S != o.S {
		t.Logf("S wanted %v, got %v.", s.S, o.S)
		errored = true
	}

	if bytes.Compare(s.BS, o.BS) != 0 {
		t.Logf("BS wanted %v, got %v.", s.BS, o.BS)
		errored = true
	}

	if len(s.M) != len(o.M) {
		t.Logf("M wanted %v, got %v.", s.M, o.M)
		errored = true
	}

	for k, v := range s.M {
		errored = errored || v.NotEq(t, o.M[k])
	}
	return
}

func TestMortalKombat(t *testing.T) {
	//rnd, typ := rand.New(rand.NewSource(42)), reflect.TypeOf(&SAll{})
	check := func(s *SAll) bool {
		if s == nil {
			return true
		}
		b, err := Marshal(s)
		if err != nil {
			t.Log(err)
			t.Error(err)
			return false
		}
		var s2 SAll
		if err = Unmarshal(b, &s2); err != nil {
			t.Fatal(err)
			return false
		}
		return !s.NotEq(t, &s2)
	}
	for i := 0; i < 1000; i++ {
		if err := quick.Check(check, nil); err != nil {
			t.Fatal(err)
		}
	}
}
<commit_msg>make the quick check run longer<commit_after>package binny

import (
	"bytes"
	"encoding"
	"fmt"
	"math"
	"math/big"
	"math/rand"
	"reflect"
	"testing"
	"unsafe"

	"log"

	"encoding/binary"
	"encoding/gob"
	"testing/quick"
)

type S struct {
	Str    string   `json:",omitempty"`
	Ignore string   `json:",omitempty" binny:"-"`
	I8     int8     `json:",omitempty"`
	U8     uint8    `json:",omitempty"`
	I16    int16    `json:",omitempty"`
	U16    uint16   `json:",omitempty"`
	I32    int32    `json:",omitempty"`
	U32    uint32   `json:",omitempty"`
	I64    int64    `json:",omitempty"`
	U64    uint64   `json:",omitempty"`
	F32    float32  `json:",omitempty"`
	F64    float64  `json:",omitempty"`
	Bi     *big.Int `json:",omitempty"`
	S      *S       `binny:"s"`
	Z      uint     `json:",omitempty"`
}

var le = binary.LittleEndian

type expValue struct {
	in []string
	b  []byte
}

type Len uint64

func Exp(in ...interface{}) (ev expValue) {
L:
	for _, v := range in {
		switch v := v.(type) {
		case Type:
			ev.b = append(ev.b, byte(v))
			ev.in = append(ev.in, v.String())
			continue L
		case string:
			ev.b = append(ev.b, autoUint(uint64(len(v)), true)...)
			ev.b = append(ev.b, v...)
			ev.in = append(ev.in, fmt.Sprintf("%q", v))
			continue L
		case int:
			ev.b = append(ev.b, byte(v))
			ev.in = append(ev.in, fmt.Sprintf("%v", v))
			continue L
		case int64:
			ev.b = append(ev.b, autoInt(v)...)
			ev.in = append(ev.in, fmt.Sprintf("%v", v))
			continue L
		case uint64:
			ev.b = append(ev.b, autoUint(v, false)...)
			ev.in = append(ev.in, fmt.Sprintf("%v", v))
			continue L
		case Len:
			ev.b = append(ev.b, autoUint(uint64(v), true)...)
			ev.in = append(ev.in, fmt.Sprintf("%v", v))
			continue L
		case int32:
			i := make([]byte, 10)
			ev.b = append(ev.b, i[:binary.PutVarint(i, int64(v))]...)
		case uint32:
			i := make([]byte, 10)
			ev.b = append(ev.b, i[:binary.PutUvarint(i, uint64(v))]...)
		case float32:
			i := make([]byte, 4)
			le.PutUint32(i, math.Float32bits(v))
			ev.b = append(ev.b, i...)
			ev.in = append(ev.in, fmt.Sprintf("%v", v))
			continue L
		case float64:
			i := make([]byte, 8)
			le.PutUint64(i, math.Float64bits(v))
			ev.b = append(ev.b, i...)
			ev.in = append(ev.in, fmt.Sprintf("%v", v))
			continue L
		case gob.GobEncoder:
			b, _ := v.GobEncode()
			ev.b = append(ev.b, autoUint(uint64(len(b)), true)...)
			ev.b = append(ev.b, b...)
		case encoding.BinaryMarshaler:
			b, _ := v.MarshalBinary()
			ev.b = append(ev.b, autoUint(uint64(len(b)), true)...)
			ev.b = append(ev.b, b...)
		default:
			panic(v)
		}
		ev.in = append(ev.in, fmt.Sprintf("%T(%+v)", v, v))
	}
	return
}

func autoUint(u uint64, ln bool) (v []byte) {
	switch {
	case u <= math.MaxUint8:
		v = []byte{byte(Uint8), byte(u)}
	case u <= math.MaxUint16:
		v = append([]byte{byte(Uint16)}, (*[2]byte)(unsafe.Pointer(&u))[:2:2]...)
	case u <= math.MaxUint32:
		v = append([]byte{byte(Uint32)}, (*[4]byte)(unsafe.Pointer(&u))[:4:4]...)
	default:
		v = append([]byte{byte(Uint64)}, (*[8]byte)(unsafe.Pointer(&u))[:8:8]...)
	}
	if ln {
		return v
	}
	return v[1:]

}

func autoInt(v int64) []byte {
	u := v
	if u < 0 {
		u = -u
	}
	if u <= math.MaxInt8 {
		return []byte{byte(u)}
	}
	if u <= math.MaxInt16 {
		return (*[8]byte)(unsafe.Pointer(&v))[:2:2]
	}
	if u <= math.MaxInt32 {
		return (*[8]byte)(unsafe.Pointer(&v))[:4:4]
	}
	return (*[8]byte)(unsafe.Pointer(&v))[:8:8]
}

var SLen = len(cachedTypeFields(reflect.TypeOf(S{})))

func init() {
	log.SetFlags(log.Lshortfile)
}

var benchVal = S{
	I8:     1,
	U16:    2,
	Str:    "hello",
	Ignore: "xczczcasdsa",
	S: &S{
		I32: 3,
		Str: "bye",
		S: &S{
			U64: math.MaxUint64,
			S: &S{
				F32: math.MaxFloat32,
				F64: math.MaxFloat64,
				U64: math.MaxUint64,
				Bi:  bigIntVal,
				Str: "w00t",
			},
		},
	},
}

type SAll struct {
	I    int
	U    uint
	I8   int8
	U8   uint8
	I16  int16
	U16  uint16
	I32  int32
	U32  uint32
	I64  int64
	U64  uint64
	F32  float32
	F64  float64
	C64  complex64
	C128 complex128
	S    string
	BS   []byte
	M    map[string]*SAll
}

func (s *SAll) NotEq(t *testing.T, o *SAll) (errored bool) {
	if s == nil && o == nil || o == s {
		return false
	}
	if s == nil || o == nil {
		t.Logf("s == nil || o == nil\n%+v\n%+v", s, o)
		return true
	}
	if s.I != o.I {
		t.Logf("I wanted %v, got %v.", s.I, o.I)
		errored = true
	}

	if s.U != o.U {
		t.Logf("U wanted %v, got %v.", s.U, o.U)
		errored = true
	}

	if s.I8 != o.I8 {
		t.Logf("I8 wanted %v, got %v.", s.I8, o.I8)
		errored = true
	}

	if s.U8 != o.U8 {
		t.Logf("U8 wanted %v, got %v.", s.U8, o.U8)
		errored = true
	}

	if s.I16 != o.I16 {
		t.Logf("I16 wanted %v, got %v.", s.I16, o.I16)
		errored = true
	}

	if s.U16 != o.U16 {
		t.Logf("U16 wanted %v, got %v.", s.U16, o.U16)
		errored = true
	}

	if s.I32 != o.I32 {
		t.Logf("I32 wanted %v, got %v.", s.I32, o.I32)
		errored = true
	}

	if s.U32 != o.U32 {
		t.Logf("U32 wanted %v, got %v.", s.U32, o.U32)
		errored = true
	}

	if s.I64 != o.I64 {
		t.Logf("I64 wanted %v, got %v.", s.I64, o.I64)
		errored = true
	}

	if s.U64 != o.U64 {
		t.Logf("U64 wanted %v, got %v.", s.U64, o.U64)
		errored = true
	}

	if s.F32 != o.F32 {
		t.Logf("F32 wanted %v, got %v.", s.F32, o.F32)
		errored = true
	}

	if s.F64 != o.F64 {
		t.Logf("F64 wanted %v, got %v.", s.F64, o.F64)
		errored = true
	}

	if s.C64 != o.C64 {
		t.Logf("C64 wanted %v, got %v.", s.C64, o.C64)
		errored = true
	}

	if s.C128 != o.C128 {
		t.Logf("C128 wanted %v, got %v.", s.C128, o.C128)
		errored = true
	}

	if s.S != o.S {
		t.Logf("S wanted %v, got %v.", s.S, o.S)
		errored = true
	}

	if bytes.Compare(s.BS, o.BS) != 0 {
		t.Logf("BS wanted %v, got %v.", s.BS, o.BS)
		errored = true
	}

	if len(s.M) != len(o.M) {
		t.Logf("M wanted %v, got %v.", s.M, o.M)
		errored = true
	}

	for k, v := range s.M {
		errored = errored || v.NotEq(t, o.M[k])
	}
	return
}

func TestMortalKombat(t *testing.T) {
	cfg := &quick.Config{
		Rand: rand.New(rand.NewSource(42)),
	}
	check := func(s *SAll) bool {
		if s == nil {
			return true
		}
		b, err := Marshal(s)
		if err != nil {
			t.Error(err)
			return false
		}
		var s2 SAll
		if err = Unmarshal(b, &s2); err != nil {
			t.Error(err)
			return false
		}
		return !s.NotEq(t, &s2)
	}
	for i := 0; i < 1e4; i++ {
		if err := quick.Check(check, cfg); err != nil {
			t.Fatal(err)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/jonas747/discordgo"
)

// Variables used for command line parameters
var (
	Token string
	vc    *discordgo.VoiceConnection
)

func init() {

	flag.StringVar(&Token, "t", "", "Bot Token")
	flag.Parse()
}

func main() {

	// Create a new Discord session using the provided bot token.
	dg, err := discordgo.New("Bot " + Token)
	if err != nil {
		fmt.Println("error creating Discord session,", err)
		return
	}
	resp, err := dg.GatewayBot()
	if err != nil {
		fmt.Println("error creating Discord session,", err)
		return
	}

	dg.ShardCount = resp.Shards
	dg.ShardID = 0

	dg.LogLevel = discordgo.LogDebug

	// manager := dshardmanager.New("Bot " + Token)
	// manager.SessionFunc = func(token string) (*discordgo.Session, error) {
	// 	session, err := discordgo.New(token)
	// 	if err != nil {
	// 		return nil, err
	// 	}

	// 	session.LogLevel = discordgo.LogDebug
	// 	return session, nil
	// }
	// err := manager.Start()
	// if err != nil {
	// 	fmt.Println("error opening connections,", err)
	// 	return
	// }

	// // Register the messageCreate func as a callback for MessageCreate events.
	// dg.AddHandler(messageCreate)
	// dg.AddHandler(dumpAll)

	// Open a websocket connection to Discord and begin listening.
	err = dg.Open()
	if err != nil {
		fmt.Println("error opening connection,", err)
		return
	}

	// Wait here until CTRL-C or other term signal is received.
	fmt.Println("Bot is now running.  Press CTRL-C to exit.")
	sc := make(chan os.Signal, 1)
	signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
	<-sc

	// Cleanly close down the Discord session.
	// manager.StopAll()
	dg.Close()
}

func dumpAll(s *discordgo.Session, evt interface{}) {
	if _, ok := evt.(*discordgo.Event); !ok {
		// fmt.Printf("Inc event: %#v\n", evt)
	}
}

// This function will be called (due to AddHandler above) every time a new
// message is created on any channel that the autenticated bot has access to.
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
	if m.Author.ID != "138700441876692992" {
		return
	}

	if m.Content == "yaboi recon" {
		fmt.Println("Reconnecting...")
		err := s.GatewayManager.Reconnect(false)
		if err != nil {
			fmt.Println("Failed reconnecting")
		}
	}

	if m.Content == "yaboi joinvoice" {
		fmt.Println("joining cvoice")
		if vc != nil {
			fmt.Println("already in vc")
			return
		}

		channel, _ := s.State.Channel(m.ChannelID)
		g, _ := s.State.Guild(channel.GuildID)

		vcId := ""
		for _, v := range g.VoiceStates {
			if v.UserID == m.Author.ID {
				vcId = v.ChannelID
				break
			}
		}

		if vcId == "" {
			fmt.Println("Not in voice")
			return
		}

		var err error
		vc, err = s.GatewayManager.ChannelVoiceJoin(g.ID, vcId, true, true)
		if err != nil {
			fmt.Println("failed joining voice: ", err)
			return
		}
		fmt.Println("Joined voice")
	}

	if m.Content == "yaboi leavevoice" {
		if vc == nil {
			fmt.Println("Not in voice")
			return
		}

		err := vc.Disconnect()
		if err != nil {
			fmt.Println("failed leaving voice: ", err)
			return
		}
		vc = nil
	}

	// fmt.Println("\nReceived message my dude!\n")
	// // Ignore all messages created by the bot itself
	// // This isn't required in this specific example but it's a good practice.
	// if m.Author.ID == s.State.User.ID {
	// 	return
	// }
	// // If the message is "ping" reply with "Pong!"
	// if m.Content == "ping" {
	// 	s.ChannelMessageSend(m.ChannelID, "Pong!")
	// }

	// // If the message is "pong" reply with "Ping!"
	// if m.Content == "pong" {
	// 	s.ChannelMessageSend(m.ChannelID, "Ping!")
	// }
}
<commit_msg>fix example<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/jonas747/discordgo"
)

// Variables used for command line parameters
var (
	Token string
	vc    *discordgo.VoiceConnection
)

func init() {

	flag.StringVar(&Token, "t", "", "Bot Token")
	flag.Parse()
}

func main() {

	// Create a new Discord session using the provided bot token.
	dg, err := discordgo.New("Bot " + Token)
	if err != nil {
		fmt.Println("error creating Discord session,", err)
		return
	}
	resp, err := dg.GatewayBot()
	if err != nil {
		fmt.Println("error creating Discord session,", err)
		return
	}

	dg.ShardCount = resp.Shards
	dg.ShardID = 0

	dg.LogLevel = discordgo.LogDebug

	// manager := dshardmanager.New("Bot " + Token)
	// manager.SessionFunc = func(token string) (*discordgo.Session, error) {
	// 	session, err := discordgo.New(token)
	// 	if err != nil {
	// 		return nil, err
	// 	}

	// 	session.LogLevel = discordgo.LogDebug
	// 	return session, nil
	// }
	// err := manager.Start()
	// if err != nil {
	// 	fmt.Println("error opening connections,", err)
	// 	return
	// }

	// // Register the messageCreate func as a callback for MessageCreate events.
	// dg.AddHandler(messageCreate)
	// dg.AddHandler(dumpAll)

	// Open a websocket connection to Discord and begin listening.
	err = dg.Open()
	if err != nil {
		fmt.Println("error opening connection,", err)
		return
	}

	// Wait here until CTRL-C or other term signal is received.
	fmt.Println("Bot is now running.  Press CTRL-C to exit.")
	sc := make(chan os.Signal, 1)
	signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
	<-sc

	// Cleanly close down the Discord session.
	// manager.StopAll()
	dg.Close()
}

func dumpAll(s *discordgo.Session, evt interface{}) {
	if _, ok := evt.(*discordgo.Event); !ok {
		// fmt.Printf("Inc event: %#v\n", evt)
	}
}

// This function will be called (due to AddHandler above) every time a new
// message is created on any channel that the autenticated bot has access to.
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
	if m.Author.ID != 138700441876692992 {
		return
	}

	if m.Content == "yaboi recon" {
		fmt.Println("Reconnecting...")
		err := s.GatewayManager.Reconnect(false)
		if err != nil {
			fmt.Println("Failed reconnecting")
		}
	}

	if m.Content == "yaboi joinvoice" {
		fmt.Println("joining cvoice")
		if vc != nil {
			fmt.Println("already in vc")
			return
		}

		channel, _ := s.State.Channel(m.ChannelID)
		g, _ := s.State.Guild(channel.GuildID)

		vcId := int64(0)
		for _, v := range g.VoiceStates {
			if v.UserID == m.Author.ID {
				vcId = v.ChannelID
				break
			}
		}

		if vcId == 0 {
			fmt.Println("Not in voice")
			return
		}

		var err error
		vc, err = s.GatewayManager.ChannelVoiceJoin(g.ID, vcId, true, true)
		if err != nil {
			fmt.Println("failed joining voice: ", err)
			return
		}
		fmt.Println("Joined voice")
	}

	if m.Content == "yaboi leavevoice" {
		if vc == nil {
			fmt.Println("Not in voice")
			return
		}

		err := vc.Disconnect()
		if err != nil {
			fmt.Println("failed leaving voice: ", err)
			return
		}
		vc = nil
	}

	// fmt.Println("\nReceived message my dude!\n")
	// // Ignore all messages created by the bot itself
	// // This isn't required in this specific example but it's a good practice.
	// if m.Author.ID == s.State.User.ID {
	// 	return
	// }
	// // If the message is "ping" reply with "Pong!"
	// if m.Content == "ping" {
	// 	s.ChannelMessageSend(m.ChannelID, "Pong!")
	// }

	// // If the message is "pong" reply with "Ping!"
	// if m.Content == "pong" {
	// 	s.ChannelMessageSend(m.ChannelID, "Ping!")
	// }
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"log"
	"math"
	"math/rand"
	"net"
	"net/url"
	"strconv"
	"time"
)

type UdpTracker struct {
	*tracker
	connectionId  uint64
	transactionId uint32
	serverAddr    *net.UDPAddr
	conn          *net.UDPConn
}

type connectRequest struct {
	connectionId  uint64
	action        uint32
	transactionId uint32
}

type connectResponse struct {
	action        uint32
	transactionId uint32
	connectionId  uint64
}

type errorResponse struct {
	action        uint32
	transactionId uint32
	message       string
}

type shortString []byte

type announceRequest struct {
	connectionId  uint64
	action        uint32
	transactionId uint32
	infoHash      shortString
	peerId        shortString
	downloaded    uint64
	left          uint64
	uploaded      uint64
	event         uint32
	ipAddr        uint32
	key           uint32
	numWant       int32
	port          uint16
}

type announceResponse struct {
	action        uint32
	transactionId uint32
	interval      uint32
	leechers      uint32
	seeders       uint32
	peers         []PeerTuple
}

func (r *connectRequest) MarshalBinary() ([]byte, error) {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, r.connectionId)
	err = binary.Write(buf, binary.BigEndian, r.action)
	err = binary.Write(buf, binary.BigEndian, r.transactionId)
	return buf.Bytes(), err
}

func (r *connectResponse) UnmarshalBinary(data []byte) error {
	buf := bytes.NewReader(data[4:])
	actionBytes := append([]byte{0, 0, 0}, data[0])
	err := binary.Read(bytes.NewReader(actionBytes), binary.BigEndian, &r.action)
	err = binary.Read(buf, binary.BigEndian, &r.transactionId)
	err = binary.Read(buf, binary.BigEndian, &r.connectionId)
	return err
}

func (r *connectResponse) MarshalBinary() ([]byte, error) {
	var b bytes.Buffer
	fmt.Fprintln(&b, r.connectionId, r.action, r.transactionId)
	return b.Bytes(), nil
}

func (r *errorResponse) UnmarshalBinary(data []byte) error {
	actionBytes := append([]byte{0, 0, 0}, data[0])
	err := binary.Read(bytes.NewReader(actionBytes), binary.BigEndian, &r.action)
	err = binary.Read(bytes.NewReader(data[4:]), binary.BigEndian, &r.transactionId)
	r.message = string(data[8:])
	return err
}

func (r *announceRequest) MarshalBinary() ([]byte, error) {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, r.connectionId)
	err = binary.Write(buf, binary.BigEndian, r.action)
	err = binary.Write(buf, binary.BigEndian, r.transactionId)
	err = binary.Write(buf, binary.BigEndian, r.infoHash)
	err = binary.Write(buf, binary.BigEndian, r.peerId)
	err = binary.Write(buf, binary.BigEndian, r.downloaded)
	err = binary.Write(buf, binary.BigEndian, r.left)
	err = binary.Write(buf, binary.BigEndian, r.uploaded)
	err = binary.Write(buf, binary.BigEndian, r.event)
	err = binary.Write(buf, binary.BigEndian, r.ipAddr)
	err = binary.Write(buf, binary.BigEndian, r.key)
	err = binary.Write(buf, binary.BigEndian, r.numWant)
	err = binary.Write(buf, binary.BigEndian, r.port)
	return buf.Bytes(), err
}

func (r *announceResponse) UnmarshalBinary(data []byte) error {
	buf := bytes.NewReader(data[4:])
	actionBytes := append([]byte{0, 0, 0}, data[0])
	err := binary.Read(bytes.NewReader(actionBytes), binary.BigEndian, &r.action)
	err = binary.Read(buf, binary.BigEndian, &r.transactionId)
	err = binary.Read(buf, binary.BigEndian, &r.interval)
	err = binary.Read(buf, binary.BigEndian, &r.leechers)
	err = binary.Read(buf, binary.BigEndian, &r.seeders)

	peerBytes := data[20:]
	peers := len(peerBytes) / 6

	for i := 0; i < peers; i++ {
		peer := PeerTuple{}

		ipBytes := peerBytes[i*6 : i*6+4]
		//log.Printf("ipbytes: %v\n", ipBytes)
		peer.IP = net.IPv4(ipBytes[0], ipBytes[1], ipBytes[2], ipBytes[3])
		binary.Read(bytes.NewReader(peerBytes[i*6+4:i*6+6]), binary.BigEndian, &peer.Port)

		log.Printf("peer: %s:%d\n", peer.IP.String(), peer.Port)
		r.peers = append(r.peers, peer)
	}

	return err
}

func NewUdpTracker(key string, chans trackerPeerChans, port uint16, infoHash []byte, announce *url.URL) *UdpTracker {
	return &UdpTracker{&tracker{key: key, peerChans: chans, port: port, infoHash: infoHash, announceURL: announce}, 0, 0, &net.UDPAddr{}, &net.UDPConn{}}
}

func (p *PeerTuple) String() string {
	return fmt.Sprintf("%s:%d\n", p.IP.String(), p.Port)
}

func (tr *UdpTracker) Announce(event int) {
	err := tr.connect()
	if err != nil {
		log.Printf("Tracker : Could not connect to tracker %s\n", tr.announceURL.String())
		return
	}

	key, _ := strconv.ParseUint(tr.key, 16, 4)
	announce := &announceRequest{
		connectionId:  tr.connectionId,
		action:        1,
		transactionId: tr.transactionId,
		infoHash:      tr.infoHash,
		peerId:        PeerID[:],
		downloaded:    uint64(tr.stats.Downloaded),
		left:          uint64(tr.stats.Left),
		uploaded:      uint64(tr.stats.Uploaded),
		event:         uint32(event),
		ipAddr:        0,
		key:           uint32(key),
		numWant:       -1,
		port:          6881,
	}
	announceBytes, _ := announce.MarshalBinary()

	buff := make([]byte, 600)
	length := tr.request(announceBytes, buff)

	var response announceResponse
	response.UnmarshalBinary(buff[:length])

	log.Printf("announce response: %+v\n", response.peers)

	if event != Stopped {
		if response.interval != 0 {
			nextAnnounce := time.Second * 120
			log.Printf("Tracker : Announce : Scheduling next announce in %v\n", nextAnnounce)
			tr.timer = time.After(nextAnnounce)
		}

		for _, peer := range response.peers {
			tr.peerChans.peers <- peer
		}
	}
}

func (tr *UdpTracker) connect() error {
	log.Printf("Tracker : Connect (%v)", tr.announceURL)

	connectReq := connectRequest{connectionId: 0x41727101980, action: 0, transactionId: tr.transactionId}
	connectBytes, _ := connectReq.MarshalBinary()

	buff := make([]byte, 150)
	length := tr.request(connectBytes, buff)

	var response connectResponse
	response.UnmarshalBinary(buff[:length])

	if response.action == 3 || tr.transactionId != response.transactionId {
		error := errorResponse{}
		error.UnmarshalBinary(buff)
		return errors.New("Response Error: " + error.message)
	}

	tr.connectionId = response.connectionId
	return nil
}

func (tr *UdpTracker) Run() {
	log.Printf("Tracker : Run : Started (%s)\n", tr.announceURL)
	defer log.Printf("Tracker : Run : Completed (%s)\n", tr.announceURL)

	rand.Seed(time.Now().UnixNano())

	serverAddr, err := net.ResolveUDPAddr("udp", tr.announceURL.Host)
	if err != nil {
		log.Println("Tracker : Could not resolve tracker host!")
		return
	}

	var conn *net.UDPConn
	for port := 6881; err == nil && port < 6890; port++ {
		conn, err = net.ListenUDP("udp", &net.UDPAddr{Port: port})
	}
	if err != nil {
		log.Println("Tracker : Could not bind to any port in range 6881-6889")
		return
	}

	tr.transactionId = rand.Uint32()
	tr.serverAddr = serverAddr
	tr.conn = conn
	tr.Announce(Started)

	for {
		select {
		case <-tr.quit:
			log.Println("Tracker : Stop : Stopping")
			tr.Announce(Stopped)
			return
		case <-tr.completedCh:
			go tr.Announce(Completed)
		case <-tr.timer:
			log.Printf("Tracker : Run : Interval Timer Expired (%s)\n", tr.announceURL)
			go tr.Announce(Interval)
		case stats := <-tr.peerChans.stats:
			log.Println("read from stats", stats)
		}
	}
}

// Send a udp packet to the tracker, fill in the dest buffer with the response
func (tr *UdpTracker) request(payload []byte, dest []byte) int {
	n := 0
	totalAttempts := 0

	// notify the sender when a response is recieved
	recvChan := make(chan bool)

	// keep sending the packet and wait for the specifed time
	// before trying again, limit n to 8
	go func() {

		// initial send
		tr.conn.WriteTo(payload, tr.serverAddr)
	Listen:
		for {
			// timeout: 15 * 2 ^ n (0-8)
			timeout := time.Second * time.Duration(15 * int(math.Pow(2.0, float64(n))))
			timer := time.After(timeout)
			select {
			case <-recvChan:
				break Listen
			case <-timer:
				tr.conn.WriteTo(payload, tr.serverAddr)
				if n < 8 {
					n++
				}
				totalAttempts++
			}
		}
	}()

	// read until some data is received
	var err error
	length := 0
	for length == 0 {
		length, _, err = tr.conn.ReadFrom(dest)

		// not sure what to do here?
		if err != nil {
			log.Println("Tracker : udp packet read error")
		}
	}

	recvChan <- true
	return length
}
<commit_msg>Added length checks for packet size<commit_after>package main

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"log"
	"math"
	"math/rand"
	"net"
	"net/url"
	"strconv"
	"time"
)

type UdpTracker struct {
	*tracker
	connectionId  uint64
	transactionId uint32
	serverAddr    *net.UDPAddr
	conn          *net.UDPConn
}

type connectRequest struct {
	connectionId  uint64
	action        uint32
	transactionId uint32
}

type connectResponse struct {
	action        uint32
	transactionId uint32
	connectionId  uint64
}

type errorResponse struct {
	action        uint32
	transactionId uint32
	message       string
}

type shortString []byte

type announceRequest struct {
	connectionId  uint64
	action        uint32
	transactionId uint32
	infoHash      shortString
	peerId        shortString
	downloaded    uint64
	left          uint64
	uploaded      uint64
	event         uint32
	ipAddr        uint32
	key           uint32
	numWant       int32
	port          uint16
}

type announceResponse struct {
	action        uint32
	transactionId uint32
	interval      uint32
	leechers      uint32
	seeders       uint32
	peers         []PeerTuple
}

func (r *connectRequest) MarshalBinary() ([]byte, error) {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, r.connectionId)
	err = binary.Write(buf, binary.BigEndian, r.action)
	err = binary.Write(buf, binary.BigEndian, r.transactionId)
	return buf.Bytes(), err
}

func (r *connectResponse) UnmarshalBinary(data []byte) error {
	buf := bytes.NewReader(data[4:])
	actionBytes := append([]byte{0, 0, 0}, data[0])
	err := binary.Read(bytes.NewReader(actionBytes), binary.BigEndian, &r.action)
	err = binary.Read(buf, binary.BigEndian, &r.transactionId)
	err = binary.Read(buf, binary.BigEndian, &r.connectionId)
	return err
}

func (r *connectResponse) MarshalBinary() ([]byte, error) {
	var b bytes.Buffer
	fmt.Fprintln(&b, r.connectionId, r.action, r.transactionId)
	return b.Bytes(), nil
}

func (r *errorResponse) UnmarshalBinary(data []byte) error {
	actionBytes := append([]byte{0, 0, 0}, data[0])
	err := binary.Read(bytes.NewReader(actionBytes), binary.BigEndian, &r.action)
	err = binary.Read(bytes.NewReader(data[4:]), binary.BigEndian, &r.transactionId)
	r.message = string(data[8:])
	return err
}

func (r *announceRequest) MarshalBinary() ([]byte, error) {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, r.connectionId)
	err = binary.Write(buf, binary.BigEndian, r.action)
	err = binary.Write(buf, binary.BigEndian, r.transactionId)
	err = binary.Write(buf, binary.BigEndian, r.infoHash)
	err = binary.Write(buf, binary.BigEndian, r.peerId)
	err = binary.Write(buf, binary.BigEndian, r.downloaded)
	err = binary.Write(buf, binary.BigEndian, r.left)
	err = binary.Write(buf, binary.BigEndian, r.uploaded)
	err = binary.Write(buf, binary.BigEndian, r.event)
	err = binary.Write(buf, binary.BigEndian, r.ipAddr)
	err = binary.Write(buf, binary.BigEndian, r.key)
	err = binary.Write(buf, binary.BigEndian, r.numWant)
	err = binary.Write(buf, binary.BigEndian, r.port)
	return buf.Bytes(), err
}

func (r *announceResponse) UnmarshalBinary(data []byte) error {
	buf := bytes.NewReader(data[4:])
	actionBytes := append([]byte{0, 0, 0}, data[0])
	err := binary.Read(bytes.NewReader(actionBytes), binary.BigEndian, &r.action)
	err = binary.Read(buf, binary.BigEndian, &r.transactionId)
	err = binary.Read(buf, binary.BigEndian, &r.interval)
	err = binary.Read(buf, binary.BigEndian, &r.leechers)
	err = binary.Read(buf, binary.BigEndian, &r.seeders)

	if len(data) > 20 {
		peerBytes := data[20:]
		peers := len(peerBytes) / 6

		for i := 0; i < peers; i++ {
			peer := PeerTuple{}

			ipBytes := peerBytes[i*6 : i*6+4]
			//log.Printf("ipbytes: %v\n", ipBytes)
			peer.IP = net.IPv4(ipBytes[0], ipBytes[1], ipBytes[2], ipBytes[3])
			binary.Read(bytes.NewReader(peerBytes[i*6+4:i*6+6]), binary.BigEndian, &peer.Port)

			log.Printf("peer: %s:%d\n", peer.IP.String(), peer.Port)
			r.peers = append(r.peers, peer)
		}
	}

	return err
}

func NewUdpTracker(key string, chans trackerPeerChans, port uint16, infoHash []byte, announce *url.URL) *UdpTracker {
	return &UdpTracker{&tracker{key: key, peerChans: chans, port: port, infoHash: infoHash, announceURL: announce}, 0, 0, &net.UDPAddr{}, &net.UDPConn{}}
}

func (p *PeerTuple) String() string {
	return fmt.Sprintf("%s:%d\n", p.IP.String(), p.Port)
}

func (tr *UdpTracker) Announce(event int) {
	err := tr.connect()
	if err != nil {
		log.Printf("Tracker : Could not connect to tracker %s: \n", tr.announceURL.String(), err)
		return
	}

	key, _ := strconv.ParseUint(tr.key, 16, 4)
	announce := &announceRequest{
		connectionId:  tr.connectionId,
		action:        1,
		transactionId: tr.transactionId,
		infoHash:      tr.infoHash,
		peerId:        PeerID[:],
		downloaded:    uint64(tr.stats.Downloaded),
		left:          uint64(tr.stats.Left),
		uploaded:      uint64(tr.stats.Uploaded),
		event:         uint32(event),
		ipAddr:        0,
		key:           uint32(key),
		numWant:       -1,
		port:          6881,
	}
	announceBytes, _ := announce.MarshalBinary()

	buff := make([]byte, 60000)
	length := tr.request(announceBytes, buff)

	if length >= 20 {
		var response announceResponse
		response.UnmarshalBinary(buff[:length])

		log.Printf("announce response: %+v\n", response.peers)

		if event != Stopped {
			if response.interval != 0 {
				nextAnnounce := time.Second * 120
				log.Printf("Tracker : Announce : Scheduling next announce in %v\n", nextAnnounce)
				tr.timer = time.After(nextAnnounce)
			}

			for _, peer := range response.peers {
				tr.peerChans.peers <- peer
			}
		}
	}
}

func (tr *UdpTracker) connect() error {
	log.Printf("Tracker : Connect (%v)", tr.announceURL)

	connectReq := connectRequest{connectionId: 0x41727101980, action: 0, transactionId: tr.transactionId}
	connectBytes, _ := connectReq.MarshalBinary()

	buff := make([]byte, 150)
	length := tr.request(connectBytes, buff)

	if length >= 16 {
		var response connectResponse
		response.UnmarshalBinary(buff[:length])

		if response.action == 3 || tr.transactionId != response.transactionId {
			error := errorResponse{}
			error.UnmarshalBinary(buff)
			return errors.New("Response Error: " + error.message)
		}

		tr.connectionId = response.connectionId
		return nil
	}

	return errors.New("Invalid connect response length")
}

func (tr *UdpTracker) Run() {
	log.Printf("Tracker : Run : Started (%s)\n", tr.announceURL)
	defer log.Printf("Tracker : Run : Completed (%s)\n", tr.announceURL)

	rand.Seed(time.Now().UnixNano())

	serverAddr, err := net.ResolveUDPAddr("udp", tr.announceURL.Host)
	if err != nil {
		log.Println("Tracker : Could not resolve tracker host!")
		return
	}

	var conn *net.UDPConn
	for port := 6881; err == nil && port < 6890; port++ {
		conn, err = net.ListenUDP("udp", &net.UDPAddr{Port: port})
	}
	if err != nil {
		log.Println("Tracker : Could not bind to any port in range 6881-6889")
		return
	}

	tr.transactionId = rand.Uint32()
	tr.serverAddr = serverAddr
	tr.conn = conn
	tr.Announce(Started)

	for {
		select {
		case <-tr.quit:
			log.Println("Tracker : Stop : Stopping")
			tr.Announce(Stopped)
			return
		case <-tr.completedCh:
			go tr.Announce(Completed)
		case <-tr.timer:
			log.Printf("Tracker : Run : Interval Timer Expired (%s)\n", tr.announceURL)
			go tr.Announce(Interval)
		case stats := <-tr.peerChans.stats:
			log.Println("read from stats", stats)
		}
	}
}

// Send a udp packet to the tracker, fill in the dest buffer with the response
func (tr *UdpTracker) request(payload []byte, dest []byte) int {
	n := 0
	totalAttempts := 0

	// notify the sender when a response is recieved
	recvChan := make(chan bool)

	// keep sending the packet and wait for the specifed time
	// before trying again, limit n to 8
	go func() {

		// initial send
		tr.conn.WriteTo(payload, tr.serverAddr)
	Listen:
		for {
			// timeout: 15 * 2 ^ n (0-8)
			timeout := time.Second * time.Duration(15 * int(math.Pow(2.0, float64(n))))
			timer := time.After(timeout)
			select {
			case <-recvChan:
				break Listen
			case <-timer:
				tr.conn.WriteTo(payload, tr.serverAddr)
				if n < 8 {
					n++
				}
				totalAttempts++
			}
		}
	}()

	// read until some data is received
	var err error
	length := 0
	for length == 0 {
		length, _, err = tr.conn.ReadFrom(dest)

		// not sure what to do here?
		if err != nil {
			log.Println("Tracker : udp packet read error")
		}
	}

	recvChan <- true
	return length
}
<|endoftext|>


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

package main

import (
	"bytes"
	"encoding/binary"
	"errors"
	"log"
	"math"
	"math/rand"
	"net"
	"net/url"
	"strconv"
	"time"
)

const (
	initialConnectionId       = 0x41727101980
	connectMinResponseLength  = 16
	announceMinResponseLength = 20
	connectBufferSize         = 150
	announceBufferSize        = 20000
)

type UdpTracker struct {
	*tracker
	ConnectionId  uint64
	TransactionId uint32
	ServerAddr    *net.UDPAddr
	Conn          *net.UDPConn
}

type connectRequest struct {
	ConnectionId  uint64
	Action        uint32
	TransactionId uint32
}

type connectResponse struct {
	Action        uint32
	TransactionId uint32
	ConnectionId  uint64
}

type errorResponse struct {
	Action        uint32
	TransactionId uint32
	Message       string
}

type announceRequest struct {
	ConnectionId  uint64
	Action        uint32
	TransactionId uint32
	InfoHash      [20]byte
	PeerId        [20]byte
	Downloaded    uint64
	Left          uint64
	Uploaded      uint64
	Event         uint32
	IpAddr        uint32
	Key           uint32
	NumWant       int32
	Port          uint16
}

type announceResponse struct {
	Action        uint32
	TransactionId uint32
	Interval      uint32
	Leechers      uint32
	Seeders       uint32
	Peers         []PeerTuple
}

func (r *connectRequest) MarshalBinary() ([]byte, error) {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, r)
	return buf.Bytes(), err
}

func (r *connectResponse) UnmarshalBinary(data []byte) error {
	buf := bytes.NewReader(data)
	err := binary.Read(buf, binary.BigEndian, r)
	return err
}

func (r *errorResponse) UnmarshalBinary(data []byte) error {
	buf := bytes.NewReader(append([]byte{0, 0, 0, data[0]}, data[4:]...))
	err := binary.Read(buf, binary.BigEndian, &r.Action)
	if err != nil {
		return err
	}

	err = binary.Read(buf, binary.BigEndian, &r.TransactionId)
	if err != nil {
		return err
	}

	r.Message = string(data[8:])
	return err
}

func (r *announceRequest) MarshalBinary() ([]byte, error) {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, r)
	return buf.Bytes(), err
}

func (r *announceResponse) UnmarshalBinary(data []byte) error {
	buf := bytes.NewReader(data)

	err := binary.Read(buf, binary.BigEndian, &r.Action)
	if err != nil {
		return err
	}

	err = binary.Read(buf, binary.BigEndian, &r.TransactionId)
	if err != nil {
		return err
	}

	err = binary.Read(buf, binary.BigEndian, &r.Interval)
	if err != nil {
		return err
	}

	err = binary.Read(buf, binary.BigEndian, &r.Leechers)
	if err != nil {
		return err
	}

	err = binary.Read(buf, binary.BigEndian, &r.Seeders)
	if err != nil {
		return err
	}

	if len(data) > announceMinResponseLength {
		peerBytes := bytes.NewReader(data[announceMinResponseLength:])
		peers := (len(data) - announceMinResponseLength) / 6

		for i := 0; i < peers; i++ {
			var peer PeerTuple
			var ipBuf [4]byte

			err = binary.Read(peerBytes, binary.BigEndian, &ipBuf)
			if err != nil {
				return err
			}

			err = binary.Read(peerBytes, binary.BigEndian, &peer.Port)
			if err != nil {
				return err
			}

			peer.IP = net.IPv4(ipBuf[0], ipBuf[1], ipBuf[2], ipBuf[3])
			r.Peers = append(r.Peers, peer)
		}
	}
	return nil
}

func NewUdpTracker(key string, chans trackerPeerChans, port uint16, infoHash []byte, announce *url.URL) *UdpTracker {
	return &UdpTracker{&tracker{key: key, peerChans: chans, port: port, infoHash: infoHash, announceURL: announce}, 0, 0, &net.UDPAddr{}, &net.UDPConn{}}
}

func (tr *UdpTracker) Announce(event int) {
	err := tr.connect()
	if err != nil {
		log.Printf("Tracker : Could not connect to tracker %s: %v\n", tr.announceURL.String(), err)
		return
	}

	key, _ := strconv.ParseUint(tr.key, 16, 4)
	announce := &announceRequest{
		ConnectionId:  tr.ConnectionId,
		Action:        1,
		TransactionId: tr.TransactionId,
		PeerId:        PeerID,
		Downloaded:    uint64(tr.stats.Downloaded),
		Left:          uint64(tr.stats.Left),
		Uploaded:      uint64(tr.stats.Uploaded),
		Event:         uint32(event),
		IpAddr:        0,
		Key:           uint32(key),
		NumWant:       -1,
		Port:          6881,
	}
	copy(announce.InfoHash[:20], tr.infoHash)
	announceBytes, err := announce.MarshalBinary()
	if err != nil {
		log.Println("Tracker : Announce : Invalid response")
		return
	}

	buff := make([]byte, announceBufferSize)
	length := tr.request(announceBytes, buff)

	if length >= announceMinResponseLength {
		var response announceResponse
		err := response.UnmarshalBinary(buff[:length])
		if err != nil {
			// TODO: Handle tracker errors gracefully
			log.Fatal(err)
		}

		if event != Stopped {
			if response.Interval != 0 {
				nextAnnounce := time.Second * time.Duration(response.Interval)
				log.Println("Tracker : Announce : Scheduling next announce in", nextAnnounce)
				tr.timer = time.After(nextAnnounce)
			}

			for _, peer := range response.Peers {
				// avoid a race condition by copying peer to p
				go func(p PeerTuple) {
					// send the peer to peer manager
					tr.peerChans.peers <- p
				}(peer)
			}
		}
	}
}

func (tr *UdpTracker) connect() error {
	log.Printf("Tracker : Connect (%v)", tr.announceURL)

	connectReq := connectRequest{ConnectionId: initialConnectionId, Action: 0, TransactionId: tr.TransactionId}
	connectBytes, _ := connectReq.MarshalBinary()

	buff := make([]byte, connectBufferSize)
	length := tr.request(connectBytes, buff)

	if length >= connectMinResponseLength {
		var response connectResponse
		err := response.UnmarshalBinary(buff[:length])
		if err != nil {
			log.Println("Tracker : Connect : Invalid response")
			return err
		}

		if response.Action == 3 || tr.TransactionId != response.TransactionId {
			error := errorResponse{}
			err := error.UnmarshalBinary(buff)
			if err != nil {
				return errors.New(error.Message)
			} else {
				return err
			}
		}

		tr.ConnectionId = response.ConnectionId
		return nil
	}

	return errors.New("Invalid connect response length")
}

func (tr *UdpTracker) Run() {
	log.Printf("Tracker : Run : Started (%s)\n", tr.announceURL)
	defer log.Printf("Tracker : Run : Completed (%s)\n", tr.announceURL)

	rand.Seed(time.Now().UnixNano())

	serverAddr, err := net.ResolveUDPAddr("udp", tr.announceURL.Host)
	if err != nil {
		log.Println("Tracker : Could not resolve tracker host!")
		return
	}

	conn, err := net.ListenUDP("udp", &net.UDPAddr{Port: 0})
	if err != nil {
		log.Println("Tracker : Could not bind to any port")
		return
	}

	tr.TransactionId = rand.Uint32()
	tr.ServerAddr = serverAddr
	tr.Conn = conn
	tr.Announce(Started)

	for {
		select {
		case <-tr.quit:
			log.Println("Tracker : Stop : Stopping")
			tr.Announce(Stopped)
			return
		case <-tr.completedCh:
			go tr.Announce(Completed)
		case <-tr.timer:
			log.Printf("Tracker : Run : Interval Timer Expired (%s)\n", tr.announceURL)
			go tr.Announce(Interval)
		case stats := <-tr.peerChans.stats:
			log.Println("read from stats", stats)
		}
	}
}

// Send a udp packet to the tracker, fill in the dest buffer with the response
func (tr *UdpTracker) request(payload []byte, dest []byte) int {
	n := 0
	totalAttempts := 0

	// notify the sender when a response is recieved
	recvChan := make(chan bool)

	// keep sending the packet and wait for the specifed time
	// before trying again, limit n to 8
	go func() {

		// initial send
		tr.Conn.WriteTo(payload, tr.ServerAddr)
	Listen:
		for {
			// timeout: 15 * 2 ^ n (0-8)
			timeout := time.Second * time.Duration(15*int(math.Pow(2.0, float64(n))))
			timer := time.After(timeout)
			select {
			case <-recvChan:
				break Listen
			case <-timer:
				tr.Conn.WriteTo(payload, tr.ServerAddr)
				totalAttempts++
				if n < 8 {
					n++
				}
			}
		}
	}()

	// read until some data is received
	var err error
	length := 0
	for length == 0 {
		length, _, err = tr.Conn.ReadFrom(dest)

		// not sure what to do here?
		if err != nil {
			log.Println("Tracker : udp packet read error")
		}
	}

	recvChan <- true
	return length
}
<commit_msg>Use const and iota for actions<commit_after>// Copyright 2014 Rob Bassi. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// UDP tracker support. See BEP 15.

package main

import (
	"bytes"
	"encoding/binary"
	"errors"
	"log"
	"math"
	"math/rand"
	"net"
	"net/url"
	"strconv"
	"time"
)

const (
	initialConnectionId       = 0x41727101980
	connectMinResponseLength  = 16
	announceMinResponseLength = 20
	connectBufferSize         = 150
	announceBufferSize        = 20000
)

const (
	Connect uint32 = iota
	Announce
	Scrape
	Error
)

type UdpTracker struct {
	*tracker
	ConnectionId  uint64
	TransactionId uint32
	ServerAddr    *net.UDPAddr
	Conn          *net.UDPConn
}

type connectRequest struct {
	ConnectionId  uint64
	Action        uint32
	TransactionId uint32
}

type connectResponse struct {
	Action        uint32
	TransactionId uint32
	ConnectionId  uint64
}

type errorResponse struct {
	Action        uint32
	TransactionId uint32
	Message       string
}

type announceRequest struct {
	ConnectionId  uint64
	Action        uint32
	TransactionId uint32
	InfoHash      [20]byte
	PeerId        [20]byte
	Downloaded    uint64
	Left          uint64
	Uploaded      uint64
	Event         uint32
	IpAddr        uint32
	Key           uint32
	NumWant       int32
	Port          uint16
}

type announceResponse struct {
	Action        uint32
	TransactionId uint32
	Interval      uint32
	Leechers      uint32
	Seeders       uint32
	Peers         []PeerTuple
}

func (r *connectRequest) MarshalBinary() ([]byte, error) {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, r)
	return buf.Bytes(), err
}

func (r *connectResponse) UnmarshalBinary(data []byte) error {
	buf := bytes.NewReader(data)
	err := binary.Read(buf, binary.BigEndian, r)
	return err
}

func (r *errorResponse) UnmarshalBinary(data []byte) error {
	buf := bytes.NewReader(append([]byte{0, 0, 0, data[0]}, data[4:]...))
	err := binary.Read(buf, binary.BigEndian, &r.Action)
	if err != nil {
		return err
	}

	err = binary.Read(buf, binary.BigEndian, &r.TransactionId)
	if err != nil {
		return err
	}

	r.Message = string(data[8:])
	return err
}

func (r *announceRequest) MarshalBinary() ([]byte, error) {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, r)
	return buf.Bytes(), err
}

func (r *announceResponse) UnmarshalBinary(data []byte) error {
	buf := bytes.NewReader(data)

	err := binary.Read(buf, binary.BigEndian, &r.Action)
	if err != nil {
		return err
	}

	err = binary.Read(buf, binary.BigEndian, &r.TransactionId)
	if err != nil {
		return err
	}

	err = binary.Read(buf, binary.BigEndian, &r.Interval)
	if err != nil {
		return err
	}

	err = binary.Read(buf, binary.BigEndian, &r.Leechers)
	if err != nil {
		return err
	}

	err = binary.Read(buf, binary.BigEndian, &r.Seeders)
	if err != nil {
		return err
	}

	if len(data) > announceMinResponseLength {
		peerBytes := bytes.NewReader(data[announceMinResponseLength:])
		peers := (len(data) - announceMinResponseLength) / 6

		for i := 0; i < peers; i++ {
			var peer PeerTuple
			var ipBuf [4]byte

			err = binary.Read(peerBytes, binary.BigEndian, &ipBuf)
			if err != nil {
				return err
			}

			err = binary.Read(peerBytes, binary.BigEndian, &peer.Port)
			if err != nil {
				return err
			}

			peer.IP = net.IPv4(ipBuf[0], ipBuf[1], ipBuf[2], ipBuf[3])
			r.Peers = append(r.Peers, peer)
		}
	}
	return nil
}

func NewUdpTracker(key string, chans trackerPeerChans, port uint16, infoHash []byte, announce *url.URL) *UdpTracker {
	return &UdpTracker{&tracker{key: key, peerChans: chans, port: port, infoHash: infoHash, announceURL: announce}, 0, 0, &net.UDPAddr{}, &net.UDPConn{}}
}

func (tr *UdpTracker) Announce(event int) {
	err := tr.connect()
	if err != nil {
		log.Printf("Tracker : Could not connect to tracker %s: %v\n", tr.announceURL.String(), err)
		return
	}

	key, _ := strconv.ParseUint(tr.key, 16, 4)
	announce := &announceRequest{
		ConnectionId:  tr.ConnectionId,
		Action:        Announce,
		TransactionId: tr.TransactionId,
		PeerId:        PeerID,
		Downloaded:    uint64(tr.stats.Downloaded),
		Left:          uint64(tr.stats.Left),
		Uploaded:      uint64(tr.stats.Uploaded),
		Event:         uint32(event),
		IpAddr:        0,
		Key:           uint32(key),
		NumWant:       -1,
		Port:          6881,
	}
	copy(announce.InfoHash[:20], tr.infoHash)
	announceBytes, err := announce.MarshalBinary()
	if err != nil {
		log.Println("Tracker : Announce : Invalid response")
		return
	}

	buff := make([]byte, announceBufferSize)
	length := tr.request(announceBytes, buff)

	if length >= announceMinResponseLength {
		var response announceResponse
		err := response.UnmarshalBinary(buff[:length])
		if err != nil {
			// TODO: Handle tracker errors gracefully
			log.Fatal(err)
		}

		if event != Stopped {
			if response.Interval != 0 {
				nextAnnounce := time.Second * time.Duration(response.Interval)
				log.Println("Tracker : Announce : Scheduling next announce in", nextAnnounce)
				tr.timer = time.After(nextAnnounce)
			}

			for _, peer := range response.Peers {
				// avoid a race condition by copying peer to p
				go func(p PeerTuple) {
					// send the peer to peer manager
					tr.peerChans.peers <- p
				}(peer)
			}
		}
	}
}

func (tr *UdpTracker) connect() error {
	log.Printf("Tracker : Connect (%v)", tr.announceURL)

	connectReq := connectRequest{ConnectionId: initialConnectionId, Action: Connect, TransactionId: tr.TransactionId}
	connectBytes, _ := connectReq.MarshalBinary()

	buff := make([]byte, connectBufferSize)
	length := tr.request(connectBytes, buff)

	if length >= connectMinResponseLength {
		var response connectResponse
		err := response.UnmarshalBinary(buff[:length])
		if err != nil {
			log.Println("Tracker : Connect : Invalid response")
			return err
		}

		if response.Action == Error || tr.TransactionId != response.TransactionId {
			error := errorResponse{}
			err := error.UnmarshalBinary(buff)
			if err != nil {
				return errors.New(error.Message)
			} else {
				return err
			}
		}

		tr.ConnectionId = response.ConnectionId
		return nil
	}

	return errors.New("Invalid connect response length")
}

func (tr *UdpTracker) Run() {
	log.Printf("Tracker : Run : Started (%s)\n", tr.announceURL)
	defer log.Printf("Tracker : Run : Completed (%s)\n", tr.announceURL)

	rand.Seed(time.Now().UnixNano())

	serverAddr, err := net.ResolveUDPAddr("udp", tr.announceURL.Host)
	if err != nil {
		log.Println("Tracker : Could not resolve tracker host!")
		return
	}

	conn, err := net.ListenUDP("udp", &net.UDPAddr{Port: 0})
	if err != nil {
		log.Println("Tracker : Could not bind to any port")
		return
	}

	tr.TransactionId = rand.Uint32()
	tr.ServerAddr = serverAddr
	tr.Conn = conn
	tr.Announce(Started)

	for {
		select {
		case <-tr.quit:
			log.Println("Tracker : Stop : Stopping")
			tr.Announce(Stopped)
			return
		case <-tr.completedCh:
			go tr.Announce(Completed)
		case <-tr.timer:
			log.Printf("Tracker : Run : Interval Timer Expired (%s)\n", tr.announceURL)
			go tr.Announce(Interval)
		case stats := <-tr.peerChans.stats:
			log.Println("read from stats", stats)
		}
	}
}

// Send a udp packet to the tracker, fill in the dest buffer with the response
func (tr *UdpTracker) request(payload []byte, dest []byte) int {
	n := 0
	totalAttempts := 0

	// notify the sender when a response is recieved
	recvChan := make(chan bool)

	// keep sending the packet and wait for the specifed time
	// before trying again, limit n to 8
	go func() {

		// initial send
		tr.Conn.WriteTo(payload, tr.ServerAddr)
	Listen:
		for {
			// timeout: 15 * 2 ^ n (0-8)
			timeout := time.Second * time.Duration(15*int(math.Pow(2.0, float64(n))))
			timer := time.After(timeout)
			select {
			case <-recvChan:
				break Listen
			case <-timer:
				tr.Conn.WriteTo(payload, tr.ServerAddr)
				totalAttempts++
				if n < 8 {
					n++
				}
			}
		}
	}()

	// read until some data is received
	var err error
	length := 0
	for length == 0 {
		length, _, err = tr.Conn.ReadFrom(dest)

		// not sure what to do here?
		if err != nil {
			log.Println("Tracker : udp packet read error")
		}
	}

	recvChan <- true
	return length
}
<|endoftext|>