<commit_before>package fritz

import (
	"io"
	"log"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"reflect"
	"runtime"
	"sync/atomic"
	"testing"

	"fmt"

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

// TestFritzAPI test the FRITZ API.
func TestFritzAPI(t *testing.T) {

	serverAnswering := func(answers ...string) *httptest.Server {
		it := uint32(0)
		server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			ch, err := os.Open(answers[int(atomic.LoadUint32(&it))%len(answers)])
			defer ch.Close()
			atomic.AddUint32(&it, 1)
			if err != nil {
				w.WriteHeader(500)
				w.Write([]byte(err.Error()))
			}
			io.Copy(w, ch)
		}))
		return server
	}

	client := func() *Client {
		cl, err := NewClient("testdata/config_localhost_test.json")
		if err != nil {
			panic(err)
		}
		return cl
	}

	testCases := []struct {
		client *Client
		server *httptest.Server
		dotest func(t *testing.T, fritz *fritzImpl, server *httptest.Server)
	}{
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml"),
			dotest: testGetWithAin,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_sid_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml"),
			dotest: testGetDeviceList,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml"),
			dotest: testAPIGetDeviceListErrorServerDown,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test"),
			dotest: testAPISwitchDeviceOn,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test"),
			dotest: testAPISwitchDeviceOff,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test"),
			dotest: testAPISwitchDeviceOffErrorServerDownAtListingStage,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_empty_test.xml"),
			dotest: testAPISwitchDeviceOffErrorUnknownDevice,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_empty_test.xml"),
			dotest: testAPISwitchDeviceOnErrorUnknownDevice,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_empty_test.xml"),
			dotest: testAPISwitchOffByAinWithErrorServerDown,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test"),
			dotest: testAPIToggleDevice,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test"),
			dotest: testAPIToggleDeviceErrorServerDownAtListingStage,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test"),
			dotest: testAPIToggleDeviceErrorServerDownAtToggleStage,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test"),
			dotest: testAPISetHkr,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test"),
			dotest: testAPISetHkrDevNotFound,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test"),
			dotest: testAPISetHkrErrorServerDownAtCommandStage,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test", "testdata/answer_switch_on_test", "testdata/answer_switch_on_test"),
			dotest: testToggleConcurrent,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test", "testdata/answer_switch_on_test", ""),
			dotest: testToggleConcurrentWithOneError,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test"),
			dotest: testToggleConcurrentWithDeviceNotFound,
		},
	}
	for _, testCase := range testCases {
		t.Run(fmt.Sprintf("Test fritz api %s", runtime.FuncForPC(reflect.ValueOf(testCase.dotest).Pointer()).Name()), func(t *testing.T) {
			testCase.server.Start()
			defer testCase.server.Close()
			tsurl, err := url.Parse(testCase.server.URL)
			assert.NoError(t, err)
			testCase.client.Config.Protocol = tsurl.Scheme
			testCase.client.Config.Host = tsurl.Host
			loggedIn, err := testCase.client.Login()
			assert.NoError(t, err)
			fritz := UsingClient(loggedIn).(*fritzImpl)
			assert.NotNil(t, fritz)
			testCase.dotest(t, fritz, testCase.server)
		})
	}
}

func testAPISetHkr(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	err := fritz.Temperature(12.5, "DER device")
	assert.NoError(t, err)
}

func testAPISetHkrDevNotFound(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	err := fritz.Temperature(12.5, "DOES-NOT-EXIST")
	assert.Error(t, err)
}

func testAPISetHkrErrorServerDownAtCommandStage(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	server.Close()
	_, err := fritz.temperatureForAin("12345", 12.5)
	assert.Error(t, err)
}

func testGetWithAin(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	_, err := fritz.getWithAinAndParam("ain", "cmd", "x=y")
	assert.NoError(t, err)
}

func testGetDeviceList(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	devList, err := fritz.ListDevices()
	log.Println(*devList)
	assert.NoError(t, err)
	assert.NotNil(t, devList)
	assert.NotEmpty(t, devList.Devices)
	assert.NotEmpty(t, devList.Devices[0].ID)
	assert.NotEmpty(t, devList.Devices[0].Identifier)
	assert.NotEmpty(t, devList.Devices[0].Functionbitmask)
	assert.NotEmpty(t, devList.Devices[0].Fwversion)
	assert.NotEmpty(t, devList.Devices[0].Manufacturer)
	assert.Equal(t, devList.Devices[0].Present, 1)
	assert.NotEmpty(t, devList.Devices[0].Name)

}

func testAPIGetDeviceListErrorServerDown(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	server.Close()
	_, err := fritz.ListDevices()
	assert.Error(t, err)
}

func testAPISwitchDeviceOn(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	err := fritz.SwitchOn("DER device")
	assert.NoError(t, err)
}

func testAPISwitchDeviceOff(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	err := fritz.SwitchOff("DER device")
	assert.NoError(t, err)
}

func testAPISwitchDeviceOffErrorServerDownAtListingStage(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	server.Close()
	err := fritz.SwitchOff("DER device")
	assert.Error(t, err)
}

func testAPISwitchDeviceOffErrorUnknownDevice(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	err := fritz.SwitchOff("DER device")
	assert.Error(t, err)
}

func testAPISwitchDeviceOnErrorUnknownDevice(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	err := fritz.SwitchOn("DER device")
	assert.Error(t, err)
}

func testAPISwitchOffByAinWithErrorServerDown(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	server.Close()
	_, err := fritz.switchForAin("123344", "off")
	assert.Error(t, err)
}

func testAPIToggleDevice(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	err := fritz.Toggle("DER device")
	assert.NoError(t, err)
}

func testAPIToggleDeviceErrorServerDownAtListingStage(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	server.Close()
	err := fritz.Toggle("DER device")
	assert.Error(t, err)
}

func testAPIToggleDeviceErrorServerDownAtToggleStage(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	server.Close()
	_, err := fritz.toggleForAin("DER device")
	assert.Error(t, err)
}

func testToggleConcurrent(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	err := fritz.Toggle("DER device", "My device", "My other device")
	assert.NoError(t, err)
}

func testToggleConcurrentWithOneError(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	err := fritz.Toggle("DER device", "My device", "My other device")
	assert.Error(t, err)
}

func testToggleConcurrentWithDeviceNotFound(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	err := fritz.Toggle("DER device", "UNKNOWN", "My other device")
	assert.Error(t, err)
}
<commit_msg>fix a race condition<commit_after>package fritz

import (
	"io"
	"log"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"reflect"
	"runtime"
	"sync/atomic"
	"testing"

	"fmt"

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

// TestFritzAPI test the FRITZ API.
func TestFritzAPI(t *testing.T) {

	serverAnswering := func(answers ...string) *httptest.Server {
		it := int32(-1)
		server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			ch, err := os.Open(answers[int(atomic.AddInt32(&it, 1))%len(answers)])
			defer ch.Close()
			if err != nil {
				w.WriteHeader(500)
				w.Write([]byte(err.Error()))
			}
			io.Copy(w, ch)
		}))
		return server
	}

	client := func() *Client {
		cl, err := NewClient("testdata/config_localhost_test.json")
		if err != nil {
			panic(err)
		}
		return cl
	}

	testCases := []struct {
		client *Client
		server *httptest.Server
		dotest func(t *testing.T, fritz *fritzImpl, server *httptest.Server)
	}{
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml"),
			dotest: testGetWithAin,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_sid_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml"),
			dotest: testGetDeviceList,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml"),
			dotest: testAPIGetDeviceListErrorServerDown,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test"),
			dotest: testAPISwitchDeviceOn,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test"),
			dotest: testAPISwitchDeviceOff,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test"),
			dotest: testAPISwitchDeviceOffErrorServerDownAtListingStage,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_empty_test.xml"),
			dotest: testAPISwitchDeviceOffErrorUnknownDevice,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_empty_test.xml"),
			dotest: testAPISwitchDeviceOnErrorUnknownDevice,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_empty_test.xml"),
			dotest: testAPISwitchOffByAinWithErrorServerDown,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test"),
			dotest: testAPIToggleDevice,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test"),
			dotest: testAPIToggleDeviceErrorServerDownAtListingStage,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test"),
			dotest: testAPIToggleDeviceErrorServerDownAtToggleStage,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test"),
			dotest: testAPISetHkr,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test"),
			dotest: testAPISetHkrDevNotFound,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test"),
			dotest: testAPISetHkrErrorServerDownAtCommandStage,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test", "testdata/answer_switch_on_test", "testdata/answer_switch_on_test"),
			dotest: testToggleConcurrent,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test", "testdata/answer_switch_on_test", ""),
			dotest: testToggleConcurrentWithOneError,
		},
		{
			client: client(),
			server: serverAnswering("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test"),
			dotest: testToggleConcurrentWithDeviceNotFound,
		},
	}
	for _, testCase := range testCases {
		t.Run(fmt.Sprintf("Test fritz api %s", runtime.FuncForPC(reflect.ValueOf(testCase.dotest).Pointer()).Name()), func(t *testing.T) {
			testCase.server.Start()
			defer testCase.server.Close()
			tsurl, err := url.Parse(testCase.server.URL)
			assert.NoError(t, err)
			testCase.client.Config.Protocol = tsurl.Scheme
			testCase.client.Config.Host = tsurl.Host
			loggedIn, err := testCase.client.Login()
			assert.NoError(t, err)
			fritz := UsingClient(loggedIn).(*fritzImpl)
			assert.NotNil(t, fritz)
			testCase.dotest(t, fritz, testCase.server)
		})
	}
}

func testAPISetHkr(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	err := fritz.Temperature(12.5, "DER device")
	assert.NoError(t, err)
}

func testAPISetHkrDevNotFound(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	err := fritz.Temperature(12.5, "DOES-NOT-EXIST")
	assert.Error(t, err)
}

func testAPISetHkrErrorServerDownAtCommandStage(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	server.Close()
	_, err := fritz.temperatureForAin("12345", 12.5)
	assert.Error(t, err)
}

func testGetWithAin(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	_, err := fritz.getWithAinAndParam("ain", "cmd", "x=y")
	assert.NoError(t, err)
}

func testGetDeviceList(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	devList, err := fritz.ListDevices()
	log.Println(*devList)
	assert.NoError(t, err)
	assert.NotNil(t, devList)
	assert.NotEmpty(t, devList.Devices)
	assert.NotEmpty(t, devList.Devices[0].ID)
	assert.NotEmpty(t, devList.Devices[0].Identifier)
	assert.NotEmpty(t, devList.Devices[0].Functionbitmask)
	assert.NotEmpty(t, devList.Devices[0].Fwversion)
	assert.NotEmpty(t, devList.Devices[0].Manufacturer)
	assert.Equal(t, devList.Devices[0].Present, 1)
	assert.NotEmpty(t, devList.Devices[0].Name)

}

func testAPIGetDeviceListErrorServerDown(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	server.Close()
	_, err := fritz.ListDevices()
	assert.Error(t, err)
}

func testAPISwitchDeviceOn(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	err := fritz.SwitchOn("DER device")
	assert.NoError(t, err)
}

func testAPISwitchDeviceOff(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	err := fritz.SwitchOff("DER device")
	assert.NoError(t, err)
}

func testAPISwitchDeviceOffErrorServerDownAtListingStage(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	server.Close()
	err := fritz.SwitchOff("DER device")
	assert.Error(t, err)
}

func testAPISwitchDeviceOffErrorUnknownDevice(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	err := fritz.SwitchOff("DER device")
	assert.Error(t, err)
}

func testAPISwitchDeviceOnErrorUnknownDevice(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	err := fritz.SwitchOn("DER device")
	assert.Error(t, err)
}

func testAPISwitchOffByAinWithErrorServerDown(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	server.Close()
	_, err := fritz.switchForAin("123344", "off")
	assert.Error(t, err)
}

func testAPIToggleDevice(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	err := fritz.Toggle("DER device")
	assert.NoError(t, err)
}

func testAPIToggleDeviceErrorServerDownAtListingStage(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	server.Close()
	err := fritz.Toggle("DER device")
	assert.Error(t, err)
}

func testAPIToggleDeviceErrorServerDownAtToggleStage(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	server.Close()
	_, err := fritz.toggleForAin("DER device")
	assert.Error(t, err)
}

func testToggleConcurrent(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	err := fritz.Toggle("DER device", "My device", "My other device")
	assert.NoError(t, err)
}

func testToggleConcurrentWithOneError(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	err := fritz.Toggle("DER device", "My device", "My other device")
	assert.Error(t, err)
}

func testToggleConcurrentWithDeviceNotFound(t *testing.T, fritz *fritzImpl, server *httptest.Server) {
	err := fritz.Toggle("DER device", "UNKNOWN", "My other device")
	assert.Error(t, err)
}
<|endoftext|>


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

package os

import syscall "syscall"
import os "os"

// FDs are wrappers for file descriptors
export type FD struct {
	fd int64
}

export func NewFD(fd int64) *FD {
	if fd < 0 {
		return nil
	}
	n := new(FD);
	n.fd = fd;
	return n;
}

export var (
	Stdin = NewFD(0);
	Stdout = NewFD(1);
	Stderr = NewFD(2);
)

export const (
	O_RDONLY = syscall.O_RDONLY;
	O_WRONLY = syscall.O_WRONLY;
	O_RDWR = syscall.O_RDWR;
	O_APPEND = syscall.O_APPEND;
	O_ASYNC = syscall.O_ASYNC;
	O_CREAT = syscall.O_CREAT;
	O_NOCTTY = syscall.O_NOCTTY;
	O_NONBLOCK = syscall.O_NONBLOCK;
	O_NDELAY = O_NONBLOCK;
	O_SYNC = syscall.O_SYNC;
	O_TRUNC = syscall.O_TRUNC;
)

export func Open(name string, mode int, flags int) (fd *FD, err *Error) {
	r, e := syscall.open(name, int64(mode), int64(flags));
	return NewFD(r), ErrnoToError(e)
}

func (fd *FD) Close() *Error {
	if fd == nil {
		return EINVAL
	}
	r, e := syscall.close(fd.fd);
	fd.fd = -1;  // so it can't be closed again
	return ErrnoToError(e)
}

func (fd *FD) Read(b *[]byte) (ret int, err *Error) {
	if fd == nil {
		return -1, EINVAL
	}
	r, e := syscall.read(fd.fd, &b[0], int64(len(b)));
	return int(r), ErrnoToError(e)
}

func (fd *FD) Write(b *[]byte) (ret int, err *Error) {
	if fd == nil {
		return -1, EINVAL
	}
	r, e := syscall.write(fd.fd, &b[0], int64(len(b)));
	return int(r), ErrnoToError(e)
}

func (fd *FD) WriteString(s string) (ret int, err *Error) {
	if fd == nil {
		return -1, EINVAL
	}
	b := new([]byte, len(s)+1);
	if !syscall.StringToBytes(b, s) {
		return -1, EINVAL
	}
	r, e := syscall.write(fd.fd, &b[0], int64(len(s)));
	return int(r), ErrnoToError(e)
}
<commit_msg>add os.Pipe<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package os

import syscall "syscall"
import os "os"

// FDs are wrappers for file descriptors
export type FD struct {
	fd int64
}

export func NewFD(fd int64) *FD {
	if fd < 0 {
		return nil
	}
	n := new(FD);
	n.fd = fd;
	return n;
}

export var (
	Stdin = NewFD(0);
	Stdout = NewFD(1);
	Stderr = NewFD(2);
)

export const (
	O_RDONLY = syscall.O_RDONLY;
	O_WRONLY = syscall.O_WRONLY;
	O_RDWR = syscall.O_RDWR;
	O_APPEND = syscall.O_APPEND;
	O_ASYNC = syscall.O_ASYNC;
	O_CREAT = syscall.O_CREAT;
	O_NOCTTY = syscall.O_NOCTTY;
	O_NONBLOCK = syscall.O_NONBLOCK;
	O_NDELAY = O_NONBLOCK;
	O_SYNC = syscall.O_SYNC;
	O_TRUNC = syscall.O_TRUNC;
)

export func Open(name string, mode int, flags int) (fd *FD, err *Error) {
	r, e := syscall.open(name, int64(mode), int64(flags));
	return NewFD(r), ErrnoToError(e)
}

func (fd *FD) Close() *Error {
	if fd == nil {
		return EINVAL
	}
	r, e := syscall.close(fd.fd);
	fd.fd = -1;  // so it can't be closed again
	return ErrnoToError(e)
}

func (fd *FD) Read(b *[]byte) (ret int, err *Error) {
	if fd == nil {
		return -1, EINVAL
	}
	r, e := syscall.read(fd.fd, &b[0], int64(len(b)));
	return int(r), ErrnoToError(e)
}

func (fd *FD) Write(b *[]byte) (ret int, err *Error) {
	if fd == nil {
		return -1, EINVAL
	}
	r, e := syscall.write(fd.fd, &b[0], int64(len(b)));
	return int(r), ErrnoToError(e)
}

func (fd *FD) WriteString(s string) (ret int, err *Error) {
	if fd == nil {
		return -1, EINVAL
	}
	b := new([]byte, len(s)+1);
	if !syscall.StringToBytes(b, s) {
		return -1, EINVAL
	}
	r, e := syscall.write(fd.fd, &b[0], int64(len(s)));
	return int(r), ErrnoToError(e)
}

export func Pipe() (fd1 *FD, fd2 *FD, err *Error) {
	var p [2]int64
	r, e := syscall.pipe(&p);
	if e != 0 {
		return nil, nil, ErrnoToError(e)
	}
	return NewFD(p[0]), NewFD(p[1]), nil
}
<|endoftext|>


<commit_before>package queue

import (
	"flag"
	"testing"
	"time"

	. "github.com/onsi/ginkgo"
	// /. "github.com/onsi/ginkgo/extensions/table"
	. "github.com/onsi/gomega"

	ojson "github.com/Cepave/open-falcon-backend/common/json"
	commonQueue "github.com/Cepave/open-falcon-backend/common/queue"
	dbTest "github.com/Cepave/open-falcon-backend/common/testing/db"
	"github.com/Cepave/open-falcon-backend/modules/nqm-mng/model"
	"github.com/Cepave/open-falcon-backend/modules/nqm-mng/rdb"
)

func init() {
	flag.Parse()
}

func TestByGinkgo(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Base Suite")
}

var _ = Describe("Start(): Start the queue service", ginkgoDb.NeedDb(func() {
	It("can't be put elements without calling Start() in advance", func() {
		testedQueue := New(&commonQueue.Config{Num: 1, Dur: 0})

		testedQueue.Put(&model.NqmAgentHeartbeatRequest{
			ConnectionId: "test1-hostname@1.2.3.4",
			Hostname:     "test1-hostname",
			IpAddress:    "1.2.3.4",
			Timestamp:    ojson.JsonTime(time.Now()),
		})
		Expect(testedQueue.Count()).To(Equal(uint64(0)))
		Expect(testedQueue.Len()).To(Equal(0))
	})

	It("can be put elements by calling Start() in advance", func() {
		testedQueue := New(&commonQueue.Config{Num: 1, Dur: 0})

		testedQueue.Start()

		testedQueue.Put(&model.NqmAgentHeartbeatRequest{
			ConnectionId: "test1-hostname@1.2.3.4",
			Hostname:     "test1-hostname",
			IpAddress:    "1.2.3.4",
			Timestamp:    ojson.JsonTime(time.Now()),
		})

		testedQueue.Put(&model.NqmAgentHeartbeatRequest{
			ConnectionId: "test2-hostname@1.2.3.4",
			Hostname:     "test2-hostname",
			IpAddress:    "1.2.3.4",
			Timestamp:    ojson.JsonTime(time.Now()),
		})

		testedQueue.Stop()
		Expect(testedQueue.Count()).To(Equal(uint64(2)))
	})
}))

var _ = Describe("Stop(): Stop the queue service", ginkgoDb.NeedDb(func() {
	It("can't be put elements after being stopped", func() {
		testedQueue := New(&commonQueue.Config{Num: 1, Dur: 0})

		testedQueue.Start()
		testedQueue.Stop()

		testedQueue.Put(&model.NqmAgentHeartbeatRequest{
			ConnectionId: "test1-hostname@1.2.3.4",
			Hostname:     "test1-hostname",
			IpAddress:    "1.2.3.4",
			Timestamp:    ojson.JsonTime(time.Now()),
		})
		Expect(testedQueue.Count()).To(Equal(uint64(0)))
		Expect(testedQueue.Len()).To(Equal(0))
	})

	It("doesn't flush elements until Stop() is called", func() {
		testedQueue := New(&commonQueue.Config{Num: 10, Dur: 1 * time.Second})

		testedQueue.Start()

		for i := 0; i < 999; i++ {
			testedQueue.Put(&model.NqmAgentHeartbeatRequest{
				ConnectionId: "test1-hostname@1.2.3.4",
				Hostname:     "test1-hostname",
				IpAddress:    "1.2.3.4",
				Timestamp:    ojson.JsonTime(time.Now()),
			})
		}
		Expect(testedQueue.Len()).NotTo(Equal(0))

		testedQueue.Stop()
		Expect(testedQueue.Count()).To(Equal(uint64(999)))
		Expect(testedQueue.Len()).To(Equal(0))
	})

	It("no elements after Stop() is called", func() {
		testedQueue := New(&commonQueue.Config{Num: 10, Dur: 1 * time.Second})

		testedQueue.Start()

		go func() {
			for i := 0; i < 999; i++ {
				testedQueue.Put(&model.NqmAgentHeartbeatRequest{
					ConnectionId: "test1-hostname@1.2.3.4",
					Hostname:     "test1-hostname",
					IpAddress:    "1.2.3.4",
					Timestamp:    ojson.JsonTime(time.Now()),
				})
			}
		}()

		testedQueue.Stop()
		Expect(testedQueue.Len()).To(Equal(0))
	})
}))

var ginkgoDb = &dbTest.GinkgoDb{}

var _ = BeforeSuite(func() {
	rdb.DbFacade = ginkgoDb.InitDbFacade()
})

var _ = AfterSuite(func() {
	ginkgoDb.ReleaseDbFacade(rdb.DbFacade)
})
<commit_msg>[OWL-1730][nqm-mng] Update the comment in the test<commit_after>package queue

import (
	"flag"
	"testing"
	"time"

	. "github.com/onsi/ginkgo"
	// /. "github.com/onsi/ginkgo/extensions/table"
	. "github.com/onsi/gomega"

	ojson "github.com/Cepave/open-falcon-backend/common/json"
	commonQueue "github.com/Cepave/open-falcon-backend/common/queue"
	dbTest "github.com/Cepave/open-falcon-backend/common/testing/db"
	"github.com/Cepave/open-falcon-backend/modules/nqm-mng/model"
	"github.com/Cepave/open-falcon-backend/modules/nqm-mng/rdb"
)

func init() {
	flag.Parse()
}

func TestByGinkgo(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Base Suite")
}

var _ = Describe("Start(): Start the queue service", ginkgoDb.NeedDb(func() {
	It("can't be put elements without calling Start() in advance", func() {
		testedQueue := New(&commonQueue.Config{Num: 1, Dur: 0})

		testedQueue.Put(&model.NqmAgentHeartbeatRequest{
			ConnectionId: "test1-hostname@1.2.3.4",
			Hostname:     "test1-hostname",
			IpAddress:    "1.2.3.4",
			Timestamp:    ojson.JsonTime(time.Now()),
		})
		Expect(testedQueue.Count()).To(Equal(uint64(0)))
		Expect(testedQueue.Len()).To(Equal(0))
	})

	It("can be put elements by calling Start() in advance", func() {
		testedQueue := New(&commonQueue.Config{Num: 1, Dur: 0})

		testedQueue.Start()

		testedQueue.Put(&model.NqmAgentHeartbeatRequest{
			ConnectionId: "test1-hostname@1.2.3.4",
			Hostname:     "test1-hostname",
			IpAddress:    "1.2.3.4",
			Timestamp:    ojson.JsonTime(time.Now()),
		})

		testedQueue.Put(&model.NqmAgentHeartbeatRequest{
			ConnectionId: "test2-hostname@1.2.3.4",
			Hostname:     "test2-hostname",
			IpAddress:    "1.2.3.4",
			Timestamp:    ojson.JsonTime(time.Now()),
		})

		testedQueue.Stop()
		Expect(testedQueue.Count()).To(Equal(uint64(2)))
	})
}))

var _ = Describe("Stop(): Stop the queue service", ginkgoDb.NeedDb(func() {
	It("can't be put elements after being stopped", func() {
		testedQueue := New(&commonQueue.Config{Num: 1, Dur: 0})

		testedQueue.Start()
		testedQueue.Stop()

		testedQueue.Put(&model.NqmAgentHeartbeatRequest{
			ConnectionId: "test1-hostname@1.2.3.4",
			Hostname:     "test1-hostname",
			IpAddress:    "1.2.3.4",
			Timestamp:    ojson.JsonTime(time.Now()),
		})
		Expect(testedQueue.Count()).To(Equal(uint64(0)))
		Expect(testedQueue.Len()).To(Equal(0))
	})

	It("doesn't flush elements until Stop() is called", func() {
		testedQueue := New(&commonQueue.Config{Num: 10, Dur: 1 * time.Second})

		testedQueue.Start()

		for i := 0; i < 999; i++ {
			testedQueue.Put(&model.NqmAgentHeartbeatRequest{
				ConnectionId: "test1-hostname@1.2.3.4",
				Hostname:     "test1-hostname",
				IpAddress:    "1.2.3.4",
				Timestamp:    ojson.JsonTime(time.Now()),
			})
		}
		Expect(testedQueue.Len()).NotTo(Equal(0))

		testedQueue.Stop()
		Expect(testedQueue.Count()).To(Equal(uint64(999)))
		Expect(testedQueue.Len()).To(Equal(0))
	})

	It("has no elements after Stop() is called", func() {
		testedQueue := New(&commonQueue.Config{Num: 10, Dur: 1 * time.Second})

		testedQueue.Start()

		go func() {
			for i := 0; i < 999; i++ {
				testedQueue.Put(&model.NqmAgentHeartbeatRequest{
					ConnectionId: "test1-hostname@1.2.3.4",
					Hostname:     "test1-hostname",
					IpAddress:    "1.2.3.4",
					Timestamp:    ojson.JsonTime(time.Now()),
				})
			}
		}()

		testedQueue.Stop()
		Expect(testedQueue.Len()).To(Equal(0))
	})
}))

var ginkgoDb = &dbTest.GinkgoDb{}

var _ = BeforeSuite(func() {
	rdb.DbFacade = ginkgoDb.InitDbFacade()
})

var _ = AfterSuite(func() {
	ginkgoDb.ReleaseDbFacade(rdb.DbFacade)
})
<|endoftext|>


<commit_before>package application

import (
	"cf/terminal"
	"code.google.com/p/gogoprotobuf/proto"
	"fmt"
	"github.com/cloudfoundry/loggregatorlib/logmessage"
	"github.com/stretchr/testify/assert"
	"testing"
	"time"
)

func TestLogMessageOutput(t *testing.T) {
	cloud_controller := logmessage.LogMessage_CLOUD_CONTROLLER
	router := logmessage.LogMessage_ROUTER
	uaa := logmessage.LogMessage_UAA
	dea := logmessage.LogMessage_DEA
	wardenContainer := logmessage.LogMessage_WARDEN_CONTAINER

	stdout := logmessage.LogMessage_OUT
	stderr := logmessage.LogMessage_ERR

	date := "2013 Sep 20 09:33:30 PDT"
	logTime, err := time.Parse("2006 Jan 2 15:04:05 MST", date)
	assert.NoError(t, err)
	timestamp := logTime.UnixNano()

	expectedTZ := logTime.Format("-0700")

	sourceId := "0"

	protoMessage := &logmessage.LogMessage{
		Message:     []byte("Hello World!\n\r\n\r"),
		AppId:       proto.String("my-app-guid"),
		MessageType: &stdout,
		SourceId:    &sourceId,
		Timestamp:   &timestamp,
	}

	msg := createMessage(t, protoMessage, &cloud_controller, &stdout)
	assert.Contains(t, logMessageOutput(msg), fmt.Sprintf("2013-09-20T09:33:30.00%s [API]", expectedTZ))
	assert.Contains(t, logMessageOutput(msg), terminal.LogStdoutColor("OUT Hello World!"))

	msg = createMessage(t, protoMessage, &cloud_controller, &stderr)
	assert.Contains(t, logMessageOutput(msg), fmt.Sprintf("2013-09-20T09:33:30.00%s [API]", expectedTZ))
	assert.Contains(t, logMessageOutput(msg), terminal.LogStderrColor("ERR Hello World!"))

	sourceId = "1"
	msg = createMessage(t, protoMessage, &router, &stdout)
	assert.Contains(t, logMessageOutput(msg), fmt.Sprintf("2013-09-20T09:33:30.00%s [RTR]", expectedTZ))
	assert.Contains(t, logMessageOutput(msg), terminal.LogStdoutColor("OUT Hello World!"))
	msg = createMessage(t, protoMessage, &router, &stderr)
	assert.Contains(t, logMessageOutput(msg), fmt.Sprintf("2013-09-20T09:33:30.00%s [RTR]", expectedTZ))
	assert.Contains(t, logMessageOutput(msg), terminal.LogStderrColor("ERR Hello World!"))

	sourceId = "2"
	msg = createMessage(t, protoMessage, &uaa, &stdout)
	assert.Contains(t, logMessageOutput(msg), fmt.Sprintf("2013-09-20T09:33:30.00%s [UAA]", expectedTZ))
	assert.Contains(t, logMessageOutput(msg), terminal.LogStdoutColor("OUT Hello World!"))
	msg = createMessage(t, protoMessage, &uaa, &stderr)
	assert.Contains(t, logMessageOutput(msg), fmt.Sprintf("2013-09-20T09:33:30.00%s [UAA]", expectedTZ))
	assert.Contains(t, logMessageOutput(msg), terminal.LogStderrColor("ERR Hello World!"))

	sourceId = "3"
	msg = createMessage(t, protoMessage, &dea, &stdout)
	assert.Contains(t, logMessageOutput(msg), fmt.Sprintf("2013-09-20T09:33:30.00%s [DEA]", expectedTZ))
	assert.Contains(t, logMessageOutput(msg), terminal.LogStdoutColor("OUT Hello World!"))
	msg = createMessage(t, protoMessage, &dea, &stderr)
	assert.Contains(t, logMessageOutput(msg), fmt.Sprintf("2013-09-20T09:33:30.00%s [DEA]", expectedTZ))
	assert.Contains(t, logMessageOutput(msg), terminal.LogStderrColor("ERR Hello World!"))

	sourceId = "4"
	msg = createMessage(t, protoMessage, &wardenContainer, &stdout)
	assert.Contains(t, logMessageOutput(msg), fmt.Sprintf("2013-09-20T09:33:30.00%s [App/4]", expectedTZ))
	assert.Contains(t, logMessageOutput(msg), terminal.LogStdoutColor("OUT Hello World!"))
	msg = createMessage(t, protoMessage, &wardenContainer, &stderr)
	assert.Contains(t, logMessageOutput(msg), fmt.Sprintf("2013-09-20T09:33:30.00%s [App/4]", expectedTZ))
	assert.Contains(t, logMessageOutput(msg), terminal.LogStderrColor("ERR Hello World!"))
}

func createMessage(t *testing.T, protoMsg *logmessage.LogMessage, sourceType *logmessage.LogMessage_SourceType, msgType *logmessage.LogMessage_MessageType) (msg *logmessage.Message) {
	protoMsg.SourceType = sourceType
	protoMsg.MessageType = msgType

	data, err := proto.Marshal(protoMsg)
	assert.NoError(t, err)

	msg, err = logmessage.ParseMessage(data)
	assert.NoError(t, err)

	return
}
<commit_msg>log format tests work in all timezones<commit_after>package application

import (
	"cf/terminal"
	"code.google.com/p/gogoprotobuf/proto"
	"fmt"
	"github.com/cloudfoundry/loggregatorlib/logmessage"
	"github.com/stretchr/testify/assert"
	"testing"
	"time"
)

func TestTimestampFormat(t *testing.T) {
	assert.Equal(t,TIMESTAMP_FORMAT,"2006-01-02T15:04:05.00-0700")
}

func TestLogMessageOutput(t *testing.T) {
	cloud_controller := logmessage.LogMessage_CLOUD_CONTROLLER
	router := logmessage.LogMessage_ROUTER
	uaa := logmessage.LogMessage_UAA
	dea := logmessage.LogMessage_DEA
	wardenContainer := logmessage.LogMessage_WARDEN_CONTAINER

	stdout := logmessage.LogMessage_OUT
	stderr := logmessage.LogMessage_ERR

	date := time.Now()
	timestamp := date.UnixNano()

	sourceId := "0"

	protoMessage := &logmessage.LogMessage{
		Message:     []byte("Hello World!\n\r\n\r"),
		AppId:       proto.String("my-app-guid"),
		MessageType: &stdout,
		SourceId:    &sourceId,
		Timestamp:   &timestamp,
	}

	msg := createMessage(t, protoMessage, &cloud_controller, &stdout)
	assert.Contains(t, logMessageOutput(msg), fmt.Sprintf("%s [API]", date.Format(TIMESTAMP_FORMAT)))
	assert.Contains(t, logMessageOutput(msg), terminal.LogStdoutColor("OUT Hello World!"))

	msg = createMessage(t, protoMessage, &cloud_controller, &stderr)
	assert.Contains(t, logMessageOutput(msg), fmt.Sprintf("%s [API]", date.Format(TIMESTAMP_FORMAT)))
	assert.Contains(t, logMessageOutput(msg), terminal.LogStderrColor("ERR Hello World!"))

	sourceId = "1"
	msg = createMessage(t, protoMessage, &router, &stdout)
	assert.Contains(t, logMessageOutput(msg), fmt.Sprintf("%s [RTR]", date.Format(TIMESTAMP_FORMAT)))
	assert.Contains(t, logMessageOutput(msg), terminal.LogStdoutColor("OUT Hello World!"))
	msg = createMessage(t, protoMessage, &router, &stderr)
	assert.Contains(t, logMessageOutput(msg), fmt.Sprintf("%s [RTR]", date.Format(TIMESTAMP_FORMAT)))
	assert.Contains(t, logMessageOutput(msg), terminal.LogStderrColor("ERR Hello World!"))

	sourceId = "2"
	msg = createMessage(t, protoMessage, &uaa, &stdout)
	assert.Contains(t, logMessageOutput(msg), fmt.Sprintf("%s [UAA]", date.Format(TIMESTAMP_FORMAT)))
	assert.Contains(t, logMessageOutput(msg), terminal.LogStdoutColor("OUT Hello World!"))
	msg = createMessage(t, protoMessage, &uaa, &stderr)
	assert.Contains(t, logMessageOutput(msg), fmt.Sprintf("%s [UAA]", date.Format(TIMESTAMP_FORMAT)))
	assert.Contains(t, logMessageOutput(msg), terminal.LogStderrColor("ERR Hello World!"))

	sourceId = "3"
	msg = createMessage(t, protoMessage, &dea, &stdout)
	assert.Contains(t, logMessageOutput(msg), fmt.Sprintf("%s [DEA]", date.Format(TIMESTAMP_FORMAT)))
	assert.Contains(t, logMessageOutput(msg), terminal.LogStdoutColor("OUT Hello World!"))
	msg = createMessage(t, protoMessage, &dea, &stderr)
	assert.Contains(t, logMessageOutput(msg), fmt.Sprintf("%s [DEA]", date.Format(TIMESTAMP_FORMAT)))
	assert.Contains(t, logMessageOutput(msg), terminal.LogStderrColor("ERR Hello World!"))

	sourceId = "4"
	msg = createMessage(t, protoMessage, &wardenContainer, &stdout)
	assert.Contains(t, logMessageOutput(msg), fmt.Sprintf("%s [App/4]", date.Format(TIMESTAMP_FORMAT)))
	assert.Contains(t, logMessageOutput(msg), terminal.LogStdoutColor("OUT Hello World!"))
	msg = createMessage(t, protoMessage, &wardenContainer, &stderr)
	assert.Contains(t, logMessageOutput(msg), fmt.Sprintf("%s [App/4]", date.Format(TIMESTAMP_FORMAT)))
	assert.Contains(t, logMessageOutput(msg), terminal.LogStderrColor("ERR Hello World!"))
}

func createMessage(t *testing.T, protoMsg *logmessage.LogMessage, sourceType *logmessage.LogMessage_SourceType, msgType *logmessage.LogMessage_MessageType) (msg *logmessage.Message) {
	protoMsg.SourceType = sourceType
	protoMsg.MessageType = msgType

	data, err := proto.Marshal(protoMsg)
	assert.NoError(t, err)

	msg, err = logmessage.ParseMessage(data)
	assert.NoError(t, err)

	return
}
<|endoftext|>


<commit_before>package pmc

import (
	"math"
	"testing"
)

func TestPMCHash(t *testing.T) {
	s, _ := New(1024, 4, 4)
	dist := make(map[uint]uint)
	for k := 0; k < 100000; k++ {
		i := rand(uint(s.m))
		j := georand(uint(s.w))
		pos := s.getPos([]byte("pmc"), i, j)
		dist[pos]++
	}
	if len(dist) > 16 {
		t.Error("Expected maximum 16 different positions, got ", len(dist))
	}
}

func TestPMCHashAdd(t *testing.T) {
	flow := []byte("pmc")
	halfFlow := []byte("halfpmc")
	s, _ := NewForMaxFlows(10000000)
	//start := time.Now()
	for k := 0; k < 1000000; k++ {
		s.Increment(flow)
		if k%2 == 0 {
			s.Increment(halfFlow)
		}
	}
	//elapsed := time.Since(start)
	//fmt.Println(1500000, "x Incrememt took", elapsed)

	//start = time.Now()
	hfCount := s.GetEstimate(halfFlow)
	fCount := s.GetEstimate(flow)

	fErr := 100 * (1 - float64(fCount)/1000000)
	hfErr := 100 * (1 - float64(hfCount)/500000)
	if math.Abs(fErr) > 10 {
		t.Errorf("Expected error for flow 'flow' <= 10%%, got %f", math.Abs(fErr))
	}
	if math.Abs(hfErr) > 10 {
		t.Errorf("Expected error for flow 'flow' <= 10%%, got %f", math.Abs(hfErr))
	}

	//elapsed = time.Since(start)
	//fmt.Println("GetEstimate took", elapsed)
}
<commit_msg>Fix more unit tests<commit_after>package pmc

import (
	"math"
	"strconv"
	"testing"
)

func TestPMCHash(t *testing.T) {
	s, _ := New(1024, 4, 4)
	dist := make(map[uint]uint)
	for k := 0; k < 100000; k++ {
		i := rand(uint(s.m))
		j := georand(uint(s.w))
		pos := s.getPos([]byte("pmc"), i, j)
		dist[pos]++
	}
	if len(dist) > 16 {
		t.Error("Expected maximum 16 different positions, got ", len(dist))
	}
}

func TestPMCHashAdd(t *testing.T) {
	flows := make([]string, 5, 5)
	s, _ := NewForMaxFlows(100000)
	for j := range flows {
		flows[j] = "flow" + strconv.Itoa(j)
		for i := 0; i < 1000000; i++ {
			if i%(j+1) == 0 {
				s.Increment([]byte(flows[j]))
			}
		}
	}
	for i, v := range flows {
		fCount := s.GetEstimate([]byte(v))
		fErr := 100 - (100 * (1 - float64(fCount)/(10000000/float64(i+1))))
		if math.Abs(fErr) > 11 {
			t.Errorf("Expected error for flow '%s' <= 10%%, got %f", v, math.Abs(fErr))
		}
	}
}
<|endoftext|>


<commit_before>package glug

type node struct {
	path     string
	isParam  bool
	methods  map[string][]Plug
	children map[string]*node
}
<commit_msg>Add graft function to node struct<commit_after>package glug

import (
	"strings"
)

type node struct {
	path     string
	isParam  bool
	methods  map[string][]Plug
	children map[string]*node
}

func (curr *node) graft(method string, path string, plugs []Plug) {
	parts := strings.Split(path, "/")[1:]
	depth := len(parts)
	prev := curr

	for index, part := range parts {
		if child, ok := prev.children[part]; ok {
			curr = child
		} else {
			isParam := false

			if part[0] == ':' {
				isParam = true
			}

			curr = &node{
				path:     part,
				children: make(map[string]*node),
				isParam:  isParam,
			}
		}

		if depth == index+1 {
			curr.methods = make(map[string][]Plug)
			curr.methods[method] = plugs
		}

		prev.children[part] = curr
		prev = curr
	}
}
<|endoftext|>


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

package runtime

import _ "unsafe" // for go:linkname

//go:linkname setMaxStack runtime/debug.setMaxStack
func setMaxStack(in int) (out int) {
	out = int(maxstacksize)
	maxstacksize = uintptr(in)
	return out
}

//go:linkname setPanicOnFault runtime/debug.setPanicOnFault
func setPanicOnFault(new bool) (old bool) {
	mp := acquirem()
	old = mp.curg.paniconfault
	mp.curg.paniconfault = new
	releasem(mp)
	return old
}
<commit_msg>runtime: simplify setPanicOnFault slightly<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package runtime

import _ "unsafe" // for go:linkname

//go:linkname setMaxStack runtime/debug.setMaxStack
func setMaxStack(in int) (out int) {
	out = int(maxstacksize)
	maxstacksize = uintptr(in)
	return out
}

//go:linkname setPanicOnFault runtime/debug.setPanicOnFault
func setPanicOnFault(new bool) (old bool) {
	_g_ := getg()
	old = _g_.paniconfault
	_g_.paniconfault = new
	return old
}
<|endoftext|>


<commit_before>// Package jsonapi is for using the JSON-API format: parsing, serialization,
// checking the content-type, etc.
package jsonapi

import (
	"encoding/json"
	"net/http"

	"github.com/gin-gonic/gin"
)

// ContentType is the official mime-type for JSON-API
const ContentType = "application/vnd.api+json"

// Document is JSON-API document, identified by the mediatype
// application/vnd.api+json
// See http://jsonapi.org/format/#document-structure
type Document struct {
	Data     *json.RawMessage `json:"data,omitempty"`
	Errors   ErrorList        `json:"errors,omitempty"`
	Links    *LinksList       `json:"links,omitempty"`
	Included []interface{}    `json:"included,omitempty"`
}

// Data can be called to send an answer with a JSON-API document containing a
// single object as data
func Data(c *gin.Context, statusCode int, o Object, links *LinksList) {
	var included []interface{}
	for _, o := range o.Included() {
		data, err := MarshalObject(o)
		if err != nil {
			AbortWithError(c, InternalServerError(err))
			return
		}
		included = append(included, &data)
	}
	data, err := MarshalObject(o)
	if err != nil {
		AbortWithError(c, InternalServerError(err))
		return
	}
	doc := Document{
		Data:     &data,
		Links:    links,
		Included: included,
	}
	body, err := json.Marshal(doc)
	if err != nil {
		AbortWithError(c, InternalServerError(err))
		return
	}
	c.Data(statusCode, ContentType, body)
}

// AbortWithError can be called to abort the current http request/response
// processing, and send an error in the JSON-API format
//
// TODO could be nice to have AbortWithErrors(c *gin.Context, errors ErrorList)
func AbortWithError(c *gin.Context, e *Error) {
	doc := Document{
		Errors: ErrorList{e},
	}
	body, err := json.Marshal(doc)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}
	c.Data(e.Status, ContentType, body)
	c.Abort()
}

func Bind(req *http.Request, attrs interface{}) (*ObjectMarshalling, error) {
	decoder := json.NewDecoder(req.Body)
	var doc *Document
	if err := decoder.Decode(&doc); err != nil {
		return nil, err
	}
	var obj *ObjectMarshalling
	if err := json.Unmarshal(*doc.Data, &obj); err != nil {
		return nil, err
	}
	if err := json.Unmarshal(*obj.Attributes, &attrs); err != nil {
		return nil, err
	}
	return obj, nil
}
<commit_msg>Add comments on jsonapi.Bind function<commit_after>// Package jsonapi is for using the JSON-API format: parsing, serialization,
// checking the content-type, etc.
package jsonapi

import (
	"encoding/json"
	"net/http"

	"github.com/gin-gonic/gin"
)

// ContentType is the official mime-type for JSON-API
const ContentType = "application/vnd.api+json"

// Document is JSON-API document, identified by the mediatype
// application/vnd.api+json
// See http://jsonapi.org/format/#document-structure
type Document struct {
	Data     *json.RawMessage `json:"data,omitempty"`
	Errors   ErrorList        `json:"errors,omitempty"`
	Links    *LinksList       `json:"links,omitempty"`
	Included []interface{}    `json:"included,omitempty"`
}

// Data can be called to send an answer with a JSON-API document containing a
// single object as data
func Data(c *gin.Context, statusCode int, o Object, links *LinksList) {
	var included []interface{}
	for _, o := range o.Included() {
		data, err := MarshalObject(o)
		if err != nil {
			AbortWithError(c, InternalServerError(err))
			return
		}
		included = append(included, &data)
	}
	data, err := MarshalObject(o)
	if err != nil {
		AbortWithError(c, InternalServerError(err))
		return
	}
	doc := Document{
		Data:     &data,
		Links:    links,
		Included: included,
	}
	body, err := json.Marshal(doc)
	if err != nil {
		AbortWithError(c, InternalServerError(err))
		return
	}
	c.Data(statusCode, ContentType, body)
}

// AbortWithError can be called to abort the current http request/response
// processing, and send an error in the JSON-API format
//
// TODO could be nice to have AbortWithErrors(c *gin.Context, errors ErrorList)
func AbortWithError(c *gin.Context, e *Error) {
	doc := Document{
		Errors: ErrorList{e},
	}
	body, err := json.Marshal(doc)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}
	c.Data(e.Status, ContentType, body)
	c.Abort()
}

// Bind is used to unmarshal an input JSONApi document. It binds an
// incoming request to a attribute type.
func Bind(req *http.Request, attrs interface{}) (*ObjectMarshalling, error) {
	decoder := json.NewDecoder(req.Body)
	var doc *Document
	if err := decoder.Decode(&doc); err != nil {
		return nil, err
	}
	var obj *ObjectMarshalling
	if err := json.Unmarshal(*doc.Data, &obj); err != nil {
		return nil, err
	}
	if err := json.Unmarshal(*obj.Attributes, &attrs); err != nil {
		return nil, err
	}
	return obj, nil
}
<|endoftext|>


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

// deepcopy-gen is a tool for auto-generating DeepCopy functions.
//
// Given a list of input directories, it will generate functions that
// efficiently perform a full deep-copy of each type.  For any type that
// offers a `.DeepCopy()` method, it will simply call that.  Otherwise it will
// use standard value assignment whenever possible.  If that is not possible it
// will try to call its own generated copy function for the type, if the type is
// within the allowed root packages.  Failing that, it will fall back on
// `conversion.Cloner.DeepCopy(val)` to make the copy.  The resulting file will
// be stored in the same directory as the processed source package.
//
// Generation is governed by comment tags in the source.  Any package may
// request DeepCopy generation by including a comment in the file-comments of
// one file, of the form:
//   // +k8s:deepcopy-gen=package
//
// Packages can request that the generated DeepCopy functions be registered
// with an `init()` function call to `Scheme.AddGeneratedDeepCopyFuncs()` by
// changing the tag to:
//   // +k8s:deepcopy-gen=package,register
//
// DeepCopy functions can be generated for individual types, rather than the
// entire package by specifying a comment on the type definion of the form:
//   // +k8s:deepcopy-gen=true
//
// When generating for a whole package, individual types may opt out of
// DeepCopy generation by specifying a comment on the of the form:
//   // +k8s:deepcopy-gen=false
//
// Note that registration is a whole-package option, and is not available for
// individual types.
package main

import (
	"k8s.io/kubernetes/cmd/libs/go2idl/args"
	"k8s.io/kubernetes/cmd/libs/go2idl/deepcopy-gen/generators"

	"github.com/golang/glog"
	"github.com/spf13/pflag"
)

func main() {
	arguments := args.Default()

	// Override defaults.
	arguments.OutputFileBaseName = "deep_copy_generated"

	// Custom args.
	customArgs := &generators.CustomArgs{}
	pflag.CommandLine.StringSliceVar(&customArgs.BoundingDirs, "bounding-dirs", customArgs.BoundingDirs,
		"Comma-separated list of import paths which bound the types for which deep-copies will be generated.")
	arguments.CustomArgs = customArgs

	// Run it.
	if err := arguments.Execute(
		generators.NameSystems(),
		generators.DefaultNameSystem(),
		generators.Packages,
	); err != nil {
		glog.Fatalf("Error: %v", err)
	}
	glog.Info("Completed successfully.")
}
<commit_msg>s/deep_copy/deepcopy/<commit_after>/*
Copyright 2015 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.
*/

// deepcopy-gen is a tool for auto-generating DeepCopy functions.
//
// Given a list of input directories, it will generate functions that
// efficiently perform a full deep-copy of each type.  For any type that
// offers a `.DeepCopy()` method, it will simply call that.  Otherwise it will
// use standard value assignment whenever possible.  If that is not possible it
// will try to call its own generated copy function for the type, if the type is
// within the allowed root packages.  Failing that, it will fall back on
// `conversion.Cloner.DeepCopy(val)` to make the copy.  The resulting file will
// be stored in the same directory as the processed source package.
//
// Generation is governed by comment tags in the source.  Any package may
// request DeepCopy generation by including a comment in the file-comments of
// one file, of the form:
//   // +k8s:deepcopy-gen=package
//
// Packages can request that the generated DeepCopy functions be registered
// with an `init()` function call to `Scheme.AddGeneratedDeepCopyFuncs()` by
// changing the tag to:
//   // +k8s:deepcopy-gen=package,register
//
// DeepCopy functions can be generated for individual types, rather than the
// entire package by specifying a comment on the type definion of the form:
//   // +k8s:deepcopy-gen=true
//
// When generating for a whole package, individual types may opt out of
// DeepCopy generation by specifying a comment on the of the form:
//   // +k8s:deepcopy-gen=false
//
// Note that registration is a whole-package option, and is not available for
// individual types.
package main

import (
	"k8s.io/kubernetes/cmd/libs/go2idl/args"
	"k8s.io/kubernetes/cmd/libs/go2idl/deepcopy-gen/generators"

	"github.com/golang/glog"
	"github.com/spf13/pflag"
)

func main() {
	arguments := args.Default()

	// Override defaults.
	arguments.OutputFileBaseName = "deepcopy_generated"

	// Custom args.
	customArgs := &generators.CustomArgs{}
	pflag.CommandLine.StringSliceVar(&customArgs.BoundingDirs, "bounding-dirs", customArgs.BoundingDirs,
		"Comma-separated list of import paths which bound the types for which deep-copies will be generated.")
	arguments.CustomArgs = customArgs

	// Run it.
	if err := arguments.Execute(
		generators.NameSystems(),
		generators.DefaultNameSystem(),
		generators.Packages,
	); err != nil {
		glog.Fatalf("Error: %v", err)
	}
	glog.Info("Completed successfully.")
}
<|endoftext|>


<commit_before>// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gudp

import (
	"errors"
	"io"
	"net"
	"time"

	"github.com/gogf/gf/g/internal/errors"
)

// 封装的UDP链接对象
type Conn struct {
	*net.UDPConn                 // 底层链接对象
	raddr          *net.UDPAddr  // 远程地址
	recvDeadline   time.Time     // 读取超时时间
	sendDeadline   time.Time     // 写入超时时间
	recvBufferWait time.Duration // 读取全部缓冲区数据时，读取完毕后的写入等待间隔
}

const (
	gDEFAULT_RETRY_INTERVAL   = 100              // (毫秒)默认重试时间间隔
	gDEFAULT_READ_BUFFER_SIZE = 64               // (KB)默认数据读取缓冲区大小
	gRECV_ALL_WAIT_TIMEOUT    = time.Millisecond // 读取全部缓冲数据时，没有缓冲数据时的等待间隔
)

type Retry struct {
	Count    int // 重试次数
	Interval int // 重试间隔(毫秒)
}

// 创建TCP链接
func NewConn(raddr string, laddr ...string) (*Conn, error) {
	if conn, err := NewNetConn(raddr, laddr...); err == nil {
		return NewConnByNetConn(conn), nil
	} else {
		return nil, err
	}
}

// 将*net.UDPConn对象转换为*Conn对象
func NewConnByNetConn(udp *net.UDPConn) *Conn {
	return &Conn{
		UDPConn:        udp,
		recvDeadline:   time.Time{},
		sendDeadline:   time.Time{},
		recvBufferWait: gRECV_ALL_WAIT_TIMEOUT,
	}
}

// 发送数据
func (c *Conn) Send(data []byte, retry ...Retry) (err error) {
	for {
		if c.raddr != nil {
			_, err = c.WriteToUDP(data, c.raddr)
		} else {
			_, err = c.Write(data)
		}
		if err != nil {
			// 链接已关闭
			if err == io.EOF {
				return err
			}
			// 其他错误，重试之后仍不能成功
			if len(retry) == 0 || retry[0].Count == 0 {
				return err
			}
			if len(retry) > 0 {
				retry[0].Count--
				if retry[0].Interval == 0 {
					retry[0].Interval = gDEFAULT_RETRY_INTERVAL
				}
				time.Sleep(time.Duration(retry[0].Interval) * time.Millisecond)
			}
		} else {
			return nil
		}
	}
}

// 接收UDP协议数据.
//
// 注意事项：
// 1、UDP协议存在消息边界，因此使用 length < 0 可以获取缓冲区所有消息包数据，即一个完整包；
// 2、当length = 0时，表示获取当前的缓冲区数据，获取一次后立即返回；
func (c *Conn) Recv(length int, retry ...Retry) ([]byte, error) {
	var err error          // 读取错误
	var size int           // 读取长度
	var index int          // 已读取长度
	var raddr *net.UDPAddr // 当前读取的远程地址
	var buffer []byte      // 读取缓冲区
	var bufferWait bool    // 是否设置读取的超时时间

	if length > 0 {
		buffer = make([]byte, length)
	} else {
		buffer = make([]byte, gDEFAULT_READ_BUFFER_SIZE)
	}

	for {
		if length < 0 && index > 0 {
			bufferWait = true
			if err = c.SetReadDeadline(time.Now().Add(c.recvBufferWait)); err != nil {
				return nil, err
			}
		}
		size, raddr, err = c.ReadFromUDP(buffer[index:])
		if err == nil {
			c.raddr = raddr
		}
		if size > 0 {
			index += size
			if length > 0 {
				// 如果指定了读取大小，那么必须读取到指定长度才返回
				if index == length {
					break
				}
			} else {
				if index >= gDEFAULT_READ_BUFFER_SIZE {
					// 如果长度超过了自定义的读取缓冲区，那么自动增长
					buffer = append(buffer, make([]byte, gDEFAULT_READ_BUFFER_SIZE)...)
				} else {
					// 如果第一次读取的数据并未达到缓冲变量长度，那么直接返回
					if !bufferWait {
						break
					}
				}
			}
		}
		if err != nil {
			// 链接已关闭
			if err == io.EOF {
				break
			}
			// 判断数据是否全部读取完毕(由于超时机制的存在，获取的数据完整性不可靠)
			if bufferWait && isTimeout(err) {
				if err = c.SetReadDeadline(c.recvDeadline); err != nil {
					return nil, err
				}
				err = nil
				break
			}
			if len(retry) > 0 {
				// 其他错误，重试之后仍不能成功
				if retry[0].Count == 0 {
					break
				}
				retry[0].Count--
				if retry[0].Interval == 0 {
					retry[0].Interval = gDEFAULT_RETRY_INTERVAL
				}
				time.Sleep(time.Duration(retry[0].Interval) * time.Millisecond)
				continue
			}
			break
		}
		// 只获取一次数据
		if length == 0 {
			break
		}
	}
	return buffer[:index], err
}

// 发送数据并等待接收返回数据
func (c *Conn) SendRecv(data []byte, receive int, retry ...Retry) ([]byte, error) {
	if err := c.Send(data, retry...); err == nil {
		return c.Recv(receive, retry...)
	} else {
		return nil, err
	}
}

// 带超时时间的数据获取
func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry) (data []byte, err error) {
	if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil {
		return nil, err
	}
	defer func() {
		err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
	}()
	data, err = c.Recv(length, retry...)
	return
}

// 带超时时间的数据发送
func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retry) (err error) {
	if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil {
		return err
	}
	defer func() {
		err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
	}()
	err = c.Send(data, retry...)
	return
}

// 发送数据并等待接收返回数据(带返回超时等待时间)
func (c *Conn) SendRecvWithTimeout(data []byte, receive int, timeout time.Duration, retry ...Retry) ([]byte, error) {
	if err := c.Send(data, retry...); err == nil {
		return c.RecvWithTimeout(receive, timeout, retry...)
	} else {
		return nil, err
	}
}

func (c *Conn) SetDeadline(t time.Time) error {
	err := c.UDPConn.SetDeadline(t)
	if err == nil {
		c.recvDeadline = t
		c.sendDeadline = t
	}
	return err
}

func (c *Conn) SetRecvDeadline(t time.Time) error {
	err := c.SetReadDeadline(t)
	if err == nil {
		c.recvDeadline = t
	}
	return err
}

func (c *Conn) SetSendDeadline(t time.Time) error {
	err := c.SetWriteDeadline(t)
	if err == nil {
		c.sendDeadline = t
	}
	return err
}

// 读取全部缓冲区数据时，读取完毕后的写入等待间隔，如果超过该等待时间后仍无可读数据，那么读取操作返回。
// 该时间间隔不能设置得太大，会影响Recv读取时长(默认为1毫秒)。
func (c *Conn) SetRecvBufferWait(d time.Duration) {
	c.recvBufferWait = d
}

// 不能使用c.conn.RemoteAddr()，其返回为nil，
// 这里使用c.raddr获取远程连接地址。
func (c *Conn) RemoteAddr() net.Addr {
	//return c.conn.RemoteAddr()
	return c.raddr
}
<commit_msg>Update gudp_conn.go<commit_after>// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gudp

import (
	"io"
	"net"
	"time"

	"github.com/gogf/gf/g/internal/errors"
)

// 封装的UDP链接对象
type Conn struct {
	*net.UDPConn                 // 底层链接对象
	raddr          *net.UDPAddr  // 远程地址
	recvDeadline   time.Time     // 读取超时时间
	sendDeadline   time.Time     // 写入超时时间
	recvBufferWait time.Duration // 读取全部缓冲区数据时，读取完毕后的写入等待间隔
}

const (
	gDEFAULT_RETRY_INTERVAL   = 100              // (毫秒)默认重试时间间隔
	gDEFAULT_READ_BUFFER_SIZE = 64               // (KB)默认数据读取缓冲区大小
	gRECV_ALL_WAIT_TIMEOUT    = time.Millisecond // 读取全部缓冲数据时，没有缓冲数据时的等待间隔
)

type Retry struct {
	Count    int // 重试次数
	Interval int // 重试间隔(毫秒)
}

// 创建TCP链接
func NewConn(raddr string, laddr ...string) (*Conn, error) {
	if conn, err := NewNetConn(raddr, laddr...); err == nil {
		return NewConnByNetConn(conn), nil
	} else {
		return nil, err
	}
}

// 将*net.UDPConn对象转换为*Conn对象
func NewConnByNetConn(udp *net.UDPConn) *Conn {
	return &Conn{
		UDPConn:        udp,
		recvDeadline:   time.Time{},
		sendDeadline:   time.Time{},
		recvBufferWait: gRECV_ALL_WAIT_TIMEOUT,
	}
}

// 发送数据
func (c *Conn) Send(data []byte, retry ...Retry) (err error) {
	for {
		if c.raddr != nil {
			_, err = c.WriteToUDP(data, c.raddr)
		} else {
			_, err = c.Write(data)
		}
		if err != nil {
			// 链接已关闭
			if err == io.EOF {
				return err
			}
			// 其他错误，重试之后仍不能成功
			if len(retry) == 0 || retry[0].Count == 0 {
				return err
			}
			if len(retry) > 0 {
				retry[0].Count--
				if retry[0].Interval == 0 {
					retry[0].Interval = gDEFAULT_RETRY_INTERVAL
				}
				time.Sleep(time.Duration(retry[0].Interval) * time.Millisecond)
			}
		} else {
			return nil
		}
	}
}

// 接收UDP协议数据.
//
// 注意事项：
// 1、UDP协议存在消息边界，因此使用 length < 0 可以获取缓冲区所有消息包数据，即一个完整包；
// 2、当length = 0时，表示获取当前的缓冲区数据，获取一次后立即返回；
func (c *Conn) Recv(length int, retry ...Retry) ([]byte, error) {
	var err error          // 读取错误
	var size int           // 读取长度
	var index int          // 已读取长度
	var raddr *net.UDPAddr // 当前读取的远程地址
	var buffer []byte      // 读取缓冲区
	var bufferWait bool    // 是否设置读取的超时时间

	if length > 0 {
		buffer = make([]byte, length)
	} else {
		buffer = make([]byte, gDEFAULT_READ_BUFFER_SIZE)
	}

	for {
		if length < 0 && index > 0 {
			bufferWait = true
			if err = c.SetReadDeadline(time.Now().Add(c.recvBufferWait)); err != nil {
				return nil, err
			}
		}
		size, raddr, err = c.ReadFromUDP(buffer[index:])
		if err == nil {
			c.raddr = raddr
		}
		if size > 0 {
			index += size
			if length > 0 {
				// 如果指定了读取大小，那么必须读取到指定长度才返回
				if index == length {
					break
				}
			} else {
				if index >= gDEFAULT_READ_BUFFER_SIZE {
					// 如果长度超过了自定义的读取缓冲区，那么自动增长
					buffer = append(buffer, make([]byte, gDEFAULT_READ_BUFFER_SIZE)...)
				} else {
					// 如果第一次读取的数据并未达到缓冲变量长度，那么直接返回
					if !bufferWait {
						break
					}
				}
			}
		}
		if err != nil {
			// 链接已关闭
			if err == io.EOF {
				break
			}
			// 判断数据是否全部读取完毕(由于超时机制的存在，获取的数据完整性不可靠)
			if bufferWait && isTimeout(err) {
				if err = c.SetReadDeadline(c.recvDeadline); err != nil {
					return nil, err
				}
				err = nil
				break
			}
			if len(retry) > 0 {
				// 其他错误，重试之后仍不能成功
				if retry[0].Count == 0 {
					break
				}
				retry[0].Count--
				if retry[0].Interval == 0 {
					retry[0].Interval = gDEFAULT_RETRY_INTERVAL
				}
				time.Sleep(time.Duration(retry[0].Interval) * time.Millisecond)
				continue
			}
			break
		}
		// 只获取一次数据
		if length == 0 {
			break
		}
	}
	return buffer[:index], err
}

// 发送数据并等待接收返回数据
func (c *Conn) SendRecv(data []byte, receive int, retry ...Retry) ([]byte, error) {
	if err := c.Send(data, retry...); err == nil {
		return c.Recv(receive, retry...)
	} else {
		return nil, err
	}
}

// 带超时时间的数据获取
func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry) (data []byte, err error) {
	if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil {
		return nil, err
	}
	defer func() {
		err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
	}()
	data, err = c.Recv(length, retry...)
	return
}

// 带超时时间的数据发送
func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retry) (err error) {
	if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil {
		return err
	}
	defer func() {
		err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
	}()
	err = c.Send(data, retry...)
	return
}

// 发送数据并等待接收返回数据(带返回超时等待时间)
func (c *Conn) SendRecvWithTimeout(data []byte, receive int, timeout time.Duration, retry ...Retry) ([]byte, error) {
	if err := c.Send(data, retry...); err == nil {
		return c.RecvWithTimeout(receive, timeout, retry...)
	} else {
		return nil, err
	}
}

func (c *Conn) SetDeadline(t time.Time) error {
	err := c.UDPConn.SetDeadline(t)
	if err == nil {
		c.recvDeadline = t
		c.sendDeadline = t
	}
	return err
}

func (c *Conn) SetRecvDeadline(t time.Time) error {
	err := c.SetReadDeadline(t)
	if err == nil {
		c.recvDeadline = t
	}
	return err
}

func (c *Conn) SetSendDeadline(t time.Time) error {
	err := c.SetWriteDeadline(t)
	if err == nil {
		c.sendDeadline = t
	}
	return err
}

// 读取全部缓冲区数据时，读取完毕后的写入等待间隔，如果超过该等待时间后仍无可读数据，那么读取操作返回。
// 该时间间隔不能设置得太大，会影响Recv读取时长(默认为1毫秒)。
func (c *Conn) SetRecvBufferWait(d time.Duration) {
	c.recvBufferWait = d
}

// 不能使用c.conn.RemoteAddr()，其返回为nil，
// 这里使用c.raddr获取远程连接地址。
func (c *Conn) RemoteAddr() net.Addr {
	//return c.conn.RemoteAddr()
	return c.raddr
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 The btcsuite developers
// Copyright (c) 2017 The Lightning Network Developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package builder

import (
	"crypto/rand"
	"encoding/binary"

	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcd/txscript"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil/gcs"
)

// DefaultP is the default collision probability (2^-20)
const DefaultP = 20

// GCSBuilder is a utility class that makes building GCS filters convenient.
type GCSBuilder struct {
	p    uint8
	key  [gcs.KeySize]byte
	data [][]byte
	err  error
}

// RandomKey is a utility function that returns a cryptographically random
// [gcs.KeySize]byte usable as a key for a GCS filter.
func RandomKey() ([gcs.KeySize]byte, error) {
	var key [gcs.KeySize]byte

	// Read a byte slice from rand.Reader.
	randKey := make([]byte, gcs.KeySize)
	_, err := rand.Read(randKey)

	// This shouldn't happen unless the user is on a system that doesn't
	// have a system CSPRNG. OK to panic in this case.
	if err != nil {
		return key, err
	}

	// Copy the byte slice to a [gcs.KeySize]byte array and return it.
	copy(key[:], randKey[:])
	return key, nil
}

// DeriveKey is a utility function that derives a key from a chainhash.Hash by
// truncating the bytes of the hash to the appopriate key size.
func DeriveKey(keyHash *chainhash.Hash) [gcs.KeySize]byte {
	var key [gcs.KeySize]byte
	copy(key[:], keyHash.CloneBytes()[:])
	return key
}

// OutPointToFilterEntry is a utility function that derives a filter entry from
// a wire.OutPoint in a standardized way for use with both building and
// querying filters.
func OutPointToFilterEntry(outpoint wire.OutPoint) []byte {
	// Size of the hash plus size of int32 index
	data := make([]byte, chainhash.HashSize+4)
	copy(data[:], outpoint.Hash.CloneBytes()[:])
	binary.LittleEndian.PutUint32(data[chainhash.HashSize:], outpoint.Index)
	return data
}

// Key retrieves the key with which the builder will build a filter. This is
// useful if the builder is created with a random initial key.
func (b *GCSBuilder) Key() ([gcs.KeySize]byte, error) {
	// Do nothing if the builder's errored out.
	if b.err != nil {
		return [gcs.KeySize]byte{}, b.err
	}

	return b.key, nil
}

// SetKey sets the key with which the builder will build a filter to the passed
// [gcs.KeySize]byte.
func (b *GCSBuilder) SetKey(key [gcs.KeySize]byte) *GCSBuilder {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return b
	}

	copy(b.key[:], key[:])
	return b
}

// SetKeyFromHash sets the key with which the builder will build a filter to a
// key derived from the passed chainhash.Hash using DeriveKey().
func (b *GCSBuilder) SetKeyFromHash(keyHash *chainhash.Hash) *GCSBuilder {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return b
	}

	return b.SetKey(DeriveKey(keyHash))
}

// SetP sets the filter's probability after calling Builder().
func (b *GCSBuilder) SetP(p uint8) *GCSBuilder {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return b
	}

	// Basic sanity check.
	if p > 32 {
		b.err = gcs.ErrPTooBig
		return b
	}

	b.p = p
	return b
}

// Preallocate sets the estimated filter size after calling Builder() to reduce
// the probability of memory reallocations. If the builder has already had data
// added to it, Preallocate has no effect.
func (b *GCSBuilder) Preallocate(n uint32) *GCSBuilder {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return b
	}

	if len(b.data) == 0 {
		b.data = make([][]byte, 0, n)
	}

	return b
}

// AddEntry adds a []byte to the list of entries to be included in the GCS
// filter when it's built.
func (b *GCSBuilder) AddEntry(data []byte) *GCSBuilder {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return b
	}

	b.data = append(b.data, data)
	return b
}

// AddEntries adds all the []byte entries in a [][]byte to the list of entries
// to be included in the GCS filter when it's built.
func (b *GCSBuilder) AddEntries(data [][]byte) *GCSBuilder {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return b
	}

	for _, entry := range data {
		b.AddEntry(entry)
	}
	return b
}

// AddOutPoint adds a wire.OutPoint to the list of entries to be included in
// the GCS filter when it's built.
func (b *GCSBuilder) AddOutPoint(outpoint wire.OutPoint) *GCSBuilder {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return b
	}

	return b.AddEntry(OutPointToFilterEntry(outpoint))
}

// AddHash adds a chainhash.Hash to the list of entries to be included in the
// GCS filter when it's built.
func (b *GCSBuilder) AddHash(hash *chainhash.Hash) *GCSBuilder {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return b
	}

	return b.AddEntry(hash.CloneBytes())
}

// AddScript adds all the data pushed in the script serialized as the passed
// []byte to the list of entries to be included in the GCS filter when it's
// built.
func (b *GCSBuilder) AddScript(script []byte) *GCSBuilder {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return b
	}

	// Ignore errors and add pushed data, if any
	data, _ := txscript.PushedData(script)
	if len(data) == 0 {
		return b
	}

	return b.AddEntries(data)
}

// AddWitness adds each item of the passed filter stack to the filer.
func (b *GCSBuilder) AddWitness(witness wire.TxWitness) *GCSBuilder {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return b
	}

	return b.AddEntries(witness)
}

// Build returns a function which builds a GCS filter with the given parameters
// and data.
func (b *GCSBuilder) Build() (*gcs.Filter, error) {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return nil, b.err
	}

	return gcs.BuildGCSFilter(b.p, b.key, b.data)
}

// WithKeyPN creates a GCSBuilder with specified key and the passed probability
// and estimated filter size.
func WithKeyPN(key [gcs.KeySize]byte, p uint8, n uint32) *GCSBuilder {
	b := GCSBuilder{}
	return b.SetKey(key).SetP(p).Preallocate(n)
}

// WithKeyP creates a GCSBuilder with specified key and the passed probability.
// Estimated filter size is set to zero, which means more reallocations are
// done when building the filter.
func WithKeyP(key [gcs.KeySize]byte, p uint8) *GCSBuilder {
	return WithKeyPN(key, p, 0)
}

// WithKey creates a GCSBuilder with specified key. Probability is set to
// 20 (2^-20 collision probability). Estimated filter size is set to zero, which
// means more reallocations are done when building the filter.
func WithKey(key [gcs.KeySize]byte) *GCSBuilder {
	return WithKeyPN(key, DefaultP, 0)
}

// WithKeyHashPN creates a GCSBuilder with key derived from the specified
// chainhash.Hash and the passed probability and estimated filter size.
func WithKeyHashPN(keyHash *chainhash.Hash, p uint8, n uint32) *GCSBuilder {
	return WithKeyPN(DeriveKey(keyHash), p, n)
}

// WithKeyHashP creates a GCSBuilder with key derived from the specified
// chainhash.Hash and the passed probability. Estimated filter size is set to
// zero, which means more reallocations are done when building the filter.
func WithKeyHashP(keyHash *chainhash.Hash, p uint8) *GCSBuilder {
	return WithKeyHashPN(keyHash, p, 0)
}

// WithKeyHash creates a GCSBuilder with key derived from the specified
// chainhash.Hash. Probability is set to 20 (2^-20 collision probability).
// Estimated filter size is set to zero, which means more reallocations are
// done when building the filter.
func WithKeyHash(keyHash *chainhash.Hash) *GCSBuilder {
	return WithKeyHashPN(keyHash, DefaultP, 0)
}

// WithRandomKeyPN creates a GCSBuilder with a cryptographically random key and
// the passed probability and estimated filter size.
func WithRandomKeyPN(p uint8, n uint32) *GCSBuilder {
	key, err := RandomKey()
	if err != nil {
		b := GCSBuilder{err: err}
		return &b
	}
	return WithKeyPN(key, p, n)
}

// WithRandomKeyP creates a GCSBuilder with a cryptographically random key and
// the passed probability. Estimated filter size is set to zero, which means
// more reallocations are done when building the filter.
func WithRandomKeyP(p uint8) *GCSBuilder {
	return WithRandomKeyPN(p, 0)
}

// WithRandomKey creates a GCSBuilder with a cryptographically random key.
// Probability is set to 20 (2^-20 collision probability). Estimated filter
// size is set to zero, which means more reallocations are done when
// building the filter.
func WithRandomKey() *GCSBuilder {
	return WithRandomKeyPN(DefaultP, 0)
}

// BuildBasicFilter builds a basic GCS filter from a block. A basic GCS filter
// will contain all the previous outpoints spent within a block, as well as the
// data pushes within all the outputs created within a block.
func BuildBasicFilter(block *wire.MsgBlock) (*gcs.Filter, error) {
	blockHash := block.BlockHash()
	b := WithKeyHash(&blockHash)

	// If the filter had an issue with the specified key, then we force it
	// to bubble up here by calling the Key() function.
	_, err := b.Key()
	if err != nil {
		return nil, err
	}

	// In order to build a basic filter, we'll range over the entire block,
	// adding the outpoint data as well as the data pushes within the
	// pkScript.
	for i, tx := range block.Transactions {
		// Skip the inputs for the coinbase transaction
		if i != 0 {
			// Each each txin, we'll add a serialized version of
			// the txid:index to the filters data slices.
			for _, txIn := range tx.TxIn {
				b.AddOutPoint(txIn.PreviousOutPoint)
			}
		}

		// For each output in a transaction, we'll add each of the
		// individual data pushes within the script.
		for _, txOut := range tx.TxOut {
			b.AddScript(txOut.PkScript)
		}
	}

	return b.Build()
}

// BuildExtFilter builds an extended GCS filter from a block. An extended
// filter supplements a regular basic filter by include all the _witness_ data
// found within a block. This includes all the data pushes within any signature
// scripts as well as each element of an input's witness stack. Additionally,
// the _hashes_ of each transaction are also inserted into the filter.
func BuildExtFilter(block *wire.MsgBlock) (*gcs.Filter, error) {
	blockHash := block.BlockHash()
	b := WithKeyHash(&blockHash)

	// If the filter had an issue with the specified key, then we force it
	// to bubble up here by calling the Key() function.
	_, err := b.Key()
	if err != nil {
		return nil, err
	}

	// In order to build an extended filter, we add the hash of each
	// transaction as well as each piece of witness data included in both
	// the sigScript and the witness stack of an input.
	for i, tx := range block.Transactions {
		// First we'll compute the bash of the transaction and add that
		// directly to the filter.
		txHash := tx.TxHash()
		b.AddHash(&txHash)

		// Skip the inputs for the coinbase transaction
		if i != 0 {
			// Next, for each input, we'll add the sigScript (if
			// it's present), and also the witness stack (if it's
			// present)
			for _, txIn := range tx.TxIn {
				if txIn.SignatureScript != nil {
					b.AddScript(txIn.SignatureScript)
				}

				if len(txIn.Witness) != 0 {
					b.AddWitness(txIn.Witness)
				}
			}
		}
	}

	return b.Build()
}

// GetFilterHash returns the double-SHA256 of the filter.
func GetFilterHash(filter *gcs.Filter) chainhash.Hash {
	hash1 := chainhash.HashH(filter.NBytes())
	return chainhash.HashH(hash1[:])
}

// MakeHeaderForFilter makes a filter chain header for a filter, given the
// filter and the previous filter chain header.
func MakeHeaderForFilter(filter *gcs.Filter, prevHeader chainhash.Hash) chainhash.Hash {
	filterTip := make([]byte, 2*chainhash.HashSize)
	filterHash := GetFilterHash(filter)

	// In the buffer we created above we'll compute hash || prevHash as an
	// intermediate value.
	copy(filterTip, filterHash[:])
	copy(filterTip[chainhash.HashSize:], prevHeader[:])

	// The final filter hash is the double-sha256 of the hash computed
	// above.
	hash1 := chainhash.HashH(filterTip)
	return chainhash.HashH(hash1[:])
}
<commit_msg>gcs/builder: an empty filter has a zero-hash<commit_after>// Copyright (c) 2017 The btcsuite developers
// Copyright (c) 2017 The Lightning Network Developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package builder

import (
	"crypto/rand"
	"encoding/binary"

	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcd/txscript"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil/gcs"
)

// DefaultP is the default collision probability (2^-20)
const DefaultP = 20

// GCSBuilder is a utility class that makes building GCS filters convenient.
type GCSBuilder struct {
	p    uint8
	key  [gcs.KeySize]byte
	data [][]byte
	err  error
}

// RandomKey is a utility function that returns a cryptographically random
// [gcs.KeySize]byte usable as a key for a GCS filter.
func RandomKey() ([gcs.KeySize]byte, error) {
	var key [gcs.KeySize]byte

	// Read a byte slice from rand.Reader.
	randKey := make([]byte, gcs.KeySize)
	_, err := rand.Read(randKey)

	// This shouldn't happen unless the user is on a system that doesn't
	// have a system CSPRNG. OK to panic in this case.
	if err != nil {
		return key, err
	}

	// Copy the byte slice to a [gcs.KeySize]byte array and return it.
	copy(key[:], randKey[:])
	return key, nil
}

// DeriveKey is a utility function that derives a key from a chainhash.Hash by
// truncating the bytes of the hash to the appopriate key size.
func DeriveKey(keyHash *chainhash.Hash) [gcs.KeySize]byte {
	var key [gcs.KeySize]byte
	copy(key[:], keyHash.CloneBytes()[:])
	return key
}

// OutPointToFilterEntry is a utility function that derives a filter entry from
// a wire.OutPoint in a standardized way for use with both building and
// querying filters.
func OutPointToFilterEntry(outpoint wire.OutPoint) []byte {
	// Size of the hash plus size of int32 index
	data := make([]byte, chainhash.HashSize+4)
	copy(data[:], outpoint.Hash.CloneBytes()[:])
	binary.LittleEndian.PutUint32(data[chainhash.HashSize:], outpoint.Index)
	return data
}

// Key retrieves the key with which the builder will build a filter. This is
// useful if the builder is created with a random initial key.
func (b *GCSBuilder) Key() ([gcs.KeySize]byte, error) {
	// Do nothing if the builder's errored out.
	if b.err != nil {
		return [gcs.KeySize]byte{}, b.err
	}

	return b.key, nil
}

// SetKey sets the key with which the builder will build a filter to the passed
// [gcs.KeySize]byte.
func (b *GCSBuilder) SetKey(key [gcs.KeySize]byte) *GCSBuilder {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return b
	}

	copy(b.key[:], key[:])
	return b
}

// SetKeyFromHash sets the key with which the builder will build a filter to a
// key derived from the passed chainhash.Hash using DeriveKey().
func (b *GCSBuilder) SetKeyFromHash(keyHash *chainhash.Hash) *GCSBuilder {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return b
	}

	return b.SetKey(DeriveKey(keyHash))
}

// SetP sets the filter's probability after calling Builder().
func (b *GCSBuilder) SetP(p uint8) *GCSBuilder {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return b
	}

	// Basic sanity check.
	if p > 32 {
		b.err = gcs.ErrPTooBig
		return b
	}

	b.p = p
	return b
}

// Preallocate sets the estimated filter size after calling Builder() to reduce
// the probability of memory reallocations. If the builder has already had data
// added to it, Preallocate has no effect.
func (b *GCSBuilder) Preallocate(n uint32) *GCSBuilder {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return b
	}

	if len(b.data) == 0 {
		b.data = make([][]byte, 0, n)
	}

	return b
}

// AddEntry adds a []byte to the list of entries to be included in the GCS
// filter when it's built.
func (b *GCSBuilder) AddEntry(data []byte) *GCSBuilder {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return b
	}

	b.data = append(b.data, data)
	return b
}

// AddEntries adds all the []byte entries in a [][]byte to the list of entries
// to be included in the GCS filter when it's built.
func (b *GCSBuilder) AddEntries(data [][]byte) *GCSBuilder {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return b
	}

	for _, entry := range data {
		b.AddEntry(entry)
	}
	return b
}

// AddOutPoint adds a wire.OutPoint to the list of entries to be included in
// the GCS filter when it's built.
func (b *GCSBuilder) AddOutPoint(outpoint wire.OutPoint) *GCSBuilder {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return b
	}

	return b.AddEntry(OutPointToFilterEntry(outpoint))
}

// AddHash adds a chainhash.Hash to the list of entries to be included in the
// GCS filter when it's built.
func (b *GCSBuilder) AddHash(hash *chainhash.Hash) *GCSBuilder {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return b
	}

	return b.AddEntry(hash.CloneBytes())
}

// AddScript adds all the data pushed in the script serialized as the passed
// []byte to the list of entries to be included in the GCS filter when it's
// built.
func (b *GCSBuilder) AddScript(script []byte) *GCSBuilder {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return b
	}

	// Ignore errors and add pushed data, if any
	data, _ := txscript.PushedData(script)
	if len(data) == 0 {
		return b
	}

	return b.AddEntries(data)
}

// AddWitness adds each item of the passed filter stack to the filer.
func (b *GCSBuilder) AddWitness(witness wire.TxWitness) *GCSBuilder {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return b
	}

	return b.AddEntries(witness)
}

// Build returns a function which builds a GCS filter with the given parameters
// and data.
func (b *GCSBuilder) Build() (*gcs.Filter, error) {
	// Do nothing if the builder's already errored out.
	if b.err != nil {
		return nil, b.err
	}

	return gcs.BuildGCSFilter(b.p, b.key, b.data)
}

// WithKeyPN creates a GCSBuilder with specified key and the passed probability
// and estimated filter size.
func WithKeyPN(key [gcs.KeySize]byte, p uint8, n uint32) *GCSBuilder {
	b := GCSBuilder{}
	return b.SetKey(key).SetP(p).Preallocate(n)
}

// WithKeyP creates a GCSBuilder with specified key and the passed probability.
// Estimated filter size is set to zero, which means more reallocations are
// done when building the filter.
func WithKeyP(key [gcs.KeySize]byte, p uint8) *GCSBuilder {
	return WithKeyPN(key, p, 0)
}

// WithKey creates a GCSBuilder with specified key. Probability is set to
// 20 (2^-20 collision probability). Estimated filter size is set to zero, which
// means more reallocations are done when building the filter.
func WithKey(key [gcs.KeySize]byte) *GCSBuilder {
	return WithKeyPN(key, DefaultP, 0)
}

// WithKeyHashPN creates a GCSBuilder with key derived from the specified
// chainhash.Hash and the passed probability and estimated filter size.
func WithKeyHashPN(keyHash *chainhash.Hash, p uint8, n uint32) *GCSBuilder {
	return WithKeyPN(DeriveKey(keyHash), p, n)
}

// WithKeyHashP creates a GCSBuilder with key derived from the specified
// chainhash.Hash and the passed probability. Estimated filter size is set to
// zero, which means more reallocations are done when building the filter.
func WithKeyHashP(keyHash *chainhash.Hash, p uint8) *GCSBuilder {
	return WithKeyHashPN(keyHash, p, 0)
}

// WithKeyHash creates a GCSBuilder with key derived from the specified
// chainhash.Hash. Probability is set to 20 (2^-20 collision probability).
// Estimated filter size is set to zero, which means more reallocations are
// done when building the filter.
func WithKeyHash(keyHash *chainhash.Hash) *GCSBuilder {
	return WithKeyHashPN(keyHash, DefaultP, 0)
}

// WithRandomKeyPN creates a GCSBuilder with a cryptographically random key and
// the passed probability and estimated filter size.
func WithRandomKeyPN(p uint8, n uint32) *GCSBuilder {
	key, err := RandomKey()
	if err != nil {
		b := GCSBuilder{err: err}
		return &b
	}
	return WithKeyPN(key, p, n)
}

// WithRandomKeyP creates a GCSBuilder with a cryptographically random key and
// the passed probability. Estimated filter size is set to zero, which means
// more reallocations are done when building the filter.
func WithRandomKeyP(p uint8) *GCSBuilder {
	return WithRandomKeyPN(p, 0)
}

// WithRandomKey creates a GCSBuilder with a cryptographically random key.
// Probability is set to 20 (2^-20 collision probability). Estimated filter
// size is set to zero, which means more reallocations are done when
// building the filter.
func WithRandomKey() *GCSBuilder {
	return WithRandomKeyPN(DefaultP, 0)
}

// BuildBasicFilter builds a basic GCS filter from a block. A basic GCS filter
// will contain all the previous outpoints spent within a block, as well as the
// data pushes within all the outputs created within a block.
func BuildBasicFilter(block *wire.MsgBlock) (*gcs.Filter, error) {
	blockHash := block.BlockHash()
	b := WithKeyHash(&blockHash)

	// If the filter had an issue with the specified key, then we force it
	// to bubble up here by calling the Key() function.
	_, err := b.Key()
	if err != nil {
		return nil, err
	}

	// In order to build a basic filter, we'll range over the entire block,
	// adding the outpoint data as well as the data pushes within the
	// pkScript.
	for i, tx := range block.Transactions {
		// Skip the inputs for the coinbase transaction
		if i != 0 {
			// Each each txin, we'll add a serialized version of
			// the txid:index to the filters data slices.
			for _, txIn := range tx.TxIn {
				b.AddOutPoint(txIn.PreviousOutPoint)
			}
		}

		// For each output in a transaction, we'll add each of the
		// individual data pushes within the script.
		for _, txOut := range tx.TxOut {
			b.AddScript(txOut.PkScript)
		}
	}

	return b.Build()
}

// BuildExtFilter builds an extended GCS filter from a block. An extended
// filter supplements a regular basic filter by include all the _witness_ data
// found within a block. This includes all the data pushes within any signature
// scripts as well as each element of an input's witness stack. Additionally,
// the _hashes_ of each transaction are also inserted into the filter.
func BuildExtFilter(block *wire.MsgBlock) (*gcs.Filter, error) {
	blockHash := block.BlockHash()
	b := WithKeyHash(&blockHash)

	// If the filter had an issue with the specified key, then we force it
	// to bubble up here by calling the Key() function.
	_, err := b.Key()
	if err != nil {
		return nil, err
	}

	// In order to build an extended filter, we add the hash of each
	// transaction as well as each piece of witness data included in both
	// the sigScript and the witness stack of an input.
	for i, tx := range block.Transactions {
		// First we'll compute the bash of the transaction and add that
		// directly to the filter.
		txHash := tx.TxHash()
		b.AddHash(&txHash)

		// Skip the inputs for the coinbase transaction
		if i != 0 {
			// Next, for each input, we'll add the sigScript (if
			// it's present), and also the witness stack (if it's
			// present)
			for _, txIn := range tx.TxIn {
				if txIn.SignatureScript != nil {
					b.AddScript(txIn.SignatureScript)
				}

				if len(txIn.Witness) != 0 {
					b.AddWitness(txIn.Witness)
				}
			}
		}
	}

	return b.Build()
}

// GetFilterHash returns the double-SHA256 of the filter.
func GetFilterHash(filter *gcs.Filter) chainhash.Hash {
	var zero chainhash.Hash
	if filter == nil {
		return zero
	}

	hash1 := chainhash.HashH(filter.NBytes())
	return chainhash.HashH(hash1[:])
}

// MakeHeaderForFilter makes a filter chain header for a filter, given the
// filter and the previous filter chain header.
func MakeHeaderForFilter(filter *gcs.Filter, prevHeader chainhash.Hash) chainhash.Hash {
	filterTip := make([]byte, 2*chainhash.HashSize)
	filterHash := GetFilterHash(filter)

	// In the buffer we created above we'll compute hash || prevHash as an
	// intermediate value.
	copy(filterTip, filterHash[:])
	copy(filterTip[chainhash.HashSize:], prevHeader[:])

	// The final filter hash is the double-sha256 of the hash computed
	// above.
	hash1 := chainhash.HashH(filterTip)
	return chainhash.HashH(hash1[:])
}
<|endoftext|>


<commit_before>package resource

import (
	"errors"
	"fmt"
	"log"
	"time"
)

// StateRefreshFunc is a function type used for StateChangeConf that is
// responsible for refreshing the item being watched for a state change.
//
// It returns three results. `result` is any object that will be returned
// as the final object after waiting for state change. This allows you to
// return the final updated object, for example an EC2 instance after refreshing
// it.
//
// `state` is the latest state of that object. And `err` is any error that
// may have happened while refreshing the state.
type StateRefreshFunc func() (result interface{}, state string, err error)

// StateChangeConf is the configuration struct used for `WaitForState`.
type StateChangeConf struct {
	Pending []string         // States that are "allowed" and will continue trying
	Refresh StateRefreshFunc // Refreshes the current state
	Target  string           // Target state
	Timeout time.Duration    // The amount of time to wait before timeout
}

type waitResult struct {
	obj interface{}
	err error
}

// WaitForState watches an object and waits for it to achieve the state
// specified in the configuration using the specified Refresh() func,
// waiting the number of seconds specified in the timeout configuration.
func (conf *StateChangeConf) WaitForState() (i interface{}, err error) {
	log.Printf("[DEBUG] Waiting for state to become: %s", conf.Target)

	notfoundTick := 0

	result := make(chan waitResult, 1)

	go func() {
		for {
			var currentState string
			i, currentState, err = conf.Refresh()
			if err != nil {
				result <- waitResult{nil, err}
				return
			}

			// If we're waiting for the absense of a thing, then return
			if i == nil && conf.Target == "" {
				result <- waitResult{nil, nil}
				return
			}

			if i == nil {
				// If we didn't find the resource, check if we have been
				// not finding it for awhile, and if so, report an error.
				notfoundTick += 1
				if notfoundTick > 20 {
					result <- waitResult{nil, errors.New("couldn't find resource")}
					return
				}
			} else {
				// Reset the counter for when a resource isn't found
				notfoundTick = 0

				if currentState == conf.Target {
					result <- waitResult{i, nil}
					return
				}

				found := false
				for _, allowed := range conf.Pending {
					if currentState == allowed {
						found = true
						break
					}
				}

				if !found {
					result <- waitResult{nil, fmt.Errorf("unexpected state '%s', wanted target '%s'", currentState, conf.Target)}
					return
				}
			}

			// Wait between refreshes
			time.Sleep(2 * time.Second)
		}
	}()

	select {
	case waitResult := <-result:
		err := waitResult.err
		i = waitResult.obj
		return i, err
	case <-time.After(conf.Timeout):
		err := fmt.Errorf("timeout while waiting for state to become '%s'", conf.Target)
		i = nil
		return i, err
	}
}
<commit_msg>helper/resource: exponential backoff<commit_after>package resource

import (
	"errors"
	"fmt"
	"log"
	"math"
	"time"
)

// StateRefreshFunc is a function type used for StateChangeConf that is
// responsible for refreshing the item being watched for a state change.
//
// It returns three results. `result` is any object that will be returned
// as the final object after waiting for state change. This allows you to
// return the final updated object, for example an EC2 instance after refreshing
// it.
//
// `state` is the latest state of that object. And `err` is any error that
// may have happened while refreshing the state.
type StateRefreshFunc func() (result interface{}, state string, err error)

// StateChangeConf is the configuration struct used for `WaitForState`.
type StateChangeConf struct {
	Pending []string         // States that are "allowed" and will continue trying
	Refresh StateRefreshFunc // Refreshes the current state
	Target  string           // Target state
	Timeout time.Duration    // The amount of time to wait before timeout
}

type waitResult struct {
	obj interface{}
	err error
}

// WaitForState watches an object and waits for it to achieve the state
// specified in the configuration using the specified Refresh() func,
// waiting the number of seconds specified in the timeout configuration.
func (conf *StateChangeConf) WaitForState() (i interface{}, err error) {
	log.Printf("[DEBUG] Waiting for state to become: %s", conf.Target)

	notfoundTick := 0

	result := make(chan waitResult, 1)

	go func() {
		for tries := 0; ; tries++ {
			// Wait between refreshes
			wait := time.Duration(math.Pow(2, float64(tries))) *
				100 * time.Millisecond
			log.Printf("[TRACE] Waiting %s before next try", wait)
			time.Sleep(wait)

			var currentState string
			i, currentState, err = conf.Refresh()
			if err != nil {
				result <- waitResult{nil, err}
				return
			}

			// If we're waiting for the absense of a thing, then return
			if i == nil && conf.Target == "" {
				result <- waitResult{nil, nil}
				return
			}

			if i == nil {
				// If we didn't find the resource, check if we have been
				// not finding it for awhile, and if so, report an error.
				notfoundTick += 1
				if notfoundTick > 20 {
					result <- waitResult{nil, errors.New("couldn't find resource")}
					return
				}
			} else {
				// Reset the counter for when a resource isn't found
				notfoundTick = 0

				if currentState == conf.Target {
					result <- waitResult{i, nil}
					return
				}

				found := false
				for _, allowed := range conf.Pending {
					if currentState == allowed {
						found = true
						break
					}
				}

				if !found {
					result <- waitResult{nil, fmt.Errorf("unexpected state '%s', wanted target '%s'", currentState, conf.Target)}
					return
				}
			}
		}
	}()

	select {
	case waitResult := <-result:
		err := waitResult.err
		i = waitResult.obj
		return i, err
	case <-time.After(conf.Timeout):
		err := fmt.Errorf("timeout while waiting for state to become '%s'", conf.Target)
		i = nil
		return i, err
	}
}
<|endoftext|>