<commit_before>package veneur

import (
	"container/ring"
	"context"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"sync"
	"time"

	"github.com/DataDog/datadog-go/statsd"
	lightstep "github.com/lightstep/lightstep-tracer-go"
	opentracing "github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/ext"
	"github.com/sirupsen/logrus"
	"github.com/stripe/veneur/ssf"
	"github.com/stripe/veneur/trace"
)

const datadogResourceKey = "resource"
const datadogNameKey = "name"

const lightStepOperationKey = "name"

const totalSpansFlushedMetricKey = "worker.spans_flushed_total"

// spanSink is a receiver of spans that handles sending those spans to some
// downstream sink. Calls to `Ingest(span)` are meant to give the sink control
// of the span, with periodic calls to flush as a signal for sinks that don't
// handle their own flushing in a separate goroutine, etc.
type spanSink interface {
	Name() string
	Ingest(ssf.SSFSpan) error
	Flush()
}

// DatadogSpanSink is a sink for sending spans to a Datadog trace agent.
type datadogSpanSink struct {
	HTTPClient   *http.Client
	buffer       *ring.Ring
	bufferSize   int
	mutex        *sync.Mutex
	stats        *statsd.Client
	commonTags   map[string]string
	traceAddress string
	traceClient  *trace.Client
}

// NewDatadogSpanSink creates a new Datadog sink for trace spans.
func NewDatadogSpanSink(config *Config, stats *statsd.Client, httpClient *http.Client, traceClient *trace.Client, commonTags map[string]string) (*datadogSpanSink, error) {
	return &datadogSpanSink{
		HTTPClient:   httpClient,
		bufferSize:   config.SsfBufferSize,
		buffer:       ring.New(config.SsfBufferSize),
		mutex:        &sync.Mutex{},
		stats:        stats,
		commonTags:   commonTags,
		traceAddress: config.DatadogTraceAPIAddress,
		traceClient:  traceClient,
	}, nil
}

// Name returns the name of this sink.
func (dd *datadogSpanSink) Name() string {
	return "datadog"
}

// Ingest takes the span and adds it to the ringbuffer.
func (dd *datadogSpanSink) Ingest(span ssf.SSFSpan) error {
	dd.mutex.Lock()
	defer dd.mutex.Unlock()

	dd.buffer.Value = span
	dd.buffer = dd.buffer.Next()
	return nil
}

// Flush signals the sink to send it's spans to their destination. For this
// sync it means we'll be making an HTTP request to send them along. We assume
// it's beneficial to performance to defer these until the normal 10s flush.
func (dd *datadogSpanSink) Flush() {
	dd.mutex.Lock()

	ssfSpans := make([]ssf.SSFSpan, 0, dd.buffer.Len())

	dd.buffer.Do(func(t interface{}) {
		const tooEarly = 1497
		const tooLate = 1497629343000000

		if t != nil {
			ssfSpan, ok := t.(ssf.SSFSpan)
			if !ok {
				log.Error("Got an unknown object in tracing ring!")
				// We'll just skip this one so we don't poison pill or anything.
				return
			}

			var timeErr string
			if ssfSpan.StartTimestamp < tooEarly {
				timeErr = "type:tooEarly"
			}
			if ssfSpan.StartTimestamp > tooLate {
				timeErr = "type:tooLate"
			}
			if timeErr != "" {
				dd.stats.Incr("worker.trace.sink.timestamp_error", []string{timeErr}, 1) // TODO tag as dd?
			}

			if ssfSpan.Tags == nil {
				ssfSpan.Tags = make(map[string]string)
			}

			// Add common tags from veneur's config
			// this will overwrite tags already present on the span
			for k, v := range dd.commonTags {
				ssfSpan.Tags[k] = v
			}
			ssfSpans = append(ssfSpans, ssfSpan)
		}
	})

	// Reset the ring.
	dd.buffer = ring.New(dd.bufferSize)

	// We're done manipulating stuff, let Ingest loose again.
	dd.mutex.Unlock()

	serviceCount := make(map[string]int64)
	var finalTraces []*DatadogTraceSpan
	// Conver the SSFSpans into Datadog Spans
	for _, span := range ssfSpans {
		// -1 is a canonical way of passing in invalid info in Go
		// so we should support that too
		parentID := span.ParentId

		// check if this is the root span
		if parentID <= 0 {
			// we need parentId to be zero for json:omitempty to work
			parentID = 0
		}

		resource := span.Tags[datadogResourceKey]
		name := span.Name

		tags := map[string]string{}
		// Get the span's existing tags
		for k, v := range span.Tags {
			tags[k] = v
		}

		delete(tags, datadogResourceKey)

		// TODO implement additional metrics
		var metrics map[string]float64

		var errorCode int64
		if span.Error {
			errorCode = 2
		}

		ddspan := &DatadogTraceSpan{
			TraceID:  span.TraceId,
			SpanID:   span.Id,
			ParentID: parentID,
			Service:  span.Service,
			Name:     name,
			Resource: resource,
			Start:    span.StartTimestamp,
			Duration: span.EndTimestamp - span.StartTimestamp,
			// TODO don't hardcode
			Type:    "http",
			Error:   errorCode,
			Metrics: metrics,
			Meta:    tags,
		}
		serviceCount[span.Service]++
		finalTraces = append(finalTraces, ddspan)
	}

	if len(finalTraces) != 0 {
		// this endpoint is not documented to take an array... but it does
		// another curious constraint of this endpoint is that it does not
		// support "Content-Encoding: deflate"

		err := postHelper(context.TODO(), dd.HTTPClient, dd.stats, dd.traceClient, fmt.Sprintf("%s/spans", dd.traceAddress), finalTraces, "flush_traces", false)

		if err == nil {
			log.WithField("traces", len(finalTraces)).Info("Completed flushing traces to Datadog")
			dd.stats.Count(totalSpansFlushedMetricKey, int64(len(ssfSpans)), []string{"sink:datadog"}, 1)
			// TODO: Per service counters?
		} else {
			log.WithFields(logrus.Fields{
				"traces":        len(finalTraces),
				logrus.ErrorKey: err}).Warn("Error flushing traces to Datadog")
		}
		for service, count := range serviceCount {
			dd.stats.Count(totalSpansFlushedMetricKey, count, []string{"sink:datadog", fmt.Sprintf("service:%s", service)}, 1)
		}
	} else {
		log.Info("No traces to flush to Datadog, skipping.")
	}
}

// lightStepSpanSink is a sink for spans to be sent to the LightStep client.
type lightStepSpanSink struct {
	tracers      []opentracing.Tracer
	stats        *statsd.Client
	commonTags   map[string]string
	mutex        *sync.Mutex
	serviceCount map[string]int64
}

// NewLightStepSpanSink creates a new instance of a LightStepSpanSink.
func NewLightStepSpanSink(config *Config, stats *statsd.Client, commonTags map[string]string) (*lightStepSpanSink, error) {

	var host *url.URL
	host, err := url.Parse(config.TraceLightstepCollectorHost)
	if err != nil {
		log.WithError(err).WithField(
			"host", config.TraceLightstepCollectorHost,
		).Error("Error parsing LightStep collector URL")
		return &lightStepSpanSink{}, err
	}

	port, err := strconv.Atoi(host.Port())
	if err != nil {
		log.WithError(err).WithFields(logrus.Fields{
			"port":         port,
			"default_port": lightstepDefaultPort,
		}).Warn("Error parsing LightStep port, using default")
		port = lightstepDefaultPort
	}

	reconPeriod := lightstepDefaultInterval
	if config.TraceLightstepReconnectPeriod != "" {
		reconPeriod, err = time.ParseDuration(config.TraceLightstepReconnectPeriod)
		if err != nil {
			log.WithError(err).WithFields(logrus.Fields{
				"interval":         config.TraceLightstepReconnectPeriod,
				"default_interval": lightstepDefaultInterval,
			}).Warn("Failed to parse reconnect duration, using default.")
			reconPeriod = lightstepDefaultInterval
		}
	}

	log.WithFields(logrus.Fields{
		"Host": host.Hostname(),
		"Port": port,
	}).Info("Dialing lightstep host")

	maxSpans := config.TraceLightstepMaximumSpans
	if maxSpans == 0 {
		maxSpans = config.SsfBufferSize
		log.WithField("max spans", maxSpans).Info("Using default maximum spans — ssf_buffer_size — for LightStep")
	}

	lightstepMultiplexTracerNum := config.TraceLightstepNumClients
	// If config value is missing, this value should default to one client
	if lightstepMultiplexTracerNum <= 0 {
		lightstepMultiplexTracerNum = 1
	}

	tracers := make([]opentracing.Tracer, 0, lightstepMultiplexTracerNum)

	for i := 0; i < lightstepMultiplexTracerNum; i++ {
		tracers = append(tracers, lightstep.NewTracer(lightstep.Options{
			AccessToken:     config.TraceLightstepAccessToken,
			ReconnectPeriod: reconPeriod,
			Collector: lightstep.Endpoint{
				Host:      host.Hostname(),
				Port:      port,
				Plaintext: true,
			},
			UseGRPC:          true,
			MaxBufferedSpans: maxSpans,
		}))
	}

	return &lightStepSpanSink{
		tracers:      tracers,
		stats:        stats,
		serviceCount: make(map[string]int64),
		mutex:        &sync.Mutex{},
	}, nil
}

// Name returns this sink's name.
func (ls *lightStepSpanSink) Name() string {
	return "lightstep"
}

// Ingest takes in a span and passed it along to the LS client after
// some sanity checks and improvements are made.
func (ls *lightStepSpanSink) Ingest(ssfSpan ssf.SSFSpan) error {
	parentID := ssfSpan.ParentId
	if parentID <= 0 {
		parentID = 0
	}

	var errorCode int64
	if ssfSpan.Error {
		errorCode = 1
	}

	timestamp := time.Unix(ssfSpan.StartTimestamp/1e9, ssfSpan.StartTimestamp%1e9)

	if len(ls.tracers) == 0 {
		err := fmt.Errorf("No lightstep tracer clients initialized")
		log.Error(err)
		return err
	}
	// pick the tracer to use
	tracerIndex := ssfSpan.TraceId % int64(len(ls.tracers))
	tracer := ls.tracers[tracerIndex]

	sp := tracer.StartSpan(
		ssfSpan.Name,
		opentracing.StartTime(timestamp),
		lightstep.SetTraceID(uint64(ssfSpan.TraceId)),
		lightstep.SetSpanID(uint64(ssfSpan.Id)),
		lightstep.SetParentSpanID(uint64(parentID)))

	sp.SetTag(trace.ResourceKey, ssfSpan.Tags[trace.ResourceKey]) // TODO Why is this here?
	sp.SetTag(lightstep.ComponentNameKey, ssfSpan.Service)
	// TODO don't hardcode
	sp.SetTag("type", "http")
	sp.SetTag("error-code", errorCode)
	for k, v := range ssfSpan.Tags {
		sp.SetTag(k, v)
	}
	// And now set any veneur common tags
	for k, v := range ls.commonTags {
		sp.SetTag(k, v)
	}
	// TODO add metrics as tags to the span as well?

	if errorCode > 0 {
		// Note: this sets the OT-standard "error" tag, which
		// LightStep uses to flag error spans.
		ext.Error.Set(sp, true)
	}

	endTime := time.Unix(ssfSpan.EndTimestamp/1e9, ssfSpan.EndTimestamp%1e9)
	sp.FinishWithOptions(opentracing.FinishOptions{
		FinishTime: endTime,
	})

	service := ssfSpan.Service
	if service == "" {
		service = "unknown"
	}

	// Protect mutating the service count with a mutex
	ls.mutex.Lock()
	defer ls.mutex.Unlock()
	ls.serviceCount[service]++

	return nil
}

// Flush doesn't need to do anything to the LS tracer, so we emit metrics
// instead.
func (ls *lightStepSpanSink) Flush() {
	ls.mutex.Lock()
	defer ls.mutex.Unlock()

	totalCount := int64(0)
	for service, count := range ls.serviceCount {
		totalCount += count
		ls.stats.Count(totalSpansFlushedMetricKey, count, []string{"sink:lightstep", fmt.Sprintf("service:%s", service)}, 1)
	}
	ls.serviceCount = make(map[string]int64)
	log.WithField("total_spans", totalCount).Debug("Checkpointing flushed spans for Lightstep")
}
<commit_msg>Set up the ls span client with our internal tracing client<commit_after>package veneur

import (
	"container/ring"
	"context"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"sync"
	"time"

	"github.com/DataDog/datadog-go/statsd"
	lightstep "github.com/lightstep/lightstep-tracer-go"
	opentracing "github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/ext"
	"github.com/sirupsen/logrus"
	"github.com/stripe/veneur/ssf"
	"github.com/stripe/veneur/trace"
)

const datadogResourceKey = "resource"
const datadogNameKey = "name"

const lightStepOperationKey = "name"

const totalSpansFlushedMetricKey = "worker.spans_flushed_total"

// spanSink is a receiver of spans that handles sending those spans to some
// downstream sink. Calls to `Ingest(span)` are meant to give the sink control
// of the span, with periodic calls to flush as a signal for sinks that don't
// handle their own flushing in a separate goroutine, etc.
type spanSink interface {
	Name() string
	Ingest(ssf.SSFSpan) error
	Flush()
}

// DatadogSpanSink is a sink for sending spans to a Datadog trace agent.
type datadogSpanSink struct {
	HTTPClient   *http.Client
	buffer       *ring.Ring
	bufferSize   int
	mutex        *sync.Mutex
	stats        *statsd.Client
	commonTags   map[string]string
	traceAddress string
	traceClient  *trace.Client
}

// NewDatadogSpanSink creates a new Datadog sink for trace spans.
func NewDatadogSpanSink(config *Config, stats *statsd.Client, httpClient *http.Client, traceClient *trace.Client, commonTags map[string]string) (*datadogSpanSink, error) {
	return &datadogSpanSink{
		HTTPClient:   httpClient,
		bufferSize:   config.SsfBufferSize,
		buffer:       ring.New(config.SsfBufferSize),
		mutex:        &sync.Mutex{},
		stats:        stats,
		commonTags:   commonTags,
		traceAddress: config.DatadogTraceAPIAddress,
		traceClient:  traceClient,
	}, nil
}

// Name returns the name of this sink.
func (dd *datadogSpanSink) Name() string {
	return "datadog"
}

// Ingest takes the span and adds it to the ringbuffer.
func (dd *datadogSpanSink) Ingest(span ssf.SSFSpan) error {
	dd.mutex.Lock()
	defer dd.mutex.Unlock()

	dd.buffer.Value = span
	dd.buffer = dd.buffer.Next()
	return nil
}

// Flush signals the sink to send it's spans to their destination. For this
// sync it means we'll be making an HTTP request to send them along. We assume
// it's beneficial to performance to defer these until the normal 10s flush.
func (dd *datadogSpanSink) Flush() {
	dd.mutex.Lock()

	ssfSpans := make([]ssf.SSFSpan, 0, dd.buffer.Len())

	dd.buffer.Do(func(t interface{}) {
		const tooEarly = 1497
		const tooLate = 1497629343000000

		if t != nil {
			ssfSpan, ok := t.(ssf.SSFSpan)
			if !ok {
				log.Error("Got an unknown object in tracing ring!")
				// We'll just skip this one so we don't poison pill or anything.
				return
			}

			var timeErr string
			if ssfSpan.StartTimestamp < tooEarly {
				timeErr = "type:tooEarly"
			}
			if ssfSpan.StartTimestamp > tooLate {
				timeErr = "type:tooLate"
			}
			if timeErr != "" {
				dd.stats.Incr("worker.trace.sink.timestamp_error", []string{timeErr}, 1) // TODO tag as dd?
			}

			if ssfSpan.Tags == nil {
				ssfSpan.Tags = make(map[string]string)
			}

			// Add common tags from veneur's config
			// this will overwrite tags already present on the span
			for k, v := range dd.commonTags {
				ssfSpan.Tags[k] = v
			}
			ssfSpans = append(ssfSpans, ssfSpan)
		}
	})

	// Reset the ring.
	dd.buffer = ring.New(dd.bufferSize)

	// We're done manipulating stuff, let Ingest loose again.
	dd.mutex.Unlock()

	serviceCount := make(map[string]int64)
	var finalTraces []*DatadogTraceSpan
	// Conver the SSFSpans into Datadog Spans
	for _, span := range ssfSpans {
		// -1 is a canonical way of passing in invalid info in Go
		// so we should support that too
		parentID := span.ParentId

		// check if this is the root span
		if parentID <= 0 {
			// we need parentId to be zero for json:omitempty to work
			parentID = 0
		}

		resource := span.Tags[datadogResourceKey]
		name := span.Name

		tags := map[string]string{}
		// Get the span's existing tags
		for k, v := range span.Tags {
			tags[k] = v
		}

		delete(tags, datadogResourceKey)

		// TODO implement additional metrics
		var metrics map[string]float64

		var errorCode int64
		if span.Error {
			errorCode = 2
		}

		ddspan := &DatadogTraceSpan{
			TraceID:  span.TraceId,
			SpanID:   span.Id,
			ParentID: parentID,
			Service:  span.Service,
			Name:     name,
			Resource: resource,
			Start:    span.StartTimestamp,
			Duration: span.EndTimestamp - span.StartTimestamp,
			// TODO don't hardcode
			Type:    "http",
			Error:   errorCode,
			Metrics: metrics,
			Meta:    tags,
		}
		serviceCount[span.Service]++
		finalTraces = append(finalTraces, ddspan)
	}

	if len(finalTraces) != 0 {
		// this endpoint is not documented to take an array... but it does
		// another curious constraint of this endpoint is that it does not
		// support "Content-Encoding: deflate"

		err := postHelper(context.TODO(), dd.HTTPClient, dd.stats, dd.traceClient, fmt.Sprintf("%s/spans", dd.traceAddress), finalTraces, "flush_traces", false)

		if err == nil {
			log.WithField("traces", len(finalTraces)).Info("Completed flushing traces to Datadog")
			dd.stats.Count(totalSpansFlushedMetricKey, int64(len(ssfSpans)), []string{"sink:datadog"}, 1)
			// TODO: Per service counters?
		} else {
			log.WithFields(logrus.Fields{
				"traces":        len(finalTraces),
				logrus.ErrorKey: err}).Warn("Error flushing traces to Datadog")
		}
		for service, count := range serviceCount {
			dd.stats.Count(totalSpansFlushedMetricKey, count, []string{"sink:datadog", fmt.Sprintf("service:%s", service)}, 1)
		}
	} else {
		log.Info("No traces to flush to Datadog, skipping.")
	}
}

// lightStepSpanSink is a sink for spans to be sent to the LightStep client.
type lightStepSpanSink struct {
	tracers      []opentracing.Tracer
	stats        *statsd.Client
	commonTags   map[string]string
	mutex        *sync.Mutex
	serviceCount map[string]int64
	traceClient  *trace.Client
}

// NewLightStepSpanSink creates a new instance of a LightStepSpanSink.
func NewLightStepSpanSink(config *Config, stats *statsd.Client, traceClient *trace.Client, commonTags map[string]string) (*lightStepSpanSink, error) {

	var host *url.URL
	host, err := url.Parse(config.TraceLightstepCollectorHost)
	if err != nil {
		log.WithError(err).WithField(
			"host", config.TraceLightstepCollectorHost,
		).Error("Error parsing LightStep collector URL")
		return &lightStepSpanSink{}, err
	}

	port, err := strconv.Atoi(host.Port())
	if err != nil {
		log.WithError(err).WithFields(logrus.Fields{
			"port":         port,
			"default_port": lightstepDefaultPort,
		}).Warn("Error parsing LightStep port, using default")
		port = lightstepDefaultPort
	}

	reconPeriod := lightstepDefaultInterval
	if config.TraceLightstepReconnectPeriod != "" {
		reconPeriod, err = time.ParseDuration(config.TraceLightstepReconnectPeriod)
		if err != nil {
			log.WithError(err).WithFields(logrus.Fields{
				"interval":         config.TraceLightstepReconnectPeriod,
				"default_interval": lightstepDefaultInterval,
			}).Warn("Failed to parse reconnect duration, using default.")
			reconPeriod = lightstepDefaultInterval
		}
	}

	log.WithFields(logrus.Fields{
		"Host": host.Hostname(),
		"Port": port,
	}).Info("Dialing lightstep host")

	maxSpans := config.TraceLightstepMaximumSpans
	if maxSpans == 0 {
		maxSpans = config.SsfBufferSize
		log.WithField("max spans", maxSpans).Info("Using default maximum spans — ssf_buffer_size — for LightStep")
	}

	lightstepMultiplexTracerNum := config.TraceLightstepNumClients
	// If config value is missing, this value should default to one client
	if lightstepMultiplexTracerNum <= 0 {
		lightstepMultiplexTracerNum = 1
	}

	tracers := make([]opentracing.Tracer, 0, lightstepMultiplexTracerNum)

	for i := 0; i < lightstepMultiplexTracerNum; i++ {
		tracers = append(tracers, lightstep.NewTracer(lightstep.Options{
			AccessToken:     config.TraceLightstepAccessToken,
			ReconnectPeriod: reconPeriod,
			Collector: lightstep.Endpoint{
				Host:      host.Hostname(),
				Port:      port,
				Plaintext: true,
			},
			UseGRPC:          true,
			MaxBufferedSpans: maxSpans,
		}))
	}

	return &lightStepSpanSink{
		tracers:      tracers,
		stats:        stats,
		serviceCount: make(map[string]int64),
		mutex:        &sync.Mutex{},
		traceClient:  traceClient,
	}, nil
}

// Name returns this sink's name.
func (ls *lightStepSpanSink) Name() string {
	return "lightstep"
}

// Ingest takes in a span and passed it along to the LS client after
// some sanity checks and improvements are made.
func (ls *lightStepSpanSink) Ingest(ssfSpan ssf.SSFSpan) error {
	parentID := ssfSpan.ParentId
	if parentID <= 0 {
		parentID = 0
	}

	var errorCode int64
	if ssfSpan.Error {
		errorCode = 1
	}

	timestamp := time.Unix(ssfSpan.StartTimestamp/1e9, ssfSpan.StartTimestamp%1e9)

	if len(ls.tracers) == 0 {
		err := fmt.Errorf("No lightstep tracer clients initialized")
		log.Error(err)
		return err
	}
	// pick the tracer to use
	tracerIndex := ssfSpan.TraceId % int64(len(ls.tracers))
	tracer := ls.tracers[tracerIndex]

	sp := tracer.StartSpan(
		ssfSpan.Name,
		opentracing.StartTime(timestamp),
		lightstep.SetTraceID(uint64(ssfSpan.TraceId)),
		lightstep.SetSpanID(uint64(ssfSpan.Id)),
		lightstep.SetParentSpanID(uint64(parentID)))

	sp.SetTag(trace.ResourceKey, ssfSpan.Tags[trace.ResourceKey]) // TODO Why is this here?
	sp.SetTag(lightstep.ComponentNameKey, ssfSpan.Service)
	// TODO don't hardcode
	sp.SetTag("type", "http")
	sp.SetTag("error-code", errorCode)
	for k, v := range ssfSpan.Tags {
		sp.SetTag(k, v)
	}
	// And now set any veneur common tags
	for k, v := range ls.commonTags {
		sp.SetTag(k, v)
	}
	// TODO add metrics as tags to the span as well?

	if errorCode > 0 {
		// Note: this sets the OT-standard "error" tag, which
		// LightStep uses to flag error spans.
		ext.Error.Set(sp, true)
	}

	endTime := time.Unix(ssfSpan.EndTimestamp/1e9, ssfSpan.EndTimestamp%1e9)
	sp.ClientFinishWithOptions(sp.traceClient, opentracing.FinishOptions{
		FinishTime: endTime,
	})

	service := ssfSpan.Service
	if service == "" {
		service = "unknown"
	}

	// Protect mutating the service count with a mutex
	ls.mutex.Lock()
	defer ls.mutex.Unlock()
	ls.serviceCount[service]++

	return nil
}

// Flush doesn't need to do anything to the LS tracer, so we emit metrics
// instead.
func (ls *lightStepSpanSink) Flush() {
	ls.mutex.Lock()
	defer ls.mutex.Unlock()

	totalCount := int64(0)
	for service, count := range ls.serviceCount {
		totalCount += count
		ls.stats.Count(totalSpansFlushedMetricKey, count, []string{"sink:lightstep", fmt.Sprintf("service:%s", service)}, 1)
	}
	ls.serviceCount = make(map[string]int64)
	log.WithField("total_spans", totalCount).Debug("Checkpointing flushed spans for Lightstep")
}
<|endoftext|>


<commit_before>package sdl

/*
#include "sdl_wrapper.h"
static Sint64 RWsize(SDL_RWops *ctx)
{
	return ctx->size(ctx);
}

static Sint64 RWseek(SDL_RWops *ctx, Sint64 offset, int whence)
{
	return ctx->seek(ctx, offset, whence);
}

static size_t RWread(SDL_RWops *ctx, void *ptr, size_t size, size_t maxnum)
{
	return ctx->read(ctx, ptr, size, maxnum);
}

static size_t RWwrite(SDL_RWops *ctx, void *ptr, size_t size, size_t num)
{
	return ctx->write(ctx, ptr, size, num);
}

static int RWclose(SDL_RWops *ctx)
{
	return ctx->close(ctx);
}
*/
import "C"
import "unsafe"

/* RWops Types */
const (
	RWOPS_UNKNOWN   = 0
	RWOPS_WINFILE   = 1
	RWOPS_STDFILE   = 2
	RWOPS_JNIFILE   = 3
	RWOPS_MEMORY    = 4
	RWOPS_MEMORY_RO = 5
)

const (
	RW_SEEK_SET = C.RW_SEEK_SET
	RW_SEEK_CUR = C.RW_SEEK_CUR
	RW_SEEK_END = C.RW_SEEK_END
)

// RWops (https://wiki.libsdl.org/SDL_RWops)
type RWops C.SDL_RWops

func (rw *RWops) cptr() *C.SDL_RWops {
	return (*C.SDL_RWops)(rw)
}

// RWFromFile (https://wiki.libsdl.org/SDL_RWFromFile)
func RWFromFile(file, mode string) *RWops {
	_file := C.CString(file)
	_mode := C.CString(mode)
	defer C.free(unsafe.Pointer(_file))
	defer C.free(unsafe.Pointer(_mode))
	return (*RWops)(unsafe.Pointer(C.SDL_RWFromFile(_file, _mode)))
}

// RWFromMem (https://wiki.libsdl.org/SDL_RWFromMem)
func RWFromMem(mem unsafe.Pointer, size int) *RWops {
	if mem == nil {
		return nil
	}
	return (*RWops)(unsafe.Pointer(C.SDL_RWFromMem(mem, C.int(size))))
}

// AllocRW (https://wiki.libsdl.org/SDL_AllocRW)
func AllocRW() *RWops {
	return (*RWops)(unsafe.Pointer(C.SDL_AllocRW()))
}

// RWops (https://wiki.libsdl.org/SDL_FreeRW)
func (area *RWops) FreeRW() {
	if area == nil {
		return
	}
	C.SDL_FreeRW(area.cptr())
}

func (ctx *RWops) RWsize() int64 {
	return int64(C.RWsize(ctx.cptr()))
}

func (ctx *RWops) RWseek(offset int64, whence int) int64 {
	if ctx == nil {
		return -1
	}
	return int64(C.RWseek(ctx.cptr(), C.Sint64(offset), C.int(whence)))
}

func (ctx *RWops) RWread(ptr unsafe.Pointer, size, maxnum uint) uint {
	if ctx == nil {
		return 0
	}
	return uint(C.RWread(ctx.cptr(), ptr, C.size_t(size), C.size_t(maxnum)))
}

func (ctx *RWops) RWtell() int64 {
	if ctx == nil {
		return 0
	}
	return int64(C.RWseek(ctx.cptr(), 0, RW_SEEK_CUR))
}

func (ctx *RWops) RWwrite(ptr unsafe.Pointer, size, num uint) uint {
	if ctx == nil {
		return 0
	}
	if ptr == nil {
		return 0
	}
	return uint(C.RWwrite(ctx.cptr(), ptr, C.size_t(size), C.size_t(size)))
}

func (ctx *RWops) RWclose() int {
	if ctx == nil {
		return 0
	}
	return int(C.RWclose(ctx.cptr()))
}

func (src *RWops) ReadU8() uint8 {
	if src == nil {
		return 0
	}
	return uint8(C.SDL_ReadU8(src.cptr()))
}

// RWops (https://wiki.libsdl.org/SDL_ReadLE16)
func (src *RWops) ReadLE16() uint16 {
	if src == nil {
		return 0
	}
	return uint16(C.SDL_ReadLE16(src.cptr()))
}

// RWops (https://wiki.libsdl.org/SDL_ReadBE16)
func (src *RWops) ReadBE16() uint16 {
	if src == nil {
		return 0
	}
	return uint16(C.SDL_ReadBE16(src.cptr()))
}

// RWops (https://wiki.libsdl.org/SDL_ReadLE32)
func (src *RWops) ReadLE32() uint32 {
	if src == nil {
		return 0
	}
	return uint32(C.SDL_ReadLE32(src.cptr()))
}

// RWops (https://wiki.libsdl.org/SDL_ReadBE32)
func (src *RWops) ReadBE32() uint32 {
	if src == nil {
		return 0
	}
	return uint32(C.SDL_ReadBE32(src.cptr()))
}

// RWops (https://wiki.libsdl.org/SDL_ReadLE64)
func (src *RWops) ReadLE64() uint64 {
	if src == nil {
		return 0
	}
	return uint64(C.SDL_ReadLE64(src.cptr()))
}

// RWops (https://wiki.libsdl.org/SDL_ReadBE64)
func (src *RWops) ReadBE64() uint64 {
	if src == nil {
		return 0
	}
	return uint64(C.SDL_ReadBE64(src.cptr()))
}

func (dst *RWops) WriteU8(value uint8) uint {
	if dst == nil {
		return 0
	}
	return uint(C.SDL_WriteU8(dst.cptr(), C.Uint8(value)))
}

// RWops (https://wiki.libsdl.org/SDL_WriteLE16)
func (dst *RWops) WriteLE16(value uint16) uint {
	if dst == nil {
		return 0
	}
	return uint(C.SDL_WriteLE16(dst.cptr(), C.Uint16(value)))
}

// RWops (https://wiki.libsdl.org/SDL_WriteBE16)
func (dst *RWops) WriteBE16(value uint16) uint {
	if dst == nil {
		return 0
	}
	return uint(C.SDL_WriteBE16(dst.cptr(), C.Uint16(value)))
}

// RWops (https://wiki.libsdl.org/SDL_WriteLE32)
func (dst *RWops) WriteLE32(value uint32) uint {
	if dst == nil {
		return 0
	}
	return uint(C.SDL_WriteLE32(dst.cptr(), C.Uint32(value)))
}

// RWops (https://wiki.libsdl.org/SDL_WriteBE32)
func (dst *RWops) WriteBE32(value uint32) uint {
	if dst == nil {
		return 0
	}
	return uint(C.SDL_WriteBE32(dst.cptr(), C.Uint32(value)))
}

// RWops (https://wiki.libsdl.org/SDL_WriteLE64)
func (dst *RWops) WriteLE64(value uint64) uint {
	if dst == nil {
		return 0
	}
	return uint(C.SDL_WriteLE64(dst.cptr(), C.Uint64(value)))
}

// RWops (https://wiki.libsdl.org/SDL_WriteBE64)
func (dst *RWops) WriteBE64(value uint64) uint {
	if dst == nil {
		return 0
	}
	return uint(C.SDL_WriteBE64(dst.cptr(), C.Uint64(value)))
}
<commit_msg>Add/fix RWops doc comments<commit_after>package sdl

/*
#include "sdl_wrapper.h"
static Sint64 RWsize(SDL_RWops *ctx)
{
	return ctx->size(ctx);
}

static Sint64 RWseek(SDL_RWops *ctx, Sint64 offset, int whence)
{
	return ctx->seek(ctx, offset, whence);
}

static size_t RWread(SDL_RWops *ctx, void *ptr, size_t size, size_t maxnum)
{
	return ctx->read(ctx, ptr, size, maxnum);
}

static size_t RWwrite(SDL_RWops *ctx, void *ptr, size_t size, size_t num)
{
	return ctx->write(ctx, ptr, size, num);
}

static int RWclose(SDL_RWops *ctx)
{
	return ctx->close(ctx);
}
*/
import "C"
import "unsafe"

/* RWops Types */
const (
	RWOPS_UNKNOWN   = 0
	RWOPS_WINFILE   = 1
	RWOPS_STDFILE   = 2
	RWOPS_JNIFILE   = 3
	RWOPS_MEMORY    = 4
	RWOPS_MEMORY_RO = 5
)

const (
	RW_SEEK_SET = C.RW_SEEK_SET
	RW_SEEK_CUR = C.RW_SEEK_CUR
	RW_SEEK_END = C.RW_SEEK_END
)

// RWops (https://wiki.libsdl.org/SDL_RWops)
type RWops C.SDL_RWops

func (rw *RWops) cptr() *C.SDL_RWops {
	return (*C.SDL_RWops)(rw)
}

// RWFromFile (https://wiki.libsdl.org/SDL_RWFromFile)
func RWFromFile(file, mode string) *RWops {
	_file := C.CString(file)
	_mode := C.CString(mode)
	defer C.free(unsafe.Pointer(_file))
	defer C.free(unsafe.Pointer(_mode))
	return (*RWops)(unsafe.Pointer(C.SDL_RWFromFile(_file, _mode)))
}

// RWFromMem (https://wiki.libsdl.org/SDL_RWFromMem)
func RWFromMem(mem unsafe.Pointer, size int) *RWops {
	if mem == nil {
		return nil
	}
	return (*RWops)(unsafe.Pointer(C.SDL_RWFromMem(mem, C.int(size))))
}

// AllocRW (https://wiki.libsdl.org/SDL_AllocRW)
func AllocRW() *RWops {
	return (*RWops)(unsafe.Pointer(C.SDL_AllocRW()))
}

// FreeRW (https://wiki.libsdl.org/SDL_FreeRW)
func (area *RWops) FreeRW() {
	if area == nil {
		return
	}
	C.SDL_FreeRW(area.cptr())
}

// RWsize (https://wiki.libsdl.org/SDL_RWsize)
func (ctx *RWops) RWsize() int64 {
	return int64(C.RWsize(ctx.cptr()))
}

// RWseek (https://wiki.libsdl.org/SDL_RWseek)
func (ctx *RWops) RWseek(offset int64, whence int) int64 {
	if ctx == nil {
		return -1
	}
	return int64(C.RWseek(ctx.cptr(), C.Sint64(offset), C.int(whence)))
}

// RWread (https://wiki.libsdl.org/SDL_RWread)
func (ctx *RWops) RWread(ptr unsafe.Pointer, size, maxnum uint) uint {
	if ctx == nil {
		return 0
	}
	return uint(C.RWread(ctx.cptr(), ptr, C.size_t(size), C.size_t(maxnum)))
}

// RWtell (https://wiki.libsdl.org/SDL_RWtell)
func (ctx *RWops) RWtell() int64 {
	if ctx == nil {
		return 0
	}
	return int64(C.RWseek(ctx.cptr(), 0, RW_SEEK_CUR))
}

// RWwrite (https://wiki.libsdl.org/SDL_RWwrite)
func (ctx *RWops) RWwrite(ptr unsafe.Pointer, size, num uint) uint {
	if ctx == nil {
		return 0
	}
	if ptr == nil {
		return 0
	}
	return uint(C.RWwrite(ctx.cptr(), ptr, C.size_t(size), C.size_t(size)))
}

// RWclose (https://wiki.libsdl.org/SDL_RWclose)
func (ctx *RWops) RWclose() int {
	if ctx == nil {
		return 0
	}
	return int(C.RWclose(ctx.cptr()))
}

func (src *RWops) ReadU8() uint8 {
	if src == nil {
		return 0
	}
	return uint8(C.SDL_ReadU8(src.cptr()))
}

// ReadLE16 (https://wiki.libsdl.org/SDL_ReadLE16)
func (src *RWops) ReadLE16() uint16 {
	if src == nil {
		return 0
	}
	return uint16(C.SDL_ReadLE16(src.cptr()))
}

// ReadBE16 (https://wiki.libsdl.org/SDL_ReadBE16)
func (src *RWops) ReadBE16() uint16 {
	if src == nil {
		return 0
	}
	return uint16(C.SDL_ReadBE16(src.cptr()))
}

// ReadLE32 (https://wiki.libsdl.org/SDL_ReadLE32)
func (src *RWops) ReadLE32() uint32 {
	if src == nil {
		return 0
	}
	return uint32(C.SDL_ReadLE32(src.cptr()))
}

// ReadBE32 (https://wiki.libsdl.org/SDL_ReadBE32)
func (src *RWops) ReadBE32() uint32 {
	if src == nil {
		return 0
	}
	return uint32(C.SDL_ReadBE32(src.cptr()))
}

// ReadLE64 (https://wiki.libsdl.org/SDL_ReadLE64)
func (src *RWops) ReadLE64() uint64 {
	if src == nil {
		return 0
	}
	return uint64(C.SDL_ReadLE64(src.cptr()))
}

// ReadBE64 (https://wiki.libsdl.org/SDL_ReadBE64)
func (src *RWops) ReadBE64() uint64 {
	if src == nil {
		return 0
	}
	return uint64(C.SDL_ReadBE64(src.cptr()))
}

func (dst *RWops) WriteU8(value uint8) uint {
	if dst == nil {
		return 0
	}
	return uint(C.SDL_WriteU8(dst.cptr(), C.Uint8(value)))
}

// WriteLE16 (https://wiki.libsdl.org/SDL_WriteLE16)
func (dst *RWops) WriteLE16(value uint16) uint {
	if dst == nil {
		return 0
	}
	return uint(C.SDL_WriteLE16(dst.cptr(), C.Uint16(value)))
}

// WriteBE16 (https://wiki.libsdl.org/SDL_WriteBE16)
func (dst *RWops) WriteBE16(value uint16) uint {
	if dst == nil {
		return 0
	}
	return uint(C.SDL_WriteBE16(dst.cptr(), C.Uint16(value)))
}

// WriteLE32 (https://wiki.libsdl.org/SDL_WriteLE32)
func (dst *RWops) WriteLE32(value uint32) uint {
	if dst == nil {
		return 0
	}
	return uint(C.SDL_WriteLE32(dst.cptr(), C.Uint32(value)))
}

// WriteBE32 (https://wiki.libsdl.org/SDL_WriteBE32)
func (dst *RWops) WriteBE32(value uint32) uint {
	if dst == nil {
		return 0
	}
	return uint(C.SDL_WriteBE32(dst.cptr(), C.Uint32(value)))
}

// WriteLE64 (https://wiki.libsdl.org/SDL_WriteLE64)
func (dst *RWops) WriteLE64(value uint64) uint {
	if dst == nil {
		return 0
	}
	return uint(C.SDL_WriteLE64(dst.cptr(), C.Uint64(value)))
}

// WriteBE64 (https://wiki.libsdl.org/SDL_WriteBE64)
func (dst *RWops) WriteBE64(value uint64) uint {
	if dst == nil {
		return 0
	}
	return uint(C.SDL_WriteBE64(dst.cptr(), C.Uint64(value)))
}
<|endoftext|>


<commit_before>// Package scan TCP ports
package scan

import (
	"fmt"
	"net"
	"os"
	"regexp"
	"strconv"
	"sync"
	"time"

	"github.com/mehrdadrad/mylg/cli"
	"github.com/olekukonko/tablewriter"
)

// Scan represents the scan parameters
type Scan struct {
	minPort int
	maxPort int
	target  string
}

// NewScan creats scan object
func NewScan(args string, cfg cli.Config) (Scan, error) {
	var (
		scan Scan
		flag map[string]interface{}
		err  error
	)

	args, flag = cli.Flag(args)
	// help
	if _, ok := flag["help"]; ok || args == "" {
		help(cfg)
		return scan, fmt.Errorf("")
	}

	pRange := cli.SetFlag(flag, "p", cfg.Scan.Port).(string)

	re := regexp.MustCompile(`(\d+)(\-{0,1}(\d*))`)
	f := re.FindStringSubmatch(pRange)

	if len(f) != 4 {
		return scan, fmt.Errorf("error! please try scan help")
	}

	scan.target = args
	if len(f) == 4 && f[2] != "" {
		scan.minPort, err = strconv.Atoi(f[1])
		scan.maxPort, err = strconv.Atoi(f[3])
	} else {
		scan.minPort, err = strconv.Atoi(f[1])
		scan.maxPort, err = strconv.Atoi(f[1])
	}

	if err != nil {
		return scan, err
	}

	if !scan.IsCIDR() {
		ipAddr, err := net.ResolveIPAddr("ip", scan.target)
		if err != nil {
			return scan, err
		}
		scan.target = ipAddr.String()
	} else {
		return scan, fmt.Errorf("it doesn't support CIDR")
	}

	return scan, nil
}

// IsCIDR checks the target if it's CIDR
func (s Scan) IsCIDR() bool {
	_, _, err := net.ParseCIDR(s.target)
	if err != nil {
		return false
	}
	return true
}

// Run tries to scan wide range ports (TCP)
func (s Scan) Run() {
	if !s.IsCIDR() {
		host(s.target, s.minPort, s.maxPort)
	}
}

// host tries to scan a single host
func host(ipAddr string, minPort, maxPort int) {
	var (
		wg      sync.WaitGroup
		tStart  = time.Now()
		counter int
	)

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Protocol", "Port", "Status", "Description"})

	for i := minPort; i <= maxPort; i++ {
		wg.Add(1)
		go func(i int) {
			conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ipAddr, i), 1*time.Second)
			if err != nil {
				wg.Done()
				return
			}
			counter++
			table.Append([]string{"TCP", fmt.Sprintf("%d", i), "Open", ""})
			wg.Done()
			err = conn.Close()
			if err != nil {
				println(err.Error())
			}
		}(i)
		time.Sleep(8 * time.Millisecond)
	}

	wg.Wait()
	table.Render()

	if counter == 0 {
		println("there isn't any opened port")
	} else {
		elapsed := fmt.Sprintf("%.3f seconds", time.Since(tStart).Seconds())
		println("Scan done:", counter, "opened port(s) found in", elapsed)
	}
}

// help represents guide to user
func help(cfg cli.Config) {
	fmt.Printf(`
    usage:
          scan ip/host [-p portrange]
    example:
          scan 8.8.8.8 -p 53
          scan www.google.com -p %s
	`,
		cfg.Scan.Port)
}
<commit_msg>fixed joining ip and port specially ipv6<commit_after>// Package scan TCP ports
package scan

import (
	"fmt"
	"net"
	"os"
	"regexp"
	"strconv"
	"sync"
	"time"

	"github.com/mehrdadrad/mylg/cli"
	"github.com/olekukonko/tablewriter"
)

// Scan represents the scan parameters
type Scan struct {
	minPort int
	maxPort int
	target  string
}

// NewScan creats scan object
func NewScan(args string, cfg cli.Config) (Scan, error) {
	var (
		scan Scan
		flag map[string]interface{}
		err  error
	)

	args, flag = cli.Flag(args)
	// help
	if _, ok := flag["help"]; ok || args == "" {
		help(cfg)
		return scan, fmt.Errorf("")
	}

	pRange := cli.SetFlag(flag, "p", cfg.Scan.Port).(string)

	re := regexp.MustCompile(`(\d+)(\-{0,1}(\d*))`)
	f := re.FindStringSubmatch(pRange)

	if len(f) != 4 {
		return scan, fmt.Errorf("error! please try scan help")
	}

	scan.target = args
	if len(f) == 4 && f[2] != "" {
		scan.minPort, err = strconv.Atoi(f[1])
		scan.maxPort, err = strconv.Atoi(f[3])
	} else {
		scan.minPort, err = strconv.Atoi(f[1])
		scan.maxPort, err = strconv.Atoi(f[1])
	}

	if err != nil {
		return scan, err
	}

	if !scan.IsCIDR() {
		ipAddr, err := net.ResolveIPAddr("ip", scan.target)
		if err != nil {
			return scan, err
		}
		scan.target = ipAddr.String()
	} else {
		return scan, fmt.Errorf("it doesn't support CIDR")
	}

	return scan, nil
}

// IsCIDR checks the target if it's CIDR
func (s Scan) IsCIDR() bool {
	_, _, err := net.ParseCIDR(s.target)
	if err != nil {
		return false
	}
	return true
}

// Run tries to scan wide range ports (TCP)
func (s Scan) Run() {
	if !s.IsCIDR() {
		host(s.target, s.minPort, s.maxPort)
	}
}

// host tries to scan a single host
func host(ipAddr string, minPort, maxPort int) {
	var (
		wg      sync.WaitGroup
		tStart  = time.Now()
		counter int
	)

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Protocol", "Port", "Status", "Description"})

	for i := minPort; i <= maxPort; i++ {
		wg.Add(1)
		go func(i int) {
			conn, err := net.DialTimeout("tcp", net.JoinHostPort(ipAddr, fmt.Sprintf("%d", i)), 1*time.Second)
			if err != nil {
				wg.Done()
				return
			}
			counter++
			table.Append([]string{"TCP", fmt.Sprintf("%d", i), "Open", ""})
			wg.Done()
			err = conn.Close()
			if err != nil {
				println(err.Error())
			}
		}(i)
		time.Sleep(8 * time.Millisecond)
	}

	wg.Wait()
	table.Render()

	if counter == 0 {
		println("there isn't any opened port")
	} else {
		elapsed := fmt.Sprintf("%.3f seconds", time.Since(tStart).Seconds())
		println("Scan done:", counter, "opened port(s) found in", elapsed)
	}
}

// help represents guide to user
func help(cfg cli.Config) {
	fmt.Printf(`
    usage:
          scan ip/host [-p portrange]
    example:
          scan 8.8.8.8 -p 53
          scan www.google.com -p %s
	`,
		cfg.Scan.Port)
}
<|endoftext|>


<commit_before>package semaphore

import (
	"sync"
	"time"
)

type Semaphore struct {
	permits int
	avail   int
	channel chan int
	aMutex  *sync.Mutex
	rMutex  *sync.Mutex
}

func New(permits int) *Semaphore {
	if permits < 1 {
		panic("Invalid number of permits. Less than 1")
	}
	return &Semaphore{
		permits,
		permits,
		make(chan int, permits),
		&sync.Mutex{},
		&sync.Mutex{},
	}
}

//Acquire one permit, if its not available the goroutine will block till its available
func (s *Semaphore) Acquire() {
	s.aMutex.Lock()
	s.channel <- 1
	s.avail--
	s.aMutex.Unlock()
}

//Similar to Acquire() but for many permits
func (s *Semaphore) AcquireMany(n int) {
	if n > s.permits {
		panic("To many requested permits")
	}
	s.aMutex.Lock()
	s.avail -= n
	for ; n > 0; n-- {
		s.channel <- 1
	}
	s.avail += n
	s.aMutex.Unlock()
}

//Similar to AcquireMany() but cancels if duration elapse before getting the permits.
//Returns true if successful and false if timeout occurs.
func (s *Semaphore) AcquireWithin(n int, d time.Duration) bool {
	timeout := make(chan bool, 1)
	cancel := make(chan bool, 1)
	go func() {
		time.Sleep(d)
		timeout <- true
	}()
	go func() {
		s.AcquireMany(n)
		timeout <- false
		if <-cancel {
			s.ReleaseMany(n)
		}
	}()
	if <-timeout {
		cancel <- true
		return false
	}
	cancel <- false
	return true
}

//Release one permit
func (s *Semaphore) Release() {
	s.rMutex.Lock()
	<-s.channel
	s.avail++
	s.rMutex.Unlock()
}

//Release many permits
func (s *Semaphore) ReleaseMany(n int) {
	if n > s.permits {
		panic("Too many requested releases")
	}
	for ; n > 0; n-- {
		s.Release()
	}
}

//Number of available unacquired permits
func (s *Semaphore) AvailablePermits() int {
	if s.avail < 0 {
		return 0
	}
	return s.avail
}

//Acquire all available permits and return the number of permits acquired
func (s *Semaphore) DrainPermits() int {
	n := s.AvailablePermits()
	if n > 0 {
		s.AcquireMany(n)
	}
	return n
}

/*
acquire()
acquire(int permits)
acquireUninterruptibly()
acquireUninterruptibly(int permits)
availablePermits()
drainPermits()
getQueueLength()
hasQueuedThreads()
isFair()
reducePermits(int reduction)
release()
release(int permits)
toString()
tryAcquire()
tryAcquire(int permits)
*/
<commit_msg>Code Added<commit_after>package semaphore

import (
	"sync"
	"time"
)

type Semaphore struct {
	permits int
	avail   int
	channel chan int
	aMutex  *sync.Mutex
	rMutex  *sync.Mutex
}

func New(permits int) *Semaphore {
	if permits < 1 {
		panic("Invalid number of permits. Less than 1")
	}
	return &Semaphore{
		permits,
		permits,
		make(chan int, permits),
		&sync.Mutex{},
		&sync.Mutex{},
	}
}

//Acquire one permit, if its not available the goroutine will block till its available
func (s *Semaphore) Acquire() {
	s.aMutex.Lock()
	s.channel <- 1
	s.avail--
	s.aMutex.Unlock()
}

//Similar to Acquire() but for many permits
func (s *Semaphore) AcquireMany(n int) {
	if n > s.permits {
		panic("To many requested permits")
	}
	s.aMutex.Lock()
	s.avail -= n
	for ; n > 0; n-- {
		s.channel <- 1
	}
	s.avail += n
	s.aMutex.Unlock()
}

//Similar to AcquireMany() but cancels if duration elapse before getting the permits.
//Returns true if successful and false if timeout occurs.
func (s *Semaphore) AcquireWithin(n int, d time.Duration) bool {
	timeout := make(chan bool, 1)
	cancel := make(chan bool, 1)
	go func() {
		time.Sleep(d)
		timeout <- true
	}()
	go func() {
		s.AcquireMany(n)
		timeout <- false
		if <-cancel {
			s.ReleaseMany(n)
		}
	}()
	if <-timeout {
		cancel <- true
		return false
	}
	cancel <- false
	return true
}

//Release one permit
func (s *Semaphore) Release() {
	s.rMutex.Lock()
	<-s.channel
	s.avail++
	s.rMutex.Unlock()
}

//Release many permits
func (s *Semaphore) ReleaseMany(n int) {
	if n > s.permits {
		panic("Too many requested releases")
	}
	for ; n > 0; n-- {
		s.Release()
	}
}

//Number of available unacquired permits
func (s *Semaphore) AvailablePermits() int {
	if s.avail < 0 {
		return 0
	}
	return s.avail
}

//Acquire all available permits and return the number of permits acquired
func (s *Semaphore) DrainPermits() int {
	n := s.AvailablePermits()
	if n > 0 {
		s.AcquireMany(n)
	}
	return n
}
<|endoftext|>


<commit_before>package sdl

// #include "sdl_wrapper.h"
import "C"
import (
	"reflect"
	"unsafe"
)

const (
	AUDIO_MASK_BITSIZE  = C.SDL_AUDIO_MASK_BITSIZE
	AUDIO_MASK_DATATYPE = C.SDL_AUDIO_MASK_DATATYPE
	AUDIO_MASK_ENDIAN   = C.SDL_AUDIO_MASK_ENDIAN
	AUDIO_MASK_SIGNED   = C.SDL_AUDIO_MASK_SIGNED
)

const (
	AUDIO_U8     = C.AUDIO_U8
	AUDIO_S8     = C.AUDIO_S8
	AUDIO_U16LSB = C.AUDIO_U16LSB
	AUDIO_S16LSB = C.AUDIO_S16LSB
	AUDIO_U16MSB = C.AUDIO_U16MSB
	AUDIO_S16MSB = C.AUDIO_S16MSB
	AUDIO_U16    = C.AUDIO_U16
	AUDIO_S16    = C.AUDIO_S16
	AUDIO_S32LSB = C.AUDIO_S32LSB
	AUDIO_S32MSB = C.AUDIO_S32MSB
	AUDIO_S32    = C.AUDIO_S32
	AUDIO_F32LSB = C.AUDIO_F32LSB
	AUDIO_F32MSB = C.AUDIO_F32MSB
	AUDIO_F32    = C.AUDIO_F32
	AUDIO_U16SYS = C.AUDIO_U16SYS
	AUDIO_S16SYS = C.AUDIO_S16SYS
	AUDIO_S32SYS = C.AUDIO_S32SYS
	AUDIO_F32SYS = C.AUDIO_F32SYS
)

const (
	AUDIO_ALLOW_FREQUENCY_CHANGE = C.SDL_AUDIO_ALLOW_FREQUENCY_CHANGE
	AUDIO_ALLOW_FORMAT_CHANGE    = C.SDL_AUDIO_ALLOW_FORMAT_CHANGE
	AUDIO_ALLOW_CHANNELS_CHANGE  = C.SDL_AUDIO_ALLOW_CHANNELS_CHANGE
	AUDIO_ALLOW_ANY_CHANGE       = C.SDL_AUDIO_ALLOW_ANY_CHANGE
)

const (
	AUDIO_STOPPED AudioStatus = C.SDL_AUDIO_STOPPED
	AUDIO_PLAYING             = C.SDL_AUDIO_PLAYING
	AUDIO_PAUSED              = C.SDL_AUDIO_PAUSED
)

const MIX_MAXVOLUME = C.SDL_MIX_MAXVOLUME

// AudioFormat (https://wiki.libsdl.org/SDL_AudioFormat)
type AudioFormat uint16
type AudioCallback C.SDL_AudioCallback
type AudioFilter C.SDL_AudioFilter
type AudioDeviceID uint32

// AudioStatus (https://wiki.libsdl.org/SDL_AudioStatus)
type AudioStatus uint32
type cAudioStatus C.SDL_AudioStatus

// AudioSpec (https://wiki.libsdl.org/SDL_AudioSpec)
type AudioSpec struct {
	Freq     int32
	Format   AudioFormat
	Channels uint8
	Silence  uint8
	Samples  uint16
	Padding  uint16
	Size     uint32
	Callback AudioCallback
	UserData unsafe.Pointer
}
type cAudioSpec C.SDL_AudioSpec

// AudioCVT (https://wiki.libsdl.org/SDL_AudioCVT)
type AudioCVT struct {
	Needed      int32
	SrcFormat   AudioFormat
	DstFormat   AudioFormat
	RateIncr    float64
	Buf         *uint8 // use AudioCVT.BufAsSlice() for access via Go slice
	Len         int32
	LenCVT      int32
	LenMult     int32
	LenRatio    float64
	filters     [10]AudioFilter // internal
	filterIndex int32           // internal
}
type cAudioCVT C.SDL_AudioCVT

func (fmt AudioFormat) c() C.SDL_AudioFormat {
	return C.SDL_AudioFormat(fmt)
}

func (id AudioDeviceID) c() C.SDL_AudioDeviceID {
	return C.SDL_AudioDeviceID(id)
}

func (as *AudioSpec) cptr() *C.SDL_AudioSpec {
	return (*C.SDL_AudioSpec)(unsafe.Pointer(as))
}

func (cvt *AudioCVT) cptr() *C.SDL_AudioCVT {
	return (*C.SDL_AudioCVT)(unsafe.Pointer(cvt))
}

func (format AudioFormat) BitSize() uint8 {
	return uint8(format & AUDIO_MASK_BITSIZE)
}

func (format AudioFormat) IsFloat() bool {
	return (format & AUDIO_MASK_DATATYPE) > 0
}

func (format AudioFormat) IsBigEndian() bool {
	return (format & AUDIO_MASK_ENDIAN) > 0
}

func (format AudioFormat) IsSigned() bool {
	return (format & AUDIO_MASK_SIGNED) > 0
}

func (format AudioFormat) IsInt() bool {
	return !format.IsFloat()
}

func (format AudioFormat) IsLittleEndian() bool {
	return !format.IsBigEndian()
}

func (format AudioFormat) IsUnsigned() bool {
	return !format.IsSigned()
}

// access AudioCVT.buf as slice.
// len(slice) will return converted audio buffer length
func (cvt AudioCVT) BufAsSlice() []byte {
	var b []byte
	sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b))
	sliceHeader.Len = int(cvt.LenCVT)
	sliceHeader.Cap = int(cvt.Len * cvt.LenMult)
	sliceHeader.Data = uintptr(unsafe.Pointer(cvt.Buf))
	return b
}

// GetNumAudioDrivers (https://wiki.libsdl.org/SDL_GetNumAudioDrivers)
func GetNumAudioDrivers() int {
	return int(C.SDL_GetNumAudioDrivers())
}

// GetAudioDriver (https://wiki.libsdl.org/SDL_GetAudioDriver)
func GetAudioDriver(index int) string {
	return string(C.GoString(C.SDL_GetAudioDriver(C.int(index))))
}

// AudioInit (https://wiki.libsdl.org/SDL_AudioInit)
func AudioInit(driverName string) error {
	_driverName := C.CString(driverName)
	defer C.free(unsafe.Pointer(_driverName))
	if C.SDL_AudioInit(_driverName) != 0 {
		return GetError()
	}
	return nil
}

// AudioQuit (https://wiki.libsdl.org/SDL_AudioQuit)
func AudioQuit() {
	C.SDL_AudioQuit()
}

// GetCurrentAudioDriver (https://wiki.libsdl.org/SDL_GetCurrentAudioDriver)
func GetCurrentAudioDriver() string {
	return string(C.GoString(C.SDL_GetCurrentAudioDriver()))
}

// OpenAudio (https://wiki.libsdl.org/SDL_OpenAudio)
func OpenAudio(desired, obtained *AudioSpec) error {
	if C.SDL_OpenAudio(desired.cptr(), obtained.cptr()) != 0 {
		return GetError()
	}
	return nil
}

// GetNumAudioDevices (https://wiki.libsdl.org/SDL_GetNumAudioDevices)
func GetNumAudioDevices(isCapture bool) int {
	return int(C.SDL_GetNumAudioDevices(C.int(Btoi(isCapture))))
}

// GetAudioDeviceName (https://wiki.libsdl.org/SDL_GetAudioDeviceName)
func GetAudioDeviceName(index int, isCapture bool) string {
	return string(C.GoString(C.SDL_GetAudioDeviceName(C.int(index), C.int(Btoi(isCapture)))))
}

// OpenAudioDevice (https://wiki.libsdl.org/SDL_OpenAudioDevice)
func OpenAudioDevice(device string, isCapture bool, desired, obtained *AudioSpec, allowedChanges int) (AudioDeviceID, error) {
	_device := C.CString(device)
	if device == "" {
		_device = nil
	}
	defer C.free(unsafe.Pointer(_device))
	if id := AudioDeviceID(C.SDL_OpenAudioDevice(_device, C.int(Btoi(isCapture)), desired.cptr(), obtained.cptr(), C.int(allowedChanges))); id > 0 {
		return id, nil
	}
	return 0, GetError()
}

// GetAudioStatus (https://wiki.libsdl.org/SDL_GetAudioStatus)
func GetAudioStatus() AudioStatus {
	return (AudioStatus)(C.SDL_GetAudioStatus())
}

// GetAudioDeviceStatus (https://wiki.libsdl.org/SDL_GetAudioDeviceStatus)
func GetAudioDeviceStatus(dev AudioDeviceID) AudioStatus {
	return (AudioStatus)(C.SDL_GetAudioDeviceStatus(dev.c()))
}

// PauseAudio (https://wiki.libsdl.org/SDL_PauseAudio)
func PauseAudio(pauseOn bool) {
	C.SDL_PauseAudio(C.int(Btoi(pauseOn)))
}

// PauseAudioDevice (https://wiki.libsdl.org/SDL_PauseAudioDevice)
func PauseAudioDevice(dev AudioDeviceID, pauseOn bool) {
	C.SDL_PauseAudioDevice(dev.c(), C.int(Btoi(pauseOn)))
}

// LoadWAV_RW (https://wiki.libsdl.org/SDL_LoadWAV_RW)
func LoadWAV_RW(src *RWops, freeSrc bool, spec *AudioSpec) ([]byte, *AudioSpec) {
	var _audioBuf *C.Uint8
	var _audioLen C.Uint32
	audioSpec := (*AudioSpec)(unsafe.Pointer(C.SDL_LoadWAV_RW(src.cptr(), C.int(Btoi(freeSrc)), spec.cptr(), &_audioBuf, &_audioLen)))

	var b []byte
	sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b))
	sliceHeader.Len = (int)(_audioLen)
	sliceHeader.Cap = (int)(_audioLen)
	sliceHeader.Data = uintptr(unsafe.Pointer(_audioBuf))
	return b, audioSpec
}

// LoadWAV (https://wiki.libsdl.org/SDL_LoadWAV)
func LoadWAV(file string, spec *AudioSpec) ([]byte, *AudioSpec) {
	_file := C.CString(file)
	_rb := C.CString("rb")
	defer C.free(unsafe.Pointer(_file))
	defer C.free(unsafe.Pointer(_rb))

	var _audioBuf *C.Uint8
	var _audioLen C.Uint32
	audioSpec := (*AudioSpec)(unsafe.Pointer(C.SDL_LoadWAV_RW(C.SDL_RWFromFile(_file, _rb), 1, spec.cptr(), &_audioBuf, &_audioLen)))

	var b []byte
	sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b))
	sliceHeader.Len = (int)(_audioLen)
	sliceHeader.Cap = (int)(_audioLen)
	sliceHeader.Data = uintptr(unsafe.Pointer(_audioBuf))
	return b, audioSpec
}

// FreeWAV (https://wiki.libsdl.org/SDL_FreeWAV)
func FreeWAV(audioBuf []uint8) {
	sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&audioBuf))
	_audioBuf := (*C.Uint8)(unsafe.Pointer(sliceHeader.Data))
	C.SDL_FreeWAV(_audioBuf)
}

// BuildAudioCVT (https://wiki.libsdl.org/SDL_BuildAudioCVT)
func BuildAudioCVT(cvt *AudioCVT, srcFormat AudioFormat, srcChannels uint8, srcRate int, dstFormat AudioFormat, dstChannels uint8, dstRate int) (converted bool, err error) {
	switch int(C.SDL_BuildAudioCVT(cvt.cptr(), srcFormat.c(), C.Uint8(srcChannels), C.int(srcRate), dstFormat.c(), C.Uint8(dstChannels), C.int(dstRate))) {
	case 1:
		return true, nil
	case 0:
		return false, nil
	}
	return false, GetError()
}

// ConvertAudio (https://wiki.libsdl.org/SDL_ConvertAudio)
func ConvertAudio(cvt *AudioCVT) error {
	_cvt := (*C.SDL_AudioCVT)(unsafe.Pointer(cvt))
	if C.SDL_ConvertAudio(_cvt) != 0 {
		return GetError()
	}
	return nil
}

// MixAudio (https://wiki.libsdl.org/SDL_MixAudio)
func MixAudio(dst, src *uint8, len_ uint32, volume int) {
	_dst := (*C.Uint8)(unsafe.Pointer(dst))
	_src := (*C.Uint8)(unsafe.Pointer(src))
	C.SDL_MixAudio(_dst, _src, C.Uint32(len_), C.int(volume))
}

// MixAudioFormat (https://wiki.libsdl.org/SDL_MixAudioFormat)
func MixAudioFormat(dst, src *uint8, format AudioFormat, len_ uint32, volume int) {
	_dst := (*C.Uint8)(unsafe.Pointer(dst))
	_src := (*C.Uint8)(unsafe.Pointer(src))
	C.SDL_MixAudioFormat(_dst, _src, format.c(), C.Uint32(len_), C.int(volume))
}

// LockAudio (https://wiki.libsdl.org/SDL_LockAudio)
func LockAudio() {
	C.SDL_LockAudio()
}

// LockAudioDevice (https://wiki.libsdl.org/SDL_LockAudioDevice)
func LockAudioDevice(dev AudioDeviceID) {
	C.SDL_LockAudioDevice(dev.c())
}

// UnlockAudio (https://wiki.libsdl.org/SDL_UnlockAudio)
func UnlockAudio() {
	C.SDL_UnlockAudio()
}

// UnlockAudioDevice (https://wiki.libsdl.org/SDL_UnlockAudioDevice)
func UnlockAudioDevice(dev AudioDeviceID) {
	C.SDL_UnlockAudioDevice(dev.c())
}

// CloseAudio (https://wiki.libsdl.org/SDL_CloseAudio)
func CloseAudio() {
	C.SDL_CloseAudio()
}

// CloseAudioDevice (https://wiki.libsdl.org/SDL_CloseAudioDevice)
func CloseAudioDevice(dev AudioDeviceID) {
	C.SDL_CloseAudioDevice(dev.c())
}

/*
func AudioDeviceConnected(dev AudioDeviceID) int {
	_dev := (C.SDL_AudioDeviceID) (dev)
	return int (C.SDL_AudioDeviceConnected(_dev))
}
*/
<commit_msg>sdl: audio: add memory functions to operate on AudioCVT's Buf<commit_after>package sdl

// #include "sdl_wrapper.h"
import "C"
import (
	"reflect"
	"unsafe"
)

const (
	AUDIO_MASK_BITSIZE  = C.SDL_AUDIO_MASK_BITSIZE
	AUDIO_MASK_DATATYPE = C.SDL_AUDIO_MASK_DATATYPE
	AUDIO_MASK_ENDIAN   = C.SDL_AUDIO_MASK_ENDIAN
	AUDIO_MASK_SIGNED   = C.SDL_AUDIO_MASK_SIGNED
)

const (
	AUDIO_U8     = C.AUDIO_U8
	AUDIO_S8     = C.AUDIO_S8
	AUDIO_U16LSB = C.AUDIO_U16LSB
	AUDIO_S16LSB = C.AUDIO_S16LSB
	AUDIO_U16MSB = C.AUDIO_U16MSB
	AUDIO_S16MSB = C.AUDIO_S16MSB
	AUDIO_U16    = C.AUDIO_U16
	AUDIO_S16    = C.AUDIO_S16
	AUDIO_S32LSB = C.AUDIO_S32LSB
	AUDIO_S32MSB = C.AUDIO_S32MSB
	AUDIO_S32    = C.AUDIO_S32
	AUDIO_F32LSB = C.AUDIO_F32LSB
	AUDIO_F32MSB = C.AUDIO_F32MSB
	AUDIO_F32    = C.AUDIO_F32
	AUDIO_U16SYS = C.AUDIO_U16SYS
	AUDIO_S16SYS = C.AUDIO_S16SYS
	AUDIO_S32SYS = C.AUDIO_S32SYS
	AUDIO_F32SYS = C.AUDIO_F32SYS
)

const (
	AUDIO_ALLOW_FREQUENCY_CHANGE = C.SDL_AUDIO_ALLOW_FREQUENCY_CHANGE
	AUDIO_ALLOW_FORMAT_CHANGE    = C.SDL_AUDIO_ALLOW_FORMAT_CHANGE
	AUDIO_ALLOW_CHANNELS_CHANGE  = C.SDL_AUDIO_ALLOW_CHANNELS_CHANGE
	AUDIO_ALLOW_ANY_CHANGE       = C.SDL_AUDIO_ALLOW_ANY_CHANGE
)

const (
	AUDIO_STOPPED AudioStatus = C.SDL_AUDIO_STOPPED
	AUDIO_PLAYING             = C.SDL_AUDIO_PLAYING
	AUDIO_PAUSED              = C.SDL_AUDIO_PAUSED
)

const MIX_MAXVOLUME = C.SDL_MIX_MAXVOLUME

// AudioFormat (https://wiki.libsdl.org/SDL_AudioFormat)
type AudioFormat uint16
type AudioCallback C.SDL_AudioCallback
type AudioFilter C.SDL_AudioFilter
type AudioDeviceID uint32

// AudioStatus (https://wiki.libsdl.org/SDL_AudioStatus)
type AudioStatus uint32
type cAudioStatus C.SDL_AudioStatus

// AudioSpec (https://wiki.libsdl.org/SDL_AudioSpec)
type AudioSpec struct {
	Freq     int32
	Format   AudioFormat
	Channels uint8
	Silence  uint8
	Samples  uint16
	Padding  uint16
	Size     uint32
	Callback AudioCallback
	UserData unsafe.Pointer
}
type cAudioSpec C.SDL_AudioSpec

// AudioCVT (https://wiki.libsdl.org/SDL_AudioCVT)
type AudioCVT struct {
	Needed      int32
	SrcFormat   AudioFormat
	DstFormat   AudioFormat
	RateIncr    float64
	Buf         unsafe.Pointer // use AudioCVT.BufAsSlice() for access via Go slice
	Len         int32
	LenCVT      int32
	LenMult     int32
	LenRatio    float64
	filters     [10]AudioFilter // internal
	filterIndex int32           // internal
}
type cAudioCVT C.SDL_AudioCVT

func (fmt AudioFormat) c() C.SDL_AudioFormat {
	return C.SDL_AudioFormat(fmt)
}

func (id AudioDeviceID) c() C.SDL_AudioDeviceID {
	return C.SDL_AudioDeviceID(id)
}

func (as *AudioSpec) cptr() *C.SDL_AudioSpec {
	return (*C.SDL_AudioSpec)(unsafe.Pointer(as))
}

func (cvt *AudioCVT) cptr() *C.SDL_AudioCVT {
	return (*C.SDL_AudioCVT)(unsafe.Pointer(cvt))
}

func (format AudioFormat) BitSize() uint8 {
	return uint8(format & AUDIO_MASK_BITSIZE)
}

func (format AudioFormat) IsFloat() bool {
	return (format & AUDIO_MASK_DATATYPE) > 0
}

func (format AudioFormat) IsBigEndian() bool {
	return (format & AUDIO_MASK_ENDIAN) > 0
}

func (format AudioFormat) IsSigned() bool {
	return (format & AUDIO_MASK_SIGNED) > 0
}

func (format AudioFormat) IsInt() bool {
	return !format.IsFloat()
}

func (format AudioFormat) IsLittleEndian() bool {
	return !format.IsBigEndian()
}

func (format AudioFormat) IsUnsigned() bool {
	return !format.IsSigned()
}

func (cvt *AudioCVT) AllocBuf(size uintptr) {
	cvt.Buf = C.malloc(C.size_t(size))
}

func (cvt *AudioCVT) FreeBuf() {
	C.free(cvt.Buf)
}

// access AudioCVT.buf as slice.
// len(slice) will return converted audio buffer length
func (cvt AudioCVT) BufAsSlice() []byte {
	var b []byte
	sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b))
	sliceHeader.Len = int(cvt.LenCVT)
	sliceHeader.Cap = int(cvt.Len * cvt.LenMult)
	sliceHeader.Data = uintptr(unsafe.Pointer(cvt.Buf))
	return b
}

// GetNumAudioDrivers (https://wiki.libsdl.org/SDL_GetNumAudioDrivers)
func GetNumAudioDrivers() int {
	return int(C.SDL_GetNumAudioDrivers())
}

// GetAudioDriver (https://wiki.libsdl.org/SDL_GetAudioDriver)
func GetAudioDriver(index int) string {
	return string(C.GoString(C.SDL_GetAudioDriver(C.int(index))))
}

// AudioInit (https://wiki.libsdl.org/SDL_AudioInit)
func AudioInit(driverName string) error {
	_driverName := C.CString(driverName)
	defer C.free(unsafe.Pointer(_driverName))
	if C.SDL_AudioInit(_driverName) != 0 {
		return GetError()
	}
	return nil
}

// AudioQuit (https://wiki.libsdl.org/SDL_AudioQuit)
func AudioQuit() {
	C.SDL_AudioQuit()
}

// GetCurrentAudioDriver (https://wiki.libsdl.org/SDL_GetCurrentAudioDriver)
func GetCurrentAudioDriver() string {
	return string(C.GoString(C.SDL_GetCurrentAudioDriver()))
}

// OpenAudio (https://wiki.libsdl.org/SDL_OpenAudio)
func OpenAudio(desired, obtained *AudioSpec) error {
	if C.SDL_OpenAudio(desired.cptr(), obtained.cptr()) != 0 {
		return GetError()
	}
	return nil
}

// GetNumAudioDevices (https://wiki.libsdl.org/SDL_GetNumAudioDevices)
func GetNumAudioDevices(isCapture bool) int {
	return int(C.SDL_GetNumAudioDevices(C.int(Btoi(isCapture))))
}

// GetAudioDeviceName (https://wiki.libsdl.org/SDL_GetAudioDeviceName)
func GetAudioDeviceName(index int, isCapture bool) string {
	return string(C.GoString(C.SDL_GetAudioDeviceName(C.int(index), C.int(Btoi(isCapture)))))
}

// OpenAudioDevice (https://wiki.libsdl.org/SDL_OpenAudioDevice)
func OpenAudioDevice(device string, isCapture bool, desired, obtained *AudioSpec, allowedChanges int) (AudioDeviceID, error) {
	_device := C.CString(device)
	if device == "" {
		_device = nil
	}
	defer C.free(unsafe.Pointer(_device))
	if id := AudioDeviceID(C.SDL_OpenAudioDevice(_device, C.int(Btoi(isCapture)), desired.cptr(), obtained.cptr(), C.int(allowedChanges))); id > 0 {
		return id, nil
	}
	return 0, GetError()
}

// GetAudioStatus (https://wiki.libsdl.org/SDL_GetAudioStatus)
func GetAudioStatus() AudioStatus {
	return (AudioStatus)(C.SDL_GetAudioStatus())
}

// GetAudioDeviceStatus (https://wiki.libsdl.org/SDL_GetAudioDeviceStatus)
func GetAudioDeviceStatus(dev AudioDeviceID) AudioStatus {
	return (AudioStatus)(C.SDL_GetAudioDeviceStatus(dev.c()))
}

// PauseAudio (https://wiki.libsdl.org/SDL_PauseAudio)
func PauseAudio(pauseOn bool) {
	C.SDL_PauseAudio(C.int(Btoi(pauseOn)))
}

// PauseAudioDevice (https://wiki.libsdl.org/SDL_PauseAudioDevice)
func PauseAudioDevice(dev AudioDeviceID, pauseOn bool) {
	C.SDL_PauseAudioDevice(dev.c(), C.int(Btoi(pauseOn)))
}

// LoadWAV_RW (https://wiki.libsdl.org/SDL_LoadWAV_RW)
func LoadWAV_RW(src *RWops, freeSrc bool, spec *AudioSpec) ([]byte, *AudioSpec) {
	var _audioBuf *C.Uint8
	var _audioLen C.Uint32
	audioSpec := (*AudioSpec)(unsafe.Pointer(C.SDL_LoadWAV_RW(src.cptr(), C.int(Btoi(freeSrc)), spec.cptr(), &_audioBuf, &_audioLen)))

	var b []byte
	sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b))
	sliceHeader.Len = (int)(_audioLen)
	sliceHeader.Cap = (int)(_audioLen)
	sliceHeader.Data = uintptr(unsafe.Pointer(_audioBuf))
	return b, audioSpec
}

// LoadWAV (https://wiki.libsdl.org/SDL_LoadWAV)
func LoadWAV(file string, spec *AudioSpec) ([]byte, *AudioSpec) {
	_file := C.CString(file)
	_rb := C.CString("rb")
	defer C.free(unsafe.Pointer(_file))
	defer C.free(unsafe.Pointer(_rb))

	var _audioBuf *C.Uint8
	var _audioLen C.Uint32
	audioSpec := (*AudioSpec)(unsafe.Pointer(C.SDL_LoadWAV_RW(C.SDL_RWFromFile(_file, _rb), 1, spec.cptr(), &_audioBuf, &_audioLen)))

	var b []byte
	sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b))
	sliceHeader.Len = (int)(_audioLen)
	sliceHeader.Cap = (int)(_audioLen)
	sliceHeader.Data = uintptr(unsafe.Pointer(_audioBuf))
	return b, audioSpec
}

// FreeWAV (https://wiki.libsdl.org/SDL_FreeWAV)
func FreeWAV(audioBuf []uint8) {
	sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&audioBuf))
	_audioBuf := (*C.Uint8)(unsafe.Pointer(sliceHeader.Data))
	C.SDL_FreeWAV(_audioBuf)
}

// BuildAudioCVT (https://wiki.libsdl.org/SDL_BuildAudioCVT)
func BuildAudioCVT(cvt *AudioCVT, srcFormat AudioFormat, srcChannels uint8, srcRate int, dstFormat AudioFormat, dstChannels uint8, dstRate int) (converted bool, err error) {
	switch int(C.SDL_BuildAudioCVT(cvt.cptr(), srcFormat.c(), C.Uint8(srcChannels), C.int(srcRate), dstFormat.c(), C.Uint8(dstChannels), C.int(dstRate))) {
	case 1:
		return true, nil
	case 0:
		return false, nil
	}
	return false, GetError()
}

// ConvertAudio (https://wiki.libsdl.org/SDL_ConvertAudio)
func ConvertAudio(cvt *AudioCVT) error {
	_cvt := (*C.SDL_AudioCVT)(unsafe.Pointer(cvt))
	if C.SDL_ConvertAudio(_cvt) != 0 {
		return GetError()
	}
	return nil
}

// MixAudio (https://wiki.libsdl.org/SDL_MixAudio)
func MixAudio(dst, src *uint8, len_ uint32, volume int) {
	_dst := (*C.Uint8)(unsafe.Pointer(dst))
	_src := (*C.Uint8)(unsafe.Pointer(src))
	C.SDL_MixAudio(_dst, _src, C.Uint32(len_), C.int(volume))
}

// MixAudioFormat (https://wiki.libsdl.org/SDL_MixAudioFormat)
func MixAudioFormat(dst, src *uint8, format AudioFormat, len_ uint32, volume int) {
	_dst := (*C.Uint8)(unsafe.Pointer(dst))
	_src := (*C.Uint8)(unsafe.Pointer(src))
	C.SDL_MixAudioFormat(_dst, _src, format.c(), C.Uint32(len_), C.int(volume))
}

// LockAudio (https://wiki.libsdl.org/SDL_LockAudio)
func LockAudio() {
	C.SDL_LockAudio()
}

// LockAudioDevice (https://wiki.libsdl.org/SDL_LockAudioDevice)
func LockAudioDevice(dev AudioDeviceID) {
	C.SDL_LockAudioDevice(dev.c())
}

// UnlockAudio (https://wiki.libsdl.org/SDL_UnlockAudio)
func UnlockAudio() {
	C.SDL_UnlockAudio()
}

// UnlockAudioDevice (https://wiki.libsdl.org/SDL_UnlockAudioDevice)
func UnlockAudioDevice(dev AudioDeviceID) {
	C.SDL_UnlockAudioDevice(dev.c())
}

// CloseAudio (https://wiki.libsdl.org/SDL_CloseAudio)
func CloseAudio() {
	C.SDL_CloseAudio()
}

// CloseAudioDevice (https://wiki.libsdl.org/SDL_CloseAudioDevice)
func CloseAudioDevice(dev AudioDeviceID) {
	C.SDL_CloseAudioDevice(dev.c())
}

/*
func AudioDeviceConnected(dev AudioDeviceID) int {
	_dev := (C.SDL_AudioDeviceID) (dev)
	return int (C.SDL_AudioDeviceConnected(_dev))
}
*/
<|endoftext|>


<commit_before>package kcp

import (
	"crypto/sha1"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	_ "net/http/pprof"
	"sync"
	"testing"
	"time"

	"golang.org/x/crypto/pbkdf2"
)

const portEcho = "127.0.0.1:9999"
const portSink = "127.0.0.1:19999"
const portTinyBufferEcho = "127.0.0.1:29999"
const portListerner = "127.0.0.1:9998"
const salt = "kcptest"

var key = []byte("testkey")
var fec = 4
var pass = pbkdf2.Key(key, []byte(portSink), 4096, 32, sha1.New)

func init() {
	go func() {
		log.Println(http.ListenAndServe("localhost:6060", nil))
	}()

	go echoServer()
	go sinkServer()
	go tinyBufferEchoServer()
	println("beginning tests, encryption:salsa20, fec:10/3")
}

func dialEcho() (*UDPSession, error) {
	//block, _ := NewNoneBlockCrypt(pass)
	//block, _ := NewSimpleXORBlockCrypt(pass)
	//block, _ := NewTEABlockCrypt(pass[:16])
	//block, _ := NewAESBlockCrypt(pass)
	block, _ := NewSalsa20BlockCrypt(pass)
	sess, err := DialWithOptions(portEcho, block, 10, 3)
	if err != nil {
		panic(err)
	}

	sess.SetStreamMode(true)
	sess.SetStreamMode(false)
	sess.SetStreamMode(true)
	sess.SetWindowSize(4096, 4096)
	sess.SetReadBuffer(4 * 1024 * 1024)
	sess.SetWriteBuffer(4 * 1024 * 1024)
	sess.SetStreamMode(true)
	sess.SetNoDelay(1, 10, 2, 1)
	sess.SetMtu(1400)
	sess.SetMtu(1600)
	sess.SetMtu(1400)
	sess.SetACKNoDelay(true)
	sess.SetDeadline(time.Now().Add(time.Minute))
	return sess, err
}

func dialSink() (*UDPSession, error) {
	sess, err := DialWithOptions(portSink, nil, 0, 0)
	if err != nil {
		panic(err)
	}

	sess.SetStreamMode(true)
	sess.SetWindowSize(4096, 4096)
	sess.SetReadBuffer(4 * 1024 * 1024)
	sess.SetWriteBuffer(4 * 1024 * 1024)
	sess.SetStreamMode(true)
	sess.SetNoDelay(1, 10, 2, 1)
	sess.SetMtu(1400)
	sess.SetACKNoDelay(true)
	sess.SetDeadline(time.Now().Add(time.Minute))
	return sess, err
}

func dialTinyBufferEcho() (*UDPSession, error) {
	//block, _ := NewNoneBlockCrypt(pass)
	//block, _ := NewSimpleXORBlockCrypt(pass)
	//block, _ := NewTEABlockCrypt(pass[:16])
	//block, _ := NewAESBlockCrypt(pass)
	block, _ := NewSalsa20BlockCrypt(pass)
	sess, err := DialWithOptions(portTinyBufferEcho, block, 10, 3)
	if err != nil {
		panic(err)
	}
	return sess, err
}

//////////////////////////
func listenEcho() (net.Listener, error) {
	//block, _ := NewNoneBlockCrypt(pass)
	//block, _ := NewSimpleXORBlockCrypt(pass)
	//block, _ := NewTEABlockCrypt(pass[:16])
	//block, _ := NewAESBlockCrypt(pass)
	block, _ := NewSalsa20BlockCrypt(pass)
	return ListenWithOptions(portEcho, block, 10, 3)
}
func listenTinyBufferEcho() (net.Listener, error) {
	//block, _ := NewNoneBlockCrypt(pass)
	//block, _ := NewSimpleXORBlockCrypt(pass)
	//block, _ := NewTEABlockCrypt(pass[:16])
	//block, _ := NewAESBlockCrypt(pass)
	block, _ := NewSalsa20BlockCrypt(pass)
	return ListenWithOptions(portTinyBufferEcho, block, 10, 3)
}

func listenSink() (net.Listener, error) {
	return ListenWithOptions(portSink, nil, 0, 0)
}

func echoServer() {
	l, err := listenEcho()
	if err != nil {
		panic(err)
	}

	go func() {
		kcplistener := l.(*Listener)
		kcplistener.SetReadBuffer(4 * 1024 * 1024)
		kcplistener.SetWriteBuffer(4 * 1024 * 1024)
		kcplistener.SetDSCP(46)
		for {
			s, err := l.Accept()
			if err != nil {
				return
			}

			// coverage test
			s.(*UDPSession).SetReadBuffer(4 * 1024 * 1024)
			s.(*UDPSession).SetWriteBuffer(4 * 1024 * 1024)
			go handleEcho(s.(*UDPSession))
		}
	}()
}

func sinkServer() {
	l, err := listenSink()
	if err != nil {
		panic(err)
	}

	go func() {
		kcplistener := l.(*Listener)
		kcplistener.SetReadBuffer(4 * 1024 * 1024)
		kcplistener.SetWriteBuffer(4 * 1024 * 1024)
		kcplistener.SetDSCP(46)
		for {
			s, err := l.Accept()
			if err != nil {
				return
			}

			go handleSink(s.(*UDPSession))
		}
	}()
}

func tinyBufferEchoServer() {
	l, err := listenTinyBufferEcho()
	if err != nil {
		panic(err)
	}

	go func() {
		for {
			s, err := l.Accept()
			if err != nil {
				return
			}
			go handleTinyBufferEcho(s.(*UDPSession))
		}
	}()
}

///////////////////////////

func handleEcho(conn *UDPSession) {
	conn.SetStreamMode(true)
	conn.SetWindowSize(4096, 4096)
	conn.SetNoDelay(1, 10, 2, 1)
	conn.SetDSCP(46)
	conn.SetMtu(1400)
	conn.SetACKNoDelay(false)
	conn.SetReadDeadline(time.Now().Add(time.Hour))
	conn.SetWriteDeadline(time.Now().Add(time.Hour))
	buf := make([]byte, 65536)
	for {
		n, err := conn.Read(buf)
		if err != nil {
			panic(err)
		}
		conn.Write(buf[:n])
	}
}

func handleSink(conn *UDPSession) {
	conn.SetStreamMode(true)
	conn.SetWindowSize(4096, 4096)
	conn.SetNoDelay(1, 10, 2, 1)
	conn.SetDSCP(46)
	conn.SetMtu(1400)
	conn.SetACKNoDelay(false)
	conn.SetReadDeadline(time.Now().Add(time.Hour))
	conn.SetWriteDeadline(time.Now().Add(time.Hour))
	buf := make([]byte, 65536)
	for {
		_, err := conn.Read(buf)
		if err != nil {
			panic(err)
		}
	}
}

func handleTinyBufferEcho(conn *UDPSession) {
	conn.SetStreamMode(true)
	buf := make([]byte, 2)
	for {
		n, err := conn.Read(buf)
		if err != nil {
			panic(err)
		}
		conn.Write(buf[:n])
	}
}

///////////////////////////

func TestTimeout(t *testing.T) {
	cli, err := dialEcho()
	if err != nil {
		panic(err)
	}
	buf := make([]byte, 10)

	//timeout
	cli.SetDeadline(time.Now().Add(time.Second))
	<-time.After(2 * time.Second)
	n, err := cli.Read(buf)
	if n != 0 || err == nil {
		t.Fail()
	}
	cli.Close()
}

func TestSendRecv(t *testing.T) {
	cli, err := dialEcho()
	if err != nil {
		panic(err)
	}
	cli.SetWriteDelay(true)
	cli.SetDUP(1)
	const N = 100
	buf := make([]byte, 10)
	for i := 0; i < N; i++ {
		msg := fmt.Sprintf("hello%v", i)
		cli.Write([]byte(msg))
		if n, err := cli.Read(buf); err == nil {
			if string(buf[:n]) != msg {
				t.Fail()
			}
		} else {
			panic(err)
		}
	}
	cli.Close()
}

func TestTinyBufferReceiver(t *testing.T) {
	cli, err := dialTinyBufferEcho()
	if err != nil {
		panic(err)
	}
	const N = 100
	snd := byte(0)
	fillBuffer := func(buf []byte) {
		for i := 0; i < len(buf); i++ {
			buf[i] = snd
			snd++
		}
	}

	rcv := byte(0)
	check := func(buf []byte) bool {
		for i := 0; i < len(buf); i++ {
			if buf[i] != rcv {
				return false
			}
			rcv++
		}
		return true
	}
	sndbuf := make([]byte, 7)
	rcvbuf := make([]byte, 7)
	for i := 0; i < N; i++ {
		fillBuffer(sndbuf)
		cli.Write(sndbuf)
		if n, err := io.ReadFull(cli, rcvbuf); err == nil {
			if !check(rcvbuf[:n]) {
				t.Fail()
			}
		} else {
			panic(err)
		}
	}
	cli.Close()
}

func TestClose(t *testing.T) {
	cli, err := dialEcho()
	if err != nil {
		panic(err)
	}
	buf := make([]byte, 10)

	cli.Close()
	if cli.Close() == nil {
		t.Fail()
	}
	n, err := cli.Write(buf)
	if n != 0 || err == nil {
		t.Fail()
	}
	n, err = cli.Read(buf)
	if n != 0 || err == nil {
		t.Fail()
	}
	cli.Close()
}

func TestParallel1024CLIENT_64BMSG_64CNT(t *testing.T) {
	var wg sync.WaitGroup
	wg.Add(1024)
	for i := 0; i < 1024; i++ {
		go parallel_client(&wg)
	}
	wg.Wait()
}

func parallel_client(wg *sync.WaitGroup) (err error) {
	cli, err := dialEcho()
	if err != nil {
		panic(err)
	}

	err = echo_tester(cli, 64, 64)
	wg.Done()
	return
}

func BenchmarkEchoSpeed4K(b *testing.B) {
	speedclient(b, 4096)
}

func BenchmarkEchoSpeed64K(b *testing.B) {
	speedclient(b, 65536)
}

func BenchmarkEchoSpeed512K(b *testing.B) {
	speedclient(b, 524288)
}

func BenchmarkEchoSpeed1M(b *testing.B) {
	speedclient(b, 1048576)
}

func speedclient(b *testing.B, nbytes int) {
	b.ReportAllocs()
	cli, err := dialEcho()
	if err != nil {
		panic(err)
	}

	if err := echo_tester(cli, nbytes, b.N); err != nil {
		b.Fail()
	}
	b.SetBytes(int64(nbytes))
}

func BenchmarkSinkSpeed4K(b *testing.B) {
	sinkclient(b, 4096)
}

func BenchmarkSinkSpeed64K(b *testing.B) {
	sinkclient(b, 65536)
}

func BenchmarkSinkSpeed256K(b *testing.B) {
	sinkclient(b, 524288)
}

func BenchmarkSinkSpeed1M(b *testing.B) {
	sinkclient(b, 1048576)
}

func sinkclient(b *testing.B, nbytes int) {
	b.ReportAllocs()
	cli, err := dialSink()
	if err != nil {
		panic(err)
	}

	sink_tester(cli, nbytes, b.N)
	b.SetBytes(int64(nbytes))
}

func echo_tester(cli net.Conn, msglen, msgcount int) error {
	buf := make([]byte, msglen)
	for i := 0; i < msgcount; i++ {
		// send packet
		if _, err := cli.Write(buf); err != nil {
			return err
		}

		// receive packet
		nrecv := 0
		for {
			n, err := cli.Read(buf)
			if err != nil {
				return err
			} else {
				nrecv += n
				if nrecv == msglen {
					break
				}
			}
		}
	}
	return nil
}

func sink_tester(cli *UDPSession, msglen, msgcount int) error {
	// sender
	buf := make([]byte, msglen)
	for i := 0; i < msgcount; i++ {
		if _, err := cli.Write(buf); err != nil {
			return err
		}
	}
	return nil
}

func TestSNMP(t *testing.T) {
	t.Log(DefaultSnmp.Copy())
	t.Log(DefaultSnmp.Header())
	t.Log(DefaultSnmp.ToSlice())
	DefaultSnmp.Reset()
	t.Log(DefaultSnmp.ToSlice())
}

func TestListenerClose(t *testing.T) {
	l, err := ListenWithOptions(portListerner, nil, 10, 3)
	if err != nil {
		t.Fail()
	}
	l.SetReadDeadline(time.Now().Add(time.Second))
	l.SetWriteDeadline(time.Now().Add(time.Second))
	l.SetDeadline(time.Now().Add(time.Second))
	time.Sleep(2 * time.Second)
	if _, err := l.Accept(); err == nil {
		t.Fail()
	}

	l.Close()
	fakeaddr, _ := net.ResolveUDPAddr("udp6", "127.0.0.1:1111")
	if l.closeSession(fakeaddr) {
		t.Fail()
	}
}
<commit_msg>Fix tests<commit_after>package kcp

import (
	"crypto/sha1"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	_ "net/http/pprof"
	"sync"
	"testing"
	"time"

	"golang.org/x/crypto/pbkdf2"
)

const portEcho = "127.0.0.1:9999"
const portSink = "127.0.0.1:19999"
const portTinyBufferEcho = "127.0.0.1:29999"
const portListerner = "127.0.0.1:9998"
const salt = "kcptest"

var key = []byte("testkey")
var fec = 4
var pass = pbkdf2.Key(key, []byte(portSink), 4096, 32, sha1.New)

func init() {
	go func() {
		log.Println(http.ListenAndServe("localhost:6060", nil))
	}()

	go echoServer()
	go sinkServer()
	go tinyBufferEchoServer()
	println("beginning tests, encryption:salsa20, fec:10/3")
}

func dialEcho() (*UDPSession, error) {
	//block, _ := NewNoneBlockCrypt(pass)
	//block, _ := NewSimpleXORBlockCrypt(pass)
	//block, _ := NewTEABlockCrypt(pass[:16])
	//block, _ := NewAESBlockCrypt(pass)
	block, _ := NewSalsa20BlockCrypt(pass)
	sess, err := DialWithOptions(portEcho, block, 10, 3)
	if err != nil {
		panic(err)
	}

	sess.SetStreamMode(true)
	sess.SetStreamMode(false)
	sess.SetStreamMode(true)
	sess.SetWindowSize(4096, 4096)
	sess.SetReadBuffer(4 * 1024 * 1024)
	sess.SetWriteBuffer(4 * 1024 * 1024)
	sess.SetStreamMode(true)
	sess.SetNoDelay(1, 10, 2, 1)
	sess.SetMtu(1400)
	sess.SetMtu(1600)
	sess.SetMtu(1400)
	sess.SetACKNoDelay(true)
	sess.SetDeadline(time.Now().Add(time.Minute))
	return sess, err
}

func dialSink() (*UDPSession, error) {
	sess, err := DialWithOptions(portSink, nil, 0, 0)
	if err != nil {
		panic(err)
	}

	sess.SetStreamMode(true)
	sess.SetWindowSize(4096, 4096)
	sess.SetReadBuffer(4 * 1024 * 1024)
	sess.SetWriteBuffer(4 * 1024 * 1024)
	sess.SetStreamMode(true)
	sess.SetNoDelay(1, 10, 2, 1)
	sess.SetMtu(1400)
	sess.SetACKNoDelay(true)
	sess.SetDeadline(time.Now().Add(time.Minute))
	return sess, err
}

func dialTinyBufferEcho() (*UDPSession, error) {
	//block, _ := NewNoneBlockCrypt(pass)
	//block, _ := NewSimpleXORBlockCrypt(pass)
	//block, _ := NewTEABlockCrypt(pass[:16])
	//block, _ := NewAESBlockCrypt(pass)
	block, _ := NewSalsa20BlockCrypt(pass)
	sess, err := DialWithOptions(portTinyBufferEcho, block, 10, 3)
	if err != nil {
		panic(err)
	}
	return sess, err
}

//////////////////////////
func listenEcho() (net.Listener, error) {
	//block, _ := NewNoneBlockCrypt(pass)
	//block, _ := NewSimpleXORBlockCrypt(pass)
	//block, _ := NewTEABlockCrypt(pass[:16])
	//block, _ := NewAESBlockCrypt(pass)
	block, _ := NewSalsa20BlockCrypt(pass)
	return ListenWithOptions(portEcho, block, 10, 3)
}
func listenTinyBufferEcho() (net.Listener, error) {
	//block, _ := NewNoneBlockCrypt(pass)
	//block, _ := NewSimpleXORBlockCrypt(pass)
	//block, _ := NewTEABlockCrypt(pass[:16])
	//block, _ := NewAESBlockCrypt(pass)
	block, _ := NewSalsa20BlockCrypt(pass)
	return ListenWithOptions(portTinyBufferEcho, block, 10, 3)
}

func listenSink() (net.Listener, error) {
	return ListenWithOptions(portSink, nil, 0, 0)
}

func echoServer() {
	l, err := listenEcho()
	if err != nil {
		panic(err)
	}

	go func() {
		kcplistener := l.(*Listener)
		kcplistener.SetReadBuffer(4 * 1024 * 1024)
		kcplistener.SetWriteBuffer(4 * 1024 * 1024)
		kcplistener.SetDSCP(46)
		for {
			s, err := l.Accept()
			if err != nil {
				return
			}

			// coverage test
			s.(*UDPSession).SetReadBuffer(4 * 1024 * 1024)
			s.(*UDPSession).SetWriteBuffer(4 * 1024 * 1024)
			go handleEcho(s.(*UDPSession))
		}
	}()
}

func sinkServer() {
	l, err := listenSink()
	if err != nil {
		panic(err)
	}

	go func() {
		kcplistener := l.(*Listener)
		kcplistener.SetReadBuffer(4 * 1024 * 1024)
		kcplistener.SetWriteBuffer(4 * 1024 * 1024)
		kcplistener.SetDSCP(46)
		for {
			s, err := l.Accept()
			if err != nil {
				return
			}

			go handleSink(s.(*UDPSession))
		}
	}()
}

func tinyBufferEchoServer() {
	l, err := listenTinyBufferEcho()
	if err != nil {
		panic(err)
	}

	go func() {
		for {
			s, err := l.Accept()
			if err != nil {
				return
			}
			go handleTinyBufferEcho(s.(*UDPSession))
		}
	}()
}

///////////////////////////

func handleEcho(conn *UDPSession) {
	conn.SetStreamMode(true)
	conn.SetWindowSize(4096, 4096)
	conn.SetNoDelay(1, 10, 2, 1)
	conn.SetDSCP(46)
	conn.SetMtu(1400)
	conn.SetACKNoDelay(false)
	conn.SetReadDeadline(time.Now().Add(time.Hour))
	conn.SetWriteDeadline(time.Now().Add(time.Hour))
	buf := make([]byte, 65536)
	for {
		n, err := conn.Read(buf)
		if err != nil {
			panic(err)
		}
		conn.Write(buf[:n])
	}
}

func handleSink(conn *UDPSession) {
	conn.SetStreamMode(true)
	conn.SetWindowSize(4096, 4096)
	conn.SetNoDelay(1, 10, 2, 1)
	conn.SetDSCP(46)
	conn.SetMtu(1400)
	conn.SetACKNoDelay(false)
	conn.SetReadDeadline(time.Now().Add(time.Hour))
	conn.SetWriteDeadline(time.Now().Add(time.Hour))
	buf := make([]byte, 65536)
	for {
		_, err := conn.Read(buf)
		if err != nil {
			panic(err)
		}
	}
}

func handleTinyBufferEcho(conn *UDPSession) {
	conn.SetStreamMode(true)
	buf := make([]byte, 2)
	for {
		n, err := conn.Read(buf)
		if err != nil {
			panic(err)
		}
		conn.Write(buf[:n])
	}
}

///////////////////////////

func TestTimeout(t *testing.T) {
	cli, err := dialEcho()
	if err != nil {
		panic(err)
	}
	buf := make([]byte, 10)

	//timeout
	cli.SetDeadline(time.Now().Add(time.Second))
	<-time.After(2 * time.Second)
	n, err := cli.Read(buf)
	if n != 0 || err == nil {
		t.Fail()
	}
	cli.Close()
}

func TestSendRecv(t *testing.T) {
	cli, err := dialEcho()
	if err != nil {
		panic(err)
	}
	cli.SetWriteDelay(true)
	cli.SetDUP(1)
	const N = 100
	buf := make([]byte, 10)
	for i := 0; i < N; i++ {
		msg := fmt.Sprintf("hello%v", i)
		cli.Write([]byte(msg))
		if n, err := cli.Read(buf); err == nil {
			if string(buf[:n]) != msg {
				t.Fail()
			}
		} else {
			panic(err)
		}
	}
	cli.Close()
}

func TestTinyBufferReceiver(t *testing.T) {
	cli, err := dialTinyBufferEcho()
	if err != nil {
		panic(err)
	}
	const N = 100
	snd := byte(0)
	fillBuffer := func(buf []byte) {
		for i := 0; i < len(buf); i++ {
			buf[i] = snd
			snd++
		}
	}

	rcv := byte(0)
	check := func(buf []byte) bool {
		for i := 0; i < len(buf); i++ {
			if buf[i] != rcv {
				return false
			}
			rcv++
		}
		return true
	}
	sndbuf := make([]byte, 7)
	rcvbuf := make([]byte, 7)
	for i := 0; i < N; i++ {
		fillBuffer(sndbuf)
		cli.Write(sndbuf)
		if n, err := io.ReadFull(cli, rcvbuf); err == nil {
			if !check(rcvbuf[:n]) {
				t.Fail()
			}
		} else {
			panic(err)
		}
	}
	cli.Close()
}

func TestClose(t *testing.T) {
	cli, err := dialEcho()
	if err != nil {
		panic(err)
	}
	buf := make([]byte, 10)

	cli.Close()
	if cli.Close() == nil {
		t.Fail()
	}
	n, err := cli.Write(buf)
	if n != 0 || err == nil {
		t.Fail()
	}
	n, err = cli.Read(buf)
	if n != 0 || err == nil {
		t.Fail()
	}
	cli.Close()
}

func TestParallel1024CLIENT_64BMSG_64CNT(t *testing.T) {
	var wg sync.WaitGroup
	wg.Add(1024)
	for i := 0; i < 1024; i++ {
		go parallel_client(&wg)
	}
	wg.Wait()
}

func parallel_client(wg *sync.WaitGroup) (err error) {
	cli, err := dialEcho()
	if err != nil {
		panic(err)
	}

	err = echo_tester(cli, 64, 64)
	wg.Done()
	return
}

func BenchmarkEchoSpeed4K(b *testing.B) {
	speedclient(b, 4096)
}

func BenchmarkEchoSpeed64K(b *testing.B) {
	speedclient(b, 65536)
}

func BenchmarkEchoSpeed512K(b *testing.B) {
	speedclient(b, 524288)
}

func BenchmarkEchoSpeed1M(b *testing.B) {
	speedclient(b, 1048576)
}

func speedclient(b *testing.B, nbytes int) {
	b.ReportAllocs()
	cli, err := dialEcho()
	if err != nil {
		panic(err)
	}

	if err := echo_tester(cli, nbytes, b.N); err != nil {
		b.Fail()
	}
	b.SetBytes(int64(nbytes))
}

func BenchmarkSinkSpeed4K(b *testing.B) {
	sinkclient(b, 4096)
}

func BenchmarkSinkSpeed64K(b *testing.B) {
	sinkclient(b, 65536)
}

func BenchmarkSinkSpeed256K(b *testing.B) {
	sinkclient(b, 524288)
}

func BenchmarkSinkSpeed1M(b *testing.B) {
	sinkclient(b, 1048576)
}

func sinkclient(b *testing.B, nbytes int) {
	b.ReportAllocs()
	cli, err := dialSink()
	if err != nil {
		panic(err)
	}

	sink_tester(cli, nbytes, b.N)
	b.SetBytes(int64(nbytes))
}

func echo_tester(cli net.Conn, msglen, msgcount int) error {
	buf := make([]byte, msglen)
	for i := 0; i < msgcount; i++ {
		// send packet
		if _, err := cli.Write(buf); err != nil {
			return err
		}

		// receive packet
		nrecv := 0
		for {
			n, err := cli.Read(buf)
			if err != nil {
				return err
			} else {
				nrecv += n
				if nrecv == msglen {
					break
				}
			}
		}
	}
	return nil
}

func sink_tester(cli *UDPSession, msglen, msgcount int) error {
	// sender
	buf := make([]byte, msglen)
	for i := 0; i < msgcount; i++ {
		if _, err := cli.Write(buf); err != nil {
			return err
		}
	}
	return nil
}

func TestSNMP(t *testing.T) {
	t.Log(DefaultSnmp.Copy())
	t.Log(DefaultSnmp.Header())
	t.Log(DefaultSnmp.ToSlice())
	DefaultSnmp.Reset()
	t.Log(DefaultSnmp.ToSlice())
}

func TestListenerClose(t *testing.T) {
	l, err := ListenWithOptions(portListerner, nil, 10, 3)
	if err != nil {
		t.Fail()
	}
	l.SetReadDeadline(time.Now().Add(time.Second))
	l.SetWriteDeadline(time.Now().Add(time.Second))
	l.SetDeadline(time.Now().Add(time.Second))
	time.Sleep(2 * time.Second)
	if _, err := l.Accept(); err == nil {
		t.Fail()
	}

	l.Close()
	if l.closeSession("127.0.0.1:1111/1234") {
		t.Fail()
	}
}
<|endoftext|>


<commit_before>package libxml2

/* Serialize produces serialization of the document, canonicalized.
 * Below document is taken from libxml2 directly. Pay special attention
 * to the required settings when parsing the document to be canonicalized.
 *
 * ---
 * Canonical form of an XML document could be created if and only if
 *  a) default attributes (if any) are added to all nodes
 *  b) all character and parsed entity references are resolved
 * In order to achive this in libxml2 the document MUST be loaded with
 * following global setings:
 *
 *    xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
 *    xmlSubstituteEntitiesDefault(1);
 *
 * or corresponding parser context setting:
 *    xmlParserCtxtPtr ctxt;
 *
 *    ...
 *    ctxt->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
 *    ctxt->replaceEntities = 1;
 *    ...
 * ---
 *
 * In go-libxml2, this translates to:
 *
 *    options = XMLParserDTDLoad | XMLParserDTDAttr | XMLParserNoEnt
 *
 */
func (s C14NSerialize) Serialize(n interface{}) (string, error) {
	switch n.(type) {
	case *Document:
	default:
		return "", ErrInvalidNodeType
	}

	return xmlC14NDocDumpMemory(n.(*Document), s.Mode, s.WithComments)
}
<commit_msg>golint<commit_after>package libxml2

// Serialize produces serialization of the document, canonicalized.
func (s C14NSerialize) Serialize(n interface{}) (string, error) {
	/*
	 * Below document is taken from libxml2 directly. Pay special attention
	 * to the required settings when parsing the document to be canonicalized.
	 *
	 * ---
	 * Canonical form of an XML document could be created if and only if
	 *  a) default attributes (if any) are added to all nodes
	 *  b) all character and parsed entity references are resolved
	 * In order to achive this in libxml2 the document MUST be loaded with
	 * following global setings:
	 *
	 *    xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
	 *    xmlSubstituteEntitiesDefault(1);
	 *
	 * or corresponding parser context setting:
	 *    xmlParserCtxtPtr ctxt;
	 *
	 *    ...
	 *    ctxt->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
	 *    ctxt->replaceEntities = 1;
	 *    ...
	 * ---
	 *
	 * In go-libxml2, this translates to:
	 *
	 *    options = XMLParserDTDLoad | XMLParserDTDAttr | XMLParserNoEnt
	 *
	 */
	switch n.(type) {
	case *Document:
	default:
		return "", ErrInvalidNodeType
	}

	return xmlC14NDocDumpMemory(n.(*Document), s.Mode, s.WithComments)
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"

	"github.com/gorilla/websocket"
	"github.com/lexicality/vending/shared"
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	// Disable security until I sort out the client
	CheckOrigin: func(r *http.Request) bool { return true },
}

func wsWriteLoop(conn *shared.WSConn) {

	msgChan := messageSub()
	pingChang := conn.GetPingTicker()

	var msg string
	var err error
	var ok bool
	for {
		select {
		case msg, ok = <-msgChan:
			if !ok {
				log.Info("Killing connection due to channel closure")
				return
			} else if !conn.IsOpen() {
				log.Debug("Killing write loop due to connection closure")
				return
			}

			err = conn.WriteMessage(websocket.TextMessage, []byte(msg))
			if err != nil {
				log.Error("Unable to send message %s: %s", msg, err)
				return
			}
		case _ = <-pingChang:
			if conn.IsTimingOut() {
				log.Info("Killing connection due to timeout")
				return
			} else if !conn.IsOpen() {
				log.Debug("Killing write loop due to connection closure")
				return
			}

			err = conn.MaybeSendPing()
			if err != nil {
				log.Error("Unable to send ping: %s", err)
				return
			}
		}
	}
}

func wsHandler(w http.ResponseWriter, r *http.Request) {
	log.Notice("Connection attempt begining")
	var err error

	c, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Error(err)
		w.WriteHeader(500)
		w.Write([]byte(err.Error()))
		return
	}
	var conn = shared.NewWSConn(c)
	defer c.Close()

	_ = conn.WriteMessage(websocket.TextMessage, []byte("hi!"))

	// Ignore anything the client has to say
	go conn.ReadDiscardPump()
	// Tell them all the important things we have to say
	wsWriteLoop(conn)
}
<commit_msg>Fix debugging<commit_after>package main

import (
	"net/http"

	"github.com/gorilla/websocket"
	"github.com/lexicality/vending/shared"
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	// Disable security until I sort out the client
	CheckOrigin: func(r *http.Request) bool { return true },
}

func wsWriteLoop(conn *shared.WSConn) {

	msgChan := messageSub()
	pingChang := conn.GetPingTicker()

	var msg string
	var err error
	var ok bool
	for {
		select {
		case msg, ok = <-msgChan:
			if !ok {
				log.Info("Killing connection due to channel closure")
				return
			} else if !conn.IsOpen() {
				log.Debug("Killing write loop due to connection closure")
				return
			}

			err = conn.WriteMessage(websocket.TextMessage, []byte(msg))
			if err != nil {
				log.Errorf("Unable to send message %s: %s", msg, err)
				return
			}
		case _ = <-pingChang:
			if conn.IsTimingOut() {
				log.Info("Killing connection due to timeout")
				return
			} else if !conn.IsOpen() {
				log.Debug("Killing write loop due to connection closure")
				return
			}

			err = conn.MaybeSendPing()
			if err != nil {
				log.Errorf("Unable to send ping: %s", err)
				return
			}
		}
	}
}

func wsHandler(w http.ResponseWriter, r *http.Request) {
	log.Notice("Connection attempt begining")
	var err error

	c, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Error(err)
		w.WriteHeader(500)
		w.Write([]byte(err.Error()))
		return
	}
	var conn = shared.NewWSConn(c)
	defer c.Close()

	_ = conn.WriteMessage(websocket.TextMessage, []byte("hi!"))

	// Ignore anything the client has to say
	go conn.ReadDiscardPump()
	// Tell them all the important things we have to say
	wsWriteLoop(conn)
}
<|endoftext|>


<commit_before>package senso

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

	"github.com/cenkalti/backoff"
	"github.com/sirupsen/logrus"
)

// TODO: implement a backoff strategy
const dialTimeout = 1 * time.Second

// connectTCP creates a persistent tcp connection to address
func connectTCP(ctx context.Context, baseLogger *logrus.Entry, address string, data chan []byte) {
	var dialer net.Dialer

	var log = baseLogger.WithField("address", address)

	// attempt to open a new connection
	dialer.Deadline = time.Now().Add(dialTimeout)

	var conn net.Conn
	dialTCP := func() error {
		var connErr error
		log.Info("dialing")
		conn, connErr = dialer.DialContext(ctx, "tcp", address)
		if connErr != nil {
			log.WithError(connErr).Info("Dial error")
		}
		return connErr
	}

	connErr := backoff.Retry(dialTCP, backoff.NewExponentialBackOff())

	for connErr == nil {

		log.Info("connected")

		// Close connection if we break or return
		defer conn.Close()

		// create channel for reading data and go read
		readChannel := make(chan []byte)
		go tcpReader(log, conn, readChannel)

		// create channel for writing data
		writeChannel := make(chan []byte)
		// We need an additional channel for handling write errors, unlike the readChannel we don't want to close the channel as somebody might try to write to it
		writeErrors := make(chan error)
		defer close(writeChannel)
		go tcpWriter(conn, writeChannel, writeErrors)

		// Inner loop for handling data
		disconnected := false
		for !disconnected {
			select {

			case <-ctx.Done():
				return

			case receivedData, more := <-readChannel:
				if more {
					// Attempt to send data, if can not send immediately discard
					select {
					case data <- receivedData:
					default:
					}
				} else {
					disconnected = true
				}

			case dataToWrite := <-data:
				writeChannel <- dataToWrite

			case writeError := <-writeErrors:
				if err, ok := writeError.(net.Error); ok && err.Timeout() {
					log.Debug("timeout on write")
				} else {
					log.WithError(writeError).Error("write error")
					disconnected = true
				}
			}
		}

		// Check if connection has been cancelled
		select {
		case <-ctx.Done():
			return

		default:
			log.Debug("reconnecting")
			conn.Close()
			connErr = backoff.Retry(dialTCP, backoff.NewExponentialBackOff())
		}

	}
}

// Helper to read from TCP connection
func tcpReader(log *logrus.Entry, conn net.Conn, channel chan<- []byte) {

	defer close(channel)

	buffer := make([]byte, 1024)

	// Loop and read from connection.
	for {
		// conn.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
		readN, readErr := conn.Read(buffer)

		if readErr != nil {
			if readErr == io.EOF {
				// connection is closed
				log.Info("connection closed")
				return
			} else if err, ok := readErr.(net.Error); ok && err.Timeout() {
				// Read timeout, just continue Nothing
			} else {
				log.WithError(readErr).Error("read error")
				return
			}
		} else {
			channel <- buffer[:readN]
		}
	}
}

// Helper to write to TCP connection. Note that this requires an additional channel to report errors
func tcpWriter(conn net.Conn, channel <-chan []byte, errorChannel chan<- error) {
	for {

		dataToWrite, more := <-channel

		if more {

			if conn != nil {
				conn.SetWriteDeadline(time.Now().Add(50 * time.Millisecond))
				_, err := conn.Write(dataToWrite)

				if err != nil {
					errorChannel <- err
				}

			} else {
				errorChannel <- errors.New("not connected, can not write to TCP connection")
			}

		} else {
			return
		}

	}
}
<commit_msg>fix timeout on TCP reconnect<commit_after>package senso

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

	"github.com/cenkalti/backoff"
	"github.com/sirupsen/logrus"
)

// TODO: implement a backoff strategy
const dialTimeout = 1 * time.Second

// connectTCP creates a persistent tcp connection to address
func connectTCP(ctx context.Context, baseLogger *logrus.Entry, address string, data chan []byte) {
	var dialer net.Dialer

	var log = baseLogger.WithField("address", address)

	var conn net.Conn
	dialTCP := func() error {
		// attempt to open a new connection
		dialer.Deadline = time.Now().Add(dialTimeout)
		var connErr error
		log.Info("dialing")
		if conn != nil {
			conn.Close()
		}

		conn, connErr = dialer.DialContext(ctx, "tcp", address)
		if connErr != nil {
			log.WithError(connErr).Info("Dial error")
		}
		return connErr
	}

	connErr := backoff.Retry(dialTCP, backoff.NewExponentialBackOff())

	for connErr == nil {

		log.Info("connected")

		// Close connection if we break or return
		defer conn.Close()

		// create channel for reading data and go read
		readChannel := make(chan []byte)
		go tcpReader(log, conn, readChannel)

		// create channel for writing data
		writeChannel := make(chan []byte)
		// We need an additional channel for handling write errors, unlike the readChannel we don't want to close the channel as somebody might try to write to it
		writeErrors := make(chan error)
		defer close(writeChannel)
		go tcpWriter(conn, writeChannel, writeErrors)

		// Inner loop for handling data
		disconnected := false
		for !disconnected {
			select {

			case <-ctx.Done():
				return

			case receivedData, more := <-readChannel:
				if more {
					// Attempt to send data, if can not send immediately discard
					select {
					case data <- receivedData:
					default:
					}
				} else {
					disconnected = true
				}

			case dataToWrite := <-data:
				writeChannel <- dataToWrite

			case writeError := <-writeErrors:
				if err, ok := writeError.(net.Error); ok && err.Timeout() {
					log.Debug("timeout on write")
				} else {
					log.WithError(writeError).Error("write error")
					disconnected = true
				}
			}
		}

		// Check if connection has been cancelled
		select {
		case <-ctx.Done():
			return

		default:
			log.Debug("reconnecting")
			connErr = backoff.Retry(dialTCP, backoff.NewExponentialBackOff())
		}

	}
}

// Helper to read from TCP connection
func tcpReader(log *logrus.Entry, conn net.Conn, channel chan<- []byte) {

	defer close(channel)

	buffer := make([]byte, 1024)

	// Loop and read from connection.
	for {
		// conn.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
		readN, readErr := conn.Read(buffer)

		if readErr != nil {
			if readErr == io.EOF {
				// connection is closed
				log.Info("connection closed")
				return
			} else if err, ok := readErr.(net.Error); ok && err.Timeout() {
				// Read timeout, just continue Nothing
			} else {
				log.WithError(readErr).Error("read error")
				return
			}
		} else {
			channel <- buffer[:readN]
		}
	}
}

// Helper to write to TCP connection. Note that this requires an additional channel to report errors
func tcpWriter(conn net.Conn, channel <-chan []byte, errorChannel chan<- error) {
	for {

		dataToWrite, more := <-channel

		if more {

			if conn != nil {
				conn.SetWriteDeadline(time.Now().Add(50 * time.Millisecond))
				_, err := conn.Write(dataToWrite)

				if err != nil {
					errorChannel <- err
				}

			} else {
				errorChannel <- errors.New("not connected, can not write to TCP connection")
			}

		} else {
			return
		}

	}
}
<|endoftext|>