<commit_before>package compute

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

// VLAN represents a compute VLAN.
type VLAN struct {
	// The VLAN Id.
	ID string `json:"id"`

	// The VLAN name.
	Name string `json:"name"`

	// The VLAN description.
	Description string `json:"description"`

	// The network domain in which the VLAN is deployed associated.
	NetworkDomain EntityReference `json:"networkDomain"`

	// The VLAN's associated IPv4 network range.
	IPv4Range IPv4Range `json:"privateIpv4Range"`

	// The VLAN's IPv4 gateway address.
	IPv4GatewayAddress string `json:"ipv4GatewayAddress"`

	// The VLAN's associated IPv6 network range.
	IPv6Range IPv6Range `json:"ipv6Range"`

	// The VLAN's IPv6 gateway address.
	IPv6GatewayAddress string `json:"ipv6GatewayAddress"`

	// The date / time that the VLAN was first created.
	CreateTime string `json:"createTime"`

	// The VLAN's current state.
	State string `json:"state"`

	// The ID of the data center in which the VLAN and its containing network domain are deployed.
	DataCenterID string `json:"datacenterId"`
}

// GetID returns the VLAN's Id.
func (vlan *VLAN) GetID() string {
	return vlan.ID
}

// GetResourceType returns the network domain's resource type.
func (vlan *VLAN) GetResourceType() ResourceType {
	return ResourceTypeVLAN
}

// GetName returns the VLAN's name.
func (vlan *VLAN) GetName() string {
	return vlan.Name
}

// GetState returns the VLAN's current state.
func (vlan *VLAN) GetState() string {
	return vlan.State
}

// IsDeleted determines whether the VLAN has been deleted (is nil).
func (vlan *VLAN) IsDeleted() bool {
	return vlan == nil
}

var _ Resource = &VLAN{}

// ToEntityReference creates an EntityReference representing the VLAN.
func (vlan *VLAN) ToEntityReference() EntityReference {
	return EntityReference{
		ID:   vlan.ID,
		Name: vlan.Name,
	}
}

var _ NamedEntity = &VLAN{}

// VLANs represents the response to a "List VLANs" API call.
type VLANs struct {
	// The current page of network domains.
	VLANs []VLAN `json:"vlan"`

	PagedResult
}

// DeployVLAN represents the request body when deploying a cloud compute VLAN.
type DeployVLAN struct {
	// The Id of the network domain in which the VLAN will be deployed.
	VLANID string `json:"networkDomainId"`

	// The VLAN name.
	Name string `json:"name"`

	// The VLAN description.
	Description string `json:"description"`

	// The private IPv4 base address for the VLAN.
	IPv4BaseAddress string `json:"privateIpv4BaseAddress"`

	// The private IPv4 prefix size (i.e. netmask) for the VLAN.
	IPv4PrefixSize int `json:"privateIpv4PrefixSize"`
}

// EditVLAN represents the request body when editing a cloud compute VLAN.
type EditVLAN struct {
	// The ID of the VLAN to edit.
	ID string `json:"id"`

	// The VLAN name (optional).
	Name *string `json:"name,omitempty"`

	// The VLAN description (optional).
	Description *string `json:"description,omitempty"`
}

// DeleteVLAN represents a request to delete a compute VLAN.
type DeleteVLAN struct {
	// The VLAN Id.
	ID string `json:"id"`
}

// GetVLAN retrieves the VLAN with the specified Id.
// id is the Id of the VLAN to retrieve.
// Returns nil if no VLAN is found with the specified Id.
func (client *Client) GetVLAN(id string) (vlan *VLAN, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return nil, err
	}

	requestURI := fmt.Sprintf("%s/network/vlan/%s", organizationID, id)
	request, err := client.newRequestV22(requestURI, http.MethodGet, nil)
	if err != nil {
		return nil, err
	}
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return nil, err
	}

	if statusCode != http.StatusOK {
		var apiResponse *APIResponseV2

		apiResponse, err = readAPIResponseAsJSON(responseBody, statusCode)
		if err != nil {
			return nil, err
		}

		if apiResponse.ResponseCode == ResponseCodeResourceNotFound {
			return nil, nil // Not an error, but was not found.
		}

		return nil, apiResponse.ToError("Request to retrieve VLAN failed with status code %d (%s): %s", statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	vlan = &VLAN{}
	err = json.Unmarshal(responseBody, vlan)

	return vlan, err
}

// GetVLANByName retrieves the VLAN (if any) with the specified name in the specified network domain.
func (client *Client) GetVLANByName(name string, networkDomainID string) (*VLAN, error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return nil, err
	}

	requestURI := fmt.Sprintf("%s/network/vlan?name=%s&networkDomainId=%s",
		url.QueryEscape(organizationID),
		url.QueryEscape(name),
		url.QueryEscape(networkDomainID),
	)
	request, err := client.newRequestV22(requestURI, http.MethodGet, nil)
	if err != nil {
		return nil, err
	}

	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return nil, err
	}

	if statusCode != http.StatusOK {
		var apiResponse *APIResponseV2

		apiResponse, err = readAPIResponseAsJSON(responseBody, statusCode)
		if err != nil {
			return nil, err
		}

		return nil, apiResponse.ToError("Request failed with status code %d (%s): %s", statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	vlans := &VLANs{}
	err = json.Unmarshal(responseBody, vlans)
	if err != nil {
		return nil, err
	}
	if vlans.IsEmpty() {
		return nil, nil // No matching VLAN was found.
	}

	if len(vlans.VLANs) != 1 {
		return nil, fmt.Errorf("Found multiple VLANs (%d) named '%s' in network domain '%s'.", len(vlans.VLANs), name, networkDomainID)
	}

	return &vlans.VLANs[0], nil
}

// ListVLANs retrieves a list of all VLANs in the specified network domain.
// TODO: Support filtering and sorting.
func (client *Client) ListVLANs(networkDomainID string, paging *Paging) (vlans *VLANs, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return nil, err
	}

	requestURI := fmt.Sprintf("%s/network/vlan?networkDomainId=%s&%s",
		organizationID,
		networkDomainID,
		paging.EnsurePaging().toQueryParameters(),
	)
	request, err := client.newRequestV22(requestURI, http.MethodGet, nil)
	if err != nil {
		return nil, err
	}

	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return nil, err
	}

	if statusCode != http.StatusOK {
		var apiResponse *APIResponseV2

		apiResponse, err = readAPIResponseAsJSON(responseBody, statusCode)
		if err != nil {
			return nil, err
		}

		return nil, apiResponse.ToError("Request to list VLANs failed with status code %d (%s): %s", statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	vlans = &VLANs{}
	err = json.Unmarshal(responseBody, vlans)

	return vlans, err
}

// DeployVLAN deploys a new VLAN into a network domain.
func (client *Client) DeployVLAN(networkDomainID string, name string, description string, ipv4BaseAddress string, ipv4PrefixSize int) (vlanID string, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return "", err
	}

	requestURI := fmt.Sprintf("%s/network/deployVlan", organizationID)
	request, err := client.newRequestV22(requestURI, http.MethodPost, &DeployVLAN{
		VLANID:          networkDomainID,
		Name:            name,
		Description:     description,
		IPv4BaseAddress: ipv4BaseAddress,
		IPv4PrefixSize:  ipv4PrefixSize,
	})
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return "", err
	}

	apiResponse, err := readAPIResponseAsJSON(responseBody, statusCode)
	if err != nil {
		return "", err
	}

	if apiResponse.ResponseCode != ResponseCodeInProgress {
		return "", apiResponse.ToError("Request to deploy VLAN '%s' failed with status code %d (%s): %s", name, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	// Expected: "info" { "name": "vlanId", "value": "the-Id-of-the-new-VLAN" }
	vlanIDMessage := apiResponse.GetFieldMessage("vlanId")
	if vlanIDMessage == nil {
		return "", apiResponse.ToError("Received an unexpected response (missing 'vlanId') with status code %d (%s): %s", statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	return *vlanIDMessage, nil
}

// EditVLAN updates an existing VLAN.
// Pass an empty string for any field to retain its existing value.
// Returns an error if the operation was not successful.
func (client *Client) EditVLAN(id string, name *string, description *string) (err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return err
	}

	requestURI := fmt.Sprintf("%s/network/editVlan", organizationID)
	request, err := client.newRequestV22(requestURI, http.MethodPost, &EditVLAN{
		ID:          id,
		Name:        name,
		Description: description,
	})
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return err
	}

	apiResponse, err := readAPIResponseAsJSON(responseBody, statusCode)
	if err != nil {
		return err
	}

	if apiResponse.ResponseCode != ResponseCodeOK {
		return apiResponse.ToError("Request to edit VLAN failed with unexpected status code %d (%s): %s", statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	return nil
}

// DeleteVLAN deletes an existing VLAN.
// Returns an error if the operation was not successful.
func (client *Client) DeleteVLAN(id string) (err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return err
	}

	requestURI := fmt.Sprintf("%s/network/deleteVlan", organizationID)
	request, err := client.newRequestV22(requestURI, http.MethodPost, &DeleteVLAN{id})
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return err
	}

	apiResponse, err := readAPIResponseAsJSON(responseBody, statusCode)
	if err != nil {
		return err
	}

	if apiResponse.ResponseCode != ResponseCodeInProgress {
		return apiResponse.ToError("Request to delete VLAN failed with unexpected status code %d (%s): %s", statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	return nil
}
<commit_msg>Escape query parameters for VLAN request URIs (#7).<commit_after>package compute

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

// VLAN represents a compute VLAN.
type VLAN struct {
	// The VLAN Id.
	ID string `json:"id"`

	// The VLAN name.
	Name string `json:"name"`

	// The VLAN description.
	Description string `json:"description"`

	// The network domain in which the VLAN is deployed associated.
	NetworkDomain EntityReference `json:"networkDomain"`

	// The VLAN's associated IPv4 network range.
	IPv4Range IPv4Range `json:"privateIpv4Range"`

	// The VLAN's IPv4 gateway address.
	IPv4GatewayAddress string `json:"ipv4GatewayAddress"`

	// The VLAN's associated IPv6 network range.
	IPv6Range IPv6Range `json:"ipv6Range"`

	// The VLAN's IPv6 gateway address.
	IPv6GatewayAddress string `json:"ipv6GatewayAddress"`

	// The date / time that the VLAN was first created.
	CreateTime string `json:"createTime"`

	// The VLAN's current state.
	State string `json:"state"`

	// The ID of the data center in which the VLAN and its containing network domain are deployed.
	DataCenterID string `json:"datacenterId"`
}

// GetID returns the VLAN's Id.
func (vlan *VLAN) GetID() string {
	return vlan.ID
}

// GetResourceType returns the network domain's resource type.
func (vlan *VLAN) GetResourceType() ResourceType {
	return ResourceTypeVLAN
}

// GetName returns the VLAN's name.
func (vlan *VLAN) GetName() string {
	return vlan.Name
}

// GetState returns the VLAN's current state.
func (vlan *VLAN) GetState() string {
	return vlan.State
}

// IsDeleted determines whether the VLAN has been deleted (is nil).
func (vlan *VLAN) IsDeleted() bool {
	return vlan == nil
}

var _ Resource = &VLAN{}

// ToEntityReference creates an EntityReference representing the VLAN.
func (vlan *VLAN) ToEntityReference() EntityReference {
	return EntityReference{
		ID:   vlan.ID,
		Name: vlan.Name,
	}
}

var _ NamedEntity = &VLAN{}

// VLANs represents the response to a "List VLANs" API call.
type VLANs struct {
	// The current page of network domains.
	VLANs []VLAN `json:"vlan"`

	PagedResult
}

// DeployVLAN represents the request body when deploying a cloud compute VLAN.
type DeployVLAN struct {
	// The Id of the network domain in which the VLAN will be deployed.
	VLANID string `json:"networkDomainId"`

	// The VLAN name.
	Name string `json:"name"`

	// The VLAN description.
	Description string `json:"description"`

	// The private IPv4 base address for the VLAN.
	IPv4BaseAddress string `json:"privateIpv4BaseAddress"`

	// The private IPv4 prefix size (i.e. netmask) for the VLAN.
	IPv4PrefixSize int `json:"privateIpv4PrefixSize"`
}

// EditVLAN represents the request body when editing a cloud compute VLAN.
type EditVLAN struct {
	// The ID of the VLAN to edit.
	ID string `json:"id"`

	// The VLAN name (optional).
	Name *string `json:"name,omitempty"`

	// The VLAN description (optional).
	Description *string `json:"description,omitempty"`
}

// DeleteVLAN represents a request to delete a compute VLAN.
type DeleteVLAN struct {
	// The VLAN Id.
	ID string `json:"id"`
}

// GetVLAN retrieves the VLAN with the specified Id.
// id is the Id of the VLAN to retrieve.
// Returns nil if no VLAN is found with the specified Id.
func (client *Client) GetVLAN(id string) (vlan *VLAN, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return nil, err
	}

	requestURI := fmt.Sprintf("%s/network/vlan/%s",
		url.QueryEscape(organizationID),
		url.QueryEscape(id),
	)
	request, err := client.newRequestV22(requestURI, http.MethodGet, nil)
	if err != nil {
		return nil, err
	}
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return nil, err
	}

	if statusCode != http.StatusOK {
		var apiResponse *APIResponseV2

		apiResponse, err = readAPIResponseAsJSON(responseBody, statusCode)
		if err != nil {
			return nil, err
		}

		if apiResponse.ResponseCode == ResponseCodeResourceNotFound {
			return nil, nil // Not an error, but was not found.
		}

		return nil, apiResponse.ToError("Request to retrieve VLAN failed with status code %d (%s): %s", statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	vlan = &VLAN{}
	err = json.Unmarshal(responseBody, vlan)

	return vlan, err
}

// GetVLANByName retrieves the VLAN (if any) with the specified name in the specified network domain.
func (client *Client) GetVLANByName(name string, networkDomainID string) (*VLAN, error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return nil, err
	}

	requestURI := fmt.Sprintf("%s/network/vlan?name=%s&networkDomainId=%s",
		url.QueryEscape(organizationID),
		url.QueryEscape(name),
		url.QueryEscape(networkDomainID),
	)
	request, err := client.newRequestV22(requestURI, http.MethodGet, nil)
	if err != nil {
		return nil, err
	}

	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return nil, err
	}

	if statusCode != http.StatusOK {
		var apiResponse *APIResponseV2

		apiResponse, err = readAPIResponseAsJSON(responseBody, statusCode)
		if err != nil {
			return nil, err
		}

		return nil, apiResponse.ToError("Request failed with status code %d (%s): %s", statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	vlans := &VLANs{}
	err = json.Unmarshal(responseBody, vlans)
	if err != nil {
		return nil, err
	}
	if vlans.IsEmpty() {
		return nil, nil // No matching VLAN was found.
	}

	if len(vlans.VLANs) != 1 {
		return nil, fmt.Errorf("Found multiple VLANs (%d) named '%s' in network domain '%s'.", len(vlans.VLANs), name, networkDomainID)
	}

	return &vlans.VLANs[0], nil
}

// ListVLANs retrieves a list of all VLANs in the specified network domain.
// TODO: Support filtering and sorting.
func (client *Client) ListVLANs(networkDomainID string, paging *Paging) (vlans *VLANs, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return nil, err
	}

	requestURI := fmt.Sprintf("%s/network/vlan?networkDomainId=%s&%s",
		url.QueryEscape(organizationID),
		url.QueryEscape(networkDomainID),
		paging.EnsurePaging().toQueryParameters(),
	)
	request, err := client.newRequestV22(requestURI, http.MethodGet, nil)
	if err != nil {
		return nil, err
	}

	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return nil, err
	}

	if statusCode != http.StatusOK {
		var apiResponse *APIResponseV2

		apiResponse, err = readAPIResponseAsJSON(responseBody, statusCode)
		if err != nil {
			return nil, err
		}

		return nil, apiResponse.ToError("Request to list VLANs failed with status code %d (%s): %s", statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	vlans = &VLANs{}
	err = json.Unmarshal(responseBody, vlans)

	return vlans, err
}

// DeployVLAN deploys a new VLAN into a network domain.
func (client *Client) DeployVLAN(networkDomainID string, name string, description string, ipv4BaseAddress string, ipv4PrefixSize int) (vlanID string, err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return "", err
	}

	requestURI := fmt.Sprintf("%s/network/deployVlan",
		url.QueryEscape(organizationID),
	)
	request, err := client.newRequestV22(requestURI, http.MethodPost, &DeployVLAN{
		VLANID:          networkDomainID,
		Name:            name,
		Description:     description,
		IPv4BaseAddress: ipv4BaseAddress,
		IPv4PrefixSize:  ipv4PrefixSize,
	})
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return "", err
	}

	apiResponse, err := readAPIResponseAsJSON(responseBody, statusCode)
	if err != nil {
		return "", err
	}

	if apiResponse.ResponseCode != ResponseCodeInProgress {
		return "", apiResponse.ToError("Request to deploy VLAN '%s' failed with status code %d (%s): %s", name, statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	// Expected: "info" { "name": "vlanId", "value": "the-Id-of-the-new-VLAN" }
	vlanIDMessage := apiResponse.GetFieldMessage("vlanId")
	if vlanIDMessage == nil {
		return "", apiResponse.ToError("Received an unexpected response (missing 'vlanId') with status code %d (%s): %s", statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	return *vlanIDMessage, nil
}

// EditVLAN updates an existing VLAN.
// Pass an empty string for any field to retain its existing value.
// Returns an error if the operation was not successful.
func (client *Client) EditVLAN(id string, name *string, description *string) (err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return err
	}

	requestURI := fmt.Sprintf("%s/network/editVlan",
		url.QueryEscape(organizationID),
	)
	request, err := client.newRequestV22(requestURI, http.MethodPost, &EditVLAN{
		ID:          id,
		Name:        name,
		Description: description,
	})
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return err
	}

	apiResponse, err := readAPIResponseAsJSON(responseBody, statusCode)
	if err != nil {
		return err
	}

	if apiResponse.ResponseCode != ResponseCodeOK {
		return apiResponse.ToError("Request to edit VLAN failed with unexpected status code %d (%s): %s", statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	return nil
}

// DeleteVLAN deletes an existing VLAN.
// Returns an error if the operation was not successful.
func (client *Client) DeleteVLAN(id string) (err error) {
	organizationID, err := client.getOrganizationID()
	if err != nil {
		return err
	}

	requestURI := fmt.Sprintf("%s/network/deleteVlan",
		url.QueryEscape(organizationID),
	)
	request, err := client.newRequestV22(requestURI, http.MethodPost, &DeleteVLAN{id})
	responseBody, statusCode, err := client.executeRequest(request)
	if err != nil {
		return err
	}

	apiResponse, err := readAPIResponseAsJSON(responseBody, statusCode)
	if err != nil {
		return err
	}

	if apiResponse.ResponseCode != ResponseCodeInProgress {
		return apiResponse.ToError("Request to delete VLAN failed with unexpected status code %d (%s): %s", statusCode, apiResponse.ResponseCode, apiResponse.Message)
	}

	return nil
}
<|endoftext|>


<commit_before>package tlogclient

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"net"
	"sync"
	"time"

	"github.com/zero-os/0-Disk"
	"github.com/zero-os/0-Disk/log"
	"github.com/zero-os/0-Disk/tlog"
	"github.com/zero-os/0-Disk/tlog/schema"
	"github.com/zero-os/0-Disk/tlog/tlogclient/blockbuffer"
)

const (
	sendRetryNum     = 5
	sendSleepTime    = 500 * time.Millisecond // sleep duration before retrying the Send
	resendTimeoutDur = 5 * time.Second        // duration to wait before re-send the tlog.
	readTimeout      = 5 * time.Second
)

// Response defines a response from tlog server
type Response struct {
	Status    tlog.BlockStatus // status of the call
	Sequences []uint64         // flushed sequences number (optional)
}

// Result defines a struct that contains
// response and error from tlog.
type Result struct {
	Resp *Response
	Err  error
}

// Client defines a Tlog Client.
// This client is not thread/goroutine safe.
type Client struct {
	addr            string
	vdiskID         string
	conn            *net.TCPConn
	bw              writerFlusher
	rd              io.Reader // reader of this client
	blockBuffer     *blockbuffer.Buffer
	capnpSegmentBuf []byte

	// write lock, to protect against parallel Send
	// which is not goroutine safe yet
	wLock sync.Mutex

	// read lock, to protect it from race condition
	// caused by 'handshake' and recvOne
	rLock      sync.Mutex
	ctx        context.Context
	cancelFunc context.CancelFunc
}

// New creates a new tlog client for a vdisk with 'addr' is the tlogserver address.
// 'firstSequence' is the first sequence number this client is going to send.
// Set 'resetFirstSeq' to true to force reset the vdisk first/expected sequence.
// The client is not goroutine safe.
func New(addr, vdiskID string, firstSequence uint64, resetFirstSeq bool) (*Client, error) {
	ctx, cancelFunc := context.WithCancel(context.Background())
	client := &Client{
		addr:        addr,
		vdiskID:     vdiskID,
		blockBuffer: blockbuffer.NewBuffer(resendTimeoutDur),
		ctx:         ctx,
		cancelFunc:  cancelFunc,
	}

	if err := client.connect(firstSequence, resetFirstSeq); err != nil {
		return nil, err
	}

	go client.resender()
	return client, nil
}

func (c *Client) connect(firstSequence uint64, resetFirstSeq bool) error {
	c.rLock.Lock()
	defer c.rLock.Unlock()

	err := c.createConn()
	if err != nil {
		return fmt.Errorf("client couldn't be created: %s", err.Error())
	}

	err = c.handshake(firstSequence, resetFirstSeq)
	if err != nil {
		if err := c.conn.Close(); err != nil {
			log.Debug("couldn't close open connection of invalid client:", err)
		}
		return fmt.Errorf("client handshake failed: %s", err.Error())
	}
	return nil
}

// goroutine which re-send the block.
func (c *Client) resender() {
	for {
		select {
		case <-c.ctx.Done():
			return
		case block := <-c.blockBuffer.TimedOut():
			data, err := block.Data()
			if err != nil {
				log.Errorf("client resender failed to get data block:%v", err)
				continue
			}

			err = c.Send(block.Operation(), block.Sequence(), block.Offset(), block.Timestamp(), data, block.Size())
			if err != nil {
				log.Errorf("client resender failed to send data:%v", err)
			}
		}
	}
}

func (c *Client) handshake(firstSequence uint64, resetFirstSeq bool) error {
	// send handshake request
	err := c.encodeHandshakeCapnp(firstSequence, resetFirstSeq)
	if err != nil {
		return err
	}
	err = c.bw.Flush()
	if err != nil {
		return err
	}

	// receive handshake response
	resp, err := c.decodeHandshakeResponse()
	if err != nil {
		return err
	}

	// check server response status
	err = tlog.HandshakeStatus(resp.Status()).Error()
	if err != nil {
		return err
	}

	// all checks out, ready to go!
	return nil
}

// Recv get channel of responses and errors (Result)
func (c *Client) Recv() <-chan *Result {
	// response channel
	// three is big enough size:
	// - one waiting to be fetched by application/user
	// - one for the response we just received
	// - one for (hopefully) optimization, so we can read from network
	//	 before needed and then put it to this channel.
	reChan := make(chan *Result, 3)

	go func() {
		for {
			tr, err := c.recvOne()

			// it is timeout error
			// client can't really read something
			if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
				continue
			}

			if tr != nil && len(tr.Sequences) > 0 {
				// if it successfully received by server, delete from buffer
				if tr.Status == tlog.BlockStatusRecvOK {
					c.blockBuffer.Delete(tr.Sequences[0])
				}

				/* enclose this code part with comment because we currently doesn't
				  have this case in server.
				// if it failed to be received, promote it to be
				// timed out as soon as possible.
				if status == tlog.BlockStatusRecvFailed {
					if err := c.blockBuffer.Promote(seq); err != nil {
						// failed to promote, forward the server's response to user.
						log.Infof("tlog client failed to promote %v, err: %v", seq, err)
					} else {
						// successfully promoted, ignore the response.
						// we will resend it
						continue
					}
				}
				*/
			}

			reChan <- &Result{
				Resp: tr,
				Err:  err,
			}
		}
	}()
	return reChan
}

// recvOne receive one response
func (c *Client) recvOne() (*Response, error) {
	c.rLock.Lock()
	defer c.rLock.Unlock()

	// set read deadline, so we don't have deadlock
	// for rLock
	c.conn.SetReadDeadline(time.Now().Add(readTimeout))

	// decode capnp and build response
	tr, err := c.decodeBlockResponse()
	if err != nil {
		return nil, err
	}

	capSeqs, err := tr.Sequences()
	if err != nil {
		return nil, err
	}
	seqs := []uint64{}
	for i := 0; i < capSeqs.Len(); i++ {
		seqs = append(seqs, capSeqs.At(i))
	}
	return &Response{
		Status:    tlog.BlockStatus(tr.Status()),
		Sequences: seqs,
	}, nil
}

func (c *Client) createConn() error {

	tcpAddr, err := net.ResolveTCPAddr("tcp", c.addr)
	if err != nil {
		return err
	}

	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		return err
	}

	conn.SetKeepAlive(true)
	c.conn = conn
	c.bw = bufio.NewWriter(conn)
	c.rd = conn
	return nil
}

// ForceFlush send force flush command to server
func (c *Client) ForceFlush() error {
	c.wLock.Lock()
	defer c.wLock.Unlock()
	// we use c.conn directly because buffered writer
	// won't help anything to write one byte of message
	return tlog.WriteMessageType(c.conn, tlog.MessageForceFlush)
}

// ForceFlushAtSeq force flush at given sequence
func (c *Client) ForceFlushAtSeq(seq uint64) error {
	c.wLock.Lock()
	defer c.wLock.Unlock()

	if err := tlog.WriteMessageType(c.bw, tlog.MessageForceFlushAtSeq); err != nil {
		return err
	}
	if err := c.encodeSendCommand(c.bw, tlog.MessageForceFlushAtSeq, seq); err != nil {
		return err
	}
	return c.bw.Flush()
}

// Send sends the transaction tlog to server.
// It returns error in these cases:
// - failed to encode the capnp.
// - failed to recover from broken network connection.
func (c *Client) Send(op uint8, seq, offset, timestamp uint64,
	data []byte, size uint64) error {
	c.wLock.Lock()
	defer c.wLock.Unlock()

	block, err := c.send(op, seq, offset, timestamp, data, size)
	if err == nil && block != nil {
		c.blockBuffer.Add(block)
	}
	return err
}

// send tlog block to server
func (c *Client) send(op uint8, seq, offset, timestamp uint64,
	data []byte, size uint64) (block *schema.TlogBlock, err error) {
	hash := zerodisk.HashBytes(data)

	send := func() (*schema.TlogBlock, error) {
		if err := tlog.WriteMessageType(c.bw, tlog.MessageTlogBlock); err != nil {
			return nil, err
		}

		block, err := c.encodeBlockCapnp(c.bw, op, seq, hash[:], offset, timestamp, data, size)
		if err != nil {
			return block, err
		}

		return block, c.bw.Flush()
	}

	okToSend := true

	for i := 0; i < sendRetryNum+1; i++ {
		if okToSend {
			block, err = send()
			if err == nil {
				return
			}

			if _, ok := err.(net.Error); !ok {
				return // no need to rety if it is not network error.
			}
		}

		// First sleep = 0 second,
		// so we don't need to sleep in case of simple closed connection.
		// We sleep in next iteration because there might be something error in
		// the network connection or the tlog server that need time to be recovered.
		time.Sleep(time.Duration(i) * sendSleepTime)

		if err = c.connect(c.blockBuffer.MinSequence(), false); err != nil {
			log.Infof("tlog client : reconnect attemp(%v) failed:%v", i, err)
			okToSend = false
		} else {
			okToSend = true
		}
	}
	return
}

// Close the open connection, making this client invalid.
// It is user responsibility to call this function.
func (c *Client) Close() error {
	c.cancelFunc()
	return c.conn.Close()
}
<commit_msg>Small tlogclient cleanup.<commit_after>package tlogclient

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"net"
	"sync"
	"time"

	"github.com/zero-os/0-Disk"
	"github.com/zero-os/0-Disk/log"
	"github.com/zero-os/0-Disk/tlog"
	"github.com/zero-os/0-Disk/tlog/schema"
	"github.com/zero-os/0-Disk/tlog/tlogclient/blockbuffer"
)

const (
	sendRetryNum     = 5
	sendSleepTime    = 500 * time.Millisecond // sleep duration before retrying the Send
	resendTimeoutDur = 5 * time.Second        // duration to wait before re-send the tlog.
	readTimeout      = 5 * time.Second
)

// Response defines a response from tlog server
type Response struct {
	Status    tlog.BlockStatus // status of the call
	Sequences []uint64         // flushed sequences number (optional)
}

// Result defines a struct that contains
// response and error from tlog.
type Result struct {
	Resp *Response
	Err  error
}

// Client defines a Tlog Client.
// This client is not thread/goroutine safe.
type Client struct {
	addr            string
	vdiskID         string
	conn            *net.TCPConn
	bw              writerFlusher
	rd              io.Reader // reader of this client
	blockBuffer     *blockbuffer.Buffer
	capnpSegmentBuf []byte

	// write lock, to protect against parallel Send
	// which is not goroutine safe yet
	wLock sync.Mutex

	// read lock, to protect it from race condition
	// caused by 'handshake' and recvOne
	rLock      sync.Mutex
	ctx        context.Context
	cancelFunc context.CancelFunc
}

// New creates a new tlog client for a vdisk with 'addr' is the tlogserver address.
// 'firstSequence' is the first sequence number this client is going to send.
// Set 'resetFirstSeq' to true to force reset the vdisk first/expected sequence.
// The client is not goroutine safe.
func New(addr, vdiskID string, firstSequence uint64, resetFirstSeq bool) (*Client, error) {
	ctx, cancelFunc := context.WithCancel(context.Background())
	client := &Client{
		addr:        addr,
		vdiskID:     vdiskID,
		blockBuffer: blockbuffer.NewBuffer(resendTimeoutDur),
		ctx:         ctx,
		cancelFunc:  cancelFunc,
	}

	if err := client.connect(firstSequence, resetFirstSeq); err != nil {
		return nil, err
	}

	go client.resender()
	return client, nil
}

func (c *Client) connect(firstSequence uint64, resetFirstSeq bool) error {
	c.rLock.Lock()
	defer c.rLock.Unlock()

	err := c.createConn()
	if err != nil {
		return fmt.Errorf("client couldn't be created: %s", err.Error())
	}

	err = c.handshake(firstSequence, resetFirstSeq)
	if err != nil {
		if err := c.conn.Close(); err != nil {
			log.Debug("couldn't close open connection of invalid client:", err)
		}
		return fmt.Errorf("client handshake failed: %s", err.Error())
	}
	return nil
}

// goroutine which re-send the block.
func (c *Client) resender() {
	for {
		select {
		case <-c.ctx.Done():
			return
		case block := <-c.blockBuffer.TimedOut():
			data, err := block.Data()
			if err != nil {
				log.Errorf("client resender failed to get data block:%v", err)
				continue
			}

			err = c.Send(block.Operation(), block.Sequence(), block.Offset(), block.Timestamp(), data, block.Size())
			if err != nil {
				log.Errorf("client resender failed to send data:%v", err)
			}
		}
	}
}

func (c *Client) handshake(firstSequence uint64, resetFirstSeq bool) error {
	// send handshake request
	err := c.encodeHandshakeCapnp(firstSequence, resetFirstSeq)
	if err != nil {
		return err
	}
	err = c.bw.Flush()
	if err != nil {
		return err
	}

	// receive handshake response
	resp, err := c.decodeHandshakeResponse()
	if err != nil {
		return err
	}

	// check server response status
	err = tlog.HandshakeStatus(resp.Status()).Error()
	if err != nil {
		return err
	}

	// all checks out, ready to go!
	return nil
}

// Recv get channel of responses and errors (Result)
func (c *Client) Recv() <-chan *Result {
	// response channel
	// three is big enough size:
	// - one waiting to be fetched by application/user
	// - one for the response we just received
	// - one for (hopefully) optimization, so we can read from network
	//	 before needed and then put it to this channel.
	reChan := make(chan *Result, 3)

	go func() {
		for {
			tr, err := c.recvOne()

			// it is timeout error
			// client can't really read something
			if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
				continue
			}

			if tr != nil && len(tr.Sequences) > 0 {
				// if it successfully received by server, delete from buffer
				if tr.Status == tlog.BlockStatusRecvOK {
					if len(tr.Sequences) > 0 { // should always be true, but we anticipate.
						c.blockBuffer.Delete(tr.Sequences[0])
					}
				}
			}

			reChan <- &Result{
				Resp: tr,
				Err:  err,
			}
		}
	}()
	return reChan
}

// recvOne receive one response
func (c *Client) recvOne() (*Response, error) {
	c.rLock.Lock()
	defer c.rLock.Unlock()

	// set read deadline, so we don't have deadlock
	// for rLock
	c.conn.SetReadDeadline(time.Now().Add(readTimeout))

	// decode capnp and build response
	tr, err := c.decodeBlockResponse()
	if err != nil {
		return nil, err
	}

	capSeqs, err := tr.Sequences()
	if err != nil {
		return nil, err
	}
	seqs := []uint64{}
	for i := 0; i < capSeqs.Len(); i++ {
		seqs = append(seqs, capSeqs.At(i))
	}
	return &Response{
		Status:    tlog.BlockStatus(tr.Status()),
		Sequences: seqs,
	}, nil
}

func (c *Client) createConn() error {

	tcpAddr, err := net.ResolveTCPAddr("tcp", c.addr)
	if err != nil {
		return err
	}

	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		return err
	}

	conn.SetKeepAlive(true)
	c.conn = conn
	c.bw = bufio.NewWriter(conn)
	c.rd = conn
	return nil
}

// ForceFlush send force flush command to server
func (c *Client) ForceFlush() error {
	c.wLock.Lock()
	defer c.wLock.Unlock()
	// we use c.conn directly because buffered writer
	// won't help anything to write one byte of message
	return tlog.WriteMessageType(c.conn, tlog.MessageForceFlush)
}

// ForceFlushAtSeq force flush at given sequence
func (c *Client) ForceFlushAtSeq(seq uint64) error {
	c.wLock.Lock()
	defer c.wLock.Unlock()

	if err := tlog.WriteMessageType(c.bw, tlog.MessageForceFlushAtSeq); err != nil {
		return err
	}
	if err := c.encodeSendCommand(c.bw, tlog.MessageForceFlushAtSeq, seq); err != nil {
		return err
	}
	return c.bw.Flush()
}

// Send sends the transaction tlog to server.
// It returns error in these cases:
// - failed to encode the capnp.
// - failed to recover from broken network connection.
func (c *Client) Send(op uint8, seq, offset, timestamp uint64,
	data []byte, size uint64) error {
	c.wLock.Lock()
	defer c.wLock.Unlock()

	block, err := c.send(op, seq, offset, timestamp, data, size)
	if err == nil && block != nil {
		c.blockBuffer.Add(block)
	}
	return err
}

// send tlog block to server
func (c *Client) send(op uint8, seq, offset, timestamp uint64,
	data []byte, size uint64) (*schema.TlogBlock, error) {
	hash := zerodisk.HashBytes(data)

	send := func() (*schema.TlogBlock, error) {
		if err := tlog.WriteMessageType(c.bw, tlog.MessageTlogBlock); err != nil {
			return nil, err
		}

		block, err := c.encodeBlockCapnp(c.bw, op, seq, hash[:], offset, timestamp, data, size)
		if err != nil {
			return block, err
		}

		return block, c.bw.Flush()
	}

	var err error
	var block *schema.TlogBlock

	okToSend := true

	for i := 0; i < sendRetryNum+1; i++ {
		if okToSend {
			block, err = send()
			if err == nil {
				return block, nil
			}

			if _, ok := err.(net.Error); !ok {
				return nil, err // no need to rety if it is not network error.
			}
		}

		// First sleep = 0 second,
		// so we don't need to sleep in case of simple closed connection.
		// We sleep in next iteration because there might be something error in
		// the network connection or the tlog server that need time to be recovered.
		time.Sleep(time.Duration(i) * sendSleepTime)

		if err = c.connect(c.blockBuffer.MinSequence(), false); err != nil {
			log.Infof("tlog client : reconnect attemp(%v) failed:%v", i, err)
			okToSend = false
		} else {
			okToSend = true
		}
	}
	return nil, err
}

// Close the open connection, making this client invalid.
// It is user responsibility to call this function.
func (c *Client) Close() error {
	c.cancelFunc()
	return c.conn.Close()
}
<|endoftext|>


<commit_before>package etcd

import (
	"context"
	"errors"
	"reflect"
	"testing"
	"time"

	etcd "github.com/coreos/etcd/client"
)

func TestNewClient(t *testing.T) {
	client, err := NewClient(
		context.Background(),
		[]string{"http://irrelevant:12345"},
		ClientOptions{
			DialTimeout:             2 * time.Second,
			DialKeepAlive:           2 * time.Second,
			HeaderTimeoutPerRequest: 2 * time.Second,
		},
	)
	if err != nil {
		t.Fatalf("unexpected error creating client: %v", err)
	}
	if client == nil {
		t.Fatal("expected new Client, got nil")
	}
}

// NewClient should fail when providing invalid or missing endpoints.
func TestOptions(t *testing.T) {
	a, err := NewClient(
		context.Background(),
		[]string{},
		ClientOptions{
			Cert:                    "",
			Key:                     "",
			CACert:                  "",
			DialTimeout:             2 * time.Second,
			DialKeepAlive:           2 * time.Second,
			HeaderTimeoutPerRequest: 2 * time.Second,
		},
	)
	if err == nil {
		t.Errorf("expected error: %v", err)
	}
	if a != nil {
		t.Fatalf("expected client to be nil on failure")
	}

	_, err = NewClient(
		context.Background(),
		[]string{"http://irrelevant:12345"},
		ClientOptions{
			Cert:                    "blank.crt",
			Key:                     "blank.key",
			CACert:                  "blank.CACert",
			DialTimeout:             2 * time.Second,
			DialKeepAlive:           2 * time.Second,
			HeaderTimeoutPerRequest: 2 * time.Second,
		},
	)
	if err == nil {
		t.Errorf("expected error: %v", err)
	}
}

// Mocks of the underlying etcd.KeysAPI interface that is called by the methods we want to test

// fakeKeysAPI implements etcd.KeysAPI, event and err are channels used to emulate
// an etcd event or error, getres will be returned when etcd.KeysAPI.Get is called.
type fakeKeysAPI struct {
	event  chan bool
	err    chan bool
	getres *getResult
}

type getResult struct {
	resp *etcd.Response
	err  error
}

// Get return the content of getres or nil, nil
func (fka *fakeKeysAPI) Get(ctx context.Context, key string, opts *etcd.GetOptions) (*etcd.Response, error) {
	if fka.getres == nil {
		return nil, nil
	}
	return fka.getres.resp, fka.getres.err
}

// Set is not used in the tests
func (fka *fakeKeysAPI) Set(ctx context.Context, key, value string, opts *etcd.SetOptions) (*etcd.Response, error) {
	return nil, nil
}

// Delete is not used in the tests
func (fka *fakeKeysAPI) Delete(ctx context.Context, key string, opts *etcd.DeleteOptions) (*etcd.Response, error) {
	return nil, nil
}

// Create is not used in the tests
func (fka *fakeKeysAPI) Create(ctx context.Context, key, value string) (*etcd.Response, error) {
	return nil, nil
}

// CreateInOrder is not used in the tests
func (fka *fakeKeysAPI) CreateInOrder(ctx context.Context, dir, value string, opts *etcd.CreateInOrderOptions) (*etcd.Response, error) {
	return nil, nil
}

// Update is not used in the tests
func (fka *fakeKeysAPI) Update(ctx context.Context, key, value string) (*etcd.Response, error) {
	return nil, nil
}

// Watcher return a fakeWatcher that will forward event and error received on the channels
func (fka *fakeKeysAPI) Watcher(key string, opts *etcd.WatcherOptions) etcd.Watcher {
	return &fakeWatcher{fka.event, fka.err}
}

// fakeWatcher implements etcd.Watcher
type fakeWatcher struct {
	event chan bool
	err   chan bool
}

// Next blocks until an etcd event or error is emulated.
// When an event occurs it just return nil response and error.
// When an error occur it return a non nil error.
func (fw *fakeWatcher) Next(context.Context) (*etcd.Response, error) {
	for {
		select {
		case <-fw.event:
			return nil, nil
		case <-fw.err:
			return nil, errors.New("error from underlying etcd watcher")
		default:
		}
	}
}

// newFakeClient return a new etcd.Client built on top of the mocked interfaces
func newFakeClient(event, err chan bool, getres *getResult) Client {
	return &client{
		keysAPI: &fakeKeysAPI{event, err, getres},
		ctx:     context.Background(),
	}
}

// Register should fail when the provided service has an empty key or value
func TestRegisterClient(t *testing.T) {
	client := newFakeClient(nil, nil, nil)

	err := client.Register(Service{Key: "", Value: "value", DeleteOptions: nil})
	if want, have := ErrNoKey, err; want != have {
		t.Fatalf("want %v, have %v", want, have)
	}

	err = client.Register(Service{Key: "key", Value: "", DeleteOptions: nil})
	if want, have := ErrNoValue, err; want != have {
		t.Fatalf("want %v, have %v", want, have)
	}

	err = client.Register(Service{Key: "key", Value: "value", DeleteOptions: nil})
	if err != nil {
		t.Fatal(err)
	}
}

// Deregister should fail if the input service has an empty key
func TestDeregisterClient(t *testing.T) {
	client := newFakeClient(nil, nil, nil)

	err := client.Deregister(Service{Key: "", Value: "value", DeleteOptions: nil})
	if want, have := ErrNoKey, err; want != have {
		t.Fatalf("want %v, have %v", want, have)
	}

	err = client.Deregister(Service{Key: "key", Value: "", DeleteOptions: nil})
	if err != nil {
		t.Fatal(err)
	}
}

// WatchPrefix notify the caller by writing on the channel if an etcd event occurs
// or return in case of an underlying error
func TestWatchPrefix(t *testing.T) {
	err := make(chan bool)
	event := make(chan bool)
	watchPrefixReturned := make(chan bool, 1)
	client := newFakeClient(event, err, nil)

	ch := make(chan struct{})
	go func() {
		client.WatchPrefix("prefix", ch) // block until an etcd event or error occurs
		watchPrefixReturned <- true
	}()

	// WatchPrefix force the caller to read once from the channel before actually
	// sending notification, emulate that first read.
	<-ch

	// Emulate an etcd event
	event <- true
	if want, have := struct{}{}, <-ch; want != have {
		t.Fatalf("want %v, have %v", want, have)
	}

	// Emulate an error, WatchPrefix should return
	err <- true
	select {
	case <-watchPrefixReturned:
		break
	case <-time.After(1 * time.Second):
		t.Fatal("WatchPrefix not returning on errors")
	}
}

var errKeyAPI = errors.New("emulate error returned by KeysAPI.Get")

// table of test cases for method GetEntries
var getEntriesTestTable = []struct {
	input getResult // value returned by the underlying etcd.KeysAPI.Get
	resp  []string  // response expected in output of GetEntries
	err   error     //error expected in output of GetEntries

}{
	// test case: an error is returned by etcd.KeysAPI.Get
	{getResult{nil, errKeyAPI}, nil, errKeyAPI},
	// test case: return a single leaf node, with an empty value
	{getResult{&etcd.Response{
		Action: "get",
		Node: &etcd.Node{
			Key:           "nodekey",
			Dir:           false,
			Value:         "",
			Nodes:         nil,
			CreatedIndex:  0,
			ModifiedIndex: 0,
			Expiration:    nil,
			TTL:           0,
		},
		PrevNode: nil,
		Index:    0,
	}, nil}, []string{}, nil},
	// test case: return a single leaf node, with a value
	{getResult{&etcd.Response{
		Action: "get",
		Node: &etcd.Node{
			Key:           "nodekey",
			Dir:           false,
			Value:         "nodevalue",
			Nodes:         nil,
			CreatedIndex:  0,
			ModifiedIndex: 0,
			Expiration:    nil,
			TTL:           0,
		},
		PrevNode: nil,
		Index:    0,
	}, nil}, []string{"nodevalue"}, nil},
	// test case: return a node with two childs
	{getResult{&etcd.Response{
		Action: "get",
		Node: &etcd.Node{
			Key:   "nodekey",
			Dir:   true,
			Value: "nodevalue",
			Nodes: []*etcd.Node{
				{
					Key:           "childnode1",
					Dir:           false,
					Value:         "childvalue1",
					Nodes:         nil,
					CreatedIndex:  0,
					ModifiedIndex: 0,
					Expiration:    nil,
					TTL:           0,
				},
				{
					Key:           "childnode2",
					Dir:           false,
					Value:         "childvalue2",
					Nodes:         nil,
					CreatedIndex:  0,
					ModifiedIndex: 0,
					Expiration:    nil,
					TTL:           0,
				},
			},
			CreatedIndex:  0,
			ModifiedIndex: 0,
			Expiration:    nil,
			TTL:           0,
		},
		PrevNode: nil,
		Index:    0,
	}, nil}, []string{"childvalue1", "childvalue2"}, nil},
}

func TestGetEntries(t *testing.T) {
	for _, et := range getEntriesTestTable {
		client := newFakeClient(nil, nil, &et.input)
		resp, err := client.GetEntries("prefix")
		if want, have := et.resp, resp; !reflect.DeepEqual(want, have) {
			t.Fatalf("want %v, have %v", want, have)
		}
		if want, have := et.err, err; want != have {
			t.Fatalf("want %v, have %v", want, have)
		}
	}
}
<commit_msg>sd/etcd: not ready for stdlib context yet, apparently<commit_after>package etcd

import (
	"errors"
	"reflect"
	"testing"
	"time"

	"golang.org/x/net/context"

	etcd "github.com/coreos/etcd/client"
)

func TestNewClient(t *testing.T) {
	client, err := NewClient(
		context.Background(),
		[]string{"http://irrelevant:12345"},
		ClientOptions{
			DialTimeout:             2 * time.Second,
			DialKeepAlive:           2 * time.Second,
			HeaderTimeoutPerRequest: 2 * time.Second,
		},
	)
	if err != nil {
		t.Fatalf("unexpected error creating client: %v", err)
	}
	if client == nil {
		t.Fatal("expected new Client, got nil")
	}
}

// NewClient should fail when providing invalid or missing endpoints.
func TestOptions(t *testing.T) {
	a, err := NewClient(
		context.Background(),
		[]string{},
		ClientOptions{
			Cert:                    "",
			Key:                     "",
			CACert:                  "",
			DialTimeout:             2 * time.Second,
			DialKeepAlive:           2 * time.Second,
			HeaderTimeoutPerRequest: 2 * time.Second,
		},
	)
	if err == nil {
		t.Errorf("expected error: %v", err)
	}
	if a != nil {
		t.Fatalf("expected client to be nil on failure")
	}

	_, err = NewClient(
		context.Background(),
		[]string{"http://irrelevant:12345"},
		ClientOptions{
			Cert:                    "blank.crt",
			Key:                     "blank.key",
			CACert:                  "blank.CACert",
			DialTimeout:             2 * time.Second,
			DialKeepAlive:           2 * time.Second,
			HeaderTimeoutPerRequest: 2 * time.Second,
		},
	)
	if err == nil {
		t.Errorf("expected error: %v", err)
	}
}

// Mocks of the underlying etcd.KeysAPI interface that is called by the methods we want to test

// fakeKeysAPI implements etcd.KeysAPI, event and err are channels used to emulate
// an etcd event or error, getres will be returned when etcd.KeysAPI.Get is called.
type fakeKeysAPI struct {
	event  chan bool
	err    chan bool
	getres *getResult
}

type getResult struct {
	resp *etcd.Response
	err  error
}

// Get return the content of getres or nil, nil
func (fka *fakeKeysAPI) Get(ctx context.Context, key string, opts *etcd.GetOptions) (*etcd.Response, error) {
	if fka.getres == nil {
		return nil, nil
	}
	return fka.getres.resp, fka.getres.err
}

// Set is not used in the tests
func (fka *fakeKeysAPI) Set(ctx context.Context, key, value string, opts *etcd.SetOptions) (*etcd.Response, error) {
	return nil, nil
}

// Delete is not used in the tests
func (fka *fakeKeysAPI) Delete(ctx context.Context, key string, opts *etcd.DeleteOptions) (*etcd.Response, error) {
	return nil, nil
}

// Create is not used in the tests
func (fka *fakeKeysAPI) Create(ctx context.Context, key, value string) (*etcd.Response, error) {
	return nil, nil
}

// CreateInOrder is not used in the tests
func (fka *fakeKeysAPI) CreateInOrder(ctx context.Context, dir, value string, opts *etcd.CreateInOrderOptions) (*etcd.Response, error) {
	return nil, nil
}

// Update is not used in the tests
func (fka *fakeKeysAPI) Update(ctx context.Context, key, value string) (*etcd.Response, error) {
	return nil, nil
}

// Watcher return a fakeWatcher that will forward event and error received on the channels
func (fka *fakeKeysAPI) Watcher(key string, opts *etcd.WatcherOptions) etcd.Watcher {
	return &fakeWatcher{fka.event, fka.err}
}

// fakeWatcher implements etcd.Watcher
type fakeWatcher struct {
	event chan bool
	err   chan bool
}

// Next blocks until an etcd event or error is emulated.
// When an event occurs it just return nil response and error.
// When an error occur it return a non nil error.
func (fw *fakeWatcher) Next(context.Context) (*etcd.Response, error) {
	for {
		select {
		case <-fw.event:
			return nil, nil
		case <-fw.err:
			return nil, errors.New("error from underlying etcd watcher")
		default:
		}
	}
}

// newFakeClient return a new etcd.Client built on top of the mocked interfaces
func newFakeClient(event, err chan bool, getres *getResult) Client {
	return &client{
		keysAPI: &fakeKeysAPI{event, err, getres},
		ctx:     context.Background(),
	}
}

// Register should fail when the provided service has an empty key or value
func TestRegisterClient(t *testing.T) {
	client := newFakeClient(nil, nil, nil)

	err := client.Register(Service{Key: "", Value: "value", DeleteOptions: nil})
	if want, have := ErrNoKey, err; want != have {
		t.Fatalf("want %v, have %v", want, have)
	}

	err = client.Register(Service{Key: "key", Value: "", DeleteOptions: nil})
	if want, have := ErrNoValue, err; want != have {
		t.Fatalf("want %v, have %v", want, have)
	}

	err = client.Register(Service{Key: "key", Value: "value", DeleteOptions: nil})
	if err != nil {
		t.Fatal(err)
	}
}

// Deregister should fail if the input service has an empty key
func TestDeregisterClient(t *testing.T) {
	client := newFakeClient(nil, nil, nil)

	err := client.Deregister(Service{Key: "", Value: "value", DeleteOptions: nil})
	if want, have := ErrNoKey, err; want != have {
		t.Fatalf("want %v, have %v", want, have)
	}

	err = client.Deregister(Service{Key: "key", Value: "", DeleteOptions: nil})
	if err != nil {
		t.Fatal(err)
	}
}

// WatchPrefix notify the caller by writing on the channel if an etcd event occurs
// or return in case of an underlying error
func TestWatchPrefix(t *testing.T) {
	err := make(chan bool)
	event := make(chan bool)
	watchPrefixReturned := make(chan bool, 1)
	client := newFakeClient(event, err, nil)

	ch := make(chan struct{})
	go func() {
		client.WatchPrefix("prefix", ch) // block until an etcd event or error occurs
		watchPrefixReturned <- true
	}()

	// WatchPrefix force the caller to read once from the channel before actually
	// sending notification, emulate that first read.
	<-ch

	// Emulate an etcd event
	event <- true
	if want, have := struct{}{}, <-ch; want != have {
		t.Fatalf("want %v, have %v", want, have)
	}

	// Emulate an error, WatchPrefix should return
	err <- true
	select {
	case <-watchPrefixReturned:
		break
	case <-time.After(1 * time.Second):
		t.Fatal("WatchPrefix not returning on errors")
	}
}

var errKeyAPI = errors.New("emulate error returned by KeysAPI.Get")

// table of test cases for method GetEntries
var getEntriesTestTable = []struct {
	input getResult // value returned by the underlying etcd.KeysAPI.Get
	resp  []string  // response expected in output of GetEntries
	err   error     //error expected in output of GetEntries

}{
	// test case: an error is returned by etcd.KeysAPI.Get
	{getResult{nil, errKeyAPI}, nil, errKeyAPI},
	// test case: return a single leaf node, with an empty value
	{getResult{&etcd.Response{
		Action: "get",
		Node: &etcd.Node{
			Key:           "nodekey",
			Dir:           false,
			Value:         "",
			Nodes:         nil,
			CreatedIndex:  0,
			ModifiedIndex: 0,
			Expiration:    nil,
			TTL:           0,
		},
		PrevNode: nil,
		Index:    0,
	}, nil}, []string{}, nil},
	// test case: return a single leaf node, with a value
	{getResult{&etcd.Response{
		Action: "get",
		Node: &etcd.Node{
			Key:           "nodekey",
			Dir:           false,
			Value:         "nodevalue",
			Nodes:         nil,
			CreatedIndex:  0,
			ModifiedIndex: 0,
			Expiration:    nil,
			TTL:           0,
		},
		PrevNode: nil,
		Index:    0,
	}, nil}, []string{"nodevalue"}, nil},
	// test case: return a node with two childs
	{getResult{&etcd.Response{
		Action: "get",
		Node: &etcd.Node{
			Key:   "nodekey",
			Dir:   true,
			Value: "nodevalue",
			Nodes: []*etcd.Node{
				{
					Key:           "childnode1",
					Dir:           false,
					Value:         "childvalue1",
					Nodes:         nil,
					CreatedIndex:  0,
					ModifiedIndex: 0,
					Expiration:    nil,
					TTL:           0,
				},
				{
					Key:           "childnode2",
					Dir:           false,
					Value:         "childvalue2",
					Nodes:         nil,
					CreatedIndex:  0,
					ModifiedIndex: 0,
					Expiration:    nil,
					TTL:           0,
				},
			},
			CreatedIndex:  0,
			ModifiedIndex: 0,
			Expiration:    nil,
			TTL:           0,
		},
		PrevNode: nil,
		Index:    0,
	}, nil}, []string{"childvalue1", "childvalue2"}, nil},
}

func TestGetEntries(t *testing.T) {
	for _, et := range getEntriesTestTable {
		client := newFakeClient(nil, nil, &et.input)
		resp, err := client.GetEntries("prefix")
		if want, have := et.resp, resp; !reflect.DeepEqual(want, have) {
			t.Fatalf("want %v, have %v", want, have)
		}
		if want, have := et.err, err; want != have {
			t.Fatalf("want %v, have %v", want, have)
		}
	}
}
<|endoftext|>


<commit_before>package glacier

import (
	"crypto/sha256"
	"fmt"
	"io"
)

const (
	KiB = 1024
	MiB = 1024 * KiB
)

type treeHash struct {
	Hash  [sha256.Size]byte
	Left  *treeHash
	Right *treeHash
}

func createTreeHash(r io.Reader) (*treeHash, error) {
	hasher := sha256.New()
	hashes := make([]treeHash, 0)
	outIndex := 0

	// generate hashes for 1 MiB chunks
	n, err := io.CopyN(hasher, r, MiB)
	for err == nil {
		hashes = append(hashes, treeHash{})
		hasher.Sum(hashes[outIndex].Hash[:0])
		hasher.Reset()
		outIndex++
		fmt.Printf("%x (%d)\n", hashes[outIndex-1].Hash[:4], outIndex-1)
		n, err = io.CopyN(hasher, r, MiB)
	}
	if err != nil && err != io.EOF {
		return nil, err
	}
	if n > 0 {
		hashes = append(hashes, treeHash{})
		hasher.Sum(hashes[outIndex].Hash[:0])
		hasher.Reset()
		outIndex++
		fmt.Printf("%x (%d)\n", hashes[outIndex-1].Hash[:4], outIndex-1)
	}

	// build tree
	childIndex := 0
	added := outIndex
	remainderIndex := -1
	for added > 1 || remainderIndex != -1 {
		children := added
		fmt.Println("children", children)
		added = 0
		// pair up
		for children > 1 {
			hashes = append(hashes, newTreeHash(&hashes[childIndex], &hashes[childIndex+1]))
			childIndex += 2
			children -= 2
			outIndex++
			added++
			fmt.Printf("%x (%d -> %d %d)\n", hashes[outIndex-1].Hash[:4], outIndex-1, childIndex-2, childIndex-1)
		}
		if children == 1 {
			if remainderIndex == -1 {
				remainderIndex = childIndex
				fmt.Println("added remainder", remainderIndex)
				childIndex++
			} else {
				fmt.Println("1 child and remainder")
				hashes = append(hashes, newTreeHash(&hashes[childIndex], &hashes[remainderIndex]))
				outIndex++
				fmt.Printf("%x (%d -> %d %d)\n", hashes[outIndex-1].Hash[:4], outIndex-1, childIndex, remainderIndex)
				remainderIndex = -1
				added++
				childIndex++
			}
		}
	}
	fmt.Println()

	return &hashes[outIndex-1], nil
}

func newTreeHash(left, right *treeHash) treeHash {
	h := sha256.New()
	h.Write(toHex(left.Hash[:]))
	h.Write(toHex(right.Hash[:]))
	var hash [sha256.Size]byte
	h.Sum(hash[:0])
	return treeHash{hash, left, right}
}
<commit_msg>Inlined creation of parent in tree hash and removed debugging statements.<commit_after>package glacier

import (
	"crypto/sha256"
	"io"
)

const (
	KiB = 1024
	MiB = 1024 * KiB
)

type treeHash struct {
	Hash  [sha256.Size]byte
	Left  *treeHash
	Right *treeHash
}

func createTreeHash(r io.Reader) (*treeHash, error) {
	hasher := sha256.New()
	hashes := make([]treeHash, 0)
	outIndex := 0

	// generate hashes for 1 MiB chunks
	n, err := io.CopyN(hasher, r, MiB)
	for err == nil {
		hashes = append(hashes, treeHash{})
		hasher.Sum(hashes[outIndex].Hash[:0])
		hasher.Reset()
		outIndex++
		n, err = io.CopyN(hasher, r, MiB)
	}
	if err != nil && err != io.EOF {
		return nil, err
	}
	if n > 0 {
		hashes = append(hashes, treeHash{})
		hasher.Sum(hashes[outIndex].Hash[:0])
		hasher.Reset()
		outIndex++
	}

	// build tree
	// TODO calculate levels remaining and grow once
	childIndex := 0
	added := outIndex
	remainderIndex := -1
	for added > 1 || remainderIndex != -1 {
		children := added
		added = 0
		// pair up
		for children > 1 {
			hashes = append(hashes, treeHash{})
			hashes[outIndex].Left = &hashes[childIndex]
			hashes[outIndex].Right = &hashes[childIndex+1]
			hasher.Write(toHex(hashes[childIndex].Hash[:]))
			hasher.Write(toHex(hashes[childIndex+1].Hash[:]))
			hasher.Sum(hashes[outIndex].Hash[:0])
			hasher.Reset()
			childIndex += 2
			children -= 2
			outIndex++
			added++
		}
		if children == 1 {
			// have a remainder that couldn't be paired up
			if remainderIndex == -1 {
				// hold on to remainder for later
				remainderIndex = childIndex
				childIndex++
			} else {
				// join with remainder from a previous level
				hashes = append(hashes, treeHash{})
				hashes[outIndex].Left = &hashes[childIndex]
				hashes[outIndex].Right = &hashes[remainderIndex]
				hasher.Write(toHex(hashes[childIndex].Hash[:]))
				hasher.Write(toHex(hashes[remainderIndex].Hash[:]))
				hasher.Sum(hashes[outIndex].Hash[:0])
				hasher.Reset()
				outIndex++
				remainderIndex = -1
				added++
				childIndex++
			}
		}
	}

	return &hashes[outIndex-1], nil
}
<|endoftext|>


<commit_before>package instance

import (
	"fmt"

	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
	"github.com/mitchellh/packer/template/interpolate"
)

type bundleCmdData struct {
	AccountId    string
	Architecture string
	CertPath     string
	Destination  string
	KeyPath      string
	Prefix       string
	PrivatePath  string
}

type StepBundleVolume struct {
	Debug bool
}

func (s *StepBundleVolume) Run(state multistep.StateBag) multistep.StepAction {
	comm := state.Get("communicator").(packer.Communicator)
	config := state.Get("config").(*Config)
	instance := state.Get("instance").(*ec2.Instance)
	ui := state.Get("ui").(packer.Ui)
	x509RemoteCertPath := state.Get("x509RemoteCertPath").(string)
	x509RemoteKeyPath := state.Get("x509RemoteKeyPath").(string)

	// Bundle the volume
	var err error
	config.ctx.Data = bundleCmdData{
		AccountId:    config.AccountId,
		Architecture: *instance.Architecture,
		CertPath:     x509RemoteCertPath,
		Destination:  config.BundleDestination,
		KeyPath:      x509RemoteKeyPath,
		Prefix:       config.BundlePrefix,
		PrivatePath:  config.X509UploadPath,
	}
	config.BundleVolCommand, err = interpolate.Render(config.BundleVolCommand, &config.ctx)
	if err != nil {
		err := fmt.Errorf("Error processing bundle volume command: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	ui.Say("Bundling the volume...")
	cmd := new(packer.RemoteCmd)
	cmd.Command = config.BundleVolCommand

	if s.Debug {
		ui.Say(fmt.Sprintf("Running: %s", config.BundleVolCommand))
	}

	if err := cmd.StartWithUi(comm, ui); err != nil {
		state.Put("error", fmt.Errorf("Error bundling volume: %s", err))
		ui.Error(state.Get("error").(error).Error())
		return multistep.ActionHalt
	}

	if cmd.ExitStatus != 0 {
		state.Put("error", fmt.Errorf(
			"Volume bundling failed. Please see the output above for more\n"+
				"details on what went wrong."))
		ui.Error(state.Get("error").(error).Error())
		return multistep.ActionHalt
	}

	// Store the manifest path
	manifestName := config.BundlePrefix + ".manifest.xml"
	state.Put("manifest_name", manifestName)
	state.Put("manifest_path", fmt.Sprintf(
		"%s/%s", config.BundleDestination, manifestName))

	return multistep.ActionContinue
}

func (s *StepBundleVolume) Cleanup(multistep.StateBag) {}
<commit_msg>Add suggestion to AWS Instance builder error message<commit_after>package instance

import (
	"fmt"

	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
	"github.com/mitchellh/packer/template/interpolate"
)

type bundleCmdData struct {
	AccountId    string
	Architecture string
	CertPath     string
	Destination  string
	KeyPath      string
	Prefix       string
	PrivatePath  string
}

type StepBundleVolume struct {
	Debug bool
}

func (s *StepBundleVolume) Run(state multistep.StateBag) multistep.StepAction {
	comm := state.Get("communicator").(packer.Communicator)
	config := state.Get("config").(*Config)
	instance := state.Get("instance").(*ec2.Instance)
	ui := state.Get("ui").(packer.Ui)
	x509RemoteCertPath := state.Get("x509RemoteCertPath").(string)
	x509RemoteKeyPath := state.Get("x509RemoteKeyPath").(string)

	// Bundle the volume
	var err error
	config.ctx.Data = bundleCmdData{
		AccountId:    config.AccountId,
		Architecture: *instance.Architecture,
		CertPath:     x509RemoteCertPath,
		Destination:  config.BundleDestination,
		KeyPath:      x509RemoteKeyPath,
		Prefix:       config.BundlePrefix,
		PrivatePath:  config.X509UploadPath,
	}
	config.BundleVolCommand, err = interpolate.Render(config.BundleVolCommand, &config.ctx)
	if err != nil {
		err := fmt.Errorf("Error processing bundle volume command: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	ui.Say("Bundling the volume...")
	cmd := new(packer.RemoteCmd)
	cmd.Command = config.BundleVolCommand

	if s.Debug {
		ui.Say(fmt.Sprintf("Running: %s", config.BundleVolCommand))
	}

	if err := cmd.StartWithUi(comm, ui); err != nil {
		state.Put("error", fmt.Errorf("Error bundling volume: %s", err))
		ui.Error(state.Get("error").(error).Error())
		return multistep.ActionHalt
	}

	if cmd.ExitStatus != 0 {
		state.Put("error", fmt.Errorf(
			"Volume bundling failed. Please see the output above for more\n"+
				"details on what went wrong.\n\n"+
				"One common cause for this error is ec2-bundle-vol not being\n"+
				"available on the target instance."))
		ui.Error(state.Get("error").(error).Error())
		return multistep.ActionHalt
	}

	// Store the manifest path
	manifestName := config.BundlePrefix + ".manifest.xml"
	state.Put("manifest_name", manifestName)
	state.Put("manifest_path", fmt.Sprintf(
		"%s/%s", config.BundleDestination, manifestName))

	return multistep.ActionContinue
}

func (s *StepBundleVolume) Cleanup(multistep.StateBag) {}
<|endoftext|>


<commit_before>// Copyright 2011 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 user

import (
	"os"
	"runtime"
	"testing"
)

func skip(t *testing.T) bool {
	if !implemented {
		t.Logf("user: not implemented; skipping tests")
		return true
	}

	switch runtime.GOOS {
	case "linux", "freebsd", "darwin", "windows":
		return false
	}

	t.Logf("user: Lookup not implemented on %s; skipping test", runtime.GOOS)
	return true
}

func TestCurrent(t *testing.T) {
	if skip(t) {
		return
	}

	u, err := Current()
	if err != nil {
		t.Fatalf("Current: %v", err)
	}
	fi, err := os.Stat(u.HomeDir)
	if err != nil || !fi.IsDir() {
		t.Errorf("expected a valid HomeDir; stat(%q): err=%v", u.HomeDir, err)
	}
	if u.Username == "" {
		t.Fatalf("didn't get a username")
	}
}

func compare(t *testing.T, want, got *User) {
	if want.Uid != got.Uid {
		t.Errorf("got Uid=%q; want %q", got.Uid, want.Uid)
	}
	if want.Username != got.Username {
		t.Errorf("got Username=%q; want %q", got.Username, want.Username)
	}
	if want.Name != got.Name {
		t.Errorf("got Name=%q; want %q", got.Name, want.Name)
	}
	// TODO(brainman): fix it once we know how.
	if runtime.GOOS == "windows" {
		t.Log("skipping Gid and HomeDir comparisons")
		return
	}
	if want.Gid != got.Gid {
		t.Errorf("got Gid=%q; want %q", got.Gid, want.Gid)
	}
	if want.HomeDir != got.HomeDir {
		t.Errorf("got HomeDir=%q; want %q", got.HomeDir, want.HomeDir)
	}
}

func TestLookup(t *testing.T) {
	if skip(t) {
		return
	}

	want, err := Current()
	if err != nil {
		t.Fatalf("Current: %v", err)
	}
	got, err := Lookup(want.Username)
	if err != nil {
		t.Fatalf("Lookup: %v", err)
	}
	compare(t, want, got)
}

func TestLookupId(t *testing.T) {
	if skip(t) {
		return
	}

	want, err := Current()
	if err != nil {
		t.Fatalf("Current: %v", err)
	}
	got, err := LookupId(want.Uid)
	if err != nil {
		t.Fatalf("LookupId: %v", err)
	}
	compare(t, want, got)
}
<commit_msg>os/user: simplify test<commit_after>// Copyright 2011 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 user

import (
	"runtime"
	"testing"
)

func skip(t *testing.T) bool {
	if !implemented {
		t.Logf("user: not implemented; skipping tests")
		return true
	}

	switch runtime.GOOS {
	case "linux", "freebsd", "darwin", "windows":
		return false
	}

	t.Logf("user: Lookup not implemented on %s; skipping test", runtime.GOOS)
	return true
}

func TestCurrent(t *testing.T) {
	if skip(t) {
		return
	}

	u, err := Current()
	if err != nil {
		t.Fatalf("Current: %v", err)
	}
	if u.HomeDir == "" {
		t.Errorf("didn't get a HomeDir")
	}
	if u.Username == "" {
		t.Errorf("didn't get a username")
	}
}

func compare(t *testing.T, want, got *User) {
	if want.Uid != got.Uid {
		t.Errorf("got Uid=%q; want %q", got.Uid, want.Uid)
	}
	if want.Username != got.Username {
		t.Errorf("got Username=%q; want %q", got.Username, want.Username)
	}
	if want.Name != got.Name {
		t.Errorf("got Name=%q; want %q", got.Name, want.Name)
	}
	// TODO(brainman): fix it once we know how.
	if runtime.GOOS == "windows" {
		t.Log("skipping Gid and HomeDir comparisons")
		return
	}
	if want.Gid != got.Gid {
		t.Errorf("got Gid=%q; want %q", got.Gid, want.Gid)
	}
	if want.HomeDir != got.HomeDir {
		t.Errorf("got HomeDir=%q; want %q", got.HomeDir, want.HomeDir)
	}
}

func TestLookup(t *testing.T) {
	if skip(t) {
		return
	}

	want, err := Current()
	if err != nil {
		t.Fatalf("Current: %v", err)
	}
	got, err := Lookup(want.Username)
	if err != nil {
		t.Fatalf("Lookup: %v", err)
	}
	compare(t, want, got)
}

func TestLookupId(t *testing.T) {
	if skip(t) {
		return
	}

	want, err := Current()
	if err != nil {
		t.Fatalf("Current: %v", err)
	}
	got, err := LookupId(want.Uid)
	if err != nil {
		t.Fatalf("LookupId: %v", err)
	}
	compare(t, want, got)
}
<|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.

// Deep equality test via reflection

package reflect

// During deepValueEqual, must keep track of checks that are
// in progress.  The comparison algorithm assumes that all
// checks in progress are true when it reencounters them.
// Visited are stored in a map indexed by 17 * a1 + a2;
type visit struct {
	a1   uintptr
	a2   uintptr
	typ  Type
	next *visit
}

// Tests for deep equality using reflected types. The map argument tracks
// comparisons that have already been seen, which allows short circuiting on
// recursive types.
func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool) {
	if !v1.IsValid() || !v2.IsValid() {
		return v1.IsValid() == v2.IsValid()
	}
	if v1.Type() != v2.Type() {
		return false
	}

	// if depth > 10 { panic("deepValueEqual") }	// for debugging

	if v1.CanAddr() && v2.CanAddr() {
		addr1 := v1.UnsafeAddr()
		addr2 := v2.UnsafeAddr()
		if addr1 > addr2 {
			// Canonicalize order to reduce number of entries in visited.
			addr1, addr2 = addr2, addr1
		}

		// Short circuit if references are identical ...
		if addr1 == addr2 {
			return true
		}

		// ... or already seen
		h := 17*addr1 + addr2
		seen := visited[h]
		typ := v1.Type()
		for p := seen; p != nil; p = p.next {
			if p.a1 == addr1 && p.a2 == addr2 && p.typ == typ {
				return true
			}
		}

		// Remember for later.
		visited[h] = &visit{addr1, addr2, typ, seen}
	}

	switch v1.Kind() {
	case Array:
		if v1.Len() != v2.Len() {
			return false
		}
		for i := 0; i < v1.Len(); i++ {
			if !deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) {
				return false
			}
		}
		return true
	case Slice:
		if v1.IsNil() != v2.IsNil() {
			return false
		}
		if v1.Len() != v2.Len() {
			return false
		}
		for i := 0; i < v1.Len(); i++ {
			if !deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) {
				return false
			}
		}
		return true
	case Interface:
		if v1.IsNil() || v2.IsNil() {
			return v1.IsNil() == v2.IsNil()
		}
		return deepValueEqual(v1.Elem(), v2.Elem(), visited, depth+1)
	case Ptr:
		return deepValueEqual(v1.Elem(), v2.Elem(), visited, depth+1)
	case Struct:
		for i, n := 0, v1.NumField(); i < n; i++ {
			if !deepValueEqual(v1.Field(i), v2.Field(i), visited, depth+1) {
				return false
			}
		}
		return true
	case Map:
		if v1.IsNil() != v2.IsNil() {
			return false
		}
		if v1.Len() != v2.Len() {
			return false
		}
		for _, k := range v1.MapKeys() {
			if !deepValueEqual(v1.MapIndex(k), v2.MapIndex(k), visited, depth+1) {
				return false
			}
		}
		return true
	case Func:
		if v1.IsNil() && v2.IsNil() {
			return true
		}
		// Can't do better than this:
		return false
	default:
		// Normal equality suffices
		return valueInterface(v1, false) == valueInterface(v2, false)
	}
}

// DeepEqual tests for deep equality. It uses normal == equality where
// possible but will scan elements of arrays, slices, maps, and fields of
// structs. In maps, keys are compared with == but elements use deep
// equality. DeepEqual correctly handles recursive types. Functions are equal
// only if they are both nil.
// An empty slice is not equal to a nil slice.
func DeepEqual(a1, a2 interface{}) bool {
	if a1 == nil || a2 == nil {
		return a1 == a2
	}
	v1 := ValueOf(a1)
	v2 := ValueOf(a2)
	if v1.Type() != v2.Type() {
		return false
	}
	return deepValueEqual(v1, v2, make(map[uintptr]*visit), 0)
}
<commit_msg>reflect: use visit structure for map key in DeepEqual<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.

// Deep equality test via reflection

package reflect

// During deepValueEqual, must keep track of checks that are
// in progress.  The comparison algorithm assumes that all
// checks in progress are true when it reencounters them.
// Visited comparisons are stored in a map indexed by visit.
type visit struct {
	a1  uintptr
	a2  uintptr
	typ Type
}

// Tests for deep equality using reflected types. The map argument tracks
// comparisons that have already been seen, which allows short circuiting on
// recursive types.
func deepValueEqual(v1, v2 Value, visited map[visit]bool, depth int) bool {
	if !v1.IsValid() || !v2.IsValid() {
		return v1.IsValid() == v2.IsValid()
	}
	if v1.Type() != v2.Type() {
		return false
	}

	// if depth > 10 { panic("deepValueEqual") }	// for debugging

	if v1.CanAddr() && v2.CanAddr() {
		addr1 := v1.UnsafeAddr()
		addr2 := v2.UnsafeAddr()
		if addr1 > addr2 {
			// Canonicalize order to reduce number of entries in visited.
			addr1, addr2 = addr2, addr1
		}

		// Short circuit if references are identical ...
		if addr1 == addr2 {
			return true
		}

		// ... or already seen
		typ := v1.Type()
		v := visit{addr1, addr2, typ}
		if visited[v] {
			return true
		}

		// Remember for later.
		visited[v] = true
	}

	switch v1.Kind() {
	case Array:
		if v1.Len() != v2.Len() {
			return false
		}
		for i := 0; i < v1.Len(); i++ {
			if !deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) {
				return false
			}
		}
		return true
	case Slice:
		if v1.IsNil() != v2.IsNil() {
			return false
		}
		if v1.Len() != v2.Len() {
			return false
		}
		for i := 0; i < v1.Len(); i++ {
			if !deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) {
				return false
			}
		}
		return true
	case Interface:
		if v1.IsNil() || v2.IsNil() {
			return v1.IsNil() == v2.IsNil()
		}
		return deepValueEqual(v1.Elem(), v2.Elem(), visited, depth+1)
	case Ptr:
		return deepValueEqual(v1.Elem(), v2.Elem(), visited, depth+1)
	case Struct:
		for i, n := 0, v1.NumField(); i < n; i++ {
			if !deepValueEqual(v1.Field(i), v2.Field(i), visited, depth+1) {
				return false
			}
		}
		return true
	case Map:
		if v1.IsNil() != v2.IsNil() {
			return false
		}
		if v1.Len() != v2.Len() {
			return false
		}
		for _, k := range v1.MapKeys() {
			if !deepValueEqual(v1.MapIndex(k), v2.MapIndex(k), visited, depth+1) {
				return false
			}
		}
		return true
	case Func:
		if v1.IsNil() && v2.IsNil() {
			return true
		}
		// Can't do better than this:
		return false
	default:
		// Normal equality suffices
		return valueInterface(v1, false) == valueInterface(v2, false)
	}
}

// DeepEqual tests for deep equality. It uses normal == equality where
// possible but will scan elements of arrays, slices, maps, and fields of
// structs. In maps, keys are compared with == but elements use deep
// equality. DeepEqual correctly handles recursive types. Functions are equal
// only if they are both nil.
// An empty slice is not equal to a nil slice.
func DeepEqual(a1, a2 interface{}) bool {
	if a1 == nil || a2 == nil {
		return a1 == a2
	}
	v1 := ValueOf(a1)
	v2 := ValueOf(a2)
	if v1.Type() != v2.Type() {
		return false
	}
	return deepValueEqual(v1, v2, make(map[visit]bool), 0)
}
<|endoftext|>


<commit_before>// $G $D/$F.go && $L $F.$A && ./$A.out

// 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.

// Verify channel operations that test for blocking
// Use several sizes and types of operands

package main

func pause() {
	for i:=0; i<100; i++ { sys.gosched() }
}

func i32receiver(c *chan int32) {
	<-c
}

func i32sender(c *chan int32) {
	c -< 1
}

func i64receiver(c *chan int64) {
	<-c
}

func i64sender(c *chan int64) {
	c -< 1
}

func breceiver(c *chan bool) {
	<-c
}

func bsender(c *chan bool) {
	c -< true
}

func sreceiver(c *chan string) {
	<-c
}

func ssender(c *chan string) {
	c -< "hi"
}

func main() {
	var i32 int32;
	var i64 int64;
	var b bool;
	var s string;
	var ok bool;

	c32 := new(chan int32);
	c64 := new(chan int64);
	cb := new(chan bool);
	cs := new(chan string);

	i32, ok = <-c32;
	if ok { panic "blocked i32sender" }

	i64, ok = <-c64;
	if ok { panic "blocked i64sender" }

	b, ok = <-cb;
	if ok { panic "blocked bsender" }

	s, ok = <-cs;
	if ok { panic "blocked ssender" }

	go i32receiver(c32);
	pause();
	ok = c32 -< 1;
	if !ok { panic "i32receiver" }
	go i32sender(c32);
	pause();
	i32, ok = <-c32;
	if !ok { panic "i32sender" }

	go i64receiver(c64);
	pause();
	ok = c64 -< 1;
	if !ok { panic "i64receiver" }
	go i64sender(c64);
	pause();
	i64, ok = <-c64;
	if !ok { panic "i64sender" }

	go breceiver(cb);
	pause();
	ok = cb -< true;
	if !ok { panic "breceiver" }
	go bsender(cb);
	pause();
	b, ok = <-cb;
	if !ok { panic "bsender" }

	go sreceiver(cs);
	pause();
	ok = cs -< "hi";
	if !ok { panic "sreceiver" }
	go ssender(cs);
	pause();
	s, ok = <-cs;
	if !ok { panic "ssender" }
}
<commit_msg>strengthen test by checking values<commit_after>// $G $D/$F.go && $L $F.$A && ./$A.out

// 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.

// Verify channel operations that test for blocking
// Use several sizes and types of operands

package main

func pause() {
	for i:=0; i<100; i++ { sys.gosched() }
}

func i32receiver(c *chan int32) {
	if <-c != 123 { panic "i32 value" }
}

func i32sender(c *chan int32) {
	c -< 234
}

func i64receiver(c *chan int64) {
	if <-c != 123456 { panic "i64 value" }
}

func i64sender(c *chan int64) {
	c -< 234567
}

func breceiver(c *chan bool) {
	if ! <-c { panic "b value" }
}

func bsender(c *chan bool) {
	c -< true
}

func sreceiver(c *chan string) {
	if <-c != "hello" { panic "s value" }
}

func ssender(c *chan string) {
	c -< "hello again"
}

func main() {
	var i32 int32;
	var i64 int64;
	var b bool;
	var s string;
	var ok bool;

	c32 := new(chan int32);
	c64 := new(chan int64);
	cb := new(chan bool);
	cs := new(chan string);

	i32, ok = <-c32;
	if ok { panic "blocked i32sender" }

	i64, ok = <-c64;
	if ok { panic "blocked i64sender" }

	b, ok = <-cb;
	if ok { panic "blocked bsender" }

	s, ok = <-cs;
	if ok { panic "blocked ssender" }

	go i32receiver(c32);
	pause();
	ok = c32 -< 123;
	if !ok { panic "i32receiver" }
	go i32sender(c32);
	pause();
	i32, ok = <-c32;
	if !ok { panic "i32sender" }
	if i32 != 234 { panic "i32sender value" }

	go i64receiver(c64);
	pause();
	ok = c64 -< 123456;
	if !ok { panic "i64receiver" }
	go i64sender(c64);
	pause();
	i64, ok = <-c64;
	if !ok { panic "i64sender" }
	if i64 != 234567 { panic "i64sender value" }

	go breceiver(cb);
	pause();
	ok = cb -< true;
	if !ok { panic "breceiver" }
	go bsender(cb);
	pause();
	b, ok = <-cb;
	if !ok { panic "bsender" }
	if !b{ panic "bsender value" }

	go sreceiver(cs);
	pause();
	ok = cs -< "hello";
	if !ok { panic "sreceiver" }
	go ssender(cs);
	pause();
	s, ok = <-cs;
	if !ok { panic "ssender" }
	if s != "hello again" { panic "ssender value" }
	print "PASS\n"
}
<|endoftext|>


<commit_before>package debug

import (
	"bufio"
	"bytes"
	"encoding/hex"
	"encoding/xml"
	"errors"
	"fmt"
	uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn"
	"net"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/lunixbochs/usercorn/go/models"
)

func escape(p []byte) []byte {
	out := make([]byte, 0, len(p))
	for _, c := range p {
		if c == '#' || c == '$' || c == '}' {
			out = append(out, '}')
			out = append(out, c^0x20)
		} else {
			out = append(out, c)
		}
	}
	return out
}

func unescape(p []byte) []byte {
	out := make([]byte, 0, len(p))
	escaped := false
	for i, c := range p {
		if escaped {
			continue
		}
		if c == '{' && i < len(p)-1 {
			escaped = true
			out = append(out, p[i+1]^0x20)
		} else {
			out = append(out, c)
		}
	}
	return out
}

func checksum(p []byte) []byte {
	chk := 0
	for _, c := range p {
		chk = (chk + int(c)) % 256
	}
	return []byte(fmt.Sprintf("%02x", chk))
}

func parseRange(s string) (uint64, uint64) {
	tmp := strings.Split(s, ":")
	if len(tmp) == 0 {
		tmp = []string{s}
	}
	tmp = strings.Split(tmp[len(tmp)-1], ",")
	if len(tmp) != 2 {
		return 0, 0
	}
	a, _ := strconv.ParseUint(tmp[0], 16, 0)
	b, _ := strconv.ParseUint(tmp[1], 16, 0)
	return a, b
}

type Gdbstub struct {
	instances []models.Usercorn
}

func NewGdbstub(first models.Usercorn, extra ...models.Usercorn) *Gdbstub {
	instances := append([]models.Usercorn{first}, extra...)
	for _, u := range instances {
		u.Gate().Lock()
	}
	return &Gdbstub{instances}
}

func (d *Gdbstub) Run(c net.Conn) {
	fmt.Fprintf(os.Stderr, "GDB stub connected from %s\n", c.RemoteAddr())
	(&gdbClient{
		Conn: c,
		stub: d,
		u:    d.instances[0],

		breakpoints: make(map[uint64]uc.Hook),

		verbose: false,
	}).Run()
}

type gdbClient struct {
	net.Conn
	noAck     bool
	noAckTest bool
	stub      *Gdbstub
	u         models.Usercorn

	regData  map[int]gdbReg
	regEnums map[int]int
	regList  []int

	breakpoints map[uint64]uc.Hook

	verbose bool
}

func (c *gdbClient) fmtaddr(addr uint64) string {
	var tmp [8]byte
	packed, _ := c.u.PackAddr(tmp[:], addr)
	return hex.EncodeToString(packed)
}

func (c *gdbClient) Send(s string) error {
	if c.verbose {
		fmt.Printf("sending %v\n", s)
	}
	data := escape([]byte(s))
	data = []byte("$" + string(data) + "#" + string(checksum(data)))
	_, err := c.Write(data)
	return err
}

func (c *gdbClient) Wait() {
	u := c.u
	pc, _ := u.RegRead(u.Arch().PC)
	c.Send(fmt.Sprintf("T%02xpc:%s;thread:1;", 0, c.fmtaddr(pc)))
}

func (c *gdbClient) Handle(cmdb []byte) error {
	if c.verbose {
		fmt.Printf("handling %v\n", string(cmdb))
	}
	u := c.u
	if len(cmdb) == 0 {
		return nil
	}
	b, rest := cmdb[0], string(cmdb[1:])
	var cmd, args string
	if strings.Contains(rest, ":") {
		tmp := strings.SplitN(rest, ":", 2)
		cmd, args = tmp[0], tmp[1]
	} else {
		cmd = rest
	}
	switch b {
	case 'q': // query
		switch cmd {
		case "Supported":
			c.Send("PacketSize=4000;qXfer:features:read+") // ;qXfer:memory-map:read+
		case "Attached":
			c.Send("1")
		case "Symbol":
			c.Send("OK")
		case "C":
			c.Send("OK")
		case "Xfer":
			if strings.HasPrefix(args, "features:read:target.xml:") {
				a, b := parseRange(args)
				tdesc := u.Arch().GdbXml
				if a >= 0 && a < uint64(len(tdesc)) {
					if a+b > uint64(len(tdesc)) {
						b = uint64(len(tdesc)) - a
					}
					c.Send("m" + tdesc[a:a+b])
				} else {
					c.Send("l")
				}
			} else {
				if c.verbose {
					fmt.Println("unknown q Xfer:", args)
				}
			}
		/*
			case "TStatus":
				c.Send("T0")
		*/
		case "Rcmd":
			tmp := strings.SplitN(cmd, ",", 2)
			if c.verbose {
				fmt.Println("would send input:", tmp[1])
			}
			// c.Send("O" + (output + "\n").encode("hex"))
			c.Send("OK")
		default:
			if c.verbose {
				fmt.Println("unknown cmd q", cmd, args)
			}
			c.Send("")
		}
	case 'Q': // set query
		switch cmd {
		case "StartNoAckMode":
			c.noAckTest = true
		default:
			if c.verbose {
				fmt.Println("unknown cmd Q", cmd, args)
			}
			c.Send("")
		}
	case 'v': // resume
		if cmd == "Cont?" {
			c.Send("")
		}
	case 'g': // read regs
		/*
			var vals []string
			for _, v := range c.regList {
				if v > 0 {
					enum := v - 1
					r, _ := u.RegRead(enum)
					vals = append(vals, c.fmtaddr(r))
				}
			}
			c.Send(strings.Repeat("0", 8))
			// c.Send(strings.Join(vals, ""))
		*/
		// FIXME
		c.Send("00000000")
	case 'G': // write regs
		if c.verbose {
			fmt.Println("should write regs")
		}
	case 'p': // read one reg
		i, _ := strconv.ParseUint(cmd, 16, 0)
		if int(i) < len(c.regList) {
			v := c.regList[i]
			if v > 0 {
				val, _ := u.RegRead(v - 1)
				c.Send(c.fmtaddr(val))
			} else {
				c.Send("00000000")
			}
		} else {
			c.Send("")
		}
	case 'm': // read memory
		a, b := parseRange(rest)
		mem, err := u.MemRead(a, b)
		if err != nil {
			if c.verbose {
				fmt.Println("error reading mem", err)
			}
			c.Send("")
		} else {
			c.Send(hex.EncodeToString(mem))
		}
	case 'M': // write memory
		a, _ := parseRange(rest)
		data, err := hex.DecodeString(rest)
		if err != nil {
			if c.verbose {
				fmt.Println("error parsing hex", rest, err)
			}
			c.Send("")
		} else {
			err := u.MemWrite(a, data)
			if err != nil {
				if c.verbose {
					fmt.Println("error writing mem", err)
				}
			}
		}
	case 'Z': // add breakpoint
		args := strings.Split(rest, ",")
		if len(args) != 3 {
			break
		}
		addr, _ := strconv.ParseUint(args[1], 16, 0)
		if _, ok := c.breakpoints[addr]; ok {
			c.Send("OK")
			break
		}
		h, _ := u.HookAdd(uc.HOOK_CODE, func(_ uc.Unicorn, addr uint64, size uint32) {
			u.Trampoline(func() error { return nil })
			u.Stop()
		}, addr, addr+1)
		c.breakpoints[addr] = h
		c.Send("OK")
	case 'z': // remove breakpoint
		// TODO: this seems to freeze gdb
		args := strings.Split(rest, ",")
		if len(args) != 3 {
			break
		}
		addr, _ := strconv.ParseUint(args[1], 16, 0)
		if h, ok := c.breakpoints[addr]; ok {
			u.HookDel(h)
		}
		c.Send("OK")
	case 'c': // continue
		u.Gate().UnlockStopRelock()
		c.Wait()
	case 's': // step
		first := true
		h, _ := u.HookAdd(uc.HOOK_CODE, func(_ uc.Unicorn, addr uint64, size uint32) {
			if first {
				first = false
			} else {
				u.Trampoline(func() error { return nil })
				u.Stop()
				return
			}
		}, 1, 0)
		u.Gate().UnlockStopRelock()
		u.HookDel(h)
		c.Wait()
	case '?': // last signal
		c.Wait()
	case 'H': // do thread op
		if c.verbose {
			fmt.Println("thread op", b, cmd, args)
		}
		c.Send("OK")
	case 'D': // detach
		return errors.New("detached")
	default:
		if c.verbose {
			fmt.Printf("unknown command %c %s %s\n", b, cmd, args)
		}
	}
	return nil
}

type gdbReg struct {
	XMLName xml.Name `xml:"reg"`
	Name    string   `xml:"name,attr"`
	Bitsize int      `xml:"bitsize,attr"`
	Type    string   `xml:"type,attr"`
	Regnum  int      `xml:"regnum,attr"`
}

type gdbTarget struct {
	XMLName xml.Name `xml:"target"`
	Regs    []gdbReg `xml:"feature>reg"`
}

func (c *gdbClient) parseXml(x string) {
	regLookup := make(map[string]int)
	c.regData = make(map[int]gdbReg)
	c.regEnums = make(map[int]int)

	var target gdbTarget
	xml.Unmarshal([]byte(x), &target)
	base := 0
	for i, v := range target.Regs {
		if v.Regnum > 0 {
			base = v.Regnum - i
		}
		c.regData[base+i] = v
		regLookup[v.Name] = base + i
	}
	a := c.u.Arch()
	regNames := a.RegNames()
	max := 0
	for enum, name := range regNames {
		if i, ok := regLookup[name]; ok {
			c.regEnums[i] = enum
			if i > max {
				max = i
			}
		}
	}
	c.regList = make([]int, max+1)
	for i, v := range c.regEnums {
		c.regList[i] = v + 1
	}
}

func (c *gdbClient) Run() {
	c.parseXml(c.u.Arch().GdbXml)

	input := bufio.NewReader(c)
	var err error

	var loop sync.Mutex
	go func() {
		for {
			loop.Lock()
			b, err := input.Peek(1)
			if err != nil {
				break
			}
			// TODO: this won't interrupt pending syscalls
			if b[0] == '\x03' {
				input.Discard(1)
				c.u.Trampoline(func() error { return nil })
				c.u.Stop()
			}
			loop.Unlock()
			<-time.After(100 * time.Millisecond)
		}
	}()

	for {
		loop.Lock()
		var b, chk []byte

		b, err = input.Peek(1)
		if err != nil {
			break
		} else if b[0] == 0x03 {
			loop.Unlock()
			continue
		} else if b[0] == '+' || b[0] == '-' {
			// ack
			input.Discard(1)
			if c.noAckTest && b[0] == '+' {
				c.noAck = true
			}
			c.noAckTest = false
		}
		if b, err = input.ReadBytes('#'); err != nil {
			break
		}
		if chk, err = input.Peek(2); err != nil {
			break
		}
		input.Discard(2)

		data := b[1 : len(b)-1]
		if bytes.Equal(checksum(data), chk) {
			c.ack('+')
			loop.Unlock()
			if err = c.Handle(unescape(data)); err != nil {
				break
			}
		} else {
			c.ack('-')
			loop.Unlock()
		}
	}
	loop.Unlock()
	if err != nil {
		fmt.Fprintf(os.Stderr, "GDB stub error: %v\n", err)
	}
	c.Close()
}

func (c *gdbClient) ack(b byte) {
	if !c.noAck {
		c.Write([]byte{b})
	}
}
<commit_msg>fix gdbstub bugs with newer gdb<commit_after>package debug

import (
	"bufio"
	"bytes"
	"encoding/hex"
	"encoding/xml"
	"errors"
	"fmt"
	uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn"
	"net"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/lunixbochs/usercorn/go/models"
)

func escape(p []byte) []byte {
	out := make([]byte, 0, len(p))
	for _, c := range p {
		if c == '#' || c == '$' || c == '}' {
			out = append(out, '}')
			out = append(out, c^0x20)
		} else {
			out = append(out, c)
		}
	}
	return out
}

func unescape(p []byte) []byte {
	out := make([]byte, 0, len(p))
	escaped := false
	for i, c := range p {
		if escaped {
			continue
		}
		if c == '{' && i < len(p)-1 {
			escaped = true
			out = append(out, p[i+1]^0x20)
		} else {
			out = append(out, c)
		}
	}
	return out
}

func checksum(p []byte) []byte {
	chk := 0
	for _, c := range p {
		chk = (chk + int(c)) % 256
	}
	return []byte(fmt.Sprintf("%02x", chk))
}

func parseRange(s string) (uint64, uint64) {
	tmp := strings.Split(s, ":")
	if len(tmp) == 0 {
		tmp = []string{s}
	}
	tmp = strings.Split(tmp[len(tmp)-1], ",")
	if len(tmp) != 2 {
		return 0, 0
	}
	a, _ := strconv.ParseUint(tmp[0], 16, 0)
	b, _ := strconv.ParseUint(tmp[1], 16, 0)
	return a, b
}

type Gdbstub struct {
	instances []models.Usercorn
}

func NewGdbstub(first models.Usercorn, extra ...models.Usercorn) *Gdbstub {
	instances := append([]models.Usercorn{first}, extra...)
	for _, u := range instances {
		u.Gate().Lock()
	}
	return &Gdbstub{instances}
}

func (d *Gdbstub) Run(c net.Conn) {
	fmt.Fprintf(os.Stderr, "GDB stub connected from %s\n", c.RemoteAddr())
	(&gdbClient{
		Conn: c,
		stub: d,
		u:    d.instances[0],

		breakpoints: make(map[uint64]uc.Hook),

		verbose: false,
	}).Run()
}

type gdbClient struct {
	net.Conn
	noAck     bool
	noAckTest bool
	stub      *Gdbstub
	u         models.Usercorn

	regData  map[int]gdbReg
	regEnums map[int]int
	regList  []int

	breakpoints map[uint64]uc.Hook

	verbose bool
}

func (c *gdbClient) fmtaddr(addr uint64) string {
	var tmp [8]byte
	packed, _ := c.u.PackAddr(tmp[:], addr)
	return hex.EncodeToString(packed)
}

func (c *gdbClient) Send(s string) error {
	if c.verbose {
		fmt.Printf("sending %v\n", s)
	}
	data := escape([]byte(s))
	data = []byte("$" + string(data) + "#" + string(checksum(data)))
	_, err := c.Write(data)
	return err
}

func (c *gdbClient) Wait() {
	u := c.u
	pc, _ := u.RegRead(u.Arch().PC)
	c.Send(fmt.Sprintf("T%02xpc:%s;thread:1;", 0, c.fmtaddr(pc)))
}

func (c *gdbClient) Handle(cmdb []byte) error {
	if c.verbose {
		fmt.Printf("handling %v\n", string(cmdb))
	}
	u := c.u
	if len(cmdb) == 0 {
		return nil
	}
	b, rest := cmdb[0], string(cmdb[1:])
	var cmd, args string
	if strings.Contains(rest, ":") {
		tmp := strings.SplitN(rest, ":", 2)
		cmd, args = tmp[0], tmp[1]
	} else {
		cmd = rest
	}
	switch b {
	case 'q': // query
		switch cmd {
		case "Supported":
			c.Send("PacketSize=4000;qXfer:features:read+") // ;qXfer:memory-map:read+
		case "Attached":
			c.Send("1")
		case "Symbol":
			c.Send("OK")
		case "C":
			c.Send("OK")
		case "Xfer":
			if strings.HasPrefix(args, "features:read:target.xml:") {
				a, b := parseRange(args)
				tdesc := u.Arch().GdbXml
				if a >= 0 && a < uint64(len(tdesc)) {
					if a+b > uint64(len(tdesc)) {
						b = uint64(len(tdesc)) - a
					}
					c.Send("m" + tdesc[a:a+b])
				} else {
					c.Send("l")
				}
			} else {
				if c.verbose {
					fmt.Println("unknown q Xfer:", args)
				}
			}
		case "TStatus":
			c.Send("T0")
		case "Rcmd":
			tmp := strings.SplitN(cmd, ",", 2)
			if c.verbose {
				fmt.Println("would send input:", tmp[1])
			}
			// c.Send("O" + (output + "\n").encode("hex"))
			c.Send("OK")
		default:
			if c.verbose {
				fmt.Println("unknown cmd q", cmd, args)
			}
			c.Send("")
		}
	case 'Q': // set query
		switch cmd {
		case "StartNoAckMode":
			c.noAckTest = true
		default:
			if c.verbose {
				fmt.Println("unknown cmd Q", cmd, args)
			}
			c.Send("")
		}
	case 'v': // resume
		if cmd == "Cont?" {
			c.Send("")
		}
	case 'g': // read regs
		/*
			var vals []string
			for _, v := range c.regList {
				if v > 0 {
					enum := v - 1
					r, _ := u.RegRead(enum)
					vals = append(vals, c.fmtaddr(r))
				}
			}
			c.Send(strings.Repeat("0", 8))
			// c.Send(strings.Join(vals, ""))
		*/
		// FIXME
		c.Send("00000000")
	case 'G': // write regs
		if c.verbose {
			fmt.Println("should write regs")
		}
	case 'p': // read one reg
		i, _ := strconv.ParseUint(cmd, 16, 0)
		if int(i) < len(c.regList) {
			v := c.regList[i]
			if v > 0 {
				val, _ := u.RegRead(v - 1)
				c.Send(c.fmtaddr(val))
			} else {
				c.Send("00000000")
			}
		} else {
			c.Send("")
		}
	case 'm': // read memory
		a, b := parseRange(rest)
		mem, err := u.MemRead(a, b)
		if err != nil {
			if c.verbose {
				fmt.Println("error reading mem", err)
			}
			c.Send("")
		} else {
			c.Send(hex.EncodeToString(mem))
		}
	case 'M': // write memory
		a, _ := parseRange(rest)
		data, err := hex.DecodeString(rest)
		if err != nil {
			if c.verbose {
				fmt.Println("error parsing hex", rest, err)
			}
			c.Send("")
		} else {
			err := u.MemWrite(a, data)
			if err != nil {
				if c.verbose {
					fmt.Println("error writing mem", err)
				}
			}
		}
	case 'Z': // add breakpoint
		args := strings.Split(rest, ",")
		if len(args) != 3 {
			break
		}
		addr, _ := strconv.ParseUint(args[1], 16, 0)
		if _, ok := c.breakpoints[addr]; ok {
			c.Send("OK")
			break
		}
		h, _ := u.HookAdd(uc.HOOK_CODE, func(_ uc.Unicorn, addr uint64, size uint32) {
			u.Trampoline(func() error { return nil })
			u.Stop()
		}, addr, addr+1)
		c.breakpoints[addr] = h
		c.Send("OK")
	case 'z': // remove breakpoint
		// TODO: this seems to freeze gdb
		args := strings.Split(rest, ",")
		if len(args) != 3 {
			break
		}
		addr, _ := strconv.ParseUint(args[1], 16, 0)
		if h, ok := c.breakpoints[addr]; ok {
			u.HookDel(h)
		}
		c.Send("OK")
	case 'c': // continue
		u.Gate().UnlockStopRelock()
		c.Wait()
	case 's': // step
		first := true
		h, _ := u.HookAdd(uc.HOOK_CODE, func(_ uc.Unicorn, addr uint64, size uint32) {
			if first {
				first = false
			} else {
				u.Trampoline(func() error { return nil })
				u.Stop()
				return
			}
		}, 1, 0)
		u.Gate().UnlockStopRelock()
		u.HookDel(h)
		c.Wait()
	case '?': // last signal
		c.Wait()
	case 'H': // do thread op
		if c.verbose {
			fmt.Println("thread op", b, cmd, args)
		}
		c.Send("OK")
	case 'D': // detach
		return errors.New("detached")
	case 'T': // thread
		c.Send("OK")
	default:
		if c.verbose {
			fmt.Printf("unknown command %c %s %s\n", b, cmd, args)
		}
	}
	return nil
}

type gdbReg struct {
	XMLName xml.Name `xml:"reg"`
	Name    string   `xml:"name,attr"`
	Bitsize int      `xml:"bitsize,attr"`
	Type    string   `xml:"type,attr"`
	Regnum  int      `xml:"regnum,attr"`
}

type gdbTarget struct {
	XMLName xml.Name `xml:"target"`
	Regs    []gdbReg `xml:"feature>reg"`
}

func (c *gdbClient) parseXml(x string) {
	regLookup := make(map[string]int)
	c.regData = make(map[int]gdbReg)
	c.regEnums = make(map[int]int)

	var target gdbTarget
	xml.Unmarshal([]byte(x), &target)
	base := 0
	for i, v := range target.Regs {
		if v.Regnum > 0 {
			base = v.Regnum - i
		}
		c.regData[base+i] = v
		regLookup[v.Name] = base + i
	}
	a := c.u.Arch()
	regNames := a.RegNames()
	max := 0
	for enum, name := range regNames {
		if i, ok := regLookup[name]; ok {
			c.regEnums[i] = enum
			if i > max {
				max = i
			}
		}
	}
	c.regList = make([]int, max+1)
	for i, v := range c.regEnums {
		c.regList[i] = v + 1
	}
}

func (c *gdbClient) Run() {
	c.parseXml(c.u.Arch().GdbXml)

	input := bufio.NewReader(c)
	var err error

	var loop sync.Mutex
	go func() {
		for {
			loop.Lock()
			b, err := input.Peek(1)
			if err != nil {
				break
			}
			// TODO: this won't interrupt pending syscalls
			if b[0] == '\x03' {
				input.Discard(1)
				c.u.Trampoline(func() error { return nil })
				c.u.Stop()
			}
			loop.Unlock()
			<-time.After(100 * time.Millisecond)
		}
	}()

	for {
		loop.Lock()
		var b, chk []byte

		b, err = input.Peek(1)
		if err != nil {
			break
		} else if b[0] == 0x03 {
			loop.Unlock()
			continue
		} else if b[0] == '+' || b[0] == '-' {
			// ack
			input.Discard(1)
			if c.noAckTest && b[0] == '+' {
				c.noAck = true
			}
			c.noAckTest = false
		}
		if b, err = input.ReadBytes('#'); err != nil {
			break
		}
		if chk, err = input.Peek(2); err != nil {
			break
		}
		input.Discard(2)

		data := b[1 : len(b)-1]
		if bytes.Equal(checksum(data), chk) {
			c.ack('+')
			loop.Unlock()
			if err = c.Handle(unescape(data)); err != nil {
				break
			}
		} else {
			c.ack('-')
			loop.Unlock()
		}
	}
	loop.Unlock()
	if err != nil {
		fmt.Fprintf(os.Stderr, "GDB stub error: %v\n", err)
	}
	c.Close()
}

func (c *gdbClient) ack(b byte) {
	if !c.noAck {
		c.Write([]byte{b})
	}
}
<|endoftext|>


<commit_before>package sortseq

import (
    	"strings"
	. "github.com/BrianReallyMany/yomama/seq"
	"github.com/BrianReallyMany/yomama/seq/oligo"
)

type SortedSeq struct {
	seq Seq
	locus string
	sample string
}

type SeqSorter struct {
	primerMap map[[2]string]string
	barcodeMap map[[2]string]string
	linkers [][2]string
}

func NewSeqSorter(input string) (*SeqSorter, error) {
	lines := strings.Split(input, "\n")
	primerMap := make(map[[2]string]string)
	barcodeMap := make(map[[2]string]string)
	ok := oligo.ValidateOligoText(input)
	if !ok {
		return &SeqSorter{}, &oligo.OligoError{"Failed to validate oligo file\n"}
	}
	numLinkers := oligo.CountLinkers(input)
	linkers := make([][2]string, numLinkers)
	linkerCount := 0
	for _, line := range lines {
		if line == "" {
			continue
		}
		oligotype := oligo.OligoType(line)
		fields := strings.Split(line, "\t")
		switch oligotype {
		case "":
			continue
		case "barcode":
			oligoSeqs := [2]string{fields[1], fields[2]}
			oligoID := fields[3]
			barcodeMap[oligoSeqs] = oligoID
		case "primer":
			oligoSeqs := [2]string{fields[1], fields[2]}
			oligoID := fields[3]
			primerMap[oligoSeqs] = oligoID
		case "linker":
			linkers[linkerCount] = [2]string{fields[1], fields[2]}
			linkerCount++
		}
	}
	sorter := &SeqSorter{primerMap, barcodeMap, linkers}
	return sorter, nil
}

func (s *SeqSorter) SortSeq(seq Seq) SortedSeq {
	// TODO
	seq.Bases = "gattaca"
	return SortedSeq{seq,"", ""}
}
<commit_msg>removed silly linkersCount stuff from NewSeqSorter<commit_after>package sortseq

import (
    	"strings"
	. "github.com/BrianReallyMany/yomama/seq"
	"github.com/BrianReallyMany/yomama/seq/oligo"
)

type SortedSeq struct {
	seq Seq
	locus string
	sample string
}

type SeqSorter struct {
	primerMap map[[2]string]string
	barcodeMap map[[2]string]string
	linkers [][2]string
}

func NewSeqSorter(input string) (*SeqSorter, error) {
	lines := strings.Split(input, "\n")
	primerMap := make(map[[2]string]string)
	barcodeMap := make(map[[2]string]string)
	ok := oligo.ValidateOligoText(input)
	if !ok {
		return &SeqSorter{}, &oligo.OligoError{"Failed to validate oligo file\n"}
	}
	numLinkers := oligo.CountLinkers(input)
	linkers := make([][2]string, numLinkers)
	for _, line := range lines {
		if line == "" {
			continue
		}
		oligotype := oligo.OligoType(line)
		fields := strings.Split(line, "\t")
		switch oligotype {
		case "":
			continue
		case "barcode":
			oligoSeqs := [2]string{fields[1], fields[2]}
			oligoID := fields[3]
			barcodeMap[oligoSeqs] = oligoID
		case "primer":
			oligoSeqs := [2]string{fields[1], fields[2]}
			oligoID := fields[3]
			primerMap[oligoSeqs] = oligoID
		case "linker":
			linkers[len(linkers)-1] = [2]string{fields[1], fields[2]}
		}
	}
	sorter := &SeqSorter{primerMap, barcodeMap, linkers}
	return sorter, nil
}

func (s *SeqSorter) SortSeq(seq Seq) SortedSeq {
	// TODO
	seq.Bases = "gattaca"
	return SortedSeq{seq,"", ""}
}
<|endoftext|>