<commit_before>package accounting

import (
	"bytes"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/ncw/rclone/fs"
	"github.com/ncw/rclone/fs/fserrors"
	"github.com/ncw/rclone/fs/rc"
)

var (
	// Stats is global statistics counter
	Stats = NewStats()
)

func init() {
	// Set the function pointer up in fs
	fs.CountError = Stats.Error

	rc.Add(rc.Call{
		Path:  "core/stats",
		Fn:    Stats.RemoteStats,
		Title: "Returns stats about current transfers.",
		Help: `
This returns all available stats

	rclone rc core/stats

Returns the following values:

` + "```" + `
{
	"speed": average speed in bytes/sec since start of the process,
	"bytes": total transferred bytes since the start of the process,
	"errors": number of errors,
	"fatalError": whether there has been at least one FatalError,
	"retryError": whether there has been at least one non-NoRetryError,
	"checks": number of checked files,
	"transfers": number of transferred files,
	"deletes" : number of deleted files,
	"elapsedTime": time in seconds since the start of the process,
	"lastError": last occurred error,
	"transferring": an array of currently active file transfers:
		[
			{
				"bytes": total transferred bytes for this file,
				"eta": estimated time in seconds until file transfer completion
				"name": name of the file,
				"percentage": progress of the file transfer in percent,
				"speed": speed in bytes/sec,
				"speedAvg": speed in bytes/sec as an exponentially weighted moving average,
				"size": size of the file in bytes
			}
		],
	"checking": an array of names of currently active file checks
		[]
}
` + "```" + `
Values for "transferring", "checking" and "lastError" are only assigned if data is available.
The value for "eta" is null if an eta cannot be determined.
`,
	})
}

// StatsInfo accounts all transfers
type StatsInfo struct {
	mu                sync.RWMutex
	bytes             int64
	errors            int64
	lastError         error
	fatalError        bool
	retryError        bool
	checks            int64
	checking          *stringSet
	checkQueue        int
	checkQueueSize    int64
	transfers         int64
	transferring      *stringSet
	transferQueue     int
	transferQueueSize int64
	renameQueue       int
	renameQueueSize   int64
	deletes           int64
	start             time.Time
	inProgress        *inProgress
}

// NewStats cretates an initialised StatsInfo
func NewStats() *StatsInfo {
	return &StatsInfo{
		checking:     newStringSet(fs.Config.Checkers, "checking"),
		transferring: newStringSet(fs.Config.Transfers, "transferring"),
		start:        time.Now(),
		inProgress:   newInProgress(),
	}
}

// RemoteStats returns stats for rc
func (s *StatsInfo) RemoteStats(in rc.Params) (out rc.Params, err error) {
	out = make(rc.Params)
	s.mu.RLock()
	dt := time.Now().Sub(s.start)
	dtSeconds := dt.Seconds()
	speed := 0.0
	if dt > 0 {
		speed = float64(s.bytes) / dtSeconds
	}
	out["speed"] = speed
	out["bytes"] = s.bytes
	out["errors"] = s.errors
	out["fatalError"] = s.fatalError
	out["retryError"] = s.retryError
	out["checks"] = s.checks
	out["transfers"] = s.transfers
	out["deletes"] = s.deletes
	out["elapsedTime"] = dtSeconds
	s.mu.RUnlock()
	if !s.checking.empty() {
		var c []string
		s.checking.mu.RLock()
		defer s.checking.mu.RUnlock()
		for name := range s.checking.items {
			c = append(c, name)
		}
		out["checking"] = c
	}
	if !s.transferring.empty() {
		var t []interface{}
		s.transferring.mu.RLock()
		defer s.transferring.mu.RUnlock()
		for name := range s.transferring.items {
			if acc := s.inProgress.get(name); acc != nil {
				t = append(t, acc.RemoteStats())
			} else {
				t = append(t, name)
			}
		}
		out["transferring"] = t
	}
	if s.errors > 0 {
		out["lastError"] = s.lastError
	}
	return out, nil
}

// eta returns the ETA of the current operation,
// rounded to full seconds.
// If the ETA cannot be determined 'ok' returns false.
func eta(size, total int64, rate float64) (eta time.Duration, ok bool) {
	if total <= 0 || size < 0 || rate <= 0 {
		return 0, false
	}
	remaining := total - size
	if remaining < 0 {
		return 0, false
	}
	seconds := float64(remaining) / rate
	return time.Second * time.Duration(seconds), true
}

// etaString returns the ETA of the current operation,
// rounded to full seconds.
// If the ETA cannot be determined it returns "-"
func etaString(done, total int64, rate float64) string {
	d, ok := eta(done, total, rate)
	if !ok {
		return "-"
	}
	return d.String()
}

// percent returns a/b as a percentage rounded to the nearest integer
// as a string
//
// if the percentage is invalid it returns "-"
func percent(a int64, b int64) string {
	if a < 0 || b <= 0 {
		return "-"
	}
	return fmt.Sprintf("%d%%", int(float64(a)*100/float64(b)+0.5))
}

// String convert the StatsInfo to a string for printing
func (s *StatsInfo) String() string {
	// checking and transferring have their own locking so read
	// here before lock to prevent deadlock on GetBytes
	transferring, checking := s.transferring.count(), s.checking.count()
	transferringBytesDone, transferringBytesTotal := s.transferring.progress()

	s.mu.RLock()

	dt := time.Now().Sub(s.start)
	dtSeconds := dt.Seconds()
	speed := 0.0
	if dt > 0 {
		speed = float64(s.bytes) / dtSeconds
	}
	dtRounded := dt - (dt % (time.Second / 10))

	if fs.Config.DataRateUnit == "bits" {
		speed = speed * 8
	}

	var (
		totalChecks   = int64(s.checkQueue) + s.checks + int64(checking)
		totalTransfer = int64(s.transferQueue) + s.transfers + int64(transferring)
		// note that s.bytes already includes transferringBytesDone so
		// we take it off here to avoid double counting
		totalSize    = s.transferQueueSize + s.bytes + transferringBytesTotal - transferringBytesDone
		currentSize  = s.bytes
		buf          = &bytes.Buffer{}
		xfrchkString = ""
	)

	if !fs.Config.StatsOneLine {
		_, _ = fmt.Fprintf(buf, "\nTransferred:   	")
	} else {
		xfrchk := []string{}
		if totalTransfer > 0 && s.transferQueue > 0 {
			xfrchk = append(xfrchk, fmt.Sprintf("xfr#%d/%d", s.transfers, totalTransfer))
		}
		if totalChecks > 0 && s.checkQueue > 0 {
			xfrchk = append(xfrchk, fmt.Sprintf("chk#%d/%d", s.checks, totalChecks))
		}
		if len(xfrchk) > 0 {
			xfrchkString = fmt.Sprintf(" (%s)", strings.Join(xfrchk, ", "))
		}
	}

	_, _ = fmt.Fprintf(buf, "%10s / %s, %s, %s, ETA %s%s",
		fs.SizeSuffix(s.bytes),
		fs.SizeSuffix(totalSize).Unit("Bytes"),
		percent(s.bytes, totalSize),
		fs.SizeSuffix(speed).Unit(strings.Title(fs.Config.DataRateUnit)+"/s"),
		etaString(currentSize, totalSize, speed),
		xfrchkString,
	)

	if !fs.Config.StatsOneLine {
		errorDetails := ""
		switch {
		case s.fatalError:
			errorDetails = " (fatal error encountered)"
		case s.retryError:
			errorDetails = " (retrying may help)"
		case s.errors != 0:
			errorDetails = " (no need to retry)"
		}

		_, _ = fmt.Fprintf(buf, `
Errors:        %10d%s
Checks:        %10d / %d, %s
Transferred:   %10d / %d, %s
Elapsed time:  %10v
`,
			s.errors, errorDetails,
			s.checks, totalChecks, percent(s.checks, totalChecks),
			s.transfers, totalTransfer, percent(s.transfers, totalTransfer),
			dtRounded)
	}

	// checking and transferring have their own locking so unlock
	// here to prevent deadlock on GetBytes
	s.mu.RUnlock()

	// Add per transfer stats if required
	if !fs.Config.StatsOneLine {
		if !s.checking.empty() {
			_, _ = fmt.Fprintf(buf, "Checking:\n%s\n", s.checking)
		}
		if !s.transferring.empty() {
			_, _ = fmt.Fprintf(buf, "Transferring:\n%s\n", s.transferring)
		}
	}

	return buf.String()
}

// Log outputs the StatsInfo to the log
func (s *StatsInfo) Log() {
	fs.LogLevelPrintf(fs.Config.StatsLogLevel, nil, "%v\n", s)
}

// Bytes updates the stats for bytes bytes
func (s *StatsInfo) Bytes(bytes int64) {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.bytes += bytes
}

// GetBytes returns the number of bytes transferred so far
func (s *StatsInfo) GetBytes() int64 {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.bytes
}

// Errors updates the stats for errors
func (s *StatsInfo) Errors(errors int64) {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.errors += errors
}

// GetErrors reads the number of errors
func (s *StatsInfo) GetErrors() int64 {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.errors
}

// GetLastError returns the lastError
func (s *StatsInfo) GetLastError() error {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.lastError
}

// GetChecks returns the number of checks
func (s *StatsInfo) GetChecks() int64 {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.checks
}

// FatalError sets the fatalError flag
func (s *StatsInfo) FatalError() {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.fatalError = true
}

// HadFatalError returns whether there has been at least one FatalError
func (s *StatsInfo) HadFatalError() bool {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.fatalError
}

// RetryError sets the retryError flag
func (s *StatsInfo) RetryError() {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.retryError = true
}

// HadRetryError returns whether there has been at least one non-NoRetryError
func (s *StatsInfo) HadRetryError() bool {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.retryError
}

// Deletes updates the stats for deletes
func (s *StatsInfo) Deletes(deletes int64) int64 {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.deletes += deletes
	return s.deletes
}

// ResetCounters sets the counters (bytes, checks, errors, transfers, deletes) to 0 and resets lastError, fatalError and retryError
func (s *StatsInfo) ResetCounters() {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.bytes = 0
	s.errors = 0
	s.lastError = nil
	s.fatalError = false
	s.retryError = false
	s.checks = 0
	s.transfers = 0
	s.deletes = 0
}

// ResetErrors sets the errors count to 0 and resets lastError, fatalError and retryError
func (s *StatsInfo) ResetErrors() {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.errors = 0
	s.lastError = nil
	s.fatalError = false
	s.retryError = false
}

// Errored returns whether there have been any errors
func (s *StatsInfo) Errored() bool {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.errors != 0
}

// Error adds a single error into the stats, assigns lastError and eventually sets fatalError or retryError
func (s *StatsInfo) Error(err error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.errors++
	s.lastError = err
	switch {
	case fserrors.IsFatalError(err):
		s.fatalError = true
	case !fserrors.IsNoRetryError(err):
		s.retryError = true
	}
}

// Checking adds a check into the stats
func (s *StatsInfo) Checking(remote string) {
	s.checking.add(remote)
}

// DoneChecking removes a check from the stats
func (s *StatsInfo) DoneChecking(remote string) {
	s.checking.del(remote)
	s.mu.Lock()
	s.checks++
	s.mu.Unlock()
}

// GetTransfers reads the number of transfers
func (s *StatsInfo) GetTransfers() int64 {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.transfers
}

// Transferring adds a transfer into the stats
func (s *StatsInfo) Transferring(remote string) {
	s.transferring.add(remote)
}

// DoneTransferring removes a transfer from the stats
//
// if ok is true then it increments the transfers count
func (s *StatsInfo) DoneTransferring(remote string, ok bool) {
	s.transferring.del(remote)
	if ok {
		s.mu.Lock()
		s.transfers++
		s.mu.Unlock()
	}
}

// SetCheckQueue sets the number of queued checks
func (s *StatsInfo) SetCheckQueue(n int, size int64) {
	s.mu.Lock()
	s.checkQueue = n
	s.checkQueueSize = size
	s.mu.Unlock()
}

// SetTransferQueue sets the number of queued transfers
func (s *StatsInfo) SetTransferQueue(n int, size int64) {
	s.mu.Lock()
	s.transferQueue = n
	s.transferQueueSize = size
	s.mu.Unlock()
}

// SetRenameQueue sets the number of queued transfers
func (s *StatsInfo) SetRenameQueue(n int, size int64) {
	s.mu.Lock()
	s.renameQueue = n
	s.renameQueueSize = size
	s.mu.Unlock()
}
<commit_msg>accounting: fix total ETA when --stats-unit bits is in effect<commit_after>package accounting

import (
	"bytes"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/ncw/rclone/fs"
	"github.com/ncw/rclone/fs/fserrors"
	"github.com/ncw/rclone/fs/rc"
)

var (
	// Stats is global statistics counter
	Stats = NewStats()
)

func init() {
	// Set the function pointer up in fs
	fs.CountError = Stats.Error

	rc.Add(rc.Call{
		Path:  "core/stats",
		Fn:    Stats.RemoteStats,
		Title: "Returns stats about current transfers.",
		Help: `
This returns all available stats

	rclone rc core/stats

Returns the following values:

` + "```" + `
{
	"speed": average speed in bytes/sec since start of the process,
	"bytes": total transferred bytes since the start of the process,
	"errors": number of errors,
	"fatalError": whether there has been at least one FatalError,
	"retryError": whether there has been at least one non-NoRetryError,
	"checks": number of checked files,
	"transfers": number of transferred files,
	"deletes" : number of deleted files,
	"elapsedTime": time in seconds since the start of the process,
	"lastError": last occurred error,
	"transferring": an array of currently active file transfers:
		[
			{
				"bytes": total transferred bytes for this file,
				"eta": estimated time in seconds until file transfer completion
				"name": name of the file,
				"percentage": progress of the file transfer in percent,
				"speed": speed in bytes/sec,
				"speedAvg": speed in bytes/sec as an exponentially weighted moving average,
				"size": size of the file in bytes
			}
		],
	"checking": an array of names of currently active file checks
		[]
}
` + "```" + `
Values for "transferring", "checking" and "lastError" are only assigned if data is available.
The value for "eta" is null if an eta cannot be determined.
`,
	})
}

// StatsInfo accounts all transfers
type StatsInfo struct {
	mu                sync.RWMutex
	bytes             int64
	errors            int64
	lastError         error
	fatalError        bool
	retryError        bool
	checks            int64
	checking          *stringSet
	checkQueue        int
	checkQueueSize    int64
	transfers         int64
	transferring      *stringSet
	transferQueue     int
	transferQueueSize int64
	renameQueue       int
	renameQueueSize   int64
	deletes           int64
	start             time.Time
	inProgress        *inProgress
}

// NewStats cretates an initialised StatsInfo
func NewStats() *StatsInfo {
	return &StatsInfo{
		checking:     newStringSet(fs.Config.Checkers, "checking"),
		transferring: newStringSet(fs.Config.Transfers, "transferring"),
		start:        time.Now(),
		inProgress:   newInProgress(),
	}
}

// RemoteStats returns stats for rc
func (s *StatsInfo) RemoteStats(in rc.Params) (out rc.Params, err error) {
	out = make(rc.Params)
	s.mu.RLock()
	dt := time.Now().Sub(s.start)
	dtSeconds := dt.Seconds()
	speed := 0.0
	if dt > 0 {
		speed = float64(s.bytes) / dtSeconds
	}
	out["speed"] = speed
	out["bytes"] = s.bytes
	out["errors"] = s.errors
	out["fatalError"] = s.fatalError
	out["retryError"] = s.retryError
	out["checks"] = s.checks
	out["transfers"] = s.transfers
	out["deletes"] = s.deletes
	out["elapsedTime"] = dtSeconds
	s.mu.RUnlock()
	if !s.checking.empty() {
		var c []string
		s.checking.mu.RLock()
		defer s.checking.mu.RUnlock()
		for name := range s.checking.items {
			c = append(c, name)
		}
		out["checking"] = c
	}
	if !s.transferring.empty() {
		var t []interface{}
		s.transferring.mu.RLock()
		defer s.transferring.mu.RUnlock()
		for name := range s.transferring.items {
			if acc := s.inProgress.get(name); acc != nil {
				t = append(t, acc.RemoteStats())
			} else {
				t = append(t, name)
			}
		}
		out["transferring"] = t
	}
	if s.errors > 0 {
		out["lastError"] = s.lastError
	}
	return out, nil
}

// eta returns the ETA of the current operation,
// rounded to full seconds.
// If the ETA cannot be determined 'ok' returns false.
func eta(size, total int64, rate float64) (eta time.Duration, ok bool) {
	if total <= 0 || size < 0 || rate <= 0 {
		return 0, false
	}
	remaining := total - size
	if remaining < 0 {
		return 0, false
	}
	seconds := float64(remaining) / rate
	return time.Second * time.Duration(seconds), true
}

// etaString returns the ETA of the current operation,
// rounded to full seconds.
// If the ETA cannot be determined it returns "-"
func etaString(done, total int64, rate float64) string {
	d, ok := eta(done, total, rate)
	if !ok {
		return "-"
	}
	return d.String()
}

// percent returns a/b as a percentage rounded to the nearest integer
// as a string
//
// if the percentage is invalid it returns "-"
func percent(a int64, b int64) string {
	if a < 0 || b <= 0 {
		return "-"
	}
	return fmt.Sprintf("%d%%", int(float64(a)*100/float64(b)+0.5))
}

// String convert the StatsInfo to a string for printing
func (s *StatsInfo) String() string {
	// checking and transferring have their own locking so read
	// here before lock to prevent deadlock on GetBytes
	transferring, checking := s.transferring.count(), s.checking.count()
	transferringBytesDone, transferringBytesTotal := s.transferring.progress()

	s.mu.RLock()

	dt := time.Now().Sub(s.start)
	dtSeconds := dt.Seconds()
	speed := 0.0
	if dt > 0 {
		speed = float64(s.bytes) / dtSeconds
	}
	dtRounded := dt - (dt % (time.Second / 10))

	displaySpeed := speed
	if fs.Config.DataRateUnit == "bits" {
		displaySpeed *= 8
	}

	var (
		totalChecks   = int64(s.checkQueue) + s.checks + int64(checking)
		totalTransfer = int64(s.transferQueue) + s.transfers + int64(transferring)
		// note that s.bytes already includes transferringBytesDone so
		// we take it off here to avoid double counting
		totalSize    = s.transferQueueSize + s.bytes + transferringBytesTotal - transferringBytesDone
		currentSize  = s.bytes
		buf          = &bytes.Buffer{}
		xfrchkString = ""
	)

	if !fs.Config.StatsOneLine {
		_, _ = fmt.Fprintf(buf, "\nTransferred:   	")
	} else {
		xfrchk := []string{}
		if totalTransfer > 0 && s.transferQueue > 0 {
			xfrchk = append(xfrchk, fmt.Sprintf("xfr#%d/%d", s.transfers, totalTransfer))
		}
		if totalChecks > 0 && s.checkQueue > 0 {
			xfrchk = append(xfrchk, fmt.Sprintf("chk#%d/%d", s.checks, totalChecks))
		}
		if len(xfrchk) > 0 {
			xfrchkString = fmt.Sprintf(" (%s)", strings.Join(xfrchk, ", "))
		}
	}

	_, _ = fmt.Fprintf(buf, "%10s / %s, %s, %s, ETA %s%s",
		fs.SizeSuffix(s.bytes),
		fs.SizeSuffix(totalSize).Unit("Bytes"),
		percent(s.bytes, totalSize),
		fs.SizeSuffix(displaySpeed).Unit(strings.Title(fs.Config.DataRateUnit)+"/s"),
		etaString(currentSize, totalSize, speed),
		xfrchkString,
	)

	if !fs.Config.StatsOneLine {
		errorDetails := ""
		switch {
		case s.fatalError:
			errorDetails = " (fatal error encountered)"
		case s.retryError:
			errorDetails = " (retrying may help)"
		case s.errors != 0:
			errorDetails = " (no need to retry)"
		}

		_, _ = fmt.Fprintf(buf, `
Errors:        %10d%s
Checks:        %10d / %d, %s
Transferred:   %10d / %d, %s
Elapsed time:  %10v
`,
			s.errors, errorDetails,
			s.checks, totalChecks, percent(s.checks, totalChecks),
			s.transfers, totalTransfer, percent(s.transfers, totalTransfer),
			dtRounded)
	}

	// checking and transferring have their own locking so unlock
	// here to prevent deadlock on GetBytes
	s.mu.RUnlock()

	// Add per transfer stats if required
	if !fs.Config.StatsOneLine {
		if !s.checking.empty() {
			_, _ = fmt.Fprintf(buf, "Checking:\n%s\n", s.checking)
		}
		if !s.transferring.empty() {
			_, _ = fmt.Fprintf(buf, "Transferring:\n%s\n", s.transferring)
		}
	}

	return buf.String()
}

// Log outputs the StatsInfo to the log
func (s *StatsInfo) Log() {
	fs.LogLevelPrintf(fs.Config.StatsLogLevel, nil, "%v\n", s)
}

// Bytes updates the stats for bytes bytes
func (s *StatsInfo) Bytes(bytes int64) {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.bytes += bytes
}

// GetBytes returns the number of bytes transferred so far
func (s *StatsInfo) GetBytes() int64 {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.bytes
}

// Errors updates the stats for errors
func (s *StatsInfo) Errors(errors int64) {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.errors += errors
}

// GetErrors reads the number of errors
func (s *StatsInfo) GetErrors() int64 {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.errors
}

// GetLastError returns the lastError
func (s *StatsInfo) GetLastError() error {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.lastError
}

// GetChecks returns the number of checks
func (s *StatsInfo) GetChecks() int64 {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.checks
}

// FatalError sets the fatalError flag
func (s *StatsInfo) FatalError() {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.fatalError = true
}

// HadFatalError returns whether there has been at least one FatalError
func (s *StatsInfo) HadFatalError() bool {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.fatalError
}

// RetryError sets the retryError flag
func (s *StatsInfo) RetryError() {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.retryError = true
}

// HadRetryError returns whether there has been at least one non-NoRetryError
func (s *StatsInfo) HadRetryError() bool {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.retryError
}

// Deletes updates the stats for deletes
func (s *StatsInfo) Deletes(deletes int64) int64 {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.deletes += deletes
	return s.deletes
}

// ResetCounters sets the counters (bytes, checks, errors, transfers, deletes) to 0 and resets lastError, fatalError and retryError
func (s *StatsInfo) ResetCounters() {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.bytes = 0
	s.errors = 0
	s.lastError = nil
	s.fatalError = false
	s.retryError = false
	s.checks = 0
	s.transfers = 0
	s.deletes = 0
}

// ResetErrors sets the errors count to 0 and resets lastError, fatalError and retryError
func (s *StatsInfo) ResetErrors() {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.errors = 0
	s.lastError = nil
	s.fatalError = false
	s.retryError = false
}

// Errored returns whether there have been any errors
func (s *StatsInfo) Errored() bool {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.errors != 0
}

// Error adds a single error into the stats, assigns lastError and eventually sets fatalError or retryError
func (s *StatsInfo) Error(err error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.errors++
	s.lastError = err
	switch {
	case fserrors.IsFatalError(err):
		s.fatalError = true
	case !fserrors.IsNoRetryError(err):
		s.retryError = true
	}
}

// Checking adds a check into the stats
func (s *StatsInfo) Checking(remote string) {
	s.checking.add(remote)
}

// DoneChecking removes a check from the stats
func (s *StatsInfo) DoneChecking(remote string) {
	s.checking.del(remote)
	s.mu.Lock()
	s.checks++
	s.mu.Unlock()
}

// GetTransfers reads the number of transfers
func (s *StatsInfo) GetTransfers() int64 {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.transfers
}

// Transferring adds a transfer into the stats
func (s *StatsInfo) Transferring(remote string) {
	s.transferring.add(remote)
}

// DoneTransferring removes a transfer from the stats
//
// if ok is true then it increments the transfers count
func (s *StatsInfo) DoneTransferring(remote string, ok bool) {
	s.transferring.del(remote)
	if ok {
		s.mu.Lock()
		s.transfers++
		s.mu.Unlock()
	}
}

// SetCheckQueue sets the number of queued checks
func (s *StatsInfo) SetCheckQueue(n int, size int64) {
	s.mu.Lock()
	s.checkQueue = n
	s.checkQueueSize = size
	s.mu.Unlock()
}

// SetTransferQueue sets the number of queued transfers
func (s *StatsInfo) SetTransferQueue(n int, size int64) {
	s.mu.Lock()
	s.transferQueue = n
	s.transferQueueSize = size
	s.mu.Unlock()
}

// SetRenameQueue sets the number of queued transfers
func (s *StatsInfo) SetRenameQueue(n int, size int64) {
	s.mu.Lock()
	s.renameQueue = n
	s.renameQueueSize = size
	s.mu.Unlock()
}
<|endoftext|>


<commit_before>package dcpu

import (
	"fmt"
	"unsafe"
)

type Word uint16

type ProtectionError struct {
	Address            Word
	Opcode             Word
	OperandA, OperandB Word
}

func (err *ProtectionError) Error() string {
	return fmt.Sprintf("protection violation at address %#x (instruction %#x, operands %#x, %#x)",
		err.Address, err.Opcode, err.OperandA, err.OperandB)
}

type Registers struct {
	A, B, C, X, Y, Z, I, J Word
	PC                     Word
	SP                     Word
	O                      Word
}

type Region struct {
	Start  Word
	Length Word
}

func (r Region) Contains(address Word) bool {
	return address >= r.Start && address < r.Start+r.Length
}

// End() returns the first address not contained in the region
func (r Region) End() Word {
	return r.Start + r.Length
}

func (r Region) Union(r2 Region) Region {
	var reg Region
	if r2.Start < r.Start {
		reg.Start = r2.Start
	} else {
		reg.Start = r.Start
	}
	if r2.End() > r.End() {
		reg.Length = r2.End() - reg.Start
	} else {
		reg.Length = r.End() - reg.Start
	}
	return reg
}

type State struct {
	Registers
	Ram       [0x10000]Word
	Protected []Region
}

func decodeOpcode(opcode Word) (oooo, aaaaaa, bbbbbb Word) {
	oooo = opcode & 0xF
	aaaaaa = (opcode >> 4) & 0x3F
	bbbbbb = (opcode >> 10) & 0x3F
	return
}

// wordCount counts the number of words in the instruction identified by the given opcode
func wordCount(opcode Word) Word {
	_, a, b := decodeOpcode(opcode)
	count := Word(1)
	switch {
	case a >= 16 && a <= 23:
	case a == 30:
	case a == 31:
		count++
	}
	switch {
	case b >= 16 && b <= 23:
	case b == 30:
	case b == 31:
		count++
	}
	return count
}

func (s *State) translateOperand(op Word) (val Word, assignable *Word) {
	switch op {
	// 0-7: register value - register values
	case 0:
		assignable = &s.A
	case 1:
		assignable = &s.B
	case 2:
		assignable = &s.C
	case 3:
		assignable = &s.X
	case 4:
		assignable = &s.Y
	case 5:
		assignable = &s.Z
	case 6:
		assignable = &s.I
	case 7:
		assignable = &s.J
	// 8-15: [register value] - value at address in registries
	case 8:
		assignable = &s.Ram[s.A]
	case 9:
		assignable = &s.Ram[s.B]
	case 10:
		assignable = &s.Ram[s.C]
	case 11:
		assignable = &s.Ram[s.X]
	case 12:
		assignable = &s.Ram[s.Y]
	case 13:
		assignable = &s.Ram[s.Z]
	case 14:
		assignable = &s.Ram[s.I]
	case 15:
		assignable = &s.Ram[s.J]
	// 16-23: [next word of ram + register value] - memory address offset by register value
	case 16:
		assignable = &s.Ram[s.PC+s.A]
		s.PC++
	case 17:
		assignable = &s.Ram[s.PC+s.B]
		s.PC++
	case 18:
		assignable = &s.Ram[s.PC+s.C]
		s.PC++
	case 19:
		assignable = &s.Ram[s.PC+s.X]
		s.PC++
	case 20:
		assignable = &s.Ram[s.PC+s.Y]
		s.PC++
	case 21:
		assignable = &s.Ram[s.PC+s.Z]
		s.PC++
	case 22:
		assignable = &s.Ram[s.PC+s.I]
		s.PC++
	case 23:
		assignable = &s.Ram[s.PC+s.J]
		s.PC++
	// 24: POP - value at stack address, then increases stack counter
	case 24:
		assignable = &s.Ram[s.SP]
		s.SP++
	// 25: PEEK - value at stack address
	case 25:
		assignable = &s.Ram[s.SP]
	case 26:
		// 26: PUSH - decreases stack address, then value at stack address
		s.SP--
		assignable = &s.Ram[s.SP]
	// 27: SP - current stack pointer value - current stack address
	case 27:
		assignable = &s.SP
	// 28: PC - program counter- current program counter
	case 28:
		assignable = &s.PC
	// 29: O - overflow - current value of the overflow
	case 29:
		assignable = &s.O
	// 30: [next word of ram] - memory address
	case 30:
		assignable = &s.Ram[s.Ram[s.PC]]
		s.PC++
	// 31: next word of ram - literal, does nothing on assign
	case 31:
		val = s.Ram[s.PC]
		s.PC++
	default:
		if op >= 64 {
			panic("Out of bounds operand")
		}
		val = op - 32
	}
	if assignable != nil {
		val = *assignable
	}
	return
}

func (s *State) isProtected(address Word) bool {
	for _, region := range s.Protected {
		if region.Contains(address) {
			return true
		}
		if region.Start > address {
			break
		}
	}
	return false
}

// Step iterates the CPU by one instruction.
func (s *State) Step() error {
	// fetch
	opcode := s.Ram[s.PC]
	s.PC++

	// decode
	ins, a, b := decodeOpcode(opcode)

	var assignable *Word
	a, assignable = s.translateOperand(a)
	b, _ = s.translateOperand(b)

	// execute
	var val Word
	switch ins {
	case 0:
		// marked RESERVED, lets just treat it as a NOP
	case 1:
		// SET a, b - sets value of b to a
		val = b
	case 2:
		// ADD a, b - adds b to a, sets O
		result := uint32(a) + uint32(b)
		val = Word(result & 0xFFFF)
		s.O = Word(result >> 16)
	case 3:
		// SUB a, b - subtracts b from a, sets O
		result := uint32(a) - uint32(b)
		val = Word(result & 0xFFFF)
		s.O = Word(result >> 16)
	case 4:
		// MUL a, b - multiplies a by b, sets O
		result := uint32(a) * uint32(b)
		val = Word(result & 0xFFFF)
		s.O = Word(result >> 16)
	case 5:
		// DIV a, b - divides a by b, sets O
		// NB: how can this overflow?
		// assuming for the moment that O is supposed to be the mod
		val = a / b
		s.O = a % b
	case 6:
		// MOD a, b - remainder of a over b
		val = a % b
	case 7:
		// SHL a, b - shifts a left b places, sets O
		result := uint32(a) << uint32(b)
		val = Word(result & 0xFFFF)
		s.O = Word(result >> 16)
	case 8:
		// SHR a, b - shifts a right b places, sets O
		// NB: how can this overflow?
		val = a >> b
	case 9:
		// AND a, b - binary and of a and b
		val = a & b
	case 10:
		// BOR a, b - binary or of a and b
		val = a | b
	case 11:
		// XOR a, b - binary xor of a and b
		val = a ^ b
	case 12:
		// IFE a, b - skips one instruction if a!=b
		if a != b {
			s.PC += wordCount(s.Ram[s.PC])
		}
	case 13:
		// IFN a, b - skips one instruction if a==b
		if a == b {
			s.PC += wordCount(s.Ram[s.PC])
		}
	case 14:
		// IFG a, b - skips one instruction if a<=b
		if a <= b {
			s.PC += wordCount(s.Ram[s.PC])
		}
	case 15:
		// IFB a, b - skips one instruction if (a&b)==0
		if (a & b) == 0 {
			s.PC += wordCount(s.Ram[s.PC])
		}
	default:
		panic("Out of bounds opcode")
	}

	// store
	if ins >= 1 && ins <= 11 && assignable != nil {
		// test memory protection
		// are we in our ram?
		assPtr := uintptr(unsafe.Pointer(assignable))
		ramStart := uintptr(unsafe.Pointer(&s.Ram[0]))
		ramEnd := uintptr(unsafe.Pointer(&s.Ram[len(s.Ram)-1]))
		if assPtr >= ramStart && assPtr <= ramEnd {
			index := Word((assPtr - ramStart) / unsafe.Sizeof(s.Ram[0]))
			for _, region := range s.Protected {
				if region.Contains(index) {
					// protection error
					return &ProtectionError{
						Address:  index,
						Opcode:   opcode,
						OperandA: a,
						OperandB: b,
					}
				} else if region.Start > index {
					break
				}
			}
		}
		// go ahead and store
		*assignable = val
	}

	return nil
}
<commit_msg>Fix operand decoding for 16-23<commit_after>package dcpu

import (
	"fmt"
	"unsafe"
)

type Word uint16

type ProtectionError struct {
	Address            Word
	Opcode             Word
	OperandA, OperandB Word
}

func (err *ProtectionError) Error() string {
	return fmt.Sprintf("protection violation at address %#x (instruction %#x, operands %#x, %#x)",
		err.Address, err.Opcode, err.OperandA, err.OperandB)
}

type Registers struct {
	A, B, C, X, Y, Z, I, J Word
	PC                     Word
	SP                     Word
	O                      Word
}

type Region struct {
	Start  Word
	Length Word
}

func (r Region) Contains(address Word) bool {
	return address >= r.Start && address < r.Start+r.Length
}

// End() returns the first address not contained in the region
func (r Region) End() Word {
	return r.Start + r.Length
}

func (r Region) Union(r2 Region) Region {
	var reg Region
	if r2.Start < r.Start {
		reg.Start = r2.Start
	} else {
		reg.Start = r.Start
	}
	if r2.End() > r.End() {
		reg.Length = r2.End() - reg.Start
	} else {
		reg.Length = r.End() - reg.Start
	}
	return reg
}

type State struct {
	Registers
	Ram       [0x10000]Word
	Protected []Region
}

func decodeOpcode(opcode Word) (oooo, aaaaaa, bbbbbb Word) {
	oooo = opcode & 0xF
	aaaaaa = (opcode >> 4) & 0x3F
	bbbbbb = (opcode >> 10) & 0x3F
	return
}

// wordCount counts the number of words in the instruction identified by the given opcode
func wordCount(opcode Word) Word {
	_, a, b := decodeOpcode(opcode)
	count := Word(1)
	switch {
	case a >= 16 && a <= 23:
	case a == 30:
	case a == 31:
		count++
	}
	switch {
	case b >= 16 && b <= 23:
	case b == 30:
	case b == 31:
		count++
	}
	return count
}

func (s *State) translateOperand(op Word) (val Word, assignable *Word) {
	switch op {
	// 0-7: register value - register values
	case 0:
		assignable = &s.A
	case 1:
		assignable = &s.B
	case 2:
		assignable = &s.C
	case 3:
		assignable = &s.X
	case 4:
		assignable = &s.Y
	case 5:
		assignable = &s.Z
	case 6:
		assignable = &s.I
	case 7:
		assignable = &s.J
	// 8-15: [register value] - value at address in registries
	case 8:
		assignable = &s.Ram[s.A]
	case 9:
		assignable = &s.Ram[s.B]
	case 10:
		assignable = &s.Ram[s.C]
	case 11:
		assignable = &s.Ram[s.X]
	case 12:
		assignable = &s.Ram[s.Y]
	case 13:
		assignable = &s.Ram[s.Z]
	case 14:
		assignable = &s.Ram[s.I]
	case 15:
		assignable = &s.Ram[s.J]
	// 16-23: [next word of ram + register value] - memory address offset by register value
	case 16:
		assignable = &s.Ram[s.Ram[s.PC]+s.A]
		s.PC++
	case 17:
		assignable = &s.Ram[s.Ram[s.PC]+s.B]
		s.PC++
	case 18:
		assignable = &s.Ram[s.Ram[s.PC]+s.C]
		s.PC++
	case 19:
		assignable = &s.Ram[s.Ram[s.PC]+s.X]
		s.PC++
	case 20:
		assignable = &s.Ram[s.Ram[s.PC]+s.Y]
		s.PC++
	case 21:
		assignable = &s.Ram[s.Ram[s.PC]+s.Z]
		s.PC++
	case 22:
		assignable = &s.Ram[s.Ram[s.PC]+s.I]
		s.PC++
	case 23:
		assignable = &s.Ram[s.Ram[s.PC]+s.J]
		s.PC++
	// 24: POP - value at stack address, then increases stack counter
	case 24:
		assignable = &s.Ram[s.SP]
		s.SP++
	// 25: PEEK - value at stack address
	case 25:
		assignable = &s.Ram[s.SP]
	case 26:
		// 26: PUSH - decreases stack address, then value at stack address
		s.SP--
		assignable = &s.Ram[s.SP]
	// 27: SP - current stack pointer value - current stack address
	case 27:
		assignable = &s.SP
	// 28: PC - program counter- current program counter
	case 28:
		assignable = &s.PC
	// 29: O - overflow - current value of the overflow
	case 29:
		assignable = &s.O
	// 30: [next word of ram] - memory address
	case 30:
		assignable = &s.Ram[s.Ram[s.PC]]
		s.PC++
	// 31: next word of ram - literal, does nothing on assign
	case 31:
		val = s.Ram[s.PC]
		s.PC++
	default:
		if op >= 64 {
			panic("Out of bounds operand")
		}
		val = op - 32
	}
	if assignable != nil {
		val = *assignable
	}
	return
}

func (s *State) isProtected(address Word) bool {
	for _, region := range s.Protected {
		if region.Contains(address) {
			return true
		}
		if region.Start > address {
			break
		}
	}
	return false
}

// Step iterates the CPU by one instruction.
func (s *State) Step() error {
	// fetch
	opcode := s.Ram[s.PC]
	s.PC++

	// decode
	ins, a, b := decodeOpcode(opcode)

	var assignable *Word
	a, assignable = s.translateOperand(a)
	b, _ = s.translateOperand(b)

	// execute
	var val Word
	switch ins {
	case 0:
		// marked RESERVED, lets just treat it as a NOP
	case 1:
		// SET a, b - sets value of b to a
		val = b
	case 2:
		// ADD a, b - adds b to a, sets O
		result := uint32(a) + uint32(b)
		val = Word(result & 0xFFFF)
		s.O = Word(result >> 16)
	case 3:
		// SUB a, b - subtracts b from a, sets O
		result := uint32(a) - uint32(b)
		val = Word(result & 0xFFFF)
		s.O = Word(result >> 16)
	case 4:
		// MUL a, b - multiplies a by b, sets O
		result := uint32(a) * uint32(b)
		val = Word(result & 0xFFFF)
		s.O = Word(result >> 16)
	case 5:
		// DIV a, b - divides a by b, sets O
		// NB: how can this overflow?
		// assuming for the moment that O is supposed to be the mod
		val = a / b
		s.O = a % b
	case 6:
		// MOD a, b - remainder of a over b
		val = a % b
	case 7:
		// SHL a, b - shifts a left b places, sets O
		result := uint32(a) << uint32(b)
		val = Word(result & 0xFFFF)
		s.O = Word(result >> 16)
	case 8:
		// SHR a, b - shifts a right b places, sets O
		// NB: how can this overflow?
		val = a >> b
	case 9:
		// AND a, b - binary and of a and b
		val = a & b
	case 10:
		// BOR a, b - binary or of a and b
		val = a | b
	case 11:
		// XOR a, b - binary xor of a and b
		val = a ^ b
	case 12:
		// IFE a, b - skips one instruction if a!=b
		if a != b {
			s.PC += wordCount(s.Ram[s.PC])
		}
	case 13:
		// IFN a, b - skips one instruction if a==b
		if a == b {
			s.PC += wordCount(s.Ram[s.PC])
		}
	case 14:
		// IFG a, b - skips one instruction if a<=b
		if a <= b {
			s.PC += wordCount(s.Ram[s.PC])
		}
	case 15:
		// IFB a, b - skips one instruction if (a&b)==0
		if (a & b) == 0 {
			s.PC += wordCount(s.Ram[s.PC])
		}
	default:
		panic("Out of bounds opcode")
	}

	// store
	if ins >= 1 && ins <= 11 && assignable != nil {
		// test memory protection
		// are we in our ram?
		assPtr := uintptr(unsafe.Pointer(assignable))
		ramStart := uintptr(unsafe.Pointer(&s.Ram[0]))
		ramEnd := uintptr(unsafe.Pointer(&s.Ram[len(s.Ram)-1]))
		if assPtr >= ramStart && assPtr <= ramEnd {
			index := Word((assPtr - ramStart) / unsafe.Sizeof(s.Ram[0]))
			for _, region := range s.Protected {
				if region.Contains(index) {
					// protection error
					return &ProtectionError{
						Address:  index,
						Opcode:   opcode,
						OperandA: a,
						OperandB: b,
					}
				} else if region.Start > index {
					break
				}
			}
		}
		// go ahead and store
		*assignable = val
	}

	return nil
}
<|endoftext|>


<commit_before>package deck

import (
	"io"
)

type Deck interface {
	Draw() (string, error)
	String() (string)
}

// ensure that *deck fulfills Deck interface
var _ Deck = &deck{}

type deck struct {
	cards      []card
	connection io.ReadWriteCloser
}

// Draw draws a single card from the deck
func (d *deck) Draw() (string, error) {
	return "", nil
}

func (d *deck) String() string {
	return "Deck"
}

// NewDeck creates a deck of cards and assumes that the given
// io.ReadWriteCloser is a connection of some sort to another
// deck.
func NewDeck(deckConnection io.ReadWriteCloser) (Deck, error) {
	return nil, nil
}
<commit_msg>Implement deck<commit_after>package deck

import (
	"io"
)

type Deck interface {
	Draw() (string, error)
	String() string
}

// ensure that *deck fulfills Deck interface
var _ Deck = &deck{}

type deck struct {
	cards      []card
	protocol   *Protocol
	connection io.ReadWriteCloser
}

// Draw draws a single card from the deck
func (d *deck) Draw() (string, error) {
	return "", nil
}

func (d *deck) String() string {
	return "Deck"
}

// NewDeck creates a deck of cards and assumes that the given
// io.ReadWriteCloser is a connection of some sort to another
// deck.
func NewDeck(deckConnection io.ReadWriteCloser) (Deck, error) {
	d := &deck{
		connection: deckConnection,
	}
	p, err := NewProtocol(deckConnection)
	if err != nil {
		return nil, err
	}
	d.protocol = p
	return d, nil
}
<|endoftext|>


<commit_before>package swf

import "log"

func (c *Client) PollDecisionTaskList(domain string, identity string, taskList string, taskChannel chan *PollForDecisionTaskResponse) *DecisionTaskPoller {
	poller := &DecisionTaskPoller{
		client:   c,
		Domain:   domain,
		Identity: identity,
		TaskList: taskList,
		Tasks:    taskChannel,
		stop:     make(chan bool, 1),
	}
	poller.start()
	return poller

}

type DecisionTaskPoller struct {
	client   DecisionWorkerClient
	Identity string
	Domain   string
	TaskList string
	Tasks    chan *PollForDecisionTaskResponse
	stop     chan bool
}

func (p *DecisionTaskPoller) start() {
	go func() {
		for {
			select {
			case <-p.stop:
				return
			default:
				resp, err := p.client.PollForDecisionTask(PollForDecisionTaskRequest{
					Domain:       p.Domain,
					Identity:     p.Identity,
					ReverseOrder: true,
					TaskList:     TaskList{Name: p.TaskList},
				})
				if err != nil {
					panic(p)
				} else {
					if resp.TaskToken != "" {
						log.Printf("component=DecisionTaskPoller at=decision-task-recieved workflow=%s", resp.WorkflowType.Name)
						p.Tasks <- resp
					} else {
						log.Println("component=DecisionTaskPoller at=decision-task-empty-response")
					}
				}
			}

		}
	}()
}

func (p *DecisionTaskPoller) Stop() {
	p.stop <- true
}
<commit_msg>golint changes<commit_after>package swf

import "log"

// PollDecisionTaskList returns a started DecisionTaskPoller.
func (c *Client) PollDecisionTaskList(domain string, identity string, taskList string, taskChannel chan *PollForDecisionTaskResponse) *DecisionTaskPoller {
	poller := &DecisionTaskPoller{
		client:   c,
		Domain:   domain,
		Identity: identity,
		TaskList: taskList,
		Tasks:    taskChannel,
		stop:     make(chan bool, 1),
	}
	poller.start()
	return poller

}

// DecisionTaskPoller polls a given task list in a domain for decision tasks, and sends tasks on its Tasks channel.
type DecisionTaskPoller struct {
	client   DecisionWorkerClient
	Identity string
	Domain   string
	TaskList string
	Tasks    chan *PollForDecisionTaskResponse
	stop     chan bool
}

func (p *DecisionTaskPoller) start() {
	go func() {
		for {
			select {
			case <-p.stop:
				return
			default:
				resp, err := p.client.PollForDecisionTask(PollForDecisionTaskRequest{
					Domain:       p.Domain,
					Identity:     p.Identity,
					ReverseOrder: true,
					TaskList:     TaskList{Name: p.TaskList},
				})
				if err != nil {
					panic(p)
				} else {
					if resp.TaskToken != "" {
						log.Printf("component=DecisionTaskPoller at=decision-task-recieved workflow=%s", resp.WorkflowType.Name)
						p.Tasks <- resp
					} else {
						log.Println("component=DecisionTaskPoller at=decision-task-empty-response")
					}
				}
			}

		}
	}()
}

// Stop signals the poller to stop polling after any in-flight poll requests return.
func (p *DecisionTaskPoller) Stop() {
	p.stop <- true
}
<|endoftext|>


<commit_before>// Copyright 2015 Tony Bai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package cmppconn

import (
	"bufio"
	"encoding/binary"
	"io"
	"net"

	cmpppacket "github.com/bigwhite/gocmpp/packet"
)

type Type int8

const (
	V30 Type = 0x30
	V21 Type = 0x21
	V20 Type = 0x20
)

func (t Type) String() string {
	switch {
	case t == V30:
		return "cmpp30"
	case t == V21:
		return "cmpp21"
	case t == V20:
		return "cmpp20"
	default:
		return "unknown"
	}
}

type State uint8

// Conn States
const (
	CONN_CLOSED = iota
	CONN_CONNECTED
	CONN_AUTHOK
)

type Conn struct {
	net.Conn
	State  State
	Typ    Type
	Reader *bufio.Reader
	Writer *bufio.Writer
	SeqId  <-chan uint32
	done   chan<- struct{}
}

func newSeqIdGenerator() (<-chan uint32, chan<- struct{}) {
	out := make(chan uint32)
	done := make(chan struct{})

	go func() {
		var i uint32
		for {
			select {
			case out <- i:
				i++
			case <-done:
				close(out)
				return
			}
		}
	}()
	return out, done
}

func New(conn net.Conn, typ Type) *Conn {
	seqId, done := newSeqIdGenerator()
	c := &Conn{
		Conn:   conn,
		Typ:    typ,
		State:  CONN_CONNECTED,
		Reader: bufio.NewReader(conn),
		Writer: bufio.NewWriter(conn),
		SeqId:  seqId,
		done:   done,
	}
	tc := c.Conn.(*net.TCPConn)
	tc.SetKeepAlive(true)
	return c
}

func (c *Conn) Close() {
	if c != nil {
		if c.Typ == CONN_CLOSED {
			return
		}
		if c.Writer != nil {
			c.Writer.Flush()
		}
		close(c.done)
		c.Conn.Close()
		c.Typ = CONN_CLOSED
		c = nil
	}
}

func (c *Conn) SetState(state State) {
	c.State = state
}

func (c *Conn) writeFull(data []byte) error {
	var written = 0
	for written < len(data) {
		n, err := c.Writer.Write(data[written:])
		if err != nil && err != io.ErrShortWrite {
			return err
		}
		err = c.Writer.Flush()
		if err != nil {
			return err
		}
		written += n
	}

	return nil
}

// SendPkt pack the cmpp packet structure and send it to the other peer.
func (c *Conn) SendPkt(packet cmpppacket.Packer, seqId uint32) error {
	data, err := packet.Pack(seqId)
	if err != nil {
		return err
	}

	err = c.writeFull(data)
	if err != nil {
		return err
	}

	return nil
}

// RecvAndUnpackPkt receives cmpp byte stream, and unpack it to some cmpp packet structure.
func (c *Conn) RecvAndUnpackPkt() (interface{}, error) {
	// Total_Length in packet
	var totalLen uint32
	err := binary.Read(c.Reader, binary.BigEndian, &totalLen)
	if err != nil {
		return nil, err
	}

	if c.Typ == V30 {
		if totalLen < cmpppacket.CMPP3_PACKET_MIN || totalLen > cmpppacket.CMPP3_PACKET_MAX {
			return nil, cmpppacket.ErrTotalLengthInvalid
		}
	}

	if c.Typ == V21 || c.Typ == V20 {
		if totalLen < cmpppacket.CMPP2_PACKET_MIN || totalLen > cmpppacket.CMPP2_PACKET_MAX {
			return nil, cmpppacket.ErrTotalLengthInvalid
		}
	}

	// Command_Id
	var commandId cmpppacket.CommandId
	err = binary.Read(c.Reader, binary.BigEndian, &commandId)
	if err != nil {
		return nil, err
	}

	if !((commandId > cmpppacket.CMPP_REQUEST_MIN && commandId < cmpppacket.CMPP_REQUEST_MAX) ||
		(commandId > cmpppacket.CMPP_RESPONSE_MIN && commandId < cmpppacket.CMPP_RESPONSE_MAX)) {
		return nil, cmpppacket.ErrCommandIdInvalid
	}

	// The left packet data (start from seqId in header).
	var leftData = make([]byte, totalLen-8)
	_, err = io.ReadFull(c.Reader, leftData)
	if err != nil {
		return nil, err
	}

	var p cmpppacket.Packer
	switch commandId {
	case cmpppacket.CMPP_CONNECT:
		p = &cmpppacket.CmppConnReqPkt{}
	case cmpppacket.CMPP_CONNECT_RESP:
		if c.Typ == V30 {
			p = &cmpppacket.Cmpp3ConnRspPkt{}
		} else {
			p = &cmpppacket.Cmpp2ConnRspPkt{}
		}
	case cmpppacket.CMPP_TERMINATE:
		p = &cmpppacket.CmppTerminateReqPkt{}
	case cmpppacket.CMPP_TERMINATE_RESP:
		p = &cmpppacket.CmppTerminateRspPkt{}
	case cmpppacket.CMPP_SUBMIT:
		if c.Typ == V30 {
			p = &cmpppacket.Cmpp3SubmitReqPkt{}
		} else {
			p = &cmpppacket.Cmpp2SubmitReqPkt{}
		}
	case cmpppacket.CMPP_SUBMIT_RESP:
		if c.Typ == V30 {
			p = &cmpppacket.Cmpp3SubmitRspPkt{}
		} else {
			p = &cmpppacket.Cmpp2SubmitRspPkt{}
		}
	case cmpppacket.CMPP_DELIVER:
		if c.Typ == V30 {
			p = &cmpppacket.Cmpp3DeliverReqPkt{}
		} else {
			p = &cmpppacket.Cmpp2DeliverReqPkt{}
		}
	case cmpppacket.CMPP_DELIVER_RESP:
		if c.Typ == V30 {
			p = &cmpppacket.Cmpp3DeliverRspPkt{}
		} else {
			p = &cmpppacket.Cmpp2DeliverRspPkt{}
		}
	case cmpppacket.CMPP_FWD:
		if c.Typ == V30 {
			p = &cmpppacket.Cmpp3FwdReqPkt{}
		} else {
			p = &cmpppacket.Cmpp2FwdReqPkt{}
		}
	case cmpppacket.CMPP_FWD_RESP:
		if c.Typ == V30 {
			p = &cmpppacket.Cmpp3FwdRspPkt{}
		} else {
			p = &cmpppacket.Cmpp2FwdRspPkt{}
		}
	case cmpppacket.CMPP_ACTIVE_TEST:
		p = &cmpppacket.CmppActiveTestReqPkt{}
	case cmpppacket.CMPP_ACTIVE_TEST_RESP:
		p = &cmpppacket.CmppActiveTestRspPkt{}

	default:
		p = nil
		return nil, cmpppacket.ErrCommandIdNotSupported
	}

	err = p.Unpack(leftData)
	if err != nil {
		return nil, err
	}
	return p, nil
}
<commit_msg>remove bufio's reader and writer<commit_after>// Copyright 2015 Tony Bai.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package cmppconn

import (
	"encoding/binary"
	"io"
	"net"

	cmpppacket "github.com/bigwhite/gocmpp/packet"
)

type Type int8

const (
	V30 Type = 0x30
	V21 Type = 0x21
	V20 Type = 0x20
)

func (t Type) String() string {
	switch {
	case t == V30:
		return "cmpp30"
	case t == V21:
		return "cmpp21"
	case t == V20:
		return "cmpp20"
	default:
		return "unknown"
	}
}

type State uint8

// Conn States
const (
	CONN_CLOSED = iota
	CONN_CONNECTED
	CONN_AUTHOK
)

type Conn struct {
	net.Conn
	State State
	Typ   Type
	SeqId <-chan uint32
	done  chan<- struct{}
}

func newSeqIdGenerator() (<-chan uint32, chan<- struct{}) {
	out := make(chan uint32)
	done := make(chan struct{})

	go func() {
		var i uint32
		for {
			select {
			case out <- i:
				i++
			case <-done:
				close(out)
				return
			}
		}
	}()
	return out, done
}

func New(conn net.Conn, typ Type) *Conn {
	seqId, done := newSeqIdGenerator()
	c := &Conn{
		Conn:  conn,
		Typ:   typ,
		State: CONN_CONNECTED,
		SeqId: seqId,
		done:  done,
	}
	tc := c.Conn.(*net.TCPConn) // Always tcpconn
	tc.SetKeepAlive(true)
	return c
}

func (c *Conn) Close() {
	if c != nil {
		if c.Typ == CONN_CLOSED {
			return
		}
		close(c.done)
		c.Conn.Close()
		c.Typ = CONN_CLOSED
		c = nil
	}
}

func (c *Conn) SetState(state State) {
	c.State = state
}

// SendPkt pack the cmpp packet structure and send it to the other peer.
func (c *Conn) SendPkt(packet cmpppacket.Packer, seqId uint32) error {
	data, err := packet.Pack(seqId)
	if err != nil {
		return err
	}

	_, err = c.Conn.Write(data) //block write
	if err != nil {
		return err
	}

	return nil
}

// RecvAndUnpackPkt receives cmpp byte stream, and unpack it to some cmpp packet structure.
func (c *Conn) RecvAndUnpackPkt() (interface{}, error) {
	// Total_Length in packet
	var totalLen uint32
	err := binary.Read(c.Conn, binary.BigEndian, &totalLen)
	if err != nil {
		return nil, err
	}

	if c.Typ == V30 {
		if totalLen < cmpppacket.CMPP3_PACKET_MIN || totalLen > cmpppacket.CMPP3_PACKET_MAX {
			return nil, cmpppacket.ErrTotalLengthInvalid
		}
	}

	if c.Typ == V21 || c.Typ == V20 {
		if totalLen < cmpppacket.CMPP2_PACKET_MIN || totalLen > cmpppacket.CMPP2_PACKET_MAX {
			return nil, cmpppacket.ErrTotalLengthInvalid
		}
	}

	// Command_Id
	var commandId cmpppacket.CommandId
	err = binary.Read(c.Conn, binary.BigEndian, &commandId)
	if err != nil {
		return nil, err
	}

	if !((commandId > cmpppacket.CMPP_REQUEST_MIN && commandId < cmpppacket.CMPP_REQUEST_MAX) ||
		(commandId > cmpppacket.CMPP_RESPONSE_MIN && commandId < cmpppacket.CMPP_RESPONSE_MAX)) {
		return nil, cmpppacket.ErrCommandIdInvalid
	}

	// The left packet data (start from seqId in header).
	var leftData = make([]byte, totalLen-8)
	_, err = io.ReadFull(c.Conn, leftData)
	if err != nil {
		return nil, err
	}

	var p cmpppacket.Packer
	switch commandId {
	case cmpppacket.CMPP_CONNECT:
		p = &cmpppacket.CmppConnReqPkt{}
	case cmpppacket.CMPP_CONNECT_RESP:
		if c.Typ == V30 {
			p = &cmpppacket.Cmpp3ConnRspPkt{}
		} else {
			p = &cmpppacket.Cmpp2ConnRspPkt{}
		}
	case cmpppacket.CMPP_TERMINATE:
		p = &cmpppacket.CmppTerminateReqPkt{}
	case cmpppacket.CMPP_TERMINATE_RESP:
		p = &cmpppacket.CmppTerminateRspPkt{}
	case cmpppacket.CMPP_SUBMIT:
		if c.Typ == V30 {
			p = &cmpppacket.Cmpp3SubmitReqPkt{}
		} else {
			p = &cmpppacket.Cmpp2SubmitReqPkt{}
		}
	case cmpppacket.CMPP_SUBMIT_RESP:
		if c.Typ == V30 {
			p = &cmpppacket.Cmpp3SubmitRspPkt{}
		} else {
			p = &cmpppacket.Cmpp2SubmitRspPkt{}
		}
	case cmpppacket.CMPP_DELIVER:
		if c.Typ == V30 {
			p = &cmpppacket.Cmpp3DeliverReqPkt{}
		} else {
			p = &cmpppacket.Cmpp2DeliverReqPkt{}
		}
	case cmpppacket.CMPP_DELIVER_RESP:
		if c.Typ == V30 {
			p = &cmpppacket.Cmpp3DeliverRspPkt{}
		} else {
			p = &cmpppacket.Cmpp2DeliverRspPkt{}
		}
	case cmpppacket.CMPP_FWD:
		if c.Typ == V30 {
			p = &cmpppacket.Cmpp3FwdReqPkt{}
		} else {
			p = &cmpppacket.Cmpp2FwdReqPkt{}
		}
	case cmpppacket.CMPP_FWD_RESP:
		if c.Typ == V30 {
			p = &cmpppacket.Cmpp3FwdRspPkt{}
		} else {
			p = &cmpppacket.Cmpp2FwdRspPkt{}
		}
	case cmpppacket.CMPP_ACTIVE_TEST:
		p = &cmpppacket.CmppActiveTestReqPkt{}
	case cmpppacket.CMPP_ACTIVE_TEST_RESP:
		p = &cmpppacket.CmppActiveTestRspPkt{}

	default:
		p = nil
		return nil, cmpppacket.ErrCommandIdNotSupported
	}

	err = p.Unpack(leftData)
	if err != nil {
		return nil, err
	}
	return p, nil
}
<|endoftext|>


<commit_before>package amqpirq

import (
	"github.com/satori/go.uuid"
	"github.com/streadway/amqp"
	"os"
	"runtime"
	"strconv"
	"sync"
	"testing"
	"time"
)

type dummyConnWorker struct {
	started bool
	ended   bool
}

func (d *dummyConnWorker) Do(conn *amqp.Connection, done <-chan struct{}) {
	d.started = true
	<-done
	d.ended = true
}

type dummyDeliveryConsumer struct {
	corrID *string
}

func (c *dummyDeliveryConsumer) Consume(ch *amqp.Channel, d *amqp.Delivery) {
	c.corrID = &d.CorrelationId
	d.Ack(false)
}

type dummyChanWorker struct {
	started bool
	ended   bool
}

func (d *dummyChanWorker) Do(conn *amqp.Channel, done <-chan struct{}) {
	d.started = true
	<-done
	d.ended = true
}

func TestDial(t *testing.T) {
	c, err := Dial("")
	if err != nil {
		t.Fatal(err)
	}
	if c.done == nil {
		t.Error("Expected chan got <nil>")
	}
	if got, want := c.MaxAttempts, defaultMaxAttempts; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
	if got, want := c.Delay, defaultDelay; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
}

func TestDialTLS(t *testing.T) {
	c, err := DialTLS("", nil)
	if err != nil {
		t.Fatal(err)
	}
	if c.done == nil {
		t.Error("Expected chan got <nil>")
	}
	if got, want := c.MaxAttempts, defaultMaxAttempts; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
	if got, want := c.Delay, defaultDelay; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
}

func TestConnection_Listen(t *testing.T) {
	if amqpURI() == "" {
		t.Skip("Environment variable AMQP_URI not set")
	}
	c, err := Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	if c.done == nil {
		t.Error("Expected chan got <nil>")
	}
	if got, want := c.MaxAttempts, defaultMaxAttempts; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
	if got, want := c.Delay, defaultDelay; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
	c.MaxAttempts = 2
	c.Delay = 0

	worker := new(dummyConnWorker)
	worker1 := new(dummyConnWorker)
	go c.Listen(worker)
	go c.Listen(worker1)

	for {
		if worker.started {
			break
		}
		time.Sleep(1 * time.Second)
	}
	for {
		if worker1.started {
			break
		}
		time.Sleep(1 * time.Second)
	}
	c.Close()
	for {
		if worker.ended {
			break
		}
		time.Sleep(1 * time.Second)
	}
	for {
		if worker1.ended {
			break
		}
		time.Sleep(1 * time.Second)
	}
}

func TestConnection_ListenOnClosed(t *testing.T) {
	if amqpURI() == "" {
		t.Skip("Environment variable AMQP_URI not set")
	}
	c, err := Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	if c.done == nil {
		t.Error("Expected chan got <nil>")
	}
	if got, want := c.MaxAttempts, defaultMaxAttempts; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
	if got, want := c.Delay, defaultDelay; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
	c.MaxAttempts = 1
	c.Delay = 0
	c.Close()
	if got, want := c.closing, true; got != want {
		t.Errorf("Expected closing=%b, got=%b", want, got)
	}
	c.Close()
	if got, want := c.closing, true; got != want {
		t.Errorf("Expected closing=%b, got=%b", want, got)
	}

	worker := new(dummyConnWorker)
	err = c.Listen(worker)
	if err == nil {
		t.Fatal("Expected error got <nil>")
	}
}

func TestConnection_ListenInvalidURI(t *testing.T) {
	c, err := DialConfig("amqp://non-existent-host//", amqp.Config{})
	if err != nil {
		t.Fatal(err)
	}
	c.MaxAttempts = 2
	c.Delay = 0
	err = c.Listen(new(dummyConnWorker))
	if err == nil {
		t.Fatal("Expected error got <nil>")
	}
}

func TestNewParallelMessageListener_InvalidSize(t *testing.T) {
	_, err := NewParallelConnectionWorker(nil, 0, nil)
	if err == nil {
		t.Fatal("Expected error, got <nil>")
	}
}

func TestNewParallelMessageListener_MissingQueue(t *testing.T) {
	_, err := NewParallelConnectionWorker(nil, 1, nil)
	if err == nil {
		t.Fatal("Expected error, got <nil>")
	}
}

func TestConnection_NewConnectionWorker(t *testing.T) {
	if amqpURI() == "" {
		t.Skip("Environment variable AMQP_URI not set")
	}

	c, err := Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	if c.done == nil {
		t.Error("Expected chan got <nil>")
	}
	if got, want := c.MaxAttempts, defaultMaxAttempts; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
	if got, want := c.Delay, defaultDelay; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
	c.MaxAttempts = 1
	c.Delay = 0

	conn, err := amqp.Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	defer conn.Close()

	processor := new(dummyChanWorker)
	worker := NewConnectionWorker(processor)
	go c.Listen(worker)
	for {
		if processor.started {
			break
		}
	}
	c.Close()
	for {
		if processor.ended {
			break
		}
	}
}

func TestConnection_NewParallelConnectionWorker(t *testing.T) {
	if amqpURI() == "" {
		t.Skip("Environment variable AMQP_URI not set")
	}
	c, err := Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	if c.done == nil {
		t.Error("Expected chan got <nil>")
	}
	if got, want := c.MaxAttempts, defaultMaxAttempts; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
	if got, want := c.Delay, defaultDelay; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
	c.MaxAttempts = 1
	c.Delay = 0

	conn, err := amqp.Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	defer conn.Close()

	ch, err := conn.Channel()
	if err != nil {
		t.Fatal(err)
	}
	defer ch.Close()
	tempQ := uuid.NewV4().String()
	qMaker := func(ch *amqp.Channel) (amqp.Queue, error) {
		return ch.QueueDeclare(tempQ, false, false, false, false, nil)
	}
	_, err = qMaker(ch)
	if err != nil {
		t.Fatal(err)
	}
	defer ch.QueueDelete(tempQ, false, false, false)

	consumer := new(dummyDeliveryConsumer)
	worker, err := NewParallelConnectionWorker(qMaker, 1, consumer)
	go c.Listen(worker)
	time.Sleep(1 * time.Second)

	corrID := uuid.NewV4().String()

	err = ch.Publish("", tempQ, false, false, amqp.Publishing{
		CorrelationId: corrID,
		Body:          []byte(corrID),
	})
	if err != nil {
		t.Fatal(err)
	}
	for {
		if consumer.corrID != nil {
			break
		}
		time.Sleep(1 * time.Second)
	}
	if got, want := *consumer.corrID, corrID; got != want {
		t.Errorf("Expected CorrelationId='%s', got '%s'", want, got)
	}
	c.Close()
	// wait for internal channels to close
	time.Sleep(1 * time.Second)
}

func TestNewParallelConnectionWorkerBulk(t *testing.T) {
	if amqpURI() == "" {
		t.Skip("Environment variable AMQP_URI not set")
	}
	c, err := Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	clientConn, err := amqp.Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	defer clientConn.Close()

	clientCh, err := clientConn.Channel()
	if err != nil {
		t.Fatal(err)
	}
	defer clientCh.Close()

	tempQ := uuid.NewV4().String()
	qMaker := func(ch *amqp.Channel) (amqp.Queue, error) {
		return ch.QueueDeclare(tempQ, false, false, false, false, nil)
	}
	_, err = qMaker(clientCh)
	if err != nil {
		t.Fatal(err)
	}
	defer clientCh.QueueDelete(tempQ, false, false, false)
	poolSize := 5 * runtime.NumCPU()

	consumer := new(countConsumer)
	worker, err := NewParallelConnectionWorker(qMaker, poolSize, consumer)
	if err != nil {
		t.Fatal(err)
	}
	go c.Listen(worker)
	time.Sleep(1 * time.Second)

	batchSize := 100 * runtime.NumCPU()
	for i := 0; i < batchSize; i++ {
		err := clientCh.Publish("", tempQ, false, false, amqp.Publishing{Body: []byte(strconv.FormatInt(int64(i), 10))})
		if err != nil {
			t.Fatal(err)
		}
	}

	for {
		if consumer.count == batchSize {
			break
		}
		time.Sleep(1 * time.Second)
	}
}

type countConsumer struct {
	mutex sync.Mutex
	count int
}

func (consumer *countConsumer) Consume(ch *amqp.Channel, d *amqp.Delivery) {
	consumer.mutex.Lock()
	defer consumer.mutex.Unlock()
	consumer.count++
	d.Ack(false)
}

func amqpURI() string { return os.Getenv("AMQP_URI") }
<commit_msg>Explicitly close client in tests<commit_after>package amqpirq

import (
	"github.com/satori/go.uuid"
	"github.com/streadway/amqp"
	"os"
	"runtime"
	"strconv"
	"sync"
	"testing"
	"time"
)

type dummyConnWorker struct {
	started bool
	ended   bool
}

func (d *dummyConnWorker) Do(conn *amqp.Connection, done <-chan struct{}) {
	d.started = true
	<-done
	d.ended = true
}

type dummyDeliveryConsumer struct {
	corrID *string
}

func (c *dummyDeliveryConsumer) Consume(ch *amqp.Channel, d *amqp.Delivery) {
	c.corrID = &d.CorrelationId
	d.Ack(false)
}

type dummyChanWorker struct {
	started bool
	ended   bool
}

func (d *dummyChanWorker) Do(conn *amqp.Channel, done <-chan struct{}) {
	d.started = true
	<-done
	d.ended = true
}

func TestDial(t *testing.T) {
	c, err := Dial("")
	if err != nil {
		t.Fatal(err)
	}
	if c.done == nil {
		t.Error("Expected chan got <nil>")
	}
	if got, want := c.MaxAttempts, defaultMaxAttempts; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
	if got, want := c.Delay, defaultDelay; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
}

func TestDialTLS(t *testing.T) {
	c, err := DialTLS("", nil)
	if err != nil {
		t.Fatal(err)
	}
	if c.done == nil {
		t.Error("Expected chan got <nil>")
	}
	if got, want := c.MaxAttempts, defaultMaxAttempts; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
	if got, want := c.Delay, defaultDelay; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
}

func TestConnection_Listen(t *testing.T) {
	if amqpURI() == "" {
		t.Skip("Environment variable AMQP_URI not set")
	}
	c, err := Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	if c.done == nil {
		t.Error("Expected chan got <nil>")
	}
	if got, want := c.MaxAttempts, defaultMaxAttempts; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
	if got, want := c.Delay, defaultDelay; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
	c.MaxAttempts = 2
	c.Delay = 0

	worker := new(dummyConnWorker)
	worker1 := new(dummyConnWorker)
	go c.Listen(worker)
	go c.Listen(worker1)

	for {
		if worker.started {
			break
		}
		time.Sleep(1 * time.Second)
	}
	for {
		if worker1.started {
			break
		}
		time.Sleep(1 * time.Second)
	}
	c.Close()
	for {
		if worker.ended {
			break
		}
		time.Sleep(1 * time.Second)
	}
	for {
		if worker1.ended {
			break
		}
		time.Sleep(1 * time.Second)
	}
}

func TestConnection_ListenOnClosed(t *testing.T) {
	if amqpURI() == "" {
		t.Skip("Environment variable AMQP_URI not set")
	}
	c, err := Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	if c.done == nil {
		t.Error("Expected chan got <nil>")
	}
	if got, want := c.MaxAttempts, defaultMaxAttempts; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
	if got, want := c.Delay, defaultDelay; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
	c.MaxAttempts = 1
	c.Delay = 0
	c.Close()
	if got, want := c.closing, true; got != want {
		t.Errorf("Expected closing=%b, got=%b", want, got)
	}
	c.Close()
	if got, want := c.closing, true; got != want {
		t.Errorf("Expected closing=%b, got=%b", want, got)
	}

	worker := new(dummyConnWorker)
	err = c.Listen(worker)
	if err == nil {
		t.Fatal("Expected error got <nil>")
	}
}

func TestConnection_ListenInvalidURI(t *testing.T) {
	c, err := DialConfig("amqp://non-existent-host//", amqp.Config{})
	if err != nil {
		t.Fatal(err)
	}
	c.MaxAttempts = 2
	c.Delay = 0
	err = c.Listen(new(dummyConnWorker))
	if err == nil {
		t.Fatal("Expected error got <nil>")
	}
}

func TestNewParallelMessageListener_InvalidSize(t *testing.T) {
	_, err := NewParallelConnectionWorker(nil, 0, nil)
	if err == nil {
		t.Fatal("Expected error, got <nil>")
	}
}

func TestNewParallelMessageListener_MissingQueue(t *testing.T) {
	_, err := NewParallelConnectionWorker(nil, 1, nil)
	if err == nil {
		t.Fatal("Expected error, got <nil>")
	}
}

func TestConnection_NewConnectionWorker(t *testing.T) {
	if amqpURI() == "" {
		t.Skip("Environment variable AMQP_URI not set")
	}

	c, err := Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	if c.done == nil {
		t.Error("Expected chan got <nil>")
	}
	if got, want := c.MaxAttempts, defaultMaxAttempts; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
	if got, want := c.Delay, defaultDelay; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
	c.MaxAttempts = 1
	c.Delay = 0

	conn, err := amqp.Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	defer conn.Close()

	processor := new(dummyChanWorker)
	worker := NewConnectionWorker(processor)
	go c.Listen(worker)
	for {
		if processor.started {
			break
		}
	}
	c.Close()
	for {
		if processor.ended {
			break
		}
	}
}

func TestConnection_NewParallelConnectionWorker(t *testing.T) {
	if amqpURI() == "" {
		t.Skip("Environment variable AMQP_URI not set")
	}
	c, err := Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	if c.done == nil {
		t.Error("Expected chan got <nil>")
	}
	if got, want := c.MaxAttempts, defaultMaxAttempts; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
	if got, want := c.Delay, defaultDelay; got != want {
		t.Errorf("Expected MaxAttempts=%d, got=%d", want, got)
	}
	c.MaxAttempts = 1
	c.Delay = 0

	conn, err := amqp.Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	defer conn.Close()

	ch, err := conn.Channel()
	if err != nil {
		t.Fatal(err)
	}
	defer ch.Close()
	tempQ := uuid.NewV4().String()
	qMaker := func(ch *amqp.Channel) (amqp.Queue, error) {
		return ch.QueueDeclare(tempQ, false, false, false, false, nil)
	}
	_, err = qMaker(ch)
	if err != nil {
		t.Fatal(err)
	}
	defer ch.QueueDelete(tempQ, false, false, false)

	consumer := new(dummyDeliveryConsumer)
	worker, err := NewParallelConnectionWorker(qMaker, 1, consumer)
	go func() {
		err := c.Listen(worker)
		if got, want := err.Error(), `Exception (0) Reason: "<nil> connection error"`; got != want {
			t.Errorf("Expected error='%s', got='%s'", want, got)
		}
	}()

	corrID := uuid.NewV4().String()

	err = ch.Publish("", tempQ, false, false, amqp.Publishing{
		CorrelationId: corrID,
		Body:          []byte(corrID),
	})
	if err != nil {
		t.Fatal(err)
	}
	for {
		if consumer.corrID != nil {
			break
		}
		time.Sleep(1 * time.Second)
	}
	if got, want := *consumer.corrID, corrID; got != want {
		t.Errorf("Expected CorrelationId='%s', got '%s'", want, got)
	}
	c.Close()
	// wait for internal channels to close
	time.Sleep(1 * time.Second)
}

func TestNewParallelConnectionWorkerBulk(t *testing.T) {
	if amqpURI() == "" {
		t.Skip("Environment variable AMQP_URI not set")
	}
	c, err := Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}

	clientConn, err := amqp.Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	defer clientConn.Close()

	clientCh, err := clientConn.Channel()
	if err != nil {
		t.Fatal(err)
	}
	defer clientCh.Close()

	tempQ := uuid.NewV4().String()
	qMaker := func(ch *amqp.Channel) (amqp.Queue, error) {
		return ch.QueueDeclare(tempQ, false, false, false, false, nil)
	}
	_, err = qMaker(clientCh)
	if err != nil {
		t.Fatal(err)
	}
	defer clientCh.QueueDelete(tempQ, false, false, false)
	poolSize := 5 * runtime.NumCPU()

	consumer := new(countConsumer)
	worker, err := NewParallelConnectionWorker(qMaker, poolSize, consumer)
	if err != nil {
		t.Fatal(err)
	}
	go func() {
		err := c.Listen(worker)
		if got, want := err.Error(), `Exception (0) Reason: "<nil> connection error"`; got != want {
			t.Errorf("Expected error='%s', got='%s'", want, got)
		}
	}()

	batchSize := 100 * runtime.NumCPU()
	for i := 0; i < batchSize; i++ {
		err := clientCh.Publish("", tempQ, false, false, amqp.Publishing{Body: []byte(strconv.FormatInt(int64(i), 10))})
		if err != nil {
			t.Fatal(err)
		}
	}

	for {
		if consumer.count == batchSize {
			break
		}
		time.Sleep(1 * time.Second)
	}

	c.Close()
	// wait for internal channels to close
	time.Sleep(1 * time.Second)

}

type countConsumer struct {
	mutex sync.Mutex
	count int
}

func (consumer *countConsumer) Consume(ch *amqp.Channel, d *amqp.Delivery) {
	consumer.mutex.Lock()
	defer consumer.mutex.Unlock()
	consumer.count++
	d.Ack(false)
}

func amqpURI() string { return os.Getenv("AMQP_URI") }
<|endoftext|>


<commit_before>package class

// These constants represent the possible (valid)
// values for the tag attribute in a constant pool
// entry. Based on this tag, the structure of the
// following bytes can be determined.
// http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4-140
const (
	CONSTANT_UTF8               ConstantType = 1
	CONSTANT_Integer                         = 3
	CONSTANT_Float                           = 4
	CONSTANT_Long                            = 5
	CONSTANT_Double                          = 6
	CONSTANT_Class                           = 7
	CONSTANT_String                          = 8
	CONSTANT_FieldRef                        = 9
	CONSTANT_MethodRef                       = 10
	CONSTANT_InterfaceMethodRef              = 11
	CONSTANT_NameAndType                     = 12
	CONSTANT_MethodHandle                    = 15
	CONSTANT_MethodType                      = 16
	CONSTANT_InvokeDynamic                   = 18
)

// These constants describe access flags that can
// be applied to a whole class or interface.
// http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1-200-E.1
const (
	CLASS_ACC_PUBLIC     AccessFlags = 0x0001 // Declared public; may be accessed from outside its package.
	CLASS_ACC_FINAL                  = 0x0010 // Declared final; no subclasses allowed.
	CLASS_ACC_SUPER                  = 0x0020 // Treat superclass methods specially when invoked by the invokespecial instruction.
	CLASS_ACC_INTERFACE              = 0x0200 // Is an interface, not a class.
	CLASS_ACC_ABSTRACT               = 0x0400 // Declared abstract; must not be instantiated.
	CLASS_ACC_SYNTHETIC              = 0x1000 // Declared synthetic; not present in the source code.
	CLASS_ACC_ANNOTATION             = 0x2000 // Declared as an annotation type.
	CLASS_ACC_ENUM                   = 0x4000 // Declared as an enum type.
)

// These constant define access flags and attributes
// of a field in a class or interface.
// http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.5-200-A.1
const (
	FIELD_ACC_PUBLIC    AccessFlags = 0x0001 // Declared public; may be accessed from outside its package.
	FIELD_ACC_PRIVATE               = 0x0002 // Declared private; usable only within the defining class.
	FIELD_ACC_PROTECTED             = 0x0004 // Declared protected; may be accessed within subclasses.
	FIELD_ACC_STATIC                = 0x0008 // Declared static.
	FIELD_ACC_FINAL                 = 0x0010 // Declared final; never directly assigned to after object construction (JLS §17.5).
	FIELD_ACC_VOLATILE              = 0x0040 // Declared volatile; cannot be cached.
	FIELD_ACC_TRANSIENT             = 0x0080 // Declared transient; not written or read by a persistent object manager.
	FIELD_ACC_SYNTHETIC             = 0x1000 // Declared synthetic; not present in the source code.
	FIELD_ACC_ENUM                  = 0x4000 // Declared as an element of an enum.
)

// These constants describe access flags and properties
// of methods in a class or interface.
// http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.6-200-A.1
const (
	METHOD_ACC_PUBLIC       AccessFlags = 0x0001 // Declared public; may be accessed from outside its package.
	METHOD_ACC_PRIVATE                  = 0x0002 // Declared private; accessible only within the defining class.
	METHOD_ACC_PROTECTED                = 0x0004 // Declared protected; may be accessed within subclasses.
	METHOD_ACC_STATIC                   = 0x0008 // Declared static.
	METHOD_ACC_FINAL                    = 0x0010 // Declared final; must not be overridden (§5.4.5).
	METHOD_ACC_SYNCHRONIZED             = 0x0020 // Declared synchronized; invocation is wrapped by a monitor use.
	METHOD_ACC_BRIDGE                   = 0x0040 // A bridge method, generated by the compiler.
	METHOD_ACC_VARARGS                  = 0x0080 // Declared with variable number of arguments.
	METHOD_ACC_NATIVE                   = 0x0100 // Declared native; implemented in a language other than Java.
	METHOD_ACC_ABSTRACT                 = 0x0400 // Declared abstract; no implementation is provided.
	METHOD_ACC_STRICT                   = 0x0800 // Declared strictfp; floating-point mode is FP-strict.
	METHOD_ACC_SYNTHETIC                = 0x1000 // Declared synthetic; not present in the source code.
)

// These constants define valid access flags and properties
// for inner classes of a class or interface.
// http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.6-300-D.2-5
const (
	NESTED_CLASS_ACC_PUBLIC     AccessFlags = 0x0001 // Marked or implicitly public in source.
	NESTED_CLASS_ACC_PRIVATE                = 0x0002 // Marked private in source.
	NESTED_CLASS_ACC_PROTECTED              = 0x0004 // Marked protected in source.
	NESTED_CLASS_ACC_STATIC                 = 0x0008 // Marked or implicitly static in source.
	NESTED_CLASS_ACC_FINAL                  = 0x0010 // Marked final in source.
	NESTED_CLASS_ACC_INTERFACE              = 0x0200 // Was an interface in source.
	NESTED_CLASS_ACC_ABSTRACT               = 0x0400 // Marked or implicitly abstract in source.
	NESTED_CLASS_ACC_SYNTHETIC              = 0x1000 // Declared synthetic; not present in the source code.
	NESTED_CLASS_ACC_ANNOTATION             = 0x2000 // Declared as an annotation type.
	NESTED_CLASS_ACC_ENUM                   = 0x4000 // Declared as an enum type.
)

const (
	UnknownTag AttributeType = iota
	ConstantValueTag
	CodeTag
	StackMapTableTag
	ExceptionsTag
	InnerClassesTag
	EnclosingMethodTag
	SyntheticTag
	SignatureTag
	SourceFileTag
	SourceDebugExtensionTag
	LineNumberTableTag
	LocalVariableTableTag
	LocalVariableTypeTableTag
	DeprecatedTag
	RuntimeVisibleAnnotationsTag
	RuntimeInvisibleAnnotationsTag
	RuntimeVisibleParameterAnnotationsTag
	RuntimeInvisibleParameterAnnotationsTag
	AnnotationDefaultTag
	BootstrapMethodsTag
)
<commit_msg>Added missing comments to constants<commit_after>package class

// These constants represent the possible (valid)
// values for the tag attribute in a constant pool
// entry. Based on this tag, the structure of the
// following bytes can be determined.
// http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4-140
const (
	CONSTANT_UTF8               ConstantType = 1
	CONSTANT_Integer                         = 3
	CONSTANT_Float                           = 4
	CONSTANT_Long                            = 5
	CONSTANT_Double                          = 6
	CONSTANT_Class                           = 7
	CONSTANT_String                          = 8
	CONSTANT_FieldRef                        = 9
	CONSTANT_MethodRef                       = 10
	CONSTANT_InterfaceMethodRef              = 11
	CONSTANT_NameAndType                     = 12
	CONSTANT_MethodHandle                    = 15
	CONSTANT_MethodType                      = 16
	CONSTANT_InvokeDynamic                   = 18
)

// These constants describe access flags that can
// be applied to a whole class or interface.
// http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1-200-E.1
const (
	CLASS_ACC_PUBLIC     AccessFlags = 0x0001 // Declared public; may be accessed from outside its package.
	CLASS_ACC_FINAL                  = 0x0010 // Declared final; no subclasses allowed.
	CLASS_ACC_SUPER                  = 0x0020 // Treat superclass methods specially when invoked by the invokespecial instruction.
	CLASS_ACC_INTERFACE              = 0x0200 // Is an interface, not a class.
	CLASS_ACC_ABSTRACT               = 0x0400 // Declared abstract; must not be instantiated.
	CLASS_ACC_SYNTHETIC              = 0x1000 // Declared synthetic; not present in the source code.
	CLASS_ACC_ANNOTATION             = 0x2000 // Declared as an annotation type.
	CLASS_ACC_ENUM                   = 0x4000 // Declared as an enum type.
)

// These constant define access flags and attributes
// of a field in a class or interface.
// http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.5-200-A.1
const (
	FIELD_ACC_PUBLIC    AccessFlags = 0x0001 // Declared public; may be accessed from outside its package.
	FIELD_ACC_PRIVATE               = 0x0002 // Declared private; usable only within the defining class.
	FIELD_ACC_PROTECTED             = 0x0004 // Declared protected; may be accessed within subclasses.
	FIELD_ACC_STATIC                = 0x0008 // Declared static.
	FIELD_ACC_FINAL                 = 0x0010 // Declared final; never directly assigned to after object construction (JLS §17.5).
	FIELD_ACC_VOLATILE              = 0x0040 // Declared volatile; cannot be cached.
	FIELD_ACC_TRANSIENT             = 0x0080 // Declared transient; not written or read by a persistent object manager.
	FIELD_ACC_SYNTHETIC             = 0x1000 // Declared synthetic; not present in the source code.
	FIELD_ACC_ENUM                  = 0x4000 // Declared as an element of an enum.
)

// These constants describe access flags and properties
// of methods in a class or interface.
// http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.6-200-A.1
const (
	METHOD_ACC_PUBLIC       AccessFlags = 0x0001 // Declared public; may be accessed from outside its package.
	METHOD_ACC_PRIVATE                  = 0x0002 // Declared private; accessible only within the defining class.
	METHOD_ACC_PROTECTED                = 0x0004 // Declared protected; may be accessed within subclasses.
	METHOD_ACC_STATIC                   = 0x0008 // Declared static.
	METHOD_ACC_FINAL                    = 0x0010 // Declared final; must not be overridden (§5.4.5).
	METHOD_ACC_SYNCHRONIZED             = 0x0020 // Declared synchronized; invocation is wrapped by a monitor use.
	METHOD_ACC_BRIDGE                   = 0x0040 // A bridge method, generated by the compiler.
	METHOD_ACC_VARARGS                  = 0x0080 // Declared with variable number of arguments.
	METHOD_ACC_NATIVE                   = 0x0100 // Declared native; implemented in a language other than Java.
	METHOD_ACC_ABSTRACT                 = 0x0400 // Declared abstract; no implementation is provided.
	METHOD_ACC_STRICT                   = 0x0800 // Declared strictfp; floating-point mode is FP-strict.
	METHOD_ACC_SYNTHETIC                = 0x1000 // Declared synthetic; not present in the source code.
)

// These constants define valid access flags and properties
// for inner classes of a class or interface.
// http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.6-300-D.2-5
const (
	NESTED_CLASS_ACC_PUBLIC     AccessFlags = 0x0001 // Marked or implicitly public in source.
	NESTED_CLASS_ACC_PRIVATE                = 0x0002 // Marked private in source.
	NESTED_CLASS_ACC_PROTECTED              = 0x0004 // Marked protected in source.
	NESTED_CLASS_ACC_STATIC                 = 0x0008 // Marked or implicitly static in source.
	NESTED_CLASS_ACC_FINAL                  = 0x0010 // Marked final in source.
	NESTED_CLASS_ACC_INTERFACE              = 0x0200 // Was an interface in source.
	NESTED_CLASS_ACC_ABSTRACT               = 0x0400 // Marked or implicitly abstract in source.
	NESTED_CLASS_ACC_SYNTHETIC              = 0x1000 // Declared synthetic; not present in the source code.
	NESTED_CLASS_ACC_ANNOTATION             = 0x2000 // Declared as an annotation type.
	NESTED_CLASS_ACC_ENUM                   = 0x4000 // Declared as an enum type.
)

// These tags describe types of attributes, and can be
// used to determine what type to cast a generic Attribute
// to. They have the same use case as the ConstantType tags.
// http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7-300
const (
	UnknownTag AttributeType = iota
	ConstantValueTag
	CodeTag
	StackMapTableTag
	ExceptionsTag
	InnerClassesTag
	EnclosingMethodTag
	SyntheticTag
	SignatureTag
	SourceFileTag
	SourceDebugExtensionTag
	LineNumberTableTag
	LocalVariableTableTag
	LocalVariableTypeTableTag
	DeprecatedTag
	RuntimeVisibleAnnotationsTag
	RuntimeInvisibleAnnotationsTag
	RuntimeVisibleParameterAnnotationsTag
	RuntimeInvisibleParameterAnnotationsTag
	AnnotationDefaultTag
	BootstrapMethodsTag
)
<|endoftext|>


<commit_before>package spdy

const SPDY_VERSION = 3

// Control types
const (
  CONTROL_FRAME = -1
  DATA_FRAME    = -2
)

// Frame types
const (
  SYN_STREAM    = 1
  SYN_REPLY     = 2
  RST_STREAM    = 3
  SETTINGS      = 4
  PING          = 6
  GOAWAY        = 7
  HEADERS       = 8
  WINDOW_UPDATE = 9
  CREDENTIAL    = 10
)

// Flags
const (
  FLAG_FIN                     = 1
  FLAG_UNIDIRECTIONAL          = 2
  FLAG_SETTINGS_CLEAR_SETTINGS = 1
  FLAG_SETTINGS_PERSIST_VALUE  = 1
  FLAG_SETTINGS_PERSISTED      = 2
)

// RST_STREAM status codes
const (
  RST_STREAM_PROTOCOL_ERROR        = 1
  RST_STREAM_INVALID_STREAM        = 2
  RST_STREAM_REFUSED_STREAM        = 3
  RST_STREAM_UNSUPPORTED_VERSION   = 4
  RST_STREAM_CANCEL                = 5
  RST_STREAM_INTERNAL_ERROR        = 6
  RST_STREAM_FLOW_CONTROL_ERROR    = 7
  RST_STREAM_STREAM_IN_USE         = 8
  RST_STREAM_STREAM_ALREADY_CLOSED = 9
  RST_STREAM_INVALID_CREDENTIALS   = 10
  RST_STREAM_FRAME_TOO_LARGE       = 11
)

// Settings IDs
const (
  SETTINGS_UPLOAD_BANDWIDTH               = 1
  SETTINGS_DOWNLOAD_BANDWIDTH             = 2
  SETTINGS_ROUND_TRIP_TIME                = 3
  SETTINGS_MAX_CONCURRENT_STREAMS         = 4
  SETTINGS_CURRENT_CWND                   = 5
  SETTINGS_DOWNLOAD_RETRANS_RATE          = 6
  SETTINGS_INITIAL_WINDOW_SIZE            = 7
  SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE = 8
)

// HTTP time format.
const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT"

// Maximum frame size (2 ** 24 -1).
const MAX_FRAME_SIZE = 0xffffff
<commit_msg>Added max stream ID to constants<commit_after>package spdy

const SPDY_VERSION = 3

// Control types
const (
  CONTROL_FRAME = -1
  DATA_FRAME    = -2
)

// Frame types
const (
  SYN_STREAM    = 1
  SYN_REPLY     = 2
  RST_STREAM    = 3
  SETTINGS      = 4
  PING          = 6
  GOAWAY        = 7
  HEADERS       = 8
  WINDOW_UPDATE = 9
  CREDENTIAL    = 10
)

// Flags
const (
  FLAG_FIN                     = 1
  FLAG_UNIDIRECTIONAL          = 2
  FLAG_SETTINGS_CLEAR_SETTINGS = 1
  FLAG_SETTINGS_PERSIST_VALUE  = 1
  FLAG_SETTINGS_PERSISTED      = 2
)

// RST_STREAM status codes
const (
  RST_STREAM_PROTOCOL_ERROR        = 1
  RST_STREAM_INVALID_STREAM        = 2
  RST_STREAM_REFUSED_STREAM        = 3
  RST_STREAM_UNSUPPORTED_VERSION   = 4
  RST_STREAM_CANCEL                = 5
  RST_STREAM_INTERNAL_ERROR        = 6
  RST_STREAM_FLOW_CONTROL_ERROR    = 7
  RST_STREAM_STREAM_IN_USE         = 8
  RST_STREAM_STREAM_ALREADY_CLOSED = 9
  RST_STREAM_INVALID_CREDENTIALS   = 10
  RST_STREAM_FRAME_TOO_LARGE       = 11
)

// Settings IDs
const (
  SETTINGS_UPLOAD_BANDWIDTH               = 1
  SETTINGS_DOWNLOAD_BANDWIDTH             = 2
  SETTINGS_ROUND_TRIP_TIME                = 3
  SETTINGS_MAX_CONCURRENT_STREAMS         = 4
  SETTINGS_CURRENT_CWND                   = 5
  SETTINGS_DOWNLOAD_RETRANS_RATE          = 6
  SETTINGS_INITIAL_WINDOW_SIZE            = 7
  SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE = 8
)

// HTTP time format.
const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT"

// Maximum frame size (2 ** 24 -1).
const MAX_FRAME_SIZE = 0xffffff

// Maximum stream ID (2 ** 31 -1).
const MAX_STREAM_ID = 0x7fffffff
<|endoftext|>


<commit_before>package asmd

import (
	"errors"
	"os"
	"strconv"
	"time"
)

func (v Variable) ToStdLogic(name string) string {
	str := name + " : std_logic"

	if v.BitWidth > 1 {
		str += "_vector (" + strconv.FormatUint(v.BitWidth-1, 10) + " downto 0)"
	}

	return str
}

func (v Variable) ToStdLogicSignal(name string) string {
	str := "signal " + v.ToStdLogic(name)
	if v.DefaultValue != "" {
		str += " := " + v.DefaultValue
	}
	str += ";"
	return str
}

//func (v Variable) ToGeneric(name string) string {}

func (m *StateMachine) VHDL(filename string) (err error) {
	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()

	// Comments
	write(file, "\n")
	write(file, "--------------------------------------------------------------------------------\n")
	write(file, "-- Module Name: ", m.Options.ModuleName, "\n")
	write(file, "-- Author:      ", m.Options.Author, "\n")
	write(file, "-- Date:        ", time.Now().Format("2 Jan 2006"), "\n")
	write(file, "--\n")
	write(file, "--------------------------------------------------------------------------------\n")
	write(file, "\n")
	write(file, "\n")

	// library and use statements
	// TODO infer the minimal set using given types
	write(file, "library IEEE;\n")
	write(file, "use IEEE.STD_LOGIC_1164.ALL;\n")
	write(file, "use IEEE.NUMERIC_STD.ALL;\n")
	write(file, "\n")

	// entity start
	write(file, "entity ", m.Options.trimmedModuleName, " is\n")

	// Entity - Generics
	if len(m.Parameters) > 0 {
		write(file, m.indent(1), "generic (\n")
		isFirst := true
		for name, properties := range m.Parameters {
			write(file, m.indent(2))
			if isFirst {
				write(file, "  ")
				isFirst = false
			} else {
				write(file, "; ")
			}
			write(file, name, ": ", properties.Type, " := ", properties.DefaultValue)
			write(file, "\n")
		}
		write(file, m.indent(1), ");\n")
	}

	if len(m.Inputs) > 0 || len(m.Outputs) > 0 {
		write(file, m.indent(1), "port (\n")

		// clk and rst
		write(file, m.indent(2), "  ", (Variable{1, "", ""}).ToStdLogic("clk"), "\n")
		if *m.Options.AddAsyncReset {
			write(file, m.indent(2), "; ", (Variable{1, "", ""}).ToStdLogic("rst"), "\n")
		}

		// Entity - Inputs
		for name, properties := range m.Inputs {
			write(file, m.indent(2), "; ", properties.ToStdLogic(name), "\n")
		}

		// Entity - Outputs
		// We're merely continuing the same list so don't reset isFirst.
		// TODO make this DRY with Inputs section
		for name, properties := range m.Outputs {
			write(file, m.indent(2), "; ", properties.ToStdLogic(name), "\n")
		}

		write(file, m.indent(1), ");\n")
	}

	// Entity end
	write(file, "end ", m.Options.trimmedModuleName, ";\n")
	write(file, "\n")

	// architecture start
	write(file, "architecture Behavioral of ", m.Options.trimmedModuleName, " is\n")

	// Constants (?)

	// Internal Signals
	write(file, m.indent(1), "-- Register signals\n")
	for sigName, signal := range m.Registers {
		write(file, m.indent(1), signal.ToStdLogicSignal(sigName+"_reg, "+sigName+"_next"), "\n")
	}
	write(file, "\n")

	// Internal signals for functional units
	for unitName, unit := range m.FunctionalUnits {
		write(file, m.indent(1), "-- ", unitName, " connections\n")
		for sigName, signal := range unit.Inputs {
			write(file, m.indent(1), signal.ToStdLogicSignal(sigName), "\n")
		}
		for sigName, signal := range unit.Outputs {
			write(file, m.indent(1), signal.ToStdLogicSignal(sigName), "\n")
		}
		for sigName, signal := range unit.Registers {
			write(file, m.indent(1), signal.ToStdLogicSignal(sigName+"_reg"), "\n")
			write(file, m.indent(1), signal.ToStdLogicSignal(sigName+"_reg, "+sigName+"_next"), "\n")
		}
	}
	if len(m.FunctionalUnits) > 0 {
		write(file, "\n")
	}

	write(file, m.indent(1), "-- FSM declarations\n")
	// State machine states
	write(file, m.indent(1), "type state is (")
	isFirst := true
	for stateName, state := range m.States {
		if state.IsMealy {
			continue
		}
		if isFirst {
			isFirst = false
		} else {
			write(file, ", ")
		}
		write(file, stateName)
	}
	write(file, ");\n")
	// State machine signals
	write(file, m.indent(1), "signal state_reg, state_next : state := ", m.Options.FirstState, ";\n")

	// architecture "begin"
	write(file, "begin\n")

	// Register process
	write(file, m.indent(1), "-- Register Process\n")
	write(file, m.indent(1), "process(clk")
	if *m.Options.AddAsyncReset {
		write(file, ", rst")
	}
	write(file, ")\n")
	write(file, m.indent(1), "begin\n")
	if *m.Options.AddAsyncReset {
		write(file, m.indent(2), "if (rst='1') then\n")
		write(file, m.indent(3), "-- async reset of registers\n")
		write(file, m.indent(3), "state_reg <= ", m.Options.FirstState, ";\n")
		for name, reg := range m.Registers {
			defVal := reg.DefaultValue
			if defVal == "0" {
				defVal = "(others => '0')"
			}
			write(file, m.indent(3), name+"_reg", " <= ", defVal, ";\n")
		}
	}
	write(file, m.indent(2))
	if *m.Options.AddAsyncReset {
		write(file, "els")
	}
	if m.Options.ClockType == "posedge" {
		write(file, "if (clk'event and clk='1') then\n")
	} else if m.Options.ClockType == "negedge" {
		write(file, "if (clk'event and clk='0') then\n")
	} else {
		return errors.New("Unrecognized clock type: " + m.Options.ClockType)
	}
	write(file, m.indent(3), "-- FSM state register\n")
	write(file, m.indent(3), "state_reg <= state_next;\n")
	write(file, m.indent(3), "-- algorithm registers\n")
	for name, _ := range m.Registers {
		write(file, m.indent(3), name+"_reg", " <= ", name+"_next", ";\n")
	}
	write(file, m.indent(2), "end if;\n")
	write(file, m.indent(1), "end process;\n")
	write(file, "\n")

	// Next State + Output + RTL Operation process
	write(file, m.indent(1), "-- Next State + Output + RTL Operation process\n")
	write(file, m.indent(1), "process(clk, rst, state_reg, state_next")
	// TODO add in proper sensitivity list inference instead of this "benign sledgehammer" approach
	for name, _ := range m.Inputs {
		write(file, ", ", name)
	}
	for name, _ := range m.Registers {
		write(file, ", ", name+"_reg", ", ", name+"_next")
	}
	write(file, ")\n")
	write(file, m.indent(1), "begin\n")
	// default register 'next's to previous value
	write(file, m.indent(2), "state_next <= state_reg;\n")
	for regName, _ := range m.Registers {
		write(file, m.indent(2), regName+"_next", " <= ", regName+"_reg", ";\n")
	}
	for outName, out := range m.Outputs {
		write(file, m.indent(2), outName, " <= ", out.DefaultValue, ";\n")
	}
	// state switch statement
	write(file, m.indent(2), "case state_reg is\n")
	for stateName, state := range m.States {
		if state.IsMealy {
			continue
		}
		write(file, m.indent(3), "case ", stateName, " =>\n")
		writeVhdlNextNetwork(file, 4, m, state.Next)
		for regName, operation := range state.Operations {
			write(file, m.indent(4), regName+"_next", " <= ", operation, ";\n")
		}
	}
	write(file, m.indent(2), "end case;\n")
	write(file, m.indent(1), "end process;\n")

	// Functional units
	for unitName, unit := range m.FunctionalUnits {
		write(file, "\n")
		write(file, m.indent(1), "-- FunctionalUnit ", unitName, " with ", strconv.Itoa(len(unit.Registers)), " registers\n")
	}

	// architecture end
	write(file, "end Behavioral;\n")
	write(file, "\n")

	return nil
}

func writeVhdlNextNetwork(file *os.File, indentLevel uint, m *StateMachine, nextThingName string) {
	if nextState, ok := m.States[nextThingName]; ok && !nextState.IsMealy {
		// base case
		// TODO validate this is valid during Parse
		write(file, m.indent(indentLevel), "state_next <= ", nextThingName, ";\n")
	} else if nextState, ok := m.States[nextThingName]; ok && nextState.IsMealy {
		for out, action := range nextState.Operations {
			varName := out
			if _, ok := m.Registers[out]; ok {
				varName = out + "_next"
			}
			write(file, m.indent(indentLevel), varName, " <= ", action, ";\n")
		}
		writeVhdlNextNetwork(file, indentLevel, m, nextState.Next)
	} else if cond, ok := m.Conditions[nextThingName]; ok {
		// TODO support elsif to reduce code duplication
		write(file, m.indent(indentLevel), "if ", cond.Expression, " then\n")
		writeVhdlNextNetwork(file, indentLevel+1, m, cond.TrueTarget)
		write(file, m.indent(indentLevel), "else\n")
		writeVhdlNextNetwork(file, indentLevel+1, m, cond.FalseTarget)
		write(file, m.indent(indentLevel), "end if;\n")
	}
}
<commit_msg>Fixed VHDL case statement 'when' keyword<commit_after>package asmd

import (
	"errors"
	"os"
	"strconv"
	"time"
)

func (v Variable) ToStdLogic(name string) string {
	str := name + " : std_logic"

	if v.BitWidth > 1 {
		str += "_vector (" + strconv.FormatUint(v.BitWidth-1, 10) + " downto 0)"
	}

	return str
}

func (v Variable) ToStdLogicSignal(name string) string {
	str := "signal " + v.ToStdLogic(name)
	if v.DefaultValue != "" {
		str += " := " + v.DefaultValue
	}
	str += ";"
	return str
}

//func (v Variable) ToGeneric(name string) string {}

func (m *StateMachine) VHDL(filename string) (err error) {
	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()

	// Comments
	write(file, "\n")
	write(file, "--------------------------------------------------------------------------------\n")
	write(file, "-- Module Name: ", m.Options.ModuleName, "\n")
	write(file, "-- Author:      ", m.Options.Author, "\n")
	write(file, "-- Date:        ", time.Now().Format("2 Jan 2006"), "\n")
	write(file, "--\n")
	write(file, "--------------------------------------------------------------------------------\n")
	write(file, "\n")
	write(file, "\n")

	// library and use statements
	// TODO infer the minimal set using given types
	write(file, "library IEEE;\n")
	write(file, "use IEEE.STD_LOGIC_1164.ALL;\n")
	write(file, "use IEEE.NUMERIC_STD.ALL;\n")
	write(file, "\n")

	// entity start
	write(file, "entity ", m.Options.trimmedModuleName, " is\n")

	// Entity - Generics
	if len(m.Parameters) > 0 {
		write(file, m.indent(1), "generic (\n")
		isFirst := true
		for name, properties := range m.Parameters {
			write(file, m.indent(2))
			if isFirst {
				write(file, "  ")
				isFirst = false
			} else {
				write(file, "; ")
			}
			write(file, name, ": ", properties.Type, " := ", properties.DefaultValue)
			write(file, "\n")
		}
		write(file, m.indent(1), ");\n")
	}

	if len(m.Inputs) > 0 || len(m.Outputs) > 0 {
		write(file, m.indent(1), "port (\n")

		// clk and rst
		write(file, m.indent(2), "  ", (Variable{1, "", ""}).ToStdLogic("clk"), "\n")
		if *m.Options.AddAsyncReset {
			write(file, m.indent(2), "; ", (Variable{1, "", ""}).ToStdLogic("rst"), "\n")
		}

		// Entity - Inputs
		for name, properties := range m.Inputs {
			write(file, m.indent(2), "; ", properties.ToStdLogic(name), "\n")
		}

		// Entity - Outputs
		// We're merely continuing the same list so don't reset isFirst.
		// TODO make this DRY with Inputs section
		for name, properties := range m.Outputs {
			write(file, m.indent(2), "; ", properties.ToStdLogic(name), "\n")
		}

		write(file, m.indent(1), ");\n")
	}

	// Entity end
	write(file, "end ", m.Options.trimmedModuleName, ";\n")
	write(file, "\n")

	// architecture start
	write(file, "architecture Behavioral of ", m.Options.trimmedModuleName, " is\n")

	// Constants (?)

	// Internal Signals
	write(file, m.indent(1), "-- Register signals\n")
	for sigName, signal := range m.Registers {
		write(file, m.indent(1), signal.ToStdLogicSignal(sigName+"_reg, "+sigName+"_next"), "\n")
	}
	write(file, "\n")

	// Internal signals for functional units
	for unitName, unit := range m.FunctionalUnits {
		write(file, m.indent(1), "-- ", unitName, " connections\n")
		for sigName, signal := range unit.Inputs {
			write(file, m.indent(1), signal.ToStdLogicSignal(sigName), "\n")
		}
		for sigName, signal := range unit.Outputs {
			write(file, m.indent(1), signal.ToStdLogicSignal(sigName), "\n")
		}
		for sigName, signal := range unit.Registers {
			write(file, m.indent(1), signal.ToStdLogicSignal(sigName+"_reg"), "\n")
			write(file, m.indent(1), signal.ToStdLogicSignal(sigName+"_reg, "+sigName+"_next"), "\n")
		}
	}
	if len(m.FunctionalUnits) > 0 {
		write(file, "\n")
	}

	write(file, m.indent(1), "-- FSM declarations\n")
	// State machine states
	write(file, m.indent(1), "type state is (")
	isFirst := true
	for stateName, state := range m.States {
		if state.IsMealy {
			continue
		}
		if isFirst {
			isFirst = false
		} else {
			write(file, ", ")
		}
		write(file, stateName)
	}
	write(file, ");\n")
	// State machine signals
	write(file, m.indent(1), "signal state_reg, state_next : state := ", m.Options.FirstState, ";\n")

	// architecture "begin"
	write(file, "begin\n")

	// Register process
	write(file, m.indent(1), "-- Register Process\n")
	write(file, m.indent(1), "process(clk")
	if *m.Options.AddAsyncReset {
		write(file, ", rst")
	}
	write(file, ")\n")
	write(file, m.indent(1), "begin\n")
	if *m.Options.AddAsyncReset {
		write(file, m.indent(2), "if (rst='1') then\n")
		write(file, m.indent(3), "-- async reset of registers\n")
		write(file, m.indent(3), "state_reg <= ", m.Options.FirstState, ";\n")
		for name, reg := range m.Registers {
			defVal := reg.DefaultValue
			if defVal == "0" {
				defVal = "(others => '0')"
			}
			write(file, m.indent(3), name+"_reg", " <= ", defVal, ";\n")
		}
	}
	write(file, m.indent(2))
	if *m.Options.AddAsyncReset {
		write(file, "els")
	}
	if m.Options.ClockType == "posedge" {
		write(file, "if (clk'event and clk='1') then\n")
	} else if m.Options.ClockType == "negedge" {
		write(file, "if (clk'event and clk='0') then\n")
	} else {
		return errors.New("Unrecognized clock type: " + m.Options.ClockType)
	}
	write(file, m.indent(3), "-- FSM state register\n")
	write(file, m.indent(3), "state_reg <= state_next;\n")
	write(file, m.indent(3), "-- algorithm registers\n")
	for name, _ := range m.Registers {
		write(file, m.indent(3), name+"_reg", " <= ", name+"_next", ";\n")
	}
	write(file, m.indent(2), "end if;\n")
	write(file, m.indent(1), "end process;\n")
	write(file, "\n")

	// Next State + Output + RTL Operation process
	write(file, m.indent(1), "-- Next State + Output + RTL Operation process\n")
	write(file, m.indent(1), "process(clk, rst, state_reg, state_next")
	// TODO add in proper sensitivity list inference instead of this "benign sledgehammer" approach
	for name, _ := range m.Inputs {
		write(file, ", ", name)
	}
	for name, _ := range m.Registers {
		write(file, ", ", name+"_reg", ", ", name+"_next")
	}
	write(file, ")\n")
	write(file, m.indent(1), "begin\n")
	// default register 'next's to previous value
	write(file, m.indent(2), "state_next <= state_reg;\n")
	for regName, _ := range m.Registers {
		write(file, m.indent(2), regName+"_next", " <= ", regName+"_reg", ";\n")
	}
	for outName, out := range m.Outputs {
		write(file, m.indent(2), outName, " <= ", out.DefaultValue, ";\n")
	}
	// state switch statement
	write(file, m.indent(2), "case state_reg is\n")
	for stateName, state := range m.States {
		if state.IsMealy {
			continue
		}
		write(file, m.indent(3), "when ", stateName, " =>\n")
		writeVhdlNextNetwork(file, 4, m, state.Next)
		for regName, operation := range state.Operations {
			write(file, m.indent(4), regName+"_next", " <= ", operation, ";\n")
		}
	}
	write(file, m.indent(2), "end case;\n")
	write(file, m.indent(1), "end process;\n")

	// Functional units
	for unitName, unit := range m.FunctionalUnits {
		write(file, "\n")
		write(file, m.indent(1), "-- FunctionalUnit ", unitName, " with ", strconv.Itoa(len(unit.Registers)), " registers\n")
	}

	// architecture end
	write(file, "end Behavioral;\n")
	write(file, "\n")

	return nil
}

func writeVhdlNextNetwork(file *os.File, indentLevel uint, m *StateMachine, nextThingName string) {
	if nextState, ok := m.States[nextThingName]; ok && !nextState.IsMealy {
		// base case
		// TODO validate this is valid during Parse
		write(file, m.indent(indentLevel), "state_next <= ", nextThingName, ";\n")
	} else if nextState, ok := m.States[nextThingName]; ok && nextState.IsMealy {
		for out, action := range nextState.Operations {
			varName := out
			if _, ok := m.Registers[out]; ok {
				varName = out + "_next"
			}
			write(file, m.indent(indentLevel), varName, " <= ", action, ";\n")
		}
		writeVhdlNextNetwork(file, indentLevel, m, nextState.Next)
	} else if cond, ok := m.Conditions[nextThingName]; ok {
		// TODO support elsif to reduce code duplication
		write(file, m.indent(indentLevel), "if ", cond.Expression, " then\n")
		writeVhdlNextNetwork(file, indentLevel+1, m, cond.TrueTarget)
		write(file, m.indent(indentLevel), "else\n")
		writeVhdlNextNetwork(file, indentLevel+1, m, cond.FalseTarget)
		write(file, m.indent(indentLevel), "end if;\n")
	}
}
<|endoftext|>