<commit_before>package apis

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"sort"

	"github.com/ghchinoy/atmotool/cm"
	"github.com/ghchinoy/atmotool/control"
)

const (
	CMLandingIndex         = "/content/home/landing/index.htm"
	CMInternationalization = "/i18n"
	CMCustomLess           = "/less/custom.less"
	CMFavicon              = "/style/images/favicon.ico"
	// CMCustomLessURI should be a template, subsitute in Configuration.Theme
	CMCustomLessURI   = "/resources/theme/default/less?unpack=false"
	CMListAPIsURI     = "/api/apis"
	CMListAppsURI     = "/api/search?sortBy=com.soa.sort.order.alphabetical&count=20&start=0&q=type:app"
	CMListPoliciesURI = "/api/policies"
	CMListUsersURI    = "/api/search?sort=asc&sortBy=com.soa.sort.order.title_sort&Federation=false&count=20&start=0&q=type:user"
)

// API is a convenience structure for a CM API
type API struct {
	Name    string `json:"name"`
	Version string `json:"version"`
	ID      string `json:"id"`
}

// APIs is a collection of API structs
type APIs []API

// Len is an implementation of sort interface for length of APIs
func (slice APIs) Len() int {
	return len(slice)
}

// Less is an implementation of sort interface for less comparison
func (slice APIs) Less(i, j int) bool {
	return slice[i].Name < slice[j].Name
}

// Swap is an implementation of the sort interface swap function
func (slice APIs) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

// APIList returns a list of apis on the platform
func APIList(config control.Configuration, debug bool) error {
	//var request *http.Request
	if debug {
		log.Println("Listing APIs")
	}
	client, err := control.LoginToCM(config, debug)
	if err != nil {
		log.Fatalln(err)
		return err
	}

	url := config.URL + CMListAPIsURI

	//client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	req.Header.Add("Accept", "application/json")
	if debug {
		log.Println("curl command:", control.CURLThis(client, req))
	}
	resp, err := client.Do(req)

	defer resp.Body.Close()

	bodyBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	if debug {
		fmt.Printf("%s", bodyBytes)
	}
	var apis cm.ApisResponse
	err = json.Unmarshal(bodyBytes, &apis)
	if debug {
		log.Printf("Found %v APIs", len(apis.Channel.Items))
	}

	var apiList APIs

	fmt.Printf("%v APIs\n", len(apis.Channel.Items))

	for _, v := range apis.Channel.Items {
		if debug {
			fmt.Printf("%s (%s)\n", v.EntityReference.Title, v.EntityReference.Guid)
		}
		apiList = append(apiList, API{Name: v.EntityReference.Title, ID: v.EntityReference.Guid})
	}

	sort.Sort(apiList)

	for _, v := range apiList {
		fmt.Printf("%-46s %-20s\n", v.ID, v.Name)
	}

	return nil
}
<commit_msg>updated API convenience structure, listversions<commit_after>package apis

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"sort"
	"strings"

	"github.com/fatih/structs"
	"github.com/ghchinoy/atmotool/cm"
	"github.com/ghchinoy/atmotool/control"
)

const (
	CMLandingIndex         = "/content/home/landing/index.htm"
	CMInternationalization = "/i18n"
	CMCustomLess           = "/less/custom.less"
	CMFavicon              = "/style/images/favicon.ico"
	// CMCustomLessURI should be a template, subsitute in Configuration.Theme
	CMCustomLessURI      = "/resources/theme/default/less?unpack=false"
	CMListAPIsURI        = "/api/apis"
	CMListAppsURI        = "/api/search?sortBy=com.soa.sort.order.alphabetical&count=20&start=0&q=type:app"
	CMListPoliciesURI    = "/api/policies"
	CMListUsersURI       = "/api/search?sort=asc&sortBy=com.soa.sort.order.title_sort&Federation=false&count=20&start=0&q=type:user"
	CMListAPIVersionsURI = "/api/apis/versions"
)

// API is a convenience structure for a CM API
type API struct {
	Name       string `json:"name"`
	Version    string `json:"version"`
	ID         string `json:"id"`
	Endpoint   string `json:"endpoint"`
	Visibility string `json:"visibility"`
}

// APIs is a collection of API structs
type APIs []API

// Len is an implementation of sort interface for length of APIs
func (slice APIs) Len() int {
	return len(slice)
}

// Less is an implementation of sort interface for less comparison
func (slice APIs) Less(i, j int) bool {
	return slice[i].Name < slice[j].Name
}

// Swap is an implementation of the sort interface swap function
func (slice APIs) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

// APIListVersions outputs a list of all APIs
func APIListVersions(config control.Configuration, debug bool) error {
	if debug {
		log.Println("Listing API Versions")
	}
	client, userinfo, err := control.LoginToCM(config, debug)
	if err != nil {
		log.Fatalln(err)
		return err
	}

	url := config.URL + CMListAPIVersionsURI

	req, err := http.NewRequest("GET", url, nil)
	req.Header.Add("Accept", "application/json")
	if debug {
		log.Println("curl: ", control.CURLThis(client, req))
	}
	resp, err := client.Do(req)
	defer resp.Body.Close()

	bodyBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	if debug {
		fmt.Printf("%s", bodyBytes)
	}
	var apis cm.ApisResponse
	err = json.Unmarshal(bodyBytes, &apis)
	if debug {
		log.Printf("Found %v APIs", len(apis.Channel.Items))
	}

	tenantID := strings.Split(userinfo.LoginDomainID, ".")[1]

	var apiList APIs

	fmt.Printf("%v APIs\n", len(apis.Channel.Items))
	for _, v := range apis.Channel.Items {
		visibility := getVisibility(v)
		var endpoint string
		if len(v.Endpoints.Endpoint) > 0 {
			endpoint = v.Endpoints.Endpoint[0].URI
		}
		// remove that tenant suffix from API guid
		apiguid := strings.Replace(v.Guid.Value, "."+tenantID, "", -1)
		apiList = append(apiList, API{
			Version:    v.Title,
			Name:       v.EntityReferences.EntityReference[0].Title,
			ID:         apiguid,
			Endpoint:   endpoint,
			Visibility: visibility,
		})
	}

	sort.Sort(apiList)

	//pattern := "%-36s %-20s %-5s %-15s %s\n"
	pattern := fmt.Sprintf("%%-%vs %%-%vs %%-%vs %%-%vs %%-%vs\n",
		maxLengthOfField(apiList, "ID"),
		maxLengthOfField(apiList, "Name"),
		maxLengthOfField(apiList, "Version"),
		maxLengthOfField(apiList, "Visibility"),
		maxLengthOfField(apiList, "Endpoint"),
	)
	fmt.Printf(pattern, fmt.Sprintf("ID (%s)", tenantID), "Name", "Ver", "Vis", "Endpoint")

	for _, v := range apiList {
		fmt.Printf(pattern, v.ID, v.Name, v.Version, v.Visibility, v.Endpoint)
	}

	return nil
}

// APIList returns a list of apis on the platform
func APIList(config control.Configuration, debug bool) error {
	//var request *http.Request
	if debug {
		log.Println("Listing APIs")
	}
	client, userinfo, err := control.LoginToCM(config, debug)
	if err != nil {
		log.Fatalln(err)
		return err
	}

	url := config.URL + CMListAPIsURI

	//client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	req.Header.Add("Accept", "application/json")
	if debug {
		log.Println("curl command:", control.CURLThis(client, req))
	}
	resp, err := client.Do(req)

	defer resp.Body.Close()

	bodyBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	if debug {
		fmt.Printf("%s", bodyBytes)
	}
	var apis cm.ApisResponse
	err = json.Unmarshal(bodyBytes, &apis)
	if debug {
		log.Printf("Found %v APIs", len(apis.Channel.Items))
	}

	var apiList APIs

	fmt.Printf("%v APIs\n", len(apis.Channel.Items))

	// grab tenant suffix, for removal
	if debug {
		log.Printf("LoginDomainID: %s", userinfo.LoginDomainID)
	}
	tenantID := strings.Split(userinfo.LoginDomainID, ".")[1]

	for _, v := range apis.Channel.Items {
		if debug {
			fmt.Printf("%s (%s)\n", v.EntityReference.Title, v.EntityReference.Guid)
		}
		visibility := getVisibility(v)
		// remove that tenant suffix from API guid
		apiguid := strings.Replace(v.EntityReference.Guid, "."+tenantID, "", -1)
		apiList = append(apiList, API{
			Name:       v.EntityReference.Title,
			ID:         apiguid,
			Visibility: visibility,
		})
	}

	sort.Sort(apiList)
	pattern := fmt.Sprintf("%%-%vs %%-%vs\n",
		maxLengthOfField(apiList, "Name")+1,
		maxLengthOfField(apiList, "ID")+1)
	fmt.Printf(pattern, fmt.Sprintf("ID (%s)", tenantID), "Name")
	for _, v := range apiList {
		fmt.Printf(pattern, v.ID, v.Name)
	}

	return nil
}

// probably add as method on APIs struct
func maxLengthOfField(list APIs, field string) int {
	var maxlen int
	for _, v := range list {
		s := structs.Map(v)
		q := fmt.Sprintf("%v", s[field])
		if len(q) > maxlen {
			maxlen = len(q)
		}
	}
	return maxlen + 2
}

// Returns the visibility of an Item
func getVisibility(v cm.Item) string {
	var visibility string
	cats := v.Category
	for _, c := range cats {
		if c.Domain == "uddi:soa.com:visibility" {
			visibility = c.Value
		}
	}
	// Shorten Registered Users visibility
	if visibility == "com.soa.visibility.registered.users" {
		visibility = "Registered"
	}
	return visibility
}
<|endoftext|>


<commit_before>//
// cdf_test.go
//
// go test election2012.go state.go api.go cdf.go parse.go college.go cdf_test.go
//

package main

import (
	"math"
	"testing"
)

const TOL = 1e-14

func checkFloat64(x, y, tol float64, test string, t *testing.T) {
	if math.Abs(x-y) > math.Abs(x*tol) {
		t.Errorf("Found %v, but expected %v for test %v", x, y, test)
	}
}

func TestErf(t *testing.T) {
	checkFloat64(erf(3), 0.999979214581871, TOL, "cumDist", t)
	checkFloat64(erf(-3), -0.999979214581871, TOL, "cumDist", t)
}

func TestCumDist(t *testing.T) {
	checkFloat64(cdf(1.0, 0.0, 1.0), 0.841384034263321, TOL, "cumDist", t)
	checkFloat64(cdf(40.0, 47.0, 10.0), 0.24195429670945612, TOL, "cumDist", t)
	checkFloat64(cdf(12.0, 10.0, 2.5), 0.7881610565888237, TOL, "cumDist", t)
}

func TestPrOverX(t *testing.T) {
	checkFloat64(prOverX(1.0, 0.0, 1.0), 1.0-0.841384034263321, TOL, "cumDist", t)
	checkFloat64(prOverX(40.0, 47.0, 10.0), 1.0-0.24195429670945612, TOL, "cumDist", t)
	checkFloat64(prOverX(12.0, 10.0, 2.5), 1.0-0.7881610565888237, TOL, "cumDist", t)
}
<commit_msg>test cleanup<commit_after>//
// cdf_test.go
//
// go test election2012.go state.go api.go cdf.go parse.go college.go cdf_test.go
//

package main

import (
	"math"
	"testing"
)

const TOL = 1e-14

func checkFloat64(found, expected, tol float64, test string, t *testing.T) {
	if math.Abs(found-expected) > math.Abs(found*tol) {
		t.Errorf("Found %v, but expected %v for test %v", found, expected, test)
	}
}

func TestErf(t *testing.T) {
	checkFloat64(erf(3), 0.999979214581871, TOL, "erf", t)
	checkFloat64(erf(-3), -0.999979214581871, TOL, "erf", t)
}

func TestCdf(t *testing.T) {
	checkFloat64(cdf(1.0, 0.0, 1.0), 0.841384034263321, TOL, "cdf", t)
	checkFloat64(cdf(40.0, 47.0, 10.0), 0.24195429670945612, TOL, "cdf", t)
	checkFloat64(cdf(12.0, 10.0, 2.5), 0.7881610565888237, TOL, "cdf", t)
}

func TestPrOverX(t *testing.T) {
	checkFloat64(prOverX(1.0, 0.0, 1.0), 1.0-0.841384034263321, TOL, "prOverX", t)
	checkFloat64(prOverX(40.0, 47.0, 10.0), 1.0-0.24195429670945612, TOL, "prOverX", t)
	checkFloat64(prOverX(12.0, 10.0, 2.5), 1.0-0.7881610565888237, TOL, "prOverX", t)
}
<|endoftext|>


<commit_before><commit_msg>When deleting a recipe, the association tables are deleted first such that foreign key constraints don't occur. The better long term solution will be to look into propogated deletes.<commit_after><|endoftext|>


<commit_before>package chanBufs

// NewInfiniteBuffer produces a write-only channel and a read-only channel that
// behave exactly like the two ends of a single go channel with an infinite buffer in between.
// Be very careful using this, as no buffer is truly infinite - if the internal
// buffer grows too large your program will run out of memory and crash.
func NewInfiniteBuffer() (chan<- interface{}, <-chan interface{}) {
	input := make(chan interface{})
	output := make(chan interface{})

	go func() {
		var buffer []interface{}
		for {
			if len(buffer) == 0 {
				elem, open := <-input
				if open {
					buffer = append(buffer, elem)
				} else {
					close(output)
					return
				}
			} else {
				select {
				case elem, open := <-input:
					if open {
						buffer = append(buffer, elem)
					} else {
						for elem := range buffer {
							output <- elem
						}
						close(output)
						return
					}
				case output <- buffer[0]:
					buffer = buffer[1:]
				}
			}
		}
	}()

	return input, output
}
<commit_msg>NewAdjustableBuffer<commit_after>package chanBufs

// NewInfiniteBuffer produces a write-only channel and a read-only channel that
// behave exactly like the two ends of a single go channel with an infinite buffer in between.
// Be very careful using this, as no buffer is truly infinite - if the internal
// buffer grows too large your program will run out of memory and crash.
func NewInfiniteBuffer() (input chan<- interface{}, output <-chan interface{}) {
	in := make(chan interface{})
	out := make(chan interface{})

	go func() {
		var buffer []interface{}
		for {
			if len(buffer) == 0 {
				elem, open := <-in
				if open {
					buffer = append(buffer, elem)
				} else {
					close(out)
					return
				}
			} else {
				select {
				case elem, open := <-in:
					if open {
						buffer = append(buffer, elem)
					} else {
						for elem := range buffer {
							out <- elem
						}
						close(out)
						return
					}
				case out <- buffer[0]:
					buffer = buffer[1:]
				}
			}
		}
	}()

	return in, out
}

// NewAdjustableBuffer produces a write-only channel and a read-only channel that
// behave exactly like the two ends of a single go channel with an adjustable buffer in between.
// The channel initially has a buffer size of 1, but can be adjusted by sending the new desired size
// down the third, 'adjust' channel.
//
// Setting a size of 0 would ideally produce an unbuffered, blocking channel but that does not appear
// to be possible with this approach, so we do the best we can which is a buffer of 1.
//
// Setting a negative size produces an infinite buffer.
//
// It is an error to close the adjust channel, it will be closed automatically when input is closed.
// It is an error to write to the adjust channel after closing the input channel.
func NewAdjustableBuffer() (input chan<- interface{}, output <-chan interface{}, adjust chan<- int) {
	in := make(chan interface{})
	out := make(chan interface{})
	adj := make(chan int)
	size := 1

	go func() {
		var buffer []interface{}
		for {
			if len(buffer) == 0 {
				select {
				case elem, open := <-in:
					if open {
						buffer = append(buffer, elem)
					} else {
						close(out)
						close(adj)
						return
					}
				case size = <-adj:
				}
			} else if size >= 0 && len(buffer) >= size {
				select {
				case out <- buffer[0]:
					buffer = buffer[1:]
				case size = <-adj:
				}
			} else {
				select {
				case elem, open := <-in:
					if open {
						buffer = append(buffer, elem)
					} else {
						for elem := range buffer {
							out <- elem
						}
						close(out)
						close(adj)
						return
					}
				case out <- buffer[0]:
					buffer = buffer[1:]
				case size = <-adj:
				}
			}
		}
	}()

	return in, out, adj
}
<|endoftext|>


<commit_before>package main

// Very simple utility which signals an event. Used to signal a docker
// daemon on Windows to dump its stacks. Usage docker-signal --pid=daemonpid

import (
	"flag"
	"fmt"
	"syscall"
	"unsafe"
)

const EVENT_MODIFY_STATUS = 0x0002

var (
	modkernel32    = syscall.NewLazyDLL("kernel32.dll")
	procOpenEvent  = modkernel32.NewProc("OpenEventW")
	procPulseEvent = modkernel32.NewProc("PulseEvent")
)

func OpenEvent(desiredAccess uint32, inheritHandle bool, name string) (handle syscall.Handle, err error) {
	namep, _ := syscall.UTF16PtrFromString(name)
	var _p2 uint32 = 0
	if inheritHandle {
		_p2 = 1
	}
	r0, _, e1 := procOpenEvent.Call(uintptr(desiredAccess), uintptr(_p2), uintptr(unsafe.Pointer(namep)))
	use(unsafe.Pointer(namep))
	handle = syscall.Handle(r0)
	if handle == syscall.InvalidHandle {
		err = e1
	}
	return
}

func PulseEvent(handle syscall.Handle) (err error) {
	r0, _, _ := procPulseEvent.Call(uintptr(handle))
	if r0 != 0 {
		err = syscall.Errno(r0)
	}
	return
}

func main() {
	var pid int
	flag.IntVar(&pid, "pid", -1, "PID of docker daemon to signal to dump stacks")
	flag.Parse()
	if pid == -1 {
		fmt.Println("Error: pid must be supplied")
		return
	}
	ev := "Global\\docker-daemon-" + fmt.Sprint(pid)
	h2, _ := OpenEvent(EVENT_MODIFY_STATUS, false, ev)
	if h2 == 0 {
		fmt.Printf("Could not open event. Check PID %d is correct and the daemon is running.\n", pid)
		return
	}
	PulseEvent(h2)
	fmt.Println("Daemon signalled successfully. Examine its output for stacks")
}

var temp unsafe.Pointer

func use(p unsafe.Pointer) {
	temp = p
}
<commit_msg>Add signal --key= override for multiple processes<commit_after>package main

// Very simple utility which signals an event. Used to signal a docker
// daemon on Windows to dump its stacks. Usage docker-signal --pid=daemonpid

import (
	"flag"
	"fmt"
	"syscall"
	"unsafe"
)

const EVENT_MODIFY_STATUS = 0x0002

var (
	modkernel32    = syscall.NewLazyDLL("kernel32.dll")
	procOpenEvent  = modkernel32.NewProc("OpenEventW")
	procPulseEvent = modkernel32.NewProc("PulseEvent")
)

func OpenEvent(desiredAccess uint32, inheritHandle bool, name string) (handle syscall.Handle, err error) {
	namep, _ := syscall.UTF16PtrFromString(name)
	var _p2 uint32 = 0
	if inheritHandle {
		_p2 = 1
	}
	r0, _, e1 := procOpenEvent.Call(uintptr(desiredAccess), uintptr(_p2), uintptr(unsafe.Pointer(namep)))
	use(unsafe.Pointer(namep))
	handle = syscall.Handle(r0)
	if handle == syscall.InvalidHandle {
		err = e1
	}
	return
}

func PulseEvent(handle syscall.Handle) (err error) {
	r0, _, _ := procPulseEvent.Call(uintptr(handle))
	if r0 != 0 {
		err = syscall.Errno(r0)
	}
	return
}

func main() {
	var pid int
	var key string
	flag.StringVar(&key, "key", "docker-daemon", "The 'key' override in 'Global\\key-pid'. docker=docker-daemon, containerd=containerd-daemon, conatinerd-runhcs-shim-v1=containerd-shim-runhcs-v1")
	flag.IntVar(&pid, "pid", -1, "PID of process to signal to dump stacks")
	flag.Parse()
	if pid == -1 {
		fmt.Println("Error: pid must be supplied")
		return
	}
	ev := fmt.Sprintf("Global\\%s-%s", key, fmt.Sprint(pid))
	h2, _ := OpenEvent(EVENT_MODIFY_STATUS, false, ev)
	if h2 == 0 {
		fmt.Printf("Could not open event. Check PID %d is correct and the daemon is running.\n", pid)
		return
	}
	PulseEvent(h2)
	fmt.Println("Daemon signalled successfully. Examine its output for stacks")
}

var temp unsafe.Pointer

func use(p unsafe.Pointer) {
	temp = p
}
<|endoftext|>


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

package translation

import (
    "appengine"
    "appengine/datastore"
    "appengine/memcache"
)

type translationInfo struct {
    UniqueId  int64             `datastore:"-" json:"uniqueId"`
    Name      string            `datastore:",noindex" json:"name"`
    ShortName string            `datastore:",noindex" json:"shortName"`
    Language  string            `json:"language"`
    BlobKey   appengine.BlobKey `datastore:",noindex" json:"blobKey"`
    Size      int64             `datastore:",noindex" json:"size"`
    Created   int64             `json:"created"`
    Modified  int64             `json:"modified"`
}

var translations []*translationInfo

func loadTranslations(c appengine.Context, forceRefresh bool) ([]*translationInfo, error) {
    if !forceRefresh {
        if len(translations) > 0 {
            return translations, nil
        }

        memcache.Gob.Get(c, "TranslationInfo", &translations)
        if len(translations) > 0 {
            return translations, nil
        }
    }

    translations, err := loadTranslationsFromDatastore(c)
    if err != nil {
        return nil, err
    }

    // updates memcache
    item := &memcache.Item{
        Key:    "TranslationInfo",
        Object: translations,
    }
    memcache.Gob.Set(c, item)

    return translations, nil
}

func loadTranslationsFromDatastore(c appengine.Context) ([]*translationInfo, error) {
    var translations []*translationInfo
    q := datastore.NewQuery("TranslationInfo")
    keys, err := q.GetAll(c, &translations)
    if err != nil {
        return nil, err
    }
    for i, t := range translations {
        t.UniqueId = keys[i].IntID()
    }
    return translations, nil
}
<commit_msg>Should lock in-memory cache.<commit_after>/*
 * Copyright (c) 2015 ZionSoft. All rights reserved.
 * Use of this source code is governed by a BSD-style license
 * that can be found in the LICENSE file.
 */

package translation

import (
    "sync"

    "appengine"
    "appengine/datastore"
    "appengine/memcache"
)

type translationInfo struct {
    UniqueId  int64             `datastore:"-" json:"uniqueId"`
    Name      string            `datastore:",noindex" json:"name"`
    ShortName string            `datastore:",noindex" json:"shortName"`
    Language  string            `json:"language"`
    BlobKey   appengine.BlobKey `datastore:",noindex" json:"blobKey"`
    Size      int64             `datastore:",noindex" json:"size"`
    Created   int64             `json:"created"`
    Modified  int64             `json:"modified"`
}

var translationCache struct {
    mu           sync.Mutex
    translations []*translationInfo
}

func loadTranslations(c appengine.Context, forceRefresh bool) ([]*translationInfo, error) {
    translationCache.mu.Lock()
    defer translationCache.mu.Unlock()

    if !forceRefresh {
        if len(translationCache.translations) > 0 {
            return translationCache.translations, nil
        }

        memcache.Gob.Get(c, "TranslationInfo", &translationCache.translations)
        if len(translationCache.translations) > 0 {
            return translationCache.translations, nil
        }
    }

    var err error
    translationCache.translations, err = loadTranslationsFromDatastore(c)
    if err != nil {
        return nil, err
    }

    // updates memcache
    item := &memcache.Item{
        Key:    "TranslationInfo",
        Object: translationCache.translations,
    }
    memcache.Gob.Set(c, item)

    return translationCache.translations, nil
}

func loadTranslationsFromDatastore(c appengine.Context) ([]*translationInfo, error) {
    var translations []*translationInfo
    q := datastore.NewQuery("TranslationInfo")
    keys, err := q.GetAll(c, &translations)
    if err != nil {
        return nil, err
    }
    for i, t := range translations {
        t.UniqueId = keys[i].IntID()
    }
    return translations, nil
}
<|endoftext|>


<commit_before>package instructions

import (
    . "jvmgo/any"
    "jvmgo/jvm/rtda"
    rtc "jvmgo/jvm/rtda/class"
)

// Store into reference array
type aastore struct {NoOperandsInstruction}
func (self *aastore) Execute(frame *rtda.Frame) {
    arrRef, index, val := popOperands(frame)
    refArr := arrRef.Fields().([]*rtc.Obj)
    checkArrIndex(index, len(refArr))
    // todo
    ref := val.(*rtc.Obj)
    refArr[index] = ref
}

// Store into byte or boolean array
type bastore struct {NoOperandsInstruction}
func (self *bastore) Execute(frame *rtda.Frame) {
    arrRef, index, val := popOperands(frame)
    byteArr := arrRef.Fields().([]int8)
    checkArrIndex(index, len(byteArr))
    byteArr[index] = int8(val.(int32))
}

// Store into char array
type castore struct {NoOperandsInstruction}
func (self *castore) Execute(frame *rtda.Frame) {
    arrRef, index, val := popOperands(frame)
    charArr := arrRef.Fields().([]uint16)
    checkArrIndex(index, len(charArr))
    charArr[index] = uint16(val.(int32))
}

// Store into double array
type dastore struct {NoOperandsInstruction}
func (self *dastore) Execute(frame *rtda.Frame) {
    arrRef, index, val := popOperands(frame)
    doubleArr := arrRef.Fields().([]float64)
    checkArrIndex(index, len(doubleArr))
    doubleArr[index] = val.(float64)
}

// Store into float array
type fastore struct {NoOperandsInstruction}
func (self *fastore) Execute(frame *rtda.Frame) {
    arrRef, index, val := popOperands(frame)
    floatArr := arrRef.Fields().([]float32)
    checkArrIndex(index, len(floatArr))
    floatArr[index] = val.(float32)
}

// Store into int array
type iastore struct {NoOperandsInstruction}
func (self *iastore) Execute(frame *rtda.Frame) {
    arrRef, index, val := popOperands(frame)
    intArr := arrRef.Fields().([]int32)
    checkArrIndex(index, len(intArr))
    intArr[index] = val.(int32)
}

// Store into long array
type lastore struct {NoOperandsInstruction}
func (self *lastore) Execute(frame *rtda.Frame) {
    arrRef, index, val := popOperands(frame)
    longArr := arrRef.Fields().([]int64)
    checkArrIndex(index, len(longArr))
    longArr[index] = val.(int64)
}

// Store into short array
type sastore struct {NoOperandsInstruction}
func (self *sastore) Execute(frame *rtda.Frame) {
    arrRef, index, val := popOperands(frame)
    shortArr := arrRef.Fields().([]int16)
    checkArrIndex(index, len(shortArr))
    shortArr[index] = int16(val.(int32))
}

func popOperands(frame *rtda.Frame) (*rtc.Obj, int, Any) {
    stack := frame.OperandStack()
    val := stack.Pop()
    index := int(stack.PopInt())
    arrRef := stack.PopRef()
    if arrRef == nil {
        // todo
        panic("NullPointerException")
    }
    return arrRef, index, val
}
<commit_msg>fix aastore<commit_after>package instructions

import (
    . "jvmgo/any"
    "jvmgo/jvm/rtda"
    rtc "jvmgo/jvm/rtda/class"
)

// Store into reference array
type aastore struct {NoOperandsInstruction}
func (self *aastore) Execute(frame *rtda.Frame) {
    arrRef, index, val := popOperands(frame)
    refArr := arrRef.Fields().([]*rtc.Obj)
    checkArrIndex(index, len(refArr))

    if val == nil {
        refArr[index] = nil
    } else {
        ref := val.(*rtc.Obj)
        refArr[index] = ref
    }
}

// Store into byte or boolean array
type bastore struct {NoOperandsInstruction}
func (self *bastore) Execute(frame *rtda.Frame) {
    arrRef, index, val := popOperands(frame)
    byteArr := arrRef.Fields().([]int8)
    checkArrIndex(index, len(byteArr))
    byteArr[index] = int8(val.(int32))
}

// Store into char array
type castore struct {NoOperandsInstruction}
func (self *castore) Execute(frame *rtda.Frame) {
    arrRef, index, val := popOperands(frame)
    charArr := arrRef.Fields().([]uint16)
    checkArrIndex(index, len(charArr))
    charArr[index] = uint16(val.(int32))
}

// Store into double array
type dastore struct {NoOperandsInstruction}
func (self *dastore) Execute(frame *rtda.Frame) {
    arrRef, index, val := popOperands(frame)
    doubleArr := arrRef.Fields().([]float64)
    checkArrIndex(index, len(doubleArr))
    doubleArr[index] = val.(float64)
}

// Store into float array
type fastore struct {NoOperandsInstruction}
func (self *fastore) Execute(frame *rtda.Frame) {
    arrRef, index, val := popOperands(frame)
    floatArr := arrRef.Fields().([]float32)
    checkArrIndex(index, len(floatArr))
    floatArr[index] = val.(float32)
}

// Store into int array
type iastore struct {NoOperandsInstruction}
func (self *iastore) Execute(frame *rtda.Frame) {
    arrRef, index, val := popOperands(frame)
    intArr := arrRef.Fields().([]int32)
    checkArrIndex(index, len(intArr))
    intArr[index] = val.(int32)
}

// Store into long array
type lastore struct {NoOperandsInstruction}
func (self *lastore) Execute(frame *rtda.Frame) {
    arrRef, index, val := popOperands(frame)
    longArr := arrRef.Fields().([]int64)
    checkArrIndex(index, len(longArr))
    longArr[index] = val.(int64)
}

// Store into short array
type sastore struct {NoOperandsInstruction}
func (self *sastore) Execute(frame *rtda.Frame) {
    arrRef, index, val := popOperands(frame)
    shortArr := arrRef.Fields().([]int16)
    checkArrIndex(index, len(shortArr))
    shortArr[index] = int16(val.(int32))
}

func popOperands(frame *rtda.Frame) (*rtc.Obj, int, Any) {
    stack := frame.OperandStack()
    val := stack.Pop()
    index := int(stack.PopInt())
    arrRef := stack.PopRef()
    if arrRef == nil {
        // todo
        panic("NullPointerException")
    }
    return arrRef, index, val
}
<|endoftext|>


<commit_before>package player

import (
	"bytes"
	"expvar"
	"log"
	"io"
	"math"
	"net"
	"os"
	"sync"

	. "chunkymonkey/entity"
	. "chunkymonkey/interfaces"
	"chunkymonkey/inventory"
	"chunkymonkey/proto"
	"chunkymonkey/slot"
	. "chunkymonkey/types"
)

var (
	expVarPlayerConnectionCount    *expvar.Int
	expVarPlayerDisconnectionCount *expvar.Int
)

const StanceNormal = 1.62

func init() {
	expVarPlayerConnectionCount = expvar.NewInt("player-connection-count")
	expVarPlayerDisconnectionCount = expvar.NewInt("player-disconnection-count")
}

type Player struct {
	Entity
	game      IGame
	conn      net.Conn
	name      string
	position  AbsXyz
	look      LookDegrees
	chunkSubs chunkSubscriptions

	cursor    slot.Slot // Item being moved by mouse cursor.
	inventory inventory.PlayerInventory

	mainQueue chan func(IPlayer)
	txQueue   chan []byte
	lock      sync.Mutex
}

func StartPlayer(game IGame, conn net.Conn, name string) {
	player := &Player{
		game:      game,
		conn:      conn,
		name:      name,
		position:  *game.GetStartPosition(),
		look:      LookDegrees{0, 0},
		mainQueue: make(chan func(IPlayer), 128),
		txQueue:   make(chan []byte, 128),
	}

	player.chunkSubs.Init(player)

	player.cursor.Init()
	player.inventory.Init(player.EntityId, player)

	game.Enqueue(func(game IGame) {
		game.AddPlayer(player)
		buf := &bytes.Buffer{}
		// TODO pass proper dimension. This is low priority, because there is
		// currently no way to update the client's dimension after login.
		proto.ServerWriteLogin(buf, player.EntityId, 0, DimensionNormal)
		proto.WriteSpawnPosition(buf, player.position.ToBlockXyz())
		player.TransmitPacket(buf.Bytes())
		player.start()
	})
}

func (player *Player) GetEntityId() EntityId {
	return player.EntityId
}

func (player *Player) GetEntity() *Entity {
	return &player.Entity
}

func (player *Player) LockedGetChunkPosition() *ChunkXz {
	player.lock.Lock()
	defer player.lock.Unlock()
	return player.position.ToChunkXz()
}

func (player *Player) IsWithin(p1, p2 *ChunkXz) bool {
	p := player.position.ToChunkXz()
	return (p.X >= p1.X && p.X <= p2.X &&
		p.Z >= p1.Z && p.Z <= p2.Z)
}

func (player *Player) GetName() string {
	return player.name
}

func (player *Player) Enqueue(f func(IPlayer)) {
	player.mainQueue <- f
}

func (player *Player) SendSpawn(writer io.Writer) (err os.Error) {
	heldSlot, _ := player.inventory.HeldItem()

	err = proto.WriteNamedEntitySpawn(
		writer,
		player.EntityId, player.name,
		player.position.ToAbsIntXyz(),
		player.look.ToLookBytes(),
		heldSlot.GetItemTypeId(),
	)
	if err != nil {
		return
	}
	return player.inventory.SendFullEquipmentUpdate(writer)
}

func (player *Player) start() {
	go player.receiveLoop()
	go player.mainLoop()
}

// Start of packet handling code
// Note: any packet handlers that could change the player state or read a
// changeable state must use player.lock

func (player *Player) PacketKeepAlive() {
}

func (player *Player) PacketChatMessage(message string) {
	player.game.Enqueue(func(game IGame) { game.SendChatMessage(message) })
}

func (player *Player) PacketEntityAction(entityId EntityId, action EntityAction) {
}

func (player *Player) PacketUseEntity(user EntityId, target EntityId, leftClick bool) {
}

func (player *Player) PacketRespawn() {
}

func (player *Player) PacketPlayer(onGround bool) {
}

func (player *Player) PacketPlayerPosition(position *AbsXyz, stance AbsCoord, onGround bool) {
	player.lock.Lock()
	defer player.lock.Unlock()

	var delta = AbsXyz{position.X - player.position.X,
		position.Y - player.position.Y,
		position.Z - player.position.Z}
	distance := math.Sqrt(float64(delta.X*delta.X + delta.Y*delta.Y + delta.Z*delta.Z))
	if distance > 10 {
		log.Printf("Discarding player position that is too far removed (%.2f, %.2f, %.2f)",
			position.X, position.Y, position.Z)
		return
	}
	player.position = *position
	player.chunkSubs.Move(position, nil)

	// TODO: Should keep track of when players enter/leave their mutual radius
	// of "awareness". I.e a client should receive a RemoveEntity packet when
	// the player walks out of range, and no longer receive WriteEntityTeleport
	// packets for them. The converse should happen when players come in range
	// of each other.

	buf := &bytes.Buffer{}
	proto.WriteEntityTeleport(
		buf,
		player.EntityId,
		player.position.ToAbsIntXyz(),
		player.look.ToLookBytes())

	player.game.Enqueue(func(game IGame) {
		game.MulticastPacket(buf.Bytes(), player)
	})
}

func (player *Player) PacketPlayerLook(look *LookDegrees, onGround bool) {
	player.lock.Lock()
	defer player.lock.Unlock()

	// TODO input validation
	player.look = *look

	buf := &bytes.Buffer{}
	proto.WriteEntityLook(buf, player.EntityId, look.ToLookBytes())

	player.game.Enqueue(func(game IGame) {
		game.MulticastPacket(buf.Bytes(), player)
	})
}

func (player *Player) PacketPlayerDigging(status DigStatus, blockLoc *BlockXyz, face Face) {
	// TODO validate that the player is actually somewhere near the block

	// TODO validate that the player has dug long enough to stop speed
	// hacking (based on block type and tool used - non-trivial).

	if face != FaceNull {
		chunkLoc, subLoc := blockLoc.ToChunkLocal()

		player.game.Enqueue(func(game IGame) {
			chunk := game.GetChunkManager().Get(chunkLoc)

			if chunk == nil {
				return
			}

			chunk.Enqueue(func(chunk IChunk) {
				chunk.DigBlock(subLoc, status)
			})
		})
	} else {
		// TODO player dropped item
	}
}

func (player *Player) PacketPlayerBlockPlacement(itemId ItemTypeId, blockLoc *BlockXyz, face Face, amount ItemCount, uses ItemData) {
	if face < FaceMinValid || face > FaceMaxValid {
		log.Printf("Player/PacketPlayerBlockPlacement: invalid face %d", face)
		return
	}

	// The position to put the block at.
	dx, dy, dz := face.GetDxyz()
	placeAtLoc := &BlockXyz{
		blockLoc.X + dx,
		blockLoc.Y + dy,
		blockLoc.Z + dz,
	}
	placeChunkLoc, _ := placeAtLoc.ToChunkLocal()

	player.lock.Lock()
	defer player.lock.Unlock()

	heldSlot, heldSlotId := player.inventory.HeldItem()

	// Make sure that it's a valid-looking block item.
	itemType := heldSlot.ItemType
	if itemType == nil || itemType.Id < ItemTypeId(BlockIdMin) || itemType.Id > ItemTypeId(BlockIdMax) {
		log.Print("Player/PacketPlayerBlockPlacement: no or non-block item held")
		return
	}

	// Take an item from the "held" slot.
	tmpSlot := &slot.Slot{nil, 0, 0}
	tmpSlot.AddOne(heldSlot)

	buf := &bytes.Buffer{}
	heldSlot.SendUpdate(buf, WindowIdInventory, heldSlotId)
	player.TransmitPacket(buf.Bytes())

	player.game.Enqueue(func(game IGame) {
		chunk := game.GetChunkManager().Get(placeChunkLoc)

		if chunk == nil {
			// Return item to player
			// Note that this can technically fail, in which case the item vanishes.
			log.Print("Player/PacketPlayerBlockPlacement: chunk not found")
			player.OfferItem(tmpSlot)
			return
		}

		chunk.Enqueue(func(chunk IChunk) {
			// Note that we tell the chunk that the block is to get placed
			// *into* to place it (rather than the block it's being attached
			// to). The chunk itself determines if this will work.
			if !chunk.PlaceBlock(blockLoc, face, BlockId(tmpSlot.ItemType.Id)) {
				// Return item to player
				// Note that this can technically fail, in which case the item vanishes.
				player.OfferItem(tmpSlot)
			}
		})
	})
}

func (player *Player) PacketHoldingChange(slotId SlotId) {
	player.lock.Lock()
	defer player.lock.Unlock()
	player.inventory.SetHolding(slotId)
}

func (player *Player) PacketEntityAnimation(entityId EntityId, animation EntityAnimation) {
}

func (player *Player) PacketUnknown0x1b(field1, field2 float32, field3, field4 bool, field5, field6 float32) {
}

func (player *Player) PacketWindowClose(windowId WindowId) {
}

func (player *Player) PacketWindowClick(windowId WindowId, slotId SlotId, rightClick bool, txId TxId, shiftClick bool, itemId ItemTypeId, amount ItemCount, uses ItemData) {

	// Note that the parameters itemId, amount and uses are all currently
	// ignored. The item(s) involved are worked out from the server-side data.

	// Determine which inventory window is involved.
	// TODO support for more windows
	var clickedWindow inventory.IWindow
	switch windowId {
	case WindowIdInventory:
		clickedWindow = &player.inventory
	default:
		// If this happens, then it's likely that either a client is trying to
		// do something unusual, or that we haven't yet implemented something.
		log.Printf(
			"Warning: ignored window click on unknown window ID %d",
			windowId)
	}

	buf := &bytes.Buffer{}
	accepted := false

	if clickedWindow != nil {
		player.lock.Lock()
		defer player.lock.Unlock()
		accepted = clickedWindow.Click(slotId, &player.cursor, rightClick, shiftClick)

		// We send slot updates in case we have custom max counts that differ
		// from the client's own model.
		player.cursor.SendUpdate(buf, WindowIdCursor, SlotIdCursor)
	}

	// Inform client of operation status.
	proto.WriteWindowTransaction(buf, windowId, txId, accepted)

	player.TransmitPacket(buf.Bytes())
}

func (player *Player) PacketWindowTransaction(windowId WindowId, txId TxId, accepted bool) {
	// TODO investigate when this packet is sent from the client and what it
	// means when it does get sent.
}

func (player *Player) PacketSignUpdate(position *BlockXyz, lines [4]string) {
}

func (player *Player) PacketDisconnect(reason string) {
	log.Printf("Player %s disconnected reason=%s", player.name, reason)
	player.game.Enqueue(func(game IGame) {
		game.RemovePlayer(player)
		player.txQueue <- nil
		player.conn.Close()
	})
}

func (player *Player) receiveLoop() {
	for {
		err := proto.ServerReadPacket(player.conn, player)
		if err != nil {
			if err != os.EOF {
				log.Print("ReceiveLoop failed: ", err.String())
			}
			return
		}
	}
}

// End of packet handling code

func (player *Player) runQueuedCall(f func(IPlayer)) {
	player.lock.Lock()
	defer player.lock.Unlock()
	f(player)
}

func (player *Player) mainLoop() {
	expVarPlayerConnectionCount.Add(1)
	defer func() {
		expVarPlayerDisconnectionCount.Add(1)
		player.chunkSubs.clear()
	}()

	player.postLogin()

	for {
		select {
		case f := <-player.mainQueue:
			player.runQueuedCall(f)
		case bs := <-player.txQueue:
			// TODO move txQueue handling to another goroutine to avoid
			// needless deadlocking with player.Lock.
			if bs == nil {
				return // txQueue closed
			}

			_, err := player.conn.Write(bs)
			if err != nil {
				if err != os.EOF {
					log.Print("TransmitLoop failed: ", err.String())
				}
				return
			}
		}
	}
}

func (player *Player) TransmitPacket(packet []byte) {
	if packet == nil {
		return // skip empty packets
	}
	player.txQueue <- packet
}

// Used to receive items picked up from chunks.
func (player *Player) OfferItem(item *slot.Slot) {
	player.lock.Lock()
	defer player.lock.Unlock()

	player.inventory.PutItem(item)

	return
}

// Blocks until essential login packets have been transmitted.
func (player *Player) postLogin() {
	nearbySent := func() {
		player.lock.Lock()
		defer player.lock.Unlock()

		// Send player start position etc.
		buf := &bytes.Buffer{}
		proto.ServerWritePlayerPositionLook(
			buf, &player.position, &player.look,
			player.position.Y+StanceNormal, false)

		player.inventory.WriteWindowItems(buf)

		// FIXME: This could potentially deadlock with player.lock being held
		// if the txQueue is full.
		player.TransmitPacket(buf.Bytes())
	}

	player.chunkSubs.Move(&player.position, nearbySent)
}
<commit_msg>player: Separated txQueue and mainQueue into separate goroutines.<commit_after>package player

import (
	"bytes"
	"expvar"
	"log"
	"io"
	"math"
	"net"
	"os"
	"sync"

	. "chunkymonkey/entity"
	. "chunkymonkey/interfaces"
	"chunkymonkey/inventory"
	"chunkymonkey/proto"
	"chunkymonkey/slot"
	. "chunkymonkey/types"
)

var (
	expVarPlayerConnectionCount    *expvar.Int
	expVarPlayerDisconnectionCount *expvar.Int
)

const StanceNormal = 1.62

func init() {
	expVarPlayerConnectionCount = expvar.NewInt("player-connection-count")
	expVarPlayerDisconnectionCount = expvar.NewInt("player-disconnection-count")
}

type Player struct {
	Entity
	game      IGame
	conn      net.Conn
	name      string
	position  AbsXyz
	look      LookDegrees
	chunkSubs chunkSubscriptions

	cursor    slot.Slot // Item being moved by mouse cursor.
	inventory inventory.PlayerInventory

	mainQueue chan func(IPlayer)
	txQueue   chan []byte
	lock      sync.Mutex
}

func StartPlayer(game IGame, conn net.Conn, name string) {
	player := &Player{
		game:      game,
		conn:      conn,
		name:      name,
		position:  *game.GetStartPosition(),
		look:      LookDegrees{0, 0},
		mainQueue: make(chan func(IPlayer), 128),
		txQueue:   make(chan []byte, 128),
	}

	player.chunkSubs.Init(player)

	player.cursor.Init()
	player.inventory.Init(player.EntityId, player)

	game.Enqueue(func(game IGame) {
		game.AddPlayer(player)
		buf := &bytes.Buffer{}
		// TODO pass proper dimension. This is low priority, because there is
		// currently no way to update the client's dimension after login.
		proto.ServerWriteLogin(buf, player.EntityId, 0, DimensionNormal)
		proto.WriteSpawnPosition(buf, player.position.ToBlockXyz())
		player.TransmitPacket(buf.Bytes())
		player.start()
	})
}

func (player *Player) GetEntityId() EntityId {
	return player.EntityId
}

func (player *Player) GetEntity() *Entity {
	return &player.Entity
}

func (player *Player) LockedGetChunkPosition() *ChunkXz {
	player.lock.Lock()
	defer player.lock.Unlock()
	return player.position.ToChunkXz()
}

func (player *Player) IsWithin(p1, p2 *ChunkXz) bool {
	p := player.position.ToChunkXz()
	return (p.X >= p1.X && p.X <= p2.X &&
		p.Z >= p1.Z && p.Z <= p2.Z)
}

func (player *Player) GetName() string {
	return player.name
}

func (player *Player) SendSpawn(writer io.Writer) (err os.Error) {
	heldSlot, _ := player.inventory.HeldItem()

	err = proto.WriteNamedEntitySpawn(
		writer,
		player.EntityId, player.name,
		player.position.ToAbsIntXyz(),
		player.look.ToLookBytes(),
		heldSlot.GetItemTypeId(),
	)
	if err != nil {
		return
	}
	return player.inventory.SendFullEquipmentUpdate(writer)
}

func (player *Player) start() {
	go player.receiveLoop()
	go player.transmitLoop()
	go player.mainLoop()
}

// Start of packet handling code
// Note: any packet handlers that could change the player state or read a
// changeable state must use player.lock

func (player *Player) PacketKeepAlive() {
}

func (player *Player) PacketChatMessage(message string) {
	player.game.Enqueue(func(game IGame) { game.SendChatMessage(message) })
}

func (player *Player) PacketEntityAction(entityId EntityId, action EntityAction) {
}

func (player *Player) PacketUseEntity(user EntityId, target EntityId, leftClick bool) {
}

func (player *Player) PacketRespawn() {
}

func (player *Player) PacketPlayer(onGround bool) {
}

func (player *Player) PacketPlayerPosition(position *AbsXyz, stance AbsCoord, onGround bool) {
	player.lock.Lock()
	defer player.lock.Unlock()

	var delta = AbsXyz{position.X - player.position.X,
		position.Y - player.position.Y,
		position.Z - player.position.Z}
	distance := math.Sqrt(float64(delta.X*delta.X + delta.Y*delta.Y + delta.Z*delta.Z))
	if distance > 10 {
		log.Printf("Discarding player position that is too far removed (%.2f, %.2f, %.2f)",
			position.X, position.Y, position.Z)
		return
	}
	player.position = *position
	player.chunkSubs.Move(position, nil)

	// TODO: Should keep track of when players enter/leave their mutual radius
	// of "awareness". I.e a client should receive a RemoveEntity packet when
	// the player walks out of range, and no longer receive WriteEntityTeleport
	// packets for them. The converse should happen when players come in range
	// of each other.

	buf := &bytes.Buffer{}
	proto.WriteEntityTeleport(
		buf,
		player.EntityId,
		player.position.ToAbsIntXyz(),
		player.look.ToLookBytes())

	player.game.Enqueue(func(game IGame) {
		game.MulticastPacket(buf.Bytes(), player)
	})
}

func (player *Player) PacketPlayerLook(look *LookDegrees, onGround bool) {
	player.lock.Lock()
	defer player.lock.Unlock()

	// TODO input validation
	player.look = *look

	buf := &bytes.Buffer{}
	proto.WriteEntityLook(buf, player.EntityId, look.ToLookBytes())

	player.game.Enqueue(func(game IGame) {
		game.MulticastPacket(buf.Bytes(), player)
	})
}

func (player *Player) PacketPlayerDigging(status DigStatus, blockLoc *BlockXyz, face Face) {
	// TODO validate that the player is actually somewhere near the block

	// TODO validate that the player has dug long enough to stop speed
	// hacking (based on block type and tool used - non-trivial).

	if face != FaceNull {
		chunkLoc, subLoc := blockLoc.ToChunkLocal()

		player.game.Enqueue(func(game IGame) {
			chunk := game.GetChunkManager().Get(chunkLoc)

			if chunk == nil {
				return
			}

			chunk.Enqueue(func(chunk IChunk) {
				chunk.DigBlock(subLoc, status)
			})
		})
	} else {
		// TODO player dropped item
	}
}

func (player *Player) PacketPlayerBlockPlacement(itemId ItemTypeId, blockLoc *BlockXyz, face Face, amount ItemCount, uses ItemData) {
	if face < FaceMinValid || face > FaceMaxValid {
		log.Printf("Player/PacketPlayerBlockPlacement: invalid face %d", face)
		return
	}

	// The position to put the block at.
	dx, dy, dz := face.GetDxyz()
	placeAtLoc := &BlockXyz{
		blockLoc.X + dx,
		blockLoc.Y + dy,
		blockLoc.Z + dz,
	}
	placeChunkLoc, _ := placeAtLoc.ToChunkLocal()

	player.lock.Lock()
	defer player.lock.Unlock()

	heldSlot, heldSlotId := player.inventory.HeldItem()

	// Make sure that it's a valid-looking block item.
	itemType := heldSlot.ItemType
	if itemType == nil || itemType.Id < ItemTypeId(BlockIdMin) || itemType.Id > ItemTypeId(BlockIdMax) {
		log.Print("Player/PacketPlayerBlockPlacement: no or non-block item held")
		return
	}

	// Take an item from the "held" slot.
	tmpSlot := &slot.Slot{nil, 0, 0}
	tmpSlot.AddOne(heldSlot)

	buf := &bytes.Buffer{}
	heldSlot.SendUpdate(buf, WindowIdInventory, heldSlotId)
	player.TransmitPacket(buf.Bytes())

	player.game.Enqueue(func(game IGame) {
		chunk := game.GetChunkManager().Get(placeChunkLoc)

		if chunk == nil {
			// Return item to player
			// Note that this can technically fail, in which case the item vanishes.
			log.Print("Player/PacketPlayerBlockPlacement: chunk not found")
			player.OfferItem(tmpSlot)
			return
		}

		chunk.Enqueue(func(chunk IChunk) {
			// Note that we tell the chunk that the block is to get placed
			// *into* to place it (rather than the block it's being attached
			// to). The chunk itself determines if this will work.
			if !chunk.PlaceBlock(blockLoc, face, BlockId(tmpSlot.ItemType.Id)) {
				// Return item to player
				// Note that this can technically fail, in which case the item vanishes.
				player.OfferItem(tmpSlot)
			}
		})
	})
}

func (player *Player) PacketHoldingChange(slotId SlotId) {
	player.lock.Lock()
	defer player.lock.Unlock()
	player.inventory.SetHolding(slotId)
}

func (player *Player) PacketEntityAnimation(entityId EntityId, animation EntityAnimation) {
}

func (player *Player) PacketUnknown0x1b(field1, field2 float32, field3, field4 bool, field5, field6 float32) {
}

func (player *Player) PacketWindowClose(windowId WindowId) {
}

func (player *Player) PacketWindowClick(windowId WindowId, slotId SlotId, rightClick bool, txId TxId, shiftClick bool, itemId ItemTypeId, amount ItemCount, uses ItemData) {

	// Note that the parameters itemId, amount and uses are all currently
	// ignored. The item(s) involved are worked out from the server-side data.

	// Determine which inventory window is involved.
	// TODO support for more windows
	var clickedWindow inventory.IWindow
	switch windowId {
	case WindowIdInventory:
		clickedWindow = &player.inventory
	default:
		// If this happens, then it's likely that either a client is trying to
		// do something unusual, or that we haven't yet implemented something.
		log.Printf(
			"Warning: ignored window click on unknown window ID %d",
			windowId)
	}

	buf := &bytes.Buffer{}
	accepted := false

	if clickedWindow != nil {
		player.lock.Lock()
		defer player.lock.Unlock()
		accepted = clickedWindow.Click(slotId, &player.cursor, rightClick, shiftClick)

		// We send slot updates in case we have custom max counts that differ
		// from the client's own model.
		player.cursor.SendUpdate(buf, WindowIdCursor, SlotIdCursor)
	}

	// Inform client of operation status.
	proto.WriteWindowTransaction(buf, windowId, txId, accepted)

	player.TransmitPacket(buf.Bytes())
}

func (player *Player) PacketWindowTransaction(windowId WindowId, txId TxId, accepted bool) {
	// TODO investigate when this packet is sent from the client and what it
	// means when it does get sent.
}

func (player *Player) PacketSignUpdate(position *BlockXyz, lines [4]string) {
}

func (player *Player) PacketDisconnect(reason string) {
	log.Printf("Player %s disconnected reason=%s", player.name, reason)
	player.game.Enqueue(func(game IGame) {
		game.RemovePlayer(player)
	})
	player.txQueue <- nil
	player.mainQueue <- nil
	player.conn.Close()
}

func (player *Player) receiveLoop() {
	for {
		err := proto.ServerReadPacket(player.conn, player)
		if err != nil {
			if err != os.EOF {
				log.Print("ReceiveLoop failed: ", err.String())
			}
			return
		}
	}
}

// End of packet handling code

func (player *Player) transmitLoop() {
	for {
		bs := <-player.txQueue

		if bs == nil {
			return // txQueue closed
		}

		_, err := player.conn.Write(bs)
		if err != nil {
			if err != os.EOF {
				log.Print("TransmitLoop failed: ", err.String())
			}
			return
		}
	}
}

func (player *Player) TransmitPacket(packet []byte) {
	if packet == nil {
		return // skip empty packets
	}
	player.txQueue <- packet
}

func (player *Player) runQueuedCall(f func(IPlayer)) {
	player.lock.Lock()
	defer player.lock.Unlock()
	f(player)
}

func (player *Player) mainLoop() {
	expVarPlayerConnectionCount.Add(1)
	defer func() {
		expVarPlayerDisconnectionCount.Add(1)
		player.chunkSubs.clear()
	}()

	player.postLogin()

	for {
		f := <-player.mainQueue
		if f == nil {
			return
		}
		player.runQueuedCall(f)
	}
}

func (player *Player) Enqueue(f func(IPlayer)) {
	if f == nil {
		return
	}
	player.mainQueue <- f
}

// Used to receive items picked up from chunks.
func (player *Player) OfferItem(item *slot.Slot) {
	player.lock.Lock()
	defer player.lock.Unlock()

	player.inventory.PutItem(item)

	return
}

// Blocks until essential login packets have been transmitted.
func (player *Player) postLogin() {
	nearbySent := func() {
		player.lock.Lock()
		defer player.lock.Unlock()

		// Send player start position etc.
		buf := &bytes.Buffer{}
		proto.ServerWritePlayerPositionLook(
			buf, &player.position, &player.look,
			player.position.Y+StanceNormal, false)

		player.inventory.WriteWindowItems(buf)

		player.TransmitPacket(buf.Bytes())
	}

	player.chunkSubs.Move(&player.position, nearbySent)
}
<|endoftext|>


<commit_before>package comb

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestNotChar(t *testing.T) {
	s := NewState("a")
	x, err := s.NotChar(' ')()
	assert.Equal(t, 'a', x)
	assert.Equal(t, nil, err)
}

func TestNotCharFail(t *testing.T) {
	s := NewState(" ")
	x, err := s.NotChar(' ')()
	assert.Equal(t, nil, x)
	assert.NotEqual(t, nil, err)
}

func TestMany(t *testing.T) {
	for _, str := range []string{"", "  "} {
		s := NewState(str)
		x, err := s.Many(s.Char(' '))()

		t.Logf("%#v", x)

		assert.NotEqual(t, nil, x)
		assert.Equal(t, nil, err)
	}
}

func TestManyFail(t *testing.T) {
	for _, str := range []string{"="} {
		s := NewState(str)
		x, err := s.Exhaust(s.Many(func() (interface{}, error) {
			x, err := s.String("=")()

			if err != nil {
				return nil, err
			}

			if x.(string) == "=" {
				return nil, fmt.Errorf("Invalid word")
			}

			return x, nil
		}))()

		t.Logf("%#v", x)

		assert.Equal(t, nil, x)
		assert.NotEqual(t, nil, err)
	}
}

func testMany1Space(str string) (interface{}, error) {
	s := NewState(str)
	return s.Many1(s.Char(' '))()
}

func TestMany1(t *testing.T) {
	x, err := testMany1Space(" ")

	t.Logf("%#v", x)

	assert.NotEqual(t, nil, x)
	assert.Equal(t, nil, err)
}

func TestXFailMany1(t *testing.T) {
	x, err := testMany1Space("")

	t.Log(err)

	assert.Equal(t, nil, x)
	assert.NotEqual(t, nil, err)
}

func TestMany1Nest(t *testing.T) {
	s := NewState("    ")
	x, err := s.Many1(s.Many1(s.Char(' ')))()

	t.Logf("%#v", x)

	assert.NotEqual(t, nil, x)
	assert.Equal(t, nil, err)
}

func testOr(str string) (interface{}, error) {
	s := NewState(str)
	return s.Or(s.Char('a'), s.Char('b'))()
}

func TestOr(t *testing.T) {
	for _, str := range []string{"a", "b"} {
		x, err := testOr(str)

		t.Logf("%#v", x)

		assert.NotEqual(t, nil, x)
		assert.Equal(t, nil, err)
	}
}

func TestXFailOr(t *testing.T) {
	x, err := testOr("c")

	t.Log(err)

	assert.Equal(t, nil, x)
	assert.NotEqual(t, nil, err)
}

func TestMaybeSuccess(t *testing.T) {
	s := NewState("foo")
	x, err := s.Maybe(s.String("foo"))()

	t.Log(x)

	assert.Equal(t, "foo", x)
	assert.Equal(t, nil, err)
}

func TestMaybeFailure(t *testing.T) {
	s := NewState("bar")
	x, err := s.Maybe(s.String("foo"))()

	t.Log(x)

	assert.Equal(t, nil, x)
	assert.Equal(t, nil, err)
}

func TestExhaustWithErroneousParser(t *testing.T) {
	s := NewState("")
	_, err := s.Exhaust(s.String("foo"))()
	assert.NotEqual(t, nil, err)
}

func TestStringify(t *testing.T) {
	str := "foo"
	s := NewState(str)
	x, err := s.Exhaust(s.Stringify(s.And(s.String(str))))()
	assert.Equal(t, str, x)
	assert.Equal(t, nil, err)
}

func TestStringifyFail(t *testing.T) {
	defer func() {
		if r := recover(); r == nil {
			t.Fail()
		}
	}()

	stringify(42)
}

func TestVoid(t *testing.T) {
	s := NewState("foo")
	x, err := s.Void(s.String("foo"))()
	assert.Equal(t, nil, x)
	assert.Equal(t, nil, err)
}
<commit_msg>Test lazy combinator<commit_after>package comb

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestNotChar(t *testing.T) {
	s := NewState("a")
	x, err := s.NotChar(' ')()
	assert.Equal(t, 'a', x)
	assert.Equal(t, nil, err)
}

func TestNotCharFail(t *testing.T) {
	s := NewState(" ")
	x, err := s.NotChar(' ')()
	assert.Equal(t, nil, x)
	assert.NotEqual(t, nil, err)
}

func TestMany(t *testing.T) {
	for _, str := range []string{"", "  "} {
		s := NewState(str)
		x, err := s.Many(s.Char(' '))()

		t.Logf("%#v", x)

		assert.NotEqual(t, nil, x)
		assert.Equal(t, nil, err)
	}
}

func TestManyFail(t *testing.T) {
	for _, str := range []string{"="} {
		s := NewState(str)
		x, err := s.Exhaust(s.Many(func() (interface{}, error) {
			x, err := s.String("=")()

			if err != nil {
				return nil, err
			}

			if x.(string) == "=" {
				return nil, fmt.Errorf("Invalid word")
			}

			return x, nil
		}))()

		t.Logf("%#v", x)

		assert.Equal(t, nil, x)
		assert.NotEqual(t, nil, err)
	}
}

func testMany1Space(str string) (interface{}, error) {
	s := NewState(str)
	return s.Many1(s.Char(' '))()
}

func TestMany1(t *testing.T) {
	x, err := testMany1Space(" ")

	t.Logf("%#v", x)

	assert.NotEqual(t, nil, x)
	assert.Equal(t, nil, err)
}

func TestXFailMany1(t *testing.T) {
	x, err := testMany1Space("")

	t.Log(err)

	assert.Equal(t, nil, x)
	assert.NotEqual(t, nil, err)
}

func TestMany1Nest(t *testing.T) {
	s := NewState("    ")
	x, err := s.Many1(s.Many1(s.Char(' ')))()

	t.Logf("%#v", x)

	assert.NotEqual(t, nil, x)
	assert.Equal(t, nil, err)
}

func testOr(str string) (interface{}, error) {
	s := NewState(str)
	return s.Or(s.Char('a'), s.Char('b'))()
}

func TestOr(t *testing.T) {
	for _, str := range []string{"a", "b"} {
		x, err := testOr(str)

		t.Logf("%#v", x)

		assert.NotEqual(t, nil, x)
		assert.Equal(t, nil, err)
	}
}

func TestXFailOr(t *testing.T) {
	x, err := testOr("c")

	t.Log(err)

	assert.Equal(t, nil, x)
	assert.NotEqual(t, nil, err)
}

func TestMaybeSuccess(t *testing.T) {
	s := NewState("foo")
	x, err := s.Maybe(s.String("foo"))()

	t.Log(x)

	assert.Equal(t, "foo", x)
	assert.Equal(t, nil, err)
}

func TestMaybeFailure(t *testing.T) {
	s := NewState("bar")
	x, err := s.Maybe(s.String("foo"))()

	t.Log(x)

	assert.Equal(t, nil, x)
	assert.Equal(t, nil, err)
}

func TestExhaustWithErroneousParser(t *testing.T) {
	s := NewState("")
	_, err := s.Exhaust(s.String("foo"))()
	assert.NotEqual(t, nil, err)
}

func TestStringify(t *testing.T) {
	str := "foo"
	s := NewState(str)
	x, err := s.Exhaust(s.Stringify(s.And(s.String(str))))()
	assert.Equal(t, str, x)
	assert.Equal(t, nil, err)
}

func TestStringifyFail(t *testing.T) {
	defer func() {
		if r := recover(); r == nil {
			t.Fail()
		}
	}()

	stringify(42)
}

func TestLazy(t *testing.T) {
	s := NewState("foo")
	x, err := s.Lazy(func() Parser { return s.String("foo") })()
	assert.Equal(t, "foo", x)
	assert.Equal(t, nil, err)
}

func TestVoid(t *testing.T) {
	s := NewState("foo")
	x, err := s.Void(s.String("foo"))()
	assert.Equal(t, nil, x)
	assert.Equal(t, nil, err)
}
<|endoftext|>


<commit_before>package ingest

import (
	"io"
	"strings"

	"compress/bzip2"
	"encoding/json"

	"log"

	"strconv"

	"github.com/RedisLabs/RediSearchBenchmark/index"
)

type timestamp int64

func (t *timestamp) UnmarshalJSON(b []byte) (err error) {
	s := strings.Trim(string(b), "\"")
	var i int64
	if i, err = strconv.ParseInt(s, 10, 64); err == nil {
		*t = timestamp(i)
	}

	return err
}

type redditDocument struct {
	Author     string    `json:"author"`
	Body       string    `json:"body"`
	Created    timestamp `json:"created_utc"`
	Id         string    `json:"id"`
	Score      int64     `json:"score"`
	Ups        int64     `json:"ups"`
	Downs      int64     `json:"downs"`
	Subreddit  string    `json:"subreddit"`
	UvoteRatio float32   `json:"upvote_ratio"`
}

type RedditReader struct{}

func (rr *RedditReader) Read(r io.Reader, ch chan index.Document) error {
	log.Println("Reddit reader opening", r)
	bz := bzip2.NewReader(r)
	jr := json.NewDecoder(bz)

	var rd redditDocument

	//go func() {
	var err error

	for err != io.EOF {

		if err := jr.Decode(&rd); err != nil {
			log.Printf("Error decoding json: %s", err)
			break
		}
		doc := index.NewDocument(rd.Id, float32(rd.Score)).
			Set("body", rd.Body).
			Set("author", rd.Author).
			Set("sub", rd.Subreddit).
			Set("date", int64(rd.Created)/86400).
			Set("ups", rd.Ups)

		ch <- doc
	}
	//close(ch)
	//}()
	return nil
}
<commit_msg>do not index ups<commit_after>package ingest

import (
	"io"
	"strings"

	"compress/bzip2"
	"encoding/json"

	"log"

	"strconv"

	"github.com/RedisLabs/RediSearchBenchmark/index"
)

type timestamp int64

func (t *timestamp) UnmarshalJSON(b []byte) (err error) {
	s := strings.Trim(string(b), "\"")
	var i int64
	if i, err = strconv.ParseInt(s, 10, 64); err == nil {
		*t = timestamp(i)
	}

	return err
}

type redditDocument struct {
	Author     string    `json:"author"`
	Body       string    `json:"body"`
	Created    timestamp `json:"created_utc"`
	Id         string    `json:"id"`
	Score      int64     `json:"score"`
	Ups        int64     `json:"ups"`
	Downs      int64     `json:"downs"`
	Subreddit  string    `json:"subreddit"`
	UvoteRatio float32   `json:"upvote_ratio"`
}

type RedditReader struct{}

func (rr *RedditReader) Read(r io.Reader, ch chan index.Document) error {
	log.Println("Reddit reader opening", r)
	bz := bzip2.NewReader(r)
	jr := json.NewDecoder(bz)

	var rd redditDocument

	//go func() {
	var err error

	for err != io.EOF {

		if err := jr.Decode(&rd); err != nil {
			log.Printf("Error decoding json: %s", err)
			break
		}
		doc := index.NewDocument(rd.Id, float32(rd.Score)).
			Set("body", rd.Body).
			Set("author", rd.Author).
			Set("sub", rd.Subreddit).
			Set("date", int64(rd.Created)/86400)
			//Set("ups", rd.Ups)

		ch <- doc
	}
	//close(ch)
	//}()
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"crypto/sha256"
	"crypto/sha512"
	"os"
	"io"
	"hash"
	"encoding/hex"
)

func verifyChecksumOfReleaseAsset(assetPath, checksum, algorithm string) *FetchError {
	computedChecksum, err := computeChecksum(assetPath, algorithm)
	if err != nil {
		return newError(ERROR_WHILE_COMPUTING_CHECKSUM, err.Error())
	}
	if computedChecksum != checksum {
		return newError(CHECKSUM_DOES_NOT_MATCH, fmt.Sprintf("Expected to receive checksum value %s, but instead got %s for Release Asset at %s", computedChecksum, checksum, assetPath))
	}

	fmt.Printf("Checksum matches!")

	return nil
}

func computeChecksum(filePath string, algorithm string) (string, error) {
	var checksum string

	file, err := os.Open(filePath)
	if err != nil {
		return checksum, err
	}
	defer file.Close()

	switch algorithm {
	case "sha256":
		fmt.Printf("Computing checksum of release asset using SHA256\n")
		hasher := sha256.New()
		if _, err := io.Copy(hasher, file); err != nil {
			return checksum, err
		}

		checksum = hasherToString(hasher)
	case "sha512":
		fmt.Printf("Computing checksum of release asset using SHA512\n")
		hasher := sha512.New()
		if _, err := io.Copy(hasher, file); err != nil {
			return checksum, err
		}

		checksum = hasherToString(hasher)
	default:
		return checksum, fmt.Errorf("The checksum algorithm \"%s\" is not supported", algorithm)
	}

	return checksum, nil
}

// Convert a hasher instance (the common interface used by all Golang hashing functions) to the string value of that hasher
func hasherToString(hasher hash.Hash) string {
	return hex.EncodeToString(hasher.Sum(nil))
}<commit_msg>Add better error message.<commit_after>package main

import (
	"fmt"
	"crypto/sha256"
	"crypto/sha512"
	"os"
	"io"
	"hash"
	"encoding/hex"
)

func verifyChecksumOfReleaseAsset(assetPath, checksum, algorithm string) *FetchError {
	computedChecksum, err := computeChecksum(assetPath, algorithm)
	if err != nil {
		return newError(ERROR_WHILE_COMPUTING_CHECKSUM, err.Error())
	}
	if computedChecksum != checksum {
		return newError(CHECKSUM_DOES_NOT_MATCH, fmt.Sprintf("Expected to receive checksum value %s, but instead got %s for Release Asset at %s. This means that either you are using the wrong checksum value in your call to fetch (e.g., did you update the version of the module you're installing but not the checksum?) or that someone has replaced the asset with a potentially dangerous one and you should be very careful about proceeding.", computedChecksum, checksum, assetPath))
	}

	fmt.Printf("Checksum matches!")

	return nil
}

func computeChecksum(filePath string, algorithm string) (string, error) {
	var checksum string

	file, err := os.Open(filePath)
	if err != nil {
		return checksum, err
	}
	defer file.Close()

	switch algorithm {
	case "sha256":
		fmt.Printf("Computing checksum of release asset using SHA256\n")
		hasher := sha256.New()
		if _, err := io.Copy(hasher, file); err != nil {
			return checksum, err
		}

		checksum = hasherToString(hasher)
	case "sha512":
		fmt.Printf("Computing checksum of release asset using SHA512\n")
		hasher := sha512.New()
		if _, err := io.Copy(hasher, file); err != nil {
			return checksum, err
		}

		checksum = hasherToString(hasher)
	default:
		return checksum, fmt.Errorf("The checksum algorithm \"%s\" is not supported", algorithm)
	}

	return checksum, nil
}

// Convert a hasher instance (the common interface used by all Golang hashing functions) to the string value of that hasher
func hasherToString(hasher hash.Hash) string {
	return hex.EncodeToString(hasher.Sum(nil))
}<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package aggregator

import (
	"crypto/sha512"
	"fmt"
	"net/http"
	"strings"

	utiljson "k8s.io/apimachinery/pkg/util/json"
	"k8s.io/apiserver/pkg/authentication/user"
	"k8s.io/apiserver/pkg/endpoints/request"
	"k8s.io/kube-openapi/pkg/validation/spec"
)

// Downloader is the OpenAPI downloader type. It will try to download spec from /openapi/v2 or /swagger.json endpoint.
type Downloader struct {
}

// NewDownloader creates a new OpenAPI Downloader.
func NewDownloader() Downloader {
	return Downloader{}
}

func (s *Downloader) handlerWithUser(handler http.Handler, info user.Info) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		req = req.WithContext(request.WithUser(req.Context(), info))
		handler.ServeHTTP(w, req)
	})
}

func etagFor(data []byte) string {
	return fmt.Sprintf("%s%X\"", locallyGeneratedEtagPrefix, sha512.Sum512(data))
}

// Download downloads openAPI spec from /openapi/v2 endpoint of the given handler.
// httpStatus is only valid if err == nil
func (s *Downloader) Download(handler http.Handler, etag string) (returnSpec *spec.Swagger, newEtag string, httpStatus int, err error) {
	handler = s.handlerWithUser(handler, &user.DefaultInfo{Name: aggregatorUser})
	handler = http.TimeoutHandler(handler, specDownloadTimeout, "request timed out")

	req, err := http.NewRequest("GET", "/openapi/v2", nil)
	if err != nil {
		return nil, "", 0, err
	}
	req.Header.Add("Accept", "application/json")

	// Only pass eTag if it is not generated locally
	if len(etag) > 0 && !strings.HasPrefix(etag, locallyGeneratedEtagPrefix) {
		req.Header.Add("If-None-Match", etag)
	}

	writer := newInMemoryResponseWriter()
	handler.ServeHTTP(writer, req)

	switch writer.respCode {
	case http.StatusNotModified:
		if len(etag) == 0 {
			return nil, etag, http.StatusNotModified, fmt.Errorf("http.StatusNotModified is not allowed in absence of etag")
		}
		return nil, etag, http.StatusNotModified, nil
	case http.StatusNotFound:
		// Gracefully skip 404, assuming the server won't provide any spec
		return nil, "", http.StatusNotFound, nil
	case http.StatusOK:
		openAPISpec := &spec.Swagger{}
		if err := utiljson.Unmarshal(writer.data, openAPISpec); err != nil {
			return nil, "", 0, err
		}
		newEtag = writer.Header().Get("Etag")
		if len(newEtag) == 0 {
			newEtag = etagFor(writer.data)
			if len(etag) > 0 && strings.HasPrefix(etag, locallyGeneratedEtagPrefix) {
				// The function call with an etag and server does not report an etag.
				// That means this server does not support etag and the etag that passed
				// to the function generated previously by us. Just compare etags and
				// return StatusNotModified if they are the same.
				if etag == newEtag {
					return nil, etag, http.StatusNotModified, nil
				}
			}
		}
		return openAPISpec, newEtag, http.StatusOK, nil
	default:
		return nil, "", 0, fmt.Errorf("failed to retrieve openAPI spec, http error: %s", writer.String())
	}
}

// inMemoryResponseWriter is a http.Writer that keep the response in memory.
type inMemoryResponseWriter struct {
	writeHeaderCalled bool
	header            http.Header
	respCode          int
	data              []byte
}

func newInMemoryResponseWriter() *inMemoryResponseWriter {
	return &inMemoryResponseWriter{header: http.Header{}}
}

func (r *inMemoryResponseWriter) Header() http.Header {
	return r.header
}

func (r *inMemoryResponseWriter) WriteHeader(code int) {
	r.writeHeaderCalled = true
	r.respCode = code
}

func (r *inMemoryResponseWriter) Write(in []byte) (int, error) {
	if !r.writeHeaderCalled {
		r.WriteHeader(http.StatusOK)
	}
	r.data = append(r.data, in...)
	return len(in), nil
}

func (r *inMemoryResponseWriter) String() string {
	s := fmt.Sprintf("ResponseCode: %d", r.respCode)
	if r.data != nil {
		s += fmt.Sprintf(", Body: %s", string(r.data))
	}
	if r.header != nil {
		s += fmt.Sprintf(", Header: %s", r.header)
	}
	return s
}
<commit_msg>Use Swagger#UnmarshalJSON rather than json.Unmarshal<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package aggregator

import (
	"crypto/sha512"
	"fmt"
	"net/http"
	"strings"

	"k8s.io/apiserver/pkg/authentication/user"
	"k8s.io/apiserver/pkg/endpoints/request"
	"k8s.io/kube-openapi/pkg/validation/spec"
)

// Downloader is the OpenAPI downloader type. It will try to download spec from /openapi/v2 or /swagger.json endpoint.
type Downloader struct {
}

// NewDownloader creates a new OpenAPI Downloader.
func NewDownloader() Downloader {
	return Downloader{}
}

func (s *Downloader) handlerWithUser(handler http.Handler, info user.Info) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		req = req.WithContext(request.WithUser(req.Context(), info))
		handler.ServeHTTP(w, req)
	})
}

func etagFor(data []byte) string {
	return fmt.Sprintf("%s%X\"", locallyGeneratedEtagPrefix, sha512.Sum512(data))
}

// Download downloads openAPI spec from /openapi/v2 endpoint of the given handler.
// httpStatus is only valid if err == nil
func (s *Downloader) Download(handler http.Handler, etag string) (returnSpec *spec.Swagger, newEtag string, httpStatus int, err error) {
	handler = s.handlerWithUser(handler, &user.DefaultInfo{Name: aggregatorUser})
	handler = http.TimeoutHandler(handler, specDownloadTimeout, "request timed out")

	req, err := http.NewRequest("GET", "/openapi/v2", nil)
	if err != nil {
		return nil, "", 0, err
	}
	req.Header.Add("Accept", "application/json")

	// Only pass eTag if it is not generated locally
	if len(etag) > 0 && !strings.HasPrefix(etag, locallyGeneratedEtagPrefix) {
		req.Header.Add("If-None-Match", etag)
	}

	writer := newInMemoryResponseWriter()
	handler.ServeHTTP(writer, req)

	switch writer.respCode {
	case http.StatusNotModified:
		if len(etag) == 0 {
			return nil, etag, http.StatusNotModified, fmt.Errorf("http.StatusNotModified is not allowed in absence of etag")
		}
		return nil, etag, http.StatusNotModified, nil
	case http.StatusNotFound:
		// Gracefully skip 404, assuming the server won't provide any spec
		return nil, "", http.StatusNotFound, nil
	case http.StatusOK:
		openAPISpec := &spec.Swagger{}
		if err := openAPISpec.UnmarshalJSON(writer.data); err != nil {
			return nil, "", 0, err
		}
		newEtag = writer.Header().Get("Etag")
		if len(newEtag) == 0 {
			newEtag = etagFor(writer.data)
			if len(etag) > 0 && strings.HasPrefix(etag, locallyGeneratedEtagPrefix) {
				// The function call with an etag and server does not report an etag.
				// That means this server does not support etag and the etag that passed
				// to the function generated previously by us. Just compare etags and
				// return StatusNotModified if they are the same.
				if etag == newEtag {
					return nil, etag, http.StatusNotModified, nil
				}
			}
		}
		return openAPISpec, newEtag, http.StatusOK, nil
	default:
		return nil, "", 0, fmt.Errorf("failed to retrieve openAPI spec, http error: %s", writer.String())
	}
}

// inMemoryResponseWriter is a http.Writer that keep the response in memory.
type inMemoryResponseWriter struct {
	writeHeaderCalled bool
	header            http.Header
	respCode          int
	data              []byte
}

func newInMemoryResponseWriter() *inMemoryResponseWriter {
	return &inMemoryResponseWriter{header: http.Header{}}
}

func (r *inMemoryResponseWriter) Header() http.Header {
	return r.header
}

func (r *inMemoryResponseWriter) WriteHeader(code int) {
	r.writeHeaderCalled = true
	r.respCode = code
}

func (r *inMemoryResponseWriter) Write(in []byte) (int, error) {
	if !r.writeHeaderCalled {
		r.WriteHeader(http.StatusOK)
	}
	r.data = append(r.data, in...)
	return len(in), nil
}

func (r *inMemoryResponseWriter) String() string {
	s := fmt.Sprintf("ResponseCode: %d", r.respCode)
	if r.data != nil {
		s += fmt.Sprintf(", Body: %s", string(r.data))
	}
	if r.header != nil {
		s += fmt.Sprintf(", Header: %s", r.header)
	}
	return s
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: MIT

// Package mock 根据 doc 生成 mock 数据
package mock

import (
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strings"

	"github.com/issue9/mux/v2"

	"github.com/caixw/apidoc/v5/doc"
	"github.com/caixw/apidoc/v5/internal/locale"
	"github.com/caixw/apidoc/v5/message"
)

// Mock 管理 mock 数据
type Mock struct {
	h       *message.Handler
	doc     *doc.Doc
	mux     *mux.Mux
	servers map[string]string
}

// New 声明 Mock 对象
//
// h 用于处理各类输出消息，仅在 ServeHTTP 中的消息才输出到 h；
// d doc.Doc 实例，调用方需要保证该数据类型的正确性；
// servers 用于指定 d.Servers 中每一个服务对应的路由前缀
func New(h *message.Handler, d *doc.Doc, servers map[string]string) (http.Handler, error) {
	m := &Mock{
		h:       h,
		doc:     d,
		mux:     mux.New(false, false, true, nil, nil),
		servers: servers,
	}

	if err := m.parse(); err != nil {
		return nil, err
	}

	return m, nil
}

// Load 从本地或是远程加载文档内容
func Load(h *message.Handler, path string, servers map[string]string) (http.Handler, error) {
	isURL := strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://")

	if isURL {
		return LoadFromURL(h, path, servers)
	}
	return LoadFromPath(h, path, servers)
}

// LoadFromPath 加载 XML 文档用以初始化 Mock 对象
func LoadFromPath(h *message.Handler, path string, servers map[string]string) (http.Handler, error) {
	r, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	return loadContent(h, path, r, servers)
}

// LoadFromURL 从远程 URL 加载文档并初始化为 Mock 对象
func LoadFromURL(h *message.Handler, url string, servers map[string]string) (http.Handler, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	return loadContent(h, url, resp.Body, servers)
}

// path 仅用于定位错误，内容存在于 r 中。
func loadContent(h *message.Handler, path string, r io.ReadCloser, servers map[string]string) (http.Handler, error) {
	defer r.Close()

	data, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}

	// 加载并验证
	d := doc.New()
	if err = d.FromXML(path, 0, data); err != nil {
		return nil, err
	}

	return New(h, d, servers)
}

func (m *Mock) parse() error {
	for _, api := range m.doc.Apis {
		handler := m.buildAPI(api)

		if len(api.Servers) == 0 {
			err := m.mux.Handle(api.Path.Path, handler, string(api.Method))
			if err != nil {
				return err
			}
			continue
		}

		for name, prefix := range m.servers {
			prefix := m.mux.Prefix(prefix)

			if !hasServer(api.Servers, name) {
				continue
			}

			err := prefix.Handle(api.Path.Path, handler, string(api.Method))
			if err != nil {
				return err
			}
		}
	}

	for path, methods := range m.mux.All(true, true) {
		m.h.Message(message.Info, locale.LoadAPI, path, strings.Join(methods, ","))
	}

	return nil
}

func (m *Mock) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	m.mux.ServeHTTP(w, r)
}

func hasServer(tags []string, key string) bool {
	for _, tag := range tags {
		if key == tag {
			return true
		}
	}

	return false
}
<commit_msg>refactor(internal/mock): 统一 head 和 options 报头的配置<commit_after>// SPDX-License-Identifier: MIT

// Package mock 根据 doc 生成 mock 数据
package mock

import (
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strings"

	"github.com/issue9/mux/v2"

	"github.com/caixw/apidoc/v5/doc"
	"github.com/caixw/apidoc/v5/internal/locale"
	"github.com/caixw/apidoc/v5/message"
)

const (
	allowHead    = true
	allowOptions = true
)

// Mock 管理 mock 数据
type Mock struct {
	h       *message.Handler
	doc     *doc.Doc
	mux     *mux.Mux
	servers map[string]string
}

// New 声明 Mock 对象
//
// h 用于处理各类输出消息，仅在 ServeHTTP 中的消息才输出到 h；
// d doc.Doc 实例，调用方需要保证该数据类型的正确性；
// servers 用于指定 d.Servers 中每一个服务对应的路由前缀
func New(h *message.Handler, d *doc.Doc, servers map[string]string) (http.Handler, error) {
	m := &Mock{
		h:       h,
		doc:     d,
		mux:     mux.New(!allowOptions, !allowHead, true, nil, nil),
		servers: servers,
	}

	if err := m.parse(); err != nil {
		return nil, err
	}

	return m, nil
}

// Load 从本地或是远程加载文档内容
func Load(h *message.Handler, path string, servers map[string]string) (http.Handler, error) {
	isURL := strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://")

	if isURL {
		return LoadFromURL(h, path, servers)
	}
	return LoadFromPath(h, path, servers)
}

// LoadFromPath 加载 XML 文档用以初始化 Mock 对象
func LoadFromPath(h *message.Handler, path string, servers map[string]string) (http.Handler, error) {
	r, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	return loadContent(h, path, r, servers)
}

// LoadFromURL 从远程 URL 加载文档并初始化为 Mock 对象
func LoadFromURL(h *message.Handler, url string, servers map[string]string) (http.Handler, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	return loadContent(h, url, resp.Body, servers)
}

// path 仅用于定位错误，内容存在于 r 中。
func loadContent(h *message.Handler, path string, r io.ReadCloser, servers map[string]string) (http.Handler, error) {
	defer r.Close()

	data, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}

	// 加载并验证
	d := doc.New()
	if err = d.FromXML(path, 0, data); err != nil {
		return nil, err
	}

	return New(h, d, servers)
}

func (m *Mock) parse() error {
	for _, api := range m.doc.Apis {
		handler := m.buildAPI(api)

		if len(api.Servers) == 0 {
			err := m.mux.Handle(api.Path.Path, handler, string(api.Method))
			if err != nil {
				return err
			}
			continue
		}

		for name, prefix := range m.servers {
			prefix := m.mux.Prefix(prefix)

			if !hasServer(api.Servers, name) {
				continue
			}

			err := prefix.Handle(api.Path.Path, handler, string(api.Method))
			if err != nil {
				return err
			}
		}
	}

	for path, methods := range m.mux.All(!allowHead, !allowOptions) {
		m.h.Message(message.Info, locale.LoadAPI, path, strings.Join(methods, ","))
	}

	return nil
}

func (m *Mock) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	m.mux.ServeHTTP(w, r)
}

func hasServer(tags []string, key string) bool {
	for _, tag := range tags {
		if key == tag {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package peer

import (
	"bytes"
	"encoding/binary"
	"errors"
	"io"
	"io/ioutil"
	"net"
	"sync"
	"time"

	"github.com/cenkalti/rain/internal/bitfield"
	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/internal/messageid"
	"github.com/cenkalti/rain/internal/torrentdata"
)

const connReadTimeout = 3 * time.Minute

// Reject requests larger than this size.
const maxAllowedBlockSize = 32 * 1024

var ErrPeerChoking = errors.New("peer is choking")

type Peer struct {
	conn net.Conn
	id   [20]byte
	data *torrentdata.Data

	amChoking      bool
	amInterested   bool
	peerChoking    bool
	peerInterested bool

	messages     *Messages
	m            sync.Mutex
	disconnected chan struct{}
	log          logger.Logger
}

func New(conn net.Conn, id [20]byte, d *torrentdata.Data, l logger.Logger, messages *Messages) *Peer {
	return &Peer{
		conn:         conn,
		id:           id,
		data:         d,
		amChoking:    true,
		peerChoking:  true,
		messages:     messages,
		disconnected: make(chan struct{}),
		log:          l,
	}
}

func (p *Peer) ID() [20]byte {
	return p.id
}

func (p *Peer) String() string {
	return p.conn.RemoteAddr().String()
}

func (p *Peer) NotifyDisconnect() chan struct{} {
	return p.disconnected
}

func (p *Peer) Close() error {
	return p.conn.Close()
}

// Run reads and processes incoming messages after handshake.
// TODO send keep-alive messages to peers at interval.
func (p *Peer) Run(stopC chan struct{}) {
	p.log.Debugln("Communicating peer", p.conn.RemoteAddr())
	defer close(p.disconnected)

	if err := p.sendBitfield(p.data.Bitfield()); err != nil {
		p.log.Error(err)
		return
	}

	// TODO remove after implementing uploader
	p.SendUnchoke()
	p.SendInterested()

	first := true
	for {
		err := p.conn.SetReadDeadline(time.Now().Add(connReadTimeout))
		if err != nil {
			p.log.Error(err)
			return
		}

		var length uint32
		// p.log.Debug("Reading message...")
		err = binary.Read(p.conn, binary.BigEndian, &length)
		if err != nil {
			if err == io.EOF {
				p.log.Debug("Remote peer has closed the connection")
			} else {
				p.log.Error(err)
			}
			return
		}
		// p.log.Debugf("Received message of length: %d", length)

		if length == 0 { // keep-alive message
			p.log.Debug("Received message of type \"keep alive\"")
			continue
		}

		var id messageid.MessageID
		err = binary.Read(p.conn, binary.BigEndian, &id)
		if err != nil {
			p.log.Error(err)
			return
		}
		length--

		p.log.Debugf("Received message of type: %q", id)

		switch id {
		case messageid.Choke:
			p.m.Lock()
			p.peerChoking = true
			p.m.Unlock()
			select {
			case p.messages.Choke <- p:
			case <-stopC:
				return
			}
		case messageid.Unchoke:
			p.m.Lock()
			p.peerChoking = false
			p.m.Unlock()
			select {
			case p.messages.Unchoke <- p:
			case <-stopC:
				return
			}
			// TODO implement
		case messageid.Interested:
			p.m.Lock()
			p.peerInterested = true
			p.m.Unlock()
			// TODO implement
		case messageid.NotInterested:
			p.m.Lock()
			p.peerInterested = false
			p.m.Unlock()
			// TODO implement
		case messageid.Have:
			var h haveMessage
			err = binary.Read(p.conn, binary.BigEndian, &h)
			if err != nil {
				p.log.Error(err)
				return
			}
			if h.Index >= uint32(len(p.data.Pieces)) {
				p.log.Error("unexpected piece index")
				return
			}
			pi := &p.data.Pieces[h.Index]
			p.log.Debug("Peer ", p.conn.RemoteAddr(), " has piece #", pi.Index)
			select {
			case p.messages.Have <- Have{p, pi}:
			case <-stopC:
				return
			}
		case messageid.Bitfield:
			if !first {
				p.log.Error("bitfield can only be sent after handshake")
				return
			}
			numBytes := uint32(bitfield.NumBytes(uint32(len(p.data.Pieces))))
			if length != numBytes {
				p.log.Error("invalid bitfield length")
				return
			}
			b := make([]byte, numBytes)
			_, err = io.ReadFull(p.conn, b)
			if err != nil {
				p.log.Error(err)
				return
			}
			bf := bitfield.NewBytes(b, uint32(len(p.data.Pieces)))
			p.log.Debugln("Received bitfield:", bf.Hex())

			for i := uint32(0); i < bf.Len(); i++ {
				if bf.Test(i) {
					pi := &p.data.Pieces[i]
					select {
					case p.messages.Have <- Have{p, pi}:
					case <-stopC:
						return
					}
				}
			}
		case messageid.Request:
			var req requestMessage
			err = binary.Read(p.conn, binary.BigEndian, &req)
			if err != nil {
				p.log.Error(err)
				return
			}
			p.log.Debugf("Received Request: %+v", req)

			if req.Index >= uint32(len(p.data.Pieces)) {
				p.log.Error("invalid request: index")
				return
			}
			if req.Length > maxAllowedBlockSize {
				p.log.Error("received a request with block size larger than allowed")
				return
			}
			if req.Begin+req.Length > p.data.Pieces[req.Index].Length {
				p.log.Error("invalid request: length")
			}

			pi := &p.data.Pieces[req.Index]
			p.messages.Request <- Request{p, pi, req.Begin, req.Length}
		case messageid.Piece:
			var msg pieceMessage
			err = binary.Read(p.conn, binary.BigEndian, &msg)
			if err != nil {
				p.log.Error(err)
				return
			}
			length -= 8

			if msg.Index >= uint32(len(p.data.Pieces)) {
				p.log.Error("invalid piece index")
				return
			}
			piece := &p.data.Pieces[msg.Index]

			block := piece.GetBlock(msg.Begin)
			if block == nil {
				p.log.Error("invalid block begin")
				return
			}
			if length != block.Length {
				p.log.Error("invalid block length")
				return
			}

			pm := Piece{Peer: p, Piece: piece, Block: block}
			pm.Data = make([]byte, length)
			if _, err = io.ReadFull(p.conn, pm.Data); err != nil {
				p.log.Error(err)
				return
			}

			select {
			case p.messages.Piece <- pm:
			case <-stopC:
				return
			}
		// TODO handle cancel messages
		// case messageid.Cancel:
		default:
			p.log.Debugf("unhandled message type: %s", id)
			p.log.Debugln("Discarding", length, "bytes...")
			_, err = io.CopyN(ioutil.Discard, p.conn, int64(length))
			if err != nil {
				p.log.Error(err)
				return
			}
		}
		first = false
	}
}

func (p *Peer) sendBitfield(b *bitfield.Bitfield) error {
	// Sending bitfield may be omitted if have no pieces.
	if b.Count() == 0 {
		return nil
	}
	return p.writeMessage(messageid.Bitfield, b.Bytes())
}

func (p *Peer) SendInterested() error {
	p.m.Lock()
	if p.amInterested {
		p.m.Unlock()
		return nil
	}
	p.amInterested = true
	p.m.Unlock()
	return p.writeMessage(messageid.Interested, nil)
}

func (p *Peer) SendNotInterested() error {
	p.m.Lock()
	if !p.amInterested {
		p.m.Unlock()
		return nil
	}
	p.amInterested = false
	p.m.Unlock()
	return p.writeMessage(messageid.NotInterested, nil)
}

func (p *Peer) SendChoke() error {
	p.m.Lock()
	if p.amChoking {
		p.m.Unlock()
		return nil
	}
	p.amChoking = true
	p.m.Unlock()
	return p.writeMessage(messageid.Choke, nil)
}

func (p *Peer) SendUnchoke() error {
	p.m.Lock()
	if !p.amChoking {
		p.m.Unlock()
		return nil
	}
	p.amChoking = false
	p.m.Unlock()
	return p.writeMessage(messageid.Unchoke, nil)
}

func (p *Peer) SendRequest(piece, begin, length uint32) error {
	p.m.Lock()
	if p.peerChoking {
		p.m.Unlock()
		return ErrPeerChoking
	}
	p.m.Unlock()

	req := requestMessage{piece, begin, length}
	p.log.Debugf("Sending Request: %+v", req)
	buf := bytes.NewBuffer(make([]byte, 0, 12))
	_ = binary.Write(buf, binary.BigEndian, &req)
	return p.writeMessage(messageid.Request, buf.Bytes())
}

func (p *Peer) SendPiece(index, begin uint32, block []byte) error {
	msg := pieceMessage{index, begin}
	p.log.Debugf("Sending Piece: %+v", msg)
	buf := bytes.NewBuffer(make([]byte, 0, 8))
	_ = binary.Write(buf, binary.BigEndian, msg)
	buf.Write(block)
	return p.writeMessage(messageid.Piece, buf.Bytes())
}

func (p *Peer) writeMessage(id messageid.MessageID, payload []byte) error {
	p.log.Debugf("Sending message of type: %q", id)
	buf := bytes.NewBuffer(make([]byte, 0, 4+1+len(payload)))
	var header = struct {
		Length uint32
		ID     messageid.MessageID
	}{
		uint32(1 + len(payload)),
		id,
	}
	_ = binary.Write(buf, binary.BigEndian, &header)
	buf.Write(payload)
	_, err := p.conn.Write(buf.Bytes())
	return err
}
<commit_msg>send peer connect message<commit_after>package peer

import (
	"bytes"
	"encoding/binary"
	"errors"
	"io"
	"io/ioutil"
	"net"
	"sync"
	"time"

	"github.com/cenkalti/rain/internal/bitfield"
	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/internal/messageid"
	"github.com/cenkalti/rain/internal/torrentdata"
)

const connReadTimeout = 3 * time.Minute

// Reject requests larger than this size.
const maxAllowedBlockSize = 32 * 1024

var ErrPeerChoking = errors.New("peer is choking")

type Peer struct {
	conn net.Conn
	id   [20]byte
	data *torrentdata.Data

	amChoking      bool
	amInterested   bool
	peerChoking    bool
	peerInterested bool

	messages     *Messages
	m            sync.Mutex
	disconnected chan struct{}
	log          logger.Logger
}

func New(conn net.Conn, id [20]byte, d *torrentdata.Data, l logger.Logger, messages *Messages) *Peer {
	return &Peer{
		conn:         conn,
		id:           id,
		data:         d,
		amChoking:    true,
		peerChoking:  true,
		messages:     messages,
		disconnected: make(chan struct{}),
		log:          l,
	}
}

func (p *Peer) ID() [20]byte {
	return p.id
}

func (p *Peer) String() string {
	return p.conn.RemoteAddr().String()
}

func (p *Peer) NotifyDisconnect() chan struct{} {
	return p.disconnected
}

func (p *Peer) Close() error {
	return p.conn.Close()
}

// Run reads and processes incoming messages after handshake.
// TODO send keep-alive messages to peers at interval.
func (p *Peer) Run(stopC chan struct{}) {
	p.log.Debugln("Communicating peer", p.conn.RemoteAddr())
	defer close(p.disconnected)

	if err := p.sendBitfield(p.data.Bitfield()); err != nil {
		p.log.Error(err)
		return
	}

	// TODO remove after implementing uploader
	p.SendUnchoke()
	p.SendInterested()

	select {
	case p.messages.Connect <- p:
	case <-stopC:
		return
	}

	first := true
	for {
		err := p.conn.SetReadDeadline(time.Now().Add(connReadTimeout))
		if err != nil {
			p.log.Error(err)
			return
		}

		var length uint32
		// p.log.Debug("Reading message...")
		err = binary.Read(p.conn, binary.BigEndian, &length)
		if err != nil {
			if err == io.EOF {
				p.log.Debug("Remote peer has closed the connection")
			} else {
				p.log.Error(err)
			}
			return
		}
		// p.log.Debugf("Received message of length: %d", length)

		if length == 0 { // keep-alive message
			p.log.Debug("Received message of type \"keep alive\"")
			continue
		}

		var id messageid.MessageID
		err = binary.Read(p.conn, binary.BigEndian, &id)
		if err != nil {
			p.log.Error(err)
			return
		}
		length--

		p.log.Debugf("Received message of type: %q", id)

		switch id {
		case messageid.Choke:
			p.m.Lock()
			p.peerChoking = true
			p.m.Unlock()
			select {
			case p.messages.Choke <- p:
			case <-stopC:
				return
			}
		case messageid.Unchoke:
			p.m.Lock()
			p.peerChoking = false
			p.m.Unlock()
			select {
			case p.messages.Unchoke <- p:
			case <-stopC:
				return
			}
			// TODO implement
		case messageid.Interested:
			p.m.Lock()
			p.peerInterested = true
			p.m.Unlock()
			// TODO implement
		case messageid.NotInterested:
			p.m.Lock()
			p.peerInterested = false
			p.m.Unlock()
			// TODO implement
		case messageid.Have:
			var h haveMessage
			err = binary.Read(p.conn, binary.BigEndian, &h)
			if err != nil {
				p.log.Error(err)
				return
			}
			if h.Index >= uint32(len(p.data.Pieces)) {
				p.log.Error("unexpected piece index")
				return
			}
			pi := &p.data.Pieces[h.Index]
			p.log.Debug("Peer ", p.conn.RemoteAddr(), " has piece #", pi.Index)
			select {
			case p.messages.Have <- Have{p, pi}:
			case <-stopC:
				return
			}
		case messageid.Bitfield:
			if !first {
				p.log.Error("bitfield can only be sent after handshake")
				return
			}
			numBytes := uint32(bitfield.NumBytes(uint32(len(p.data.Pieces))))
			if length != numBytes {
				p.log.Error("invalid bitfield length")
				return
			}
			b := make([]byte, numBytes)
			_, err = io.ReadFull(p.conn, b)
			if err != nil {
				p.log.Error(err)
				return
			}
			bf := bitfield.NewBytes(b, uint32(len(p.data.Pieces)))
			p.log.Debugln("Received bitfield:", bf.Hex())

			for i := uint32(0); i < bf.Len(); i++ {
				if bf.Test(i) {
					pi := &p.data.Pieces[i]
					select {
					case p.messages.Have <- Have{p, pi}:
					case <-stopC:
						return
					}
				}
			}
		case messageid.Request:
			var req requestMessage
			err = binary.Read(p.conn, binary.BigEndian, &req)
			if err != nil {
				p.log.Error(err)
				return
			}
			p.log.Debugf("Received Request: %+v", req)

			if req.Index >= uint32(len(p.data.Pieces)) {
				p.log.Error("invalid request: index")
				return
			}
			if req.Length > maxAllowedBlockSize {
				p.log.Error("received a request with block size larger than allowed")
				return
			}
			if req.Begin+req.Length > p.data.Pieces[req.Index].Length {
				p.log.Error("invalid request: length")
			}

			pi := &p.data.Pieces[req.Index]
			p.messages.Request <- Request{p, pi, req.Begin, req.Length}
		case messageid.Piece:
			var msg pieceMessage
			err = binary.Read(p.conn, binary.BigEndian, &msg)
			if err != nil {
				p.log.Error(err)
				return
			}
			length -= 8

			if msg.Index >= uint32(len(p.data.Pieces)) {
				p.log.Error("invalid piece index")
				return
			}
			piece := &p.data.Pieces[msg.Index]

			block := piece.GetBlock(msg.Begin)
			if block == nil {
				p.log.Error("invalid block begin")
				return
			}
			if length != block.Length {
				p.log.Error("invalid block length")
				return
			}

			pm := Piece{Peer: p, Piece: piece, Block: block}
			pm.Data = make([]byte, length)
			if _, err = io.ReadFull(p.conn, pm.Data); err != nil {
				p.log.Error(err)
				return
			}

			select {
			case p.messages.Piece <- pm:
			case <-stopC:
				return
			}
		// TODO handle cancel messages
		// case messageid.Cancel:
		default:
			p.log.Debugf("unhandled message type: %s", id)
			p.log.Debugln("Discarding", length, "bytes...")
			_, err = io.CopyN(ioutil.Discard, p.conn, int64(length))
			if err != nil {
				p.log.Error(err)
				return
			}
		}
		first = false
	}
}

func (p *Peer) sendBitfield(b *bitfield.Bitfield) error {
	// Sending bitfield may be omitted if have no pieces.
	if b.Count() == 0 {
		return nil
	}
	return p.writeMessage(messageid.Bitfield, b.Bytes())
}

func (p *Peer) SendInterested() error {
	p.m.Lock()
	if p.amInterested {
		p.m.Unlock()
		return nil
	}
	p.amInterested = true
	p.m.Unlock()
	return p.writeMessage(messageid.Interested, nil)
}

func (p *Peer) SendNotInterested() error {
	p.m.Lock()
	if !p.amInterested {
		p.m.Unlock()
		return nil
	}
	p.amInterested = false
	p.m.Unlock()
	return p.writeMessage(messageid.NotInterested, nil)
}

func (p *Peer) SendChoke() error {
	p.m.Lock()
	if p.amChoking {
		p.m.Unlock()
		return nil
	}
	p.amChoking = true
	p.m.Unlock()
	return p.writeMessage(messageid.Choke, nil)
}

func (p *Peer) SendUnchoke() error {
	p.m.Lock()
	if !p.amChoking {
		p.m.Unlock()
		return nil
	}
	p.amChoking = false
	p.m.Unlock()
	return p.writeMessage(messageid.Unchoke, nil)
}

func (p *Peer) SendRequest(piece, begin, length uint32) error {
	p.m.Lock()
	if p.peerChoking {
		p.m.Unlock()
		return ErrPeerChoking
	}
	p.m.Unlock()

	req := requestMessage{piece, begin, length}
	p.log.Debugf("Sending Request: %+v", req)
	buf := bytes.NewBuffer(make([]byte, 0, 12))
	_ = binary.Write(buf, binary.BigEndian, &req)
	return p.writeMessage(messageid.Request, buf.Bytes())
}

func (p *Peer) SendPiece(index, begin uint32, block []byte) error {
	msg := pieceMessage{index, begin}
	p.log.Debugf("Sending Piece: %+v", msg)
	buf := bytes.NewBuffer(make([]byte, 0, 8))
	_ = binary.Write(buf, binary.BigEndian, msg)
	buf.Write(block)
	return p.writeMessage(messageid.Piece, buf.Bytes())
}

func (p *Peer) writeMessage(id messageid.MessageID, payload []byte) error {
	p.log.Debugf("Sending message of type: %q", id)
	buf := bytes.NewBuffer(make([]byte, 0, 4+1+len(payload)))
	var header = struct {
		Length uint32
		ID     messageid.MessageID
	}{
		uint32(1 + len(payload)),
		id,
	}
	_ = binary.Write(buf, binary.BigEndian, &header)
	buf.Write(payload)
	_, err := p.conn.Write(buf.Bytes())
	return err
}
<|endoftext|>