<commit_before>// Package camli provides a wrapper around the Camlistore client for
// storing git blobs.
package camli

import (
	"bytes"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"strconv"
	"time"

	"camlistore.org/pkg/blob"
	"camlistore.org/pkg/client"
	"camlistore.org/pkg/cmdmain"
	"camlistore.org/pkg/httputil"
	"camlistore.org/pkg/osutil"
	"camlistore.org/pkg/schema"
	"camlistore.org/pkg/search"

	"gopkg.in/src-d/go-git.v3/core"
)

var Verbose = false

func init() {
	osutil.AddSecretRingFlag()
	flag.Parse()
}

type Uploader struct {
	c     *client.Client
	stats *httputil.StatsTransport
	// TODO fdgate, localcache.
}

// NewUploader returns a git blob uploader.
func NewUploader() *Uploader {
	c := client.NewOrFail(
		client.OptionTransportConfig(
			&client.TransportConfig{
				Verbose: Verbose,
			}))
	stats := c.HTTPStats()

	if Verbose {
		c.SetLogger(log.New(cmdmain.Stderr, "", log.LstdFlags))
	} else {
		c.SetLogger(nil)
	}

	return &Uploader{
		c:     c,
		stats: stats,
	}
}

// PutObject uploads a blob to Camlistore.
func (u *Uploader) PutObject(obj core.Object) error {
	sum := [20]byte(obj.Hash())
	head := obj.Type().Bytes()
	head = append(head, ' ')
	head = strconv.AppendInt(head, obj.Size(), 10)
	head = append(head, 0)
	r := io.MultiReader(bytes.NewReader(head), obj.Reader())

	result, err := u.c.Upload(&client.UploadHandle{
		BlobRef:  blob.MustParse(fmt.Sprintf("sha1-%x", sum)),
		Size:     uint32(obj.Size()) + uint32(len(head)),
		Contents: r,
	})
	if err != nil {
		log.Printf("couldn't store object: %x", sum)
		return err
	}
	if result.Skipped {
		log.Printf("object %x already on the server", sum)
	} else {
		log.Printf("stored object: %x", sum)
	}
	return nil
}

// Repo represets is our Camlistore scheme to model the state of a
// particular repo at a particular point in time.
type Repo struct {
	CamliVersion int
	CamliType    string
	Name         string
	// TODO switch to Time3339 so we can to query this.
	Retrieved time.Time
	Refs      map[string]string
}

// PutRepo stores a Repo in Camlistore.
func (u *Uploader) PutRepo(r *Repo) error {
	// Set the camli specific fields.
	r.CamliVersion = 1
	r.CamliType = "camliGitRepo"

	// Upload the repo object.
	j, err := json.Marshal(r)
	if err != nil {
		return err
	}
	h := blob.NewHash()
	size, err := io.Copy(h, bytes.NewReader(j))
	if err != nil {
		return err
	}
	reporef, err := u.c.Upload(&client.UploadHandle{
		BlobRef:  blob.RefFromHash(h),
		Size:     uint32(size),
		Contents: bytes.NewReader(j),
	})
	if err != nil {
		return err
	}
	log.Printf("stored repo: %s on %s", r.Name, reporef.BlobRef)

	// Update or create its permanode.
	pn, _, err := u.findRepo(r.Name)
	if err != nil {
		// Create a new one.
		res, err := u.c.UploadNewPermanode()
		if err != nil {
			return err
		}
		pn = res.BlobRef
		log.Printf("created permanode: %s", pn)

		titleattr := schema.NewSetAttributeClaim(pn, "title", r.Name)
		claimTime := time.Now()
		titleattr.SetClaimDate(claimTime)
		signer, err := u.c.Signer()
		if err != nil {
			return err
		}
		signed, err := titleattr.SignAt(signer, claimTime)
		if err != nil {
			return fmt.Errorf("couldn't to sign title claim")
		}
		_, err = u.c.Upload(client.NewUploadHandleFromString(signed))
	}
	contentattr := schema.NewSetAttributeClaim(pn, "camliContent", reporef.BlobRef.String())
	claimTime := time.Now()
	contentattr.SetClaimDate(claimTime)
	signer, err := u.c.Signer()
	if err != nil {
		return err
	}
	signed, err := contentattr.SignAt(signer, claimTime)
	if err != nil {
		return fmt.Errorf("couldn't to sign content claim")
	}
	_, err = u.c.Upload(client.NewUploadHandleFromString(signed))
	return err
}

func (u *Uploader) findRepo(name string) (blob.Ref, search.MetaMap, error) {
	res, err := u.c.Query(&search.SearchQuery{
		Limit: 1,
		Constraint: &search.Constraint{
			Permanode: &search.PermanodeConstraint{
				Attr: "title", Value: name,
			},
		},
		Describe: &search.DescribeRequest{},
	})
	if err != nil {
		return blob.Ref{}, nil, err
	}
	if len(res.Blobs) < 1 {
		return blob.Ref{}, nil, errors.New("repo not found")
	}
	return res.Blobs[0].Blob, res.Describe.Meta, nil
}

// GetRepo querys for a repo permanode with name, and returns its
// Repo object.
func (u *Uploader) GetRepo(name string) (*Repo, error) {
	pn, meta, err := u.findRepo(name)
	if err != nil {
		return nil, err
	}
	ref, ok := meta[pn.String()].ContentRef()
	if !ok {
		return nil, errors.New("couldn't find repo data (but there's a permanode)")
	}
	r, _, err := u.c.Fetch(ref)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}

	var repo Repo
	err = json.Unmarshal(body, &repo)
	return &repo, err
}

// TODO:
//   - query permanodes for title==name to check if the repo exists
//     - if yes, upload and set contentattr
//     - if no, upload and create a new permanode, set title & contentattr
//   - root nodes?
<commit_msg>make the uploader implement the core.Storage interface<commit_after>// Package camli provides a wrapper around the Camlistore client for
// storing git blobs.
package camli

import (
	"bytes"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"strconv"
	"time"

	"camlistore.org/pkg/blob"
	"camlistore.org/pkg/client"
	"camlistore.org/pkg/httputil"
	"camlistore.org/pkg/osutil"
	"camlistore.org/pkg/schema"
	"camlistore.org/pkg/search"

	"gopkg.in/src-d/go-git.v3/core"
)

func init() {
	osutil.AddSecretRingFlag()
	flag.Parse()
}

type Uploader struct {
	c     *client.Client
	stats *httputil.StatsTransport
	// TODO fdgate, localcache.
}

// NewUploader returns a git blob uploader.
func NewUploader() *Uploader {
	c := client.NewOrFail(
		client.OptionTransportConfig(
			&client.TransportConfig{}))
	stats := c.HTTPStats()

	return &Uploader{
		c:     c,
		stats: stats,
	}
}

// PutObject uploads a blob to Camlistore.
func (u *Uploader) PutObject(obj core.Object) error {
	sum := [20]byte(obj.Hash())
	head := obj.Type().Bytes()
	head = append(head, ' ')
	head = strconv.AppendInt(head, obj.Size(), 10)
	head = append(head, 0)
	r := io.MultiReader(bytes.NewReader(head), obj.Reader())

	result, err := u.c.Upload(&client.UploadHandle{
		BlobRef:  blob.MustParse(fmt.Sprintf("sha1-%x", sum)),
		Size:     uint32(obj.Size()) + uint32(len(head)),
		Contents: r,
	})
	if err != nil {
		log.Printf("couldn't store object: %x", sum)
		return err
	}
	if result.Skipped {
		log.Printf("object %x already on the server", sum)
	} else {
		log.Printf("stored object: %x", sum)
	}
	return nil
}

// Repo represets is our Camlistore scheme to model the state of a
// particular repo at a particular point in time.
type Repo struct {
	CamliVersion int
	CamliType    string
	Name         string
	// TODO switch to Time3339 so we can to query this.
	Retrieved time.Time
	Refs      map[string]string
}

// PutRepo stores a Repo in Camlistore.
func (u *Uploader) PutRepo(r *Repo) error {
	// Set the camli specific fields.
	r.CamliVersion = 1
	r.CamliType = "camliGitRepo"

	// Upload the repo object.
	j, err := json.Marshal(r)
	if err != nil {
		return err
	}
	h := blob.NewHash()
	size, err := io.Copy(h, bytes.NewReader(j))
	if err != nil {
		return err
	}
	reporef, err := u.c.Upload(&client.UploadHandle{
		BlobRef:  blob.RefFromHash(h),
		Size:     uint32(size),
		Contents: bytes.NewReader(j),
	})
	if err != nil {
		return err
	}
	log.Printf("stored repo: %s on %s", r.Name, reporef.BlobRef)

	// Update or create its permanode.
	pn, _, err := u.findRepo(r.Name)
	if err != nil {
		// Create a new one.
		res, err := u.c.UploadNewPermanode()
		if err != nil {
			return err
		}
		pn = res.BlobRef
		log.Printf("created permanode: %s", pn)

		titleattr := schema.NewSetAttributeClaim(pn, "title", r.Name)
		claimTime := time.Now()
		titleattr.SetClaimDate(claimTime)
		signer, err := u.c.Signer()
		if err != nil {
			return err
		}
		signed, err := titleattr.SignAt(signer, claimTime)
		if err != nil {
			return fmt.Errorf("couldn't to sign title claim")
		}
		_, err = u.c.Upload(client.NewUploadHandleFromString(signed))
	}
	contentattr := schema.NewSetAttributeClaim(pn, "camliContent", reporef.BlobRef.String())
	claimTime := time.Now()
	contentattr.SetClaimDate(claimTime)
	signer, err := u.c.Signer()
	if err != nil {
		return err
	}
	signed, err := contentattr.SignAt(signer, claimTime)
	if err != nil {
		return fmt.Errorf("couldn't to sign content claim")
	}
	_, err = u.c.Upload(client.NewUploadHandleFromString(signed))
	return err
}

func (u *Uploader) findRepo(name string) (blob.Ref, search.MetaMap, error) {
	res, err := u.c.Query(&search.SearchQuery{
		Limit: 1,
		Constraint: &search.Constraint{
			Permanode: &search.PermanodeConstraint{
				Attr: "title", Value: name,
			},
		},
		Describe: &search.DescribeRequest{},
	})
	if err != nil {
		return blob.Ref{}, nil, err
	}
	if len(res.Blobs) < 1 {
		return blob.Ref{}, nil, errors.New("repo not found")
	}
	return res.Blobs[0].Blob, res.Describe.Meta, nil
}

// GetRepo querys for a repo permanode with name, and returns its
// Repo object.
func (u *Uploader) GetRepo(name string) (*Repo, error) {
	pn, meta, err := u.findRepo(name)
	if err != nil {
		return nil, err
	}
	ref, ok := meta[pn.String()].ContentRef()
	if !ok {
		return nil, errors.New("couldn't find repo data (but there's a permanode)")
	}
	r, _, err := u.c.Fetch(ref)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}

	var repo Repo
	err = json.Unmarshal(body, &repo)
	return &repo, err
}

func (u *Uploader) New() (Object, error) {
	// Lazy, just used the core in memory objects for now.
	return &core.Object{}, nil
}

func (u *Uploader) Set(obj core.Object) (Hash, error) {
	return obj.Hash(), u.PutObject(obj)
}

func (u *Uploader) Get(core.Hash) (Object, error) {
	panic("Uploader.Get called")
}

func (u *Uploader) Iter(core.ObjectType) core.ObjectIter {
	panic("Uploader.Iter called")
}
<|endoftext|>


<commit_before>package ccgen

import (
	"encoding/xml"
	"strconv"
	"strings"

	"github.com/reiver/go-stringcase"
)

type ZwClasses struct {
	XMLName        xml.Name        `xml:"zw_classes"`
	BasicDevices   []BasicDevice   `xml:"bas_dev"`
	GenericDevices []GenericDevice `xml:"gen_dev"`
	CommandClasses []CommandClass  `xml:"cmd_class"`
}

type BasicDevice struct {
	Name     string `xml:"name,attr"`
	Key      string `xml:"key,attr"`
	Help     string `xml:"help,attr"`
	ReadOnly bool   `xml:"read_only,attr"`
	Comment  string `xml:"comment,attr"`
}

type GenericDevice struct {
	Name            string           `xml:"name,attr"`
	Key             string           `xml:"key,attr"`
	Help            string           `xml:"help,attr"`
	ReadOnly        bool             `xml:"read_only,attr"`
	Comment         string           `xml:"comment,attr"`
	SpecificDevices []SpecificDevice `xml:"spec_dev"`
}

type SpecificDevice struct {
	Name     string `xml:"name,attr"`
	Key      string `xml:"key,attr"`
	Help     string `xml:"help,attr"`
	ReadOnly bool   `xml:"read_only,attr"`
	Comment  string `xml:"comment,attr"`
}

type CommandClass struct {
	Name     string    `xml:"name,attr"`
	Key      string    `xml:"key,attr"`
	Version  int       `xml:"version,attr"`
	Help     string    `xml:"help,attr"`
	Comment  string    `xml:"comment,attr"`
	Commands []Command `xml:"cmd"`
}

func (c CommandClass) GetPackageName() string {
	ccname := strings.Replace(c.Name, "COMMAND_CLASS_", "", 1)
	ccname = stringcase.ToLowerCase(stringcase.ToPascalCase(ccname))

	if c.Version > 1 {
		versionStr := strconv.Itoa(c.Version)
		ccname += "v" + versionStr
	}

	return ccname
}

func (c CommandClass) CanGenerate() (can bool, reason string) {
	if c.Name == "ZWAVE_CMD_CLASS" {
		return false, "Not an actual command class (also stupidly complicated parsing rules)"
	}

	for _, cmd := range c.Commands {
		for _, param := range cmd.Params {
			if param.Type == "MARKER" {
				return false, "Contains a MARKER"
			}
		}
	}

	return true, ""
}

type Command struct {
	Name     string `xml:"name,attr"`
	Key      string `xml:"key,attr"`
	Type     string `xml:"type,attr"`
	HashCode string
	Comment  string `xml:"comment,attr"`

	Params []Param `xml:"param"`
}
<commit_msg>Skip some generation for command classes<commit_after>package ccgen

import (
	"encoding/xml"
	"strconv"
	"strings"

	"github.com/reiver/go-stringcase"
)

type ZwClasses struct {
	XMLName        xml.Name        `xml:"zw_classes"`
	BasicDevices   []BasicDevice   `xml:"bas_dev"`
	GenericDevices []GenericDevice `xml:"gen_dev"`
	CommandClasses []CommandClass  `xml:"cmd_class"`
}

type BasicDevice struct {
	Name     string `xml:"name,attr"`
	Key      string `xml:"key,attr"`
	Help     string `xml:"help,attr"`
	ReadOnly bool   `xml:"read_only,attr"`
	Comment  string `xml:"comment,attr"`
}

type GenericDevice struct {
	Name            string           `xml:"name,attr"`
	Key             string           `xml:"key,attr"`
	Help            string           `xml:"help,attr"`
	ReadOnly        bool             `xml:"read_only,attr"`
	Comment         string           `xml:"comment,attr"`
	SpecificDevices []SpecificDevice `xml:"spec_dev"`
}

type SpecificDevice struct {
	Name     string `xml:"name,attr"`
	Key      string `xml:"key,attr"`
	Help     string `xml:"help,attr"`
	ReadOnly bool   `xml:"read_only,attr"`
	Comment  string `xml:"comment,attr"`
}

type CommandClass struct {
	Name     string    `xml:"name,attr"`
	Key      string    `xml:"key,attr"`
	Version  int       `xml:"version,attr"`
	Help     string    `xml:"help,attr"`
	Comment  string    `xml:"comment,attr"`
	Commands []Command `xml:"cmd"`
}

func (c CommandClass) GetPackageName() string {
	ccname := strings.Replace(c.Name, "COMMAND_CLASS_", "", 1)
	ccname = stringcase.ToLowerCase(stringcase.ToPascalCase(ccname))

	if c.Version > 1 {
		versionStr := strconv.Itoa(c.Version)
		ccname += "v" + versionStr
	}

	return ccname
}

func (c CommandClass) CanGenerate() (can bool, reason string) {
	if c.Name == "ZWAVE_CMD_CLASS" {
		return false, "Not an actual command class (also stupidly complicated parsing rules)"
	}

	if c.Name == "COMMAND_CLASS_ZIP_6LOWPAN" ||
		c.Name == "COMMAND_CLASS_ZIP_ND" ||
		c.Name == "COMMAND_CLASS_ZIP_GATEWAY" ||
		c.Name == "COMMAND_CLASS_ZIP_PORTAL" ||
		c.Name == "COMMAND_CLASS_ZIP" ||
		c.Name == "COMMAND_CLASS_IP_ASSOCIATION" ||
		c.Name == "COMMAND_CLASS_TRANSPORT_SERVICE" ||
		c.Name == "COMMAND_CLASS_CONTROLLER_REPLICATION" ||
		c.Name == "COMMAND_CLASS_CRC_16_ENCAP" ||
		c.Name == "COMMAND_CLASS_IP_CONFIGURATION" {
		return false, "Skipped (no current intention to support)"
	}

	for _, cmd := range c.Commands {
		for _, param := range cmd.Params {
			if param.Type == "MARKER" {
				return false, "Contains a MARKER"
			}
		}
	}

	return true, ""
}

type Command struct {
	Name     string `xml:"name,attr"`
	Key      string `xml:"key,attr"`
	Type     string `xml:"type,attr"`
	HashCode string
	Comment  string `xml:"comment,attr"`

	Params []Param `xml:"param"`
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package main

import (
	"flag"
	"os"
	"path"

	// "github.com/ceph/ceph-csi/pkg/cephfs"
	"github.com/gman0/ceph-csi/pkg/cephfs"
	"github.com/golang/glog"
)

func init() {
	flag.Set("logtostderr", "true")
}

var (
	endpoint   = flag.String("endpoint", "unix://tmp/csi.sock", "CSI endpoint")
	driverName = flag.String("drivername", "csi-cephfsplugin", "name of the driver")
	nodeID     = flag.String("nodeid", "", "node id")
)

func main() {
	flag.Parse()

	if err := createPersistentStorage(path.Join(cephfs.PluginFolder, "controller")); err != nil {
		glog.Errorf("failed to create persisten storage for controller %v", err)
		os.Exit(1)
	}

	if err := createPersistentStorage(path.Join(cephfs.PluginFolder, "node")); err != nil {
		glog.Errorf("failed to create persisten storage for node %v", err)
		os.Exit(1)
	}

	driver := cephfs.NewCephFSDriver()
	driver.Run(*driverName, *nodeID, *endpoint)

	os.Exit(0)
}

func createPersistentStorage(persistentStoragePath string) error {
	return os.MkdirAll(persistentStoragePath, os.FileMode(0755))
}
<commit_msg>cephfs/main: change import path<commit_after>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package main

import (
	"flag"
	"os"
	"path"

	"github.com/ceph/ceph-csi/pkg/cephfs"
	"github.com/golang/glog"
)

func init() {
	flag.Set("logtostderr", "true")
}

var (
	endpoint   = flag.String("endpoint", "unix://tmp/csi.sock", "CSI endpoint")
	driverName = flag.String("drivername", "csi-cephfsplugin", "name of the driver")
	nodeID     = flag.String("nodeid", "", "node id")
)

func main() {
	flag.Parse()

	if err := createPersistentStorage(path.Join(cephfs.PluginFolder, "controller")); err != nil {
		glog.Errorf("failed to create persisten storage for controller %v", err)
		os.Exit(1)
	}

	if err := createPersistentStorage(path.Join(cephfs.PluginFolder, "node")); err != nil {
		glog.Errorf("failed to create persisten storage for node %v", err)
		os.Exit(1)
	}

	driver := cephfs.NewCephFSDriver()
	driver.Run(*driverName, *nodeID, *endpoint)

	os.Exit(0)
}

func createPersistentStorage(persistentStoragePath string) error {
	return os.MkdirAll(persistentStoragePath, os.FileMode(0755))
}
<|endoftext|>


<commit_before>package core

import (
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"math/rand"
	"os"
	"path/filepath"
	"strings"
	"time"
)

func GetNick(size int) string {
	rand.Seed(time.Now().UTC().UnixNano())
	const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
	b := make([]byte, size)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}

func GetHashes(names []string) []string {
	hashes := make([]string, len(names))
	for i, name := range names {
		hashes[i] = GetHash(name)
	}
	return hashes
}

func GetHash(name string) string {
	hasher := sha1.New()
	hasher.Write([]byte(name))
	return base64.URLEncoding.EncodeToString(hasher.Sum(nil))
}

func ZeroconfHandler(zeroconfQuit chan bool) {
	return
}

func SafeFilename(name string) string {
	if _, err := os.Stat(name); err != nil {
		return name
	}
	ext := filepath.Ext(name)
	basename := strings.TrimSuffix(name, ext)
	ix := 1
	for {
		name := fmt.Sprintf("%s_%d%s", basename, ix, ext)
		if _, err := os.Stat(name); err != nil {
			return name
		}
		ix++
	}
}
<commit_msg>clean code<commit_after>package core

import (
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"math/rand"
	"os"
	"path/filepath"
	"strings"
	"time"
)

func GetNick(size int) string {
	rand.Seed(time.Now().UTC().UnixNano())
	const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
	b := make([]byte, size)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}

func GetHashes(names []string) []string {
	hashes := make([]string, len(names))
	for i, name := range names {
		hashes[i] = GetHash(name)
	}
	return hashes
}

func GetHash(name string) string {
	hasher := sha1.New()
	hasher.Write([]byte(name))
	return base64.URLEncoding.EncodeToString(hasher.Sum(nil))
}

func SafeFilename(name string) string {
	if _, err := os.Stat(name); err != nil {
		return name
	}
	ext := filepath.Ext(name)
	basename := strings.TrimSuffix(name, ext)
	ix := 1
	for {
		name := fmt.Sprintf("%s_%d%s", basename, ix, ext)
		if _, err := os.Stat(name); err != nil {
			return name
		}
		ix++
	}
}
<|endoftext|>


<commit_before>package bbox

import (
	"fmt"
	"time"
)

const (
	DEFAULT_BPM            = 120
	MIN_BPM                = 30
	MAX_BPM                = 480
	SOUNDS                 = 4
	BEATS                  = 16
	DEFAULT_TICKS_PER_BEAT = 10
	DEFAULT_TICKS          = BEATS * DEFAULT_TICKS_PER_BEAT

	TEMPO_DECAY = 3 * time.Minute
)

type Interval struct {
	TicksPerBeat int
	Ticks        int
}

type Beats [SOUNDS][BEATS]bool

type Loop struct {
	beats   Beats
	closing chan struct{}
	msgs    <-chan Beats

	bpmCh chan int
	bpm   int

	tempo      <-chan int
	tempoDecay *time.Timer

	ticks []chan<- int
	wavs  *Wavs

	iv         Interval
	intervalCh []chan<- Interval
}

func InitLoop(
	msgs <-chan Beats,
	tempo <-chan int,
	ticks []chan<- int,
	intervalCh []chan<- Interval,
) *Loop {
	return &Loop{
		beats: Beats{},

		bpmCh: make(chan int),
		bpm:   DEFAULT_BPM,

		closing: make(chan struct{}),
		msgs:    msgs,
		tempo:   tempo,
		ticks:   ticks,
		wavs:    InitWavs(),

		intervalCh: intervalCh,
		iv: Interval{
			TicksPerBeat: DEFAULT_TICKS_PER_BEAT,
			Ticks:        DEFAULT_TICKS,
		},
	}
}

func (l *Loop) Run() {
	ticker := time.NewTicker(l.bpmToInterval(l.bpm))
	defer ticker.Stop()

	tick := 0
	tickTime := time.Now()
	for {
		select {
		case _, more := <-l.closing:
			if !more {
				fmt.Printf("Loop trying to close\n")
				// return
			}
		case beats, more := <-l.msgs:
			if more {
				// incoming beat update from keyboard
				l.beats = beats
			} else {
				// closing
				l.wavs.Close()
				fmt.Printf("Loop closing\n")
				return
			}

		case bpm, more := <-l.bpmCh:
			if more {
				// incoming bpm update
				l.bpm = bpm

				// BPM: 30 -> 60 -> 120 -> 240 -> 480.0
				// TPB: 40 -> 20 ->  10 ->   5 ->   2.5
				l.iv.TicksPerBeat = 1200 / l.bpm
				l.iv.Ticks = BEATS * l.iv.TicksPerBeat

				for _, ch := range l.intervalCh {
					ch <- l.iv
				}

				ticker.Stop()
				ticker = time.NewTicker(l.bpmToInterval(l.bpm))
				defer ticker.Stop()
			} else {
				// we should never get here
				fmt.Printf("closed on bpm, invalid state")
				panic(1)
			}

		case tempo, more := <-l.tempo:
			if more {
				// incoming tempo update from keyboard
				if (l.bpm > MIN_BPM || tempo > 0) &&
					(l.bpm < MAX_BPM || tempo < 0) {

					go l.setBpm(l.bpm + tempo)

					// set a decay timer
					if l.tempoDecay != nil {
						l.tempoDecay.Stop()
					}
					l.tempoDecay = time.AfterFunc(TEMPO_DECAY, func() {
						l.setBpm(DEFAULT_BPM)
					})
				}
			} else {
				// we should never get here
				fmt.Printf("unexpected: tempo return no more\n")
				return
			}

		case <-ticker.C: // for every time interval
			// next interval
			tick = (tick + 1) % l.iv.Ticks
			tmp := tick

			for _, ch := range l.ticks {
				ch <- tmp
			}

			// for each beat type
			if tick%l.iv.TicksPerBeat == 0 {
				for i, beat := range l.beats {
					if beat[tick/l.iv.TicksPerBeat] {
						// initiate playback
						l.wavs.Play(i)
					}
				}
			}

			t := time.Now()
			tbprint(0, 5, fmt.Sprintf("______BPM:__%+v______", l.bpm))
			tbprint(0, 6, fmt.Sprintf("______int:__%+v______", l.bpmToInterval(l.bpm)))
			tbprint(0, 7, fmt.Sprintf("______time:_%+v______", t.Sub(tickTime)))
			tbprint(0, 8, fmt.Sprintf("______tick:_%+v______", tick))
			tickTime = t
		}
	}
}

func (l *Loop) Close() {
	// TODO: this doesn't block?
	close(l.closing)
}

func (l *Loop) bpmToInterval(bpm int) time.Duration {
	return 60 * time.Second / time.Duration(bpm) / (BEATS / 4) / time.Duration(l.iv.TicksPerBeat) // 4 beats per interval
}

func (l *Loop) setBpm(bpm int) {
	l.bpmCh <- bpm
}
<commit_msg>update beat loop for new wav module<commit_after>package bbox

import (
	"fmt"
	"time"

	"github.com/siggy/bbox/beatboxer/wavs"
)

const (
	DEFAULT_BPM            = 120
	MIN_BPM                = 30
	MAX_BPM                = 480
	SOUNDS                 = 4
	BEATS                  = 16
	DEFAULT_TICKS_PER_BEAT = 10
	DEFAULT_TICKS          = BEATS * DEFAULT_TICKS_PER_BEAT

	TEMPO_DECAY = 3 * time.Minute
)

type Interval struct {
	TicksPerBeat int
	Ticks        int
}

type Beats [SOUNDS][BEATS]bool

type Loop struct {
	beats   Beats
	closing chan struct{}
	msgs    <-chan Beats

	bpmCh chan int
	bpm   int

	tempo      <-chan int
	tempoDecay *time.Timer

	ticks []chan<- int
	wavs  *wavs.Wavs

	iv         Interval
	intervalCh []chan<- Interval
}

var sounds = []string{
	"hihat-808.wav",
	"kick-classic.wav",
	"perc-808.wav",
	"tom-808.wav",
}

func InitLoop(
	msgs <-chan Beats,
	tempo <-chan int,
	ticks []chan<- int,
	intervalCh []chan<- Interval,
) *Loop {
	return &Loop{
		beats: Beats{},

		bpmCh: make(chan int),
		bpm:   DEFAULT_BPM,

		closing: make(chan struct{}),
		msgs:    msgs,
		tempo:   tempo,
		ticks:   ticks,
		wavs:    wavs.InitWavs(),

		intervalCh: intervalCh,
		iv: Interval{
			TicksPerBeat: DEFAULT_TICKS_PER_BEAT,
			Ticks:        DEFAULT_TICKS,
		},
	}
}

func (l *Loop) Run() {
	ticker := time.NewTicker(l.bpmToInterval(l.bpm))
	defer ticker.Stop()

	tick := 0
	tickTime := time.Now()
	for {
		select {
		case _, more := <-l.closing:
			if !more {
				fmt.Printf("Loop trying to close\n")
				// return
			}
		case beats, more := <-l.msgs:
			if more {
				// incoming beat update from keyboard
				l.beats = beats
			} else {
				// closing
				l.wavs.Close()
				fmt.Printf("Loop closing\n")
				return
			}

		case bpm, more := <-l.bpmCh:
			if more {
				// incoming bpm update
				l.bpm = bpm

				// BPM: 30 -> 60 -> 120 -> 240 -> 480.0
				// TPB: 40 -> 20 ->  10 ->   5 ->   2.5
				l.iv.TicksPerBeat = 1200 / l.bpm
				l.iv.Ticks = BEATS * l.iv.TicksPerBeat

				for _, ch := range l.intervalCh {
					ch <- l.iv
				}

				ticker.Stop()
				ticker = time.NewTicker(l.bpmToInterval(l.bpm))
				defer ticker.Stop()
			} else {
				// we should never get here
				fmt.Printf("closed on bpm, invalid state")
				panic(1)
			}

		case tempo, more := <-l.tempo:
			if more {
				// incoming tempo update from keyboard
				if (l.bpm > MIN_BPM || tempo > 0) &&
					(l.bpm < MAX_BPM || tempo < 0) {

					go l.setBpm(l.bpm + tempo)

					// set a decay timer
					if l.tempoDecay != nil {
						l.tempoDecay.Stop()
					}
					l.tempoDecay = time.AfterFunc(TEMPO_DECAY, func() {
						l.setBpm(DEFAULT_BPM)
					})
				}
			} else {
				// we should never get here
				fmt.Printf("unexpected: tempo return no more\n")
				return
			}

		case <-ticker.C: // for every time interval
			// next interval
			tick = (tick + 1) % l.iv.Ticks
			tmp := tick

			for _, ch := range l.ticks {
				ch <- tmp
			}

			// for each beat type
			if tick%l.iv.TicksPerBeat == 0 {
				for i, beat := range l.beats {
					if beat[tick/l.iv.TicksPerBeat] {
						// initiate playback
						l.wavs.Play(sounds[i])
					}
				}
			}

			t := time.Now()
			tbprint(0, 5, fmt.Sprintf("______BPM:__%+v______", l.bpm))
			tbprint(0, 6, fmt.Sprintf("______int:__%+v______", l.bpmToInterval(l.bpm)))
			tbprint(0, 7, fmt.Sprintf("______time:_%+v______", t.Sub(tickTime)))
			tbprint(0, 8, fmt.Sprintf("______tick:_%+v______", tick))
			tickTime = t
		}
	}
}

func (l *Loop) Close() {
	// TODO: this doesn't block?
	close(l.closing)
}

func (l *Loop) bpmToInterval(bpm int) time.Duration {
	return 60 * time.Second / time.Duration(bpm) / (BEATS / 4) / time.Duration(l.iv.TicksPerBeat) // 4 beats per interval
}

func (l *Loop) setBpm(bpm int) {
	l.bpmCh <- bpm
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"loveoneanother.at/tiedot/db"
	"math/rand"
	"os"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
)

const BENCH_SIZE = 400000  // don't make it too large... unmarshaled JSON takes lots of memory!
const BENCH2_SIZE = 800000 // feel free to make this one larger!

// Run function a number of times and calculate average time consumption per iteration.
func average(name string, total int, init func(), do func()) {
	numThreads := runtime.GOMAXPROCS(-1)
	wp := new(sync.WaitGroup)
	init()
	iter := float64(total)
	start := float64(time.Now().UTC().UnixNano())
	for i := 0; i < total; i += total / numThreads {
		wp.Add(1)
		go func() {
			defer wp.Done()
			for j := 0; j < total/numThreads; j++ {
				do()
			}
		}()
	}
	wp.Wait()
	end := float64(time.Now().UTC().UnixNano())
	fmt.Printf("%s %d: %d ns/iter, %d iter/sec\n", name, int(total), int((end-start)/iter), int(1000000000/((end-start)/iter)))
}

// Individual feature benchmarks.
func benchmark() {
	// initialization
	rand.Seed(time.Now().UTC().UnixNano())
	// prepare benchmark data
	docs := [BENCH_SIZE]interface{}{}
	for i := range docs {
		if err := json.Unmarshal([]byte(
			`{"a": {"b": {"c": `+strconv.Itoa(rand.Intn(BENCH_SIZE))+`}},`+
				`"c": {"d": `+strconv.Itoa(rand.Intn(BENCH_SIZE))+`},`+
				`"more": "abcdefghijklmnopqrstuvwxyz"}`), &docs[i]); err != nil {
			panic("json error")
		}
	}
	// prepare collection
	tmp := "/tmp/tiedot_bench"
	os.RemoveAll(tmp)
	defer os.RemoveAll(tmp)
	col, err := db.OpenCol(tmp)
	if err != nil {
		panic(err)
	}
	col.Index([]string{"a", "b", "c"})
	col.Index([]string{"c", "d"})
	// start benchmarks
	average("insert", BENCH_SIZE, func() {}, func() {
		if _, err := col.Insert(docs[rand.Intn(BENCH_SIZE)]); err != nil {
			panic("insert error")
		}
	})
	ids := make([]uint64, 0)
	average("read", BENCH_SIZE, func() {
		col.ForAll(func(id uint64, doc interface{}) bool {
			ids = append(ids, id)
			return true
		})
	}, func() {
		var doc interface{}
		err = col.Read(ids[uint64(rand.Intn(BENCH_SIZE))], &doc)
		if doc == nil {
			panic("read error")
		}
	})
	average("lookup", BENCH_SIZE, func() {}, func() {
		var query interface{}
		if err := json.Unmarshal([]byte(`{"c": [{"eq": `+strconv.Itoa(rand.Intn(BENCH_SIZE))+`, "in": ["a", "b", "c"], "limit": 1}, `+
			`{"eq": `+strconv.Itoa(rand.Intn(BENCH_SIZE))+`, "in": ["c", "d"], "limit": 1}]}`), &query); err != nil {
			panic("json error")
		}
		result := make(map[uint64]struct{})
		if err := db.EvalQueryV2(query, col, &result); err != nil {
			panic("query error")
		}
	})
	average("update", BENCH_SIZE, func() {}, func() {
		if _, err := col.Update(ids[rand.Intn(BENCH_SIZE)], docs[rand.Intn(BENCH_SIZE)]); err != nil {
			panic("update error")
		}
	})
	average("delete", BENCH_SIZE, func() {}, func() {
		col.Delete(ids[rand.Intn(BENCH_SIZE)])
	})
	col.Close()
}

// Insert/update/delete/query all running at once.
func benchmark2() {
	numThreads := runtime.GOMAXPROCS(-1)
	rand.Seed(time.Now().UTC().UnixNano())
	// prepare collection
	tmp := "/tmp/tiedot_bench"
	os.RemoveAll(tmp)
	col, err := db.OpenCol(tmp)
	if err != nil {
		panic(err)
	}
	col.Index([]string{"a", "b", "c"})
	col.Index([]string{"c", "d"})
	docs := make([]uint64, 0, BENCH2_SIZE)
	// Prepare 1000 docs as a start
	var docToInsert interface{}
	for j := 0; j < 1000; j++ {
		if err = json.Unmarshal([]byte(
			`{"a": {"b": {"c": `+strconv.Itoa(rand.Intn(BENCH2_SIZE))+`}},`+
				`"c": {"d": `+strconv.Itoa(rand.Intn(BENCH2_SIZE))+`},`+
				`"more": "abcdefghijklmnopqrstuvwxyz"}`), &docToInsert); err != nil {
			panic(err)
		}
		if newID, err := col.Insert(docToInsert); err == nil {
			docs = append(docs, newID)
		} else {
			panic(err)
		}
	}
	// benchmark begins
	wp := new(sync.WaitGroup)
	wp.Add(5 * numThreads) // (CRUD + query) * number of benchmark threads
	start := float64(time.Now().UTC().UnixNano())
	// insert BENCH2_SIZE * 2 documents
	for i := 0; i < numThreads; i++ {
		go func(i int) {
			defer wp.Done()
			var docToInsert interface{}
			var err error
			for j := 0; j < BENCH2_SIZE/numThreads*2; j++ {
				if err = json.Unmarshal([]byte(
					`{"a": {"b": {"c": `+strconv.Itoa(rand.Intn(BENCH2_SIZE))+`}},`+
						`"c": {"d": `+strconv.Itoa(rand.Intn(BENCH2_SIZE))+`},`+
						`"more": "abcdefghijklmnopqrstuvwxyz"}`), &docToInsert); err != nil {
					panic(err)
				}
				if newID, err := col.Insert(docToInsert); err == nil {
					docs = append(docs, newID)
				} else {
					panic(err)
				}
			}
			fmt.Printf("Insert thread %d completed\n", i)
		}(i)
	}
	// read BENCH2_SIZE * 2 documents
	for i := 0; i < numThreads; i++ {
		go func(i int) {
			defer wp.Done()
			var doc interface{}
			for j := 0; j < BENCH2_SIZE/numThreads*2; j++ {
				col.Read(docs[uint64(rand.Intn(len(docs)))], &doc)
			}
			fmt.Printf("Read thread %d completed\n", i)
		}(i)
	}
	// query BENCH2_SIZE times
	for i := 0; i < numThreads; i++ {
		go func(i int) {
			defer wp.Done()
			var query interface{}
			var err error
			for j := 0; j < BENCH2_SIZE/numThreads; j++ {
				if err = json.Unmarshal([]byte(`{"c": [{"eq": `+strconv.Itoa(rand.Intn(BENCH2_SIZE))+`, "in": ["a", "b", "c"], "limit": 1}, `+
					`{"eq": `+strconv.Itoa(rand.Intn(BENCH2_SIZE))+`, "in": ["c", "d"], "limit": 1}]}`), &query); err != nil {
					panic("json error")
				}
				result := make(map[uint64]struct{})
				if err = db.EvalQueryV2(query, col, &result); err != nil {
					panic("query error")
				}
			}
			fmt.Printf("Query thread %d completed\n", i)
		}(i)
	}
	// update BENCH2_SIZE documents
	for i := 0; i < numThreads; i++ {
		go func(i int) {
			defer wp.Done()
			var updated interface{}
			var err error
			for j := 0; j < BENCH2_SIZE/numThreads; j++ {
				if err = json.Unmarshal([]byte(
					`{"a": {"b": {"c": `+strconv.Itoa(rand.Intn(BENCH2_SIZE))+`}},`+
						`"c": {"d": `+strconv.Itoa(rand.Intn(BENCH2_SIZE))+`},`+
						`"more": "abcdefghijklmnopqrstuvwxyz"}`), &updated); err != nil {
					panic(err)
				}
				if _, err = col.Update(docs[uint64(rand.Intn(len(docs)))], updated); err != nil {
					// "does not exist" indicates that a deleted document is being updated, it is safe to ignore
					if !strings.Contains(fmt.Sprint(err), "does not exist") {
						fmt.Println(err)
					}
				}
			}
			fmt.Printf("Update thread %d completed\n", i)
		}(i)
	}
	// delete BENCH2_SIZE documents
	for i := 0; i < numThreads; i++ {
		go func(i int) {
			defer wp.Done()
			for j := 0; j < BENCH2_SIZE/numThreads; j++ {
				col.Delete(docs[uint64(rand.Intn(len(docs)))])
			}
			fmt.Printf("Delete thread %d completed\n", i)
		}(i)
	}
	wp.Wait()
	end := float64(time.Now().UTC().UnixNano())
	fmt.Printf("Total operations %d: %d ns/iter, %d iter/sec\n", BENCH2_SIZE*7, int((end-start)/BENCH2_SIZE/7), int(1000000000/((end-start)/BENCH2_SIZE/7)))
}
<commit_msg>in benchmark, print log message when a goroutine is about to begin its job<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"loveoneanother.at/tiedot/db"
	"math/rand"
	"os"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
)

const BENCH_SIZE = 400000  // don't make it too large... unmarshaled JSON takes lots of memory!
const BENCH2_SIZE = 800000 // feel free to make this one larger!

// Run function a number of times and calculate average time consumption per iteration.
func average(name string, total int, init func(), do func()) {
	numThreads := runtime.GOMAXPROCS(-1)
	wp := new(sync.WaitGroup)
	init()
	iter := float64(total)
	start := float64(time.Now().UTC().UnixNano())
	for i := 0; i < total; i += total / numThreads {
		wp.Add(1)
		go func() {
			defer wp.Done()
			for j := 0; j < total/numThreads; j++ {
				do()
			}
		}()
	}
	wp.Wait()
	end := float64(time.Now().UTC().UnixNano())
	fmt.Printf("%s %d: %d ns/iter, %d iter/sec\n", name, int(total), int((end-start)/iter), int(1000000000/((end-start)/iter)))
}

// Individual feature benchmarks.
func benchmark() {
	// initialization
	rand.Seed(time.Now().UTC().UnixNano())
	// prepare benchmark data
	docs := [BENCH_SIZE]interface{}{}
	for i := range docs {
		if err := json.Unmarshal([]byte(
			`{"a": {"b": {"c": `+strconv.Itoa(rand.Intn(BENCH_SIZE))+`}},`+
				`"c": {"d": `+strconv.Itoa(rand.Intn(BENCH_SIZE))+`},`+
				`"more": "abcdefghijklmnopqrstuvwxyz"}`), &docs[i]); err != nil {
			panic("json error")
		}
	}
	// prepare collection
	tmp := "/tmp/tiedot_bench"
	os.RemoveAll(tmp)
	defer os.RemoveAll(tmp)
	col, err := db.OpenCol(tmp)
	if err != nil {
		panic(err)
	}
	col.Index([]string{"a", "b", "c"})
	col.Index([]string{"c", "d"})
	// start benchmarks
	average("insert", BENCH_SIZE, func() {}, func() {
		if _, err := col.Insert(docs[rand.Intn(BENCH_SIZE)]); err != nil {
			panic("insert error")
		}
	})
	ids := make([]uint64, 0)
	average("read", BENCH_SIZE, func() {
		col.ForAll(func(id uint64, doc interface{}) bool {
			ids = append(ids, id)
			return true
		})
	}, func() {
		var doc interface{}
		err = col.Read(ids[uint64(rand.Intn(BENCH_SIZE))], &doc)
		if doc == nil {
			panic("read error")
		}
	})
	average("lookup", BENCH_SIZE, func() {}, func() {
		var query interface{}
		if err := json.Unmarshal([]byte(`{"c": [{"eq": `+strconv.Itoa(rand.Intn(BENCH_SIZE))+`, "in": ["a", "b", "c"], "limit": 1}, `+
			`{"eq": `+strconv.Itoa(rand.Intn(BENCH_SIZE))+`, "in": ["c", "d"], "limit": 1}]}`), &query); err != nil {
			panic("json error")
		}
		result := make(map[uint64]struct{})
		if err := db.EvalQueryV2(query, col, &result); err != nil {
			panic("query error")
		}
	})
	average("update", BENCH_SIZE, func() {}, func() {
		if _, err := col.Update(ids[rand.Intn(BENCH_SIZE)], docs[rand.Intn(BENCH_SIZE)]); err != nil {
			panic("update error")
		}
	})
	average("delete", BENCH_SIZE, func() {}, func() {
		col.Delete(ids[rand.Intn(BENCH_SIZE)])
	})
	col.Close()
}

// Insert/update/delete/query all running at once.
func benchmark2() {
	numThreads := runtime.GOMAXPROCS(-1)
	rand.Seed(time.Now().UTC().UnixNano())
	// prepare collection
	tmp := "/tmp/tiedot_bench"
	os.RemoveAll(tmp)
	col, err := db.OpenCol(tmp)
	if err != nil {
		panic(err)
	}
	col.Index([]string{"a", "b", "c"})
	col.Index([]string{"c", "d"})
	docs := make([]uint64, 0, BENCH2_SIZE)
	// Prepare 1000 docs as a start
	var docToInsert interface{}
	for j := 0; j < 1000; j++ {
		if err = json.Unmarshal([]byte(
			`{"a": {"b": {"c": `+strconv.Itoa(rand.Intn(BENCH2_SIZE))+`}},`+
				`"c": {"d": `+strconv.Itoa(rand.Intn(BENCH2_SIZE))+`},`+
				`"more": "abcdefghijklmnopqrstuvwxyz"}`), &docToInsert); err != nil {
			panic(err)
		}
		if newID, err := col.Insert(docToInsert); err == nil {
			docs = append(docs, newID)
		} else {
			panic(err)
		}
	}
	// benchmark begins
	wp := new(sync.WaitGroup)
	wp.Add(5 * numThreads) // (CRUD + query) * number of benchmark threads
	start := float64(time.Now().UTC().UnixNano())
	// insert BENCH2_SIZE * 2 documents
	for i := 0; i < numThreads; i++ {
		go func(i int) {
			fmt.Printf("Insert thread %d starting\n", i)
			defer wp.Done()
			var docToInsert interface{}
			var err error
			for j := 0; j < BENCH2_SIZE/numThreads*2; j++ {
				if err = json.Unmarshal([]byte(
					`{"a": {"b": {"c": `+strconv.Itoa(rand.Intn(BENCH2_SIZE))+`}},`+
						`"c": {"d": `+strconv.Itoa(rand.Intn(BENCH2_SIZE))+`},`+
						`"more": "abcdefghijklmnopqrstuvwxyz"}`), &docToInsert); err != nil {
					panic(err)
				}
				if newID, err := col.Insert(docToInsert); err == nil {
					docs = append(docs, newID)
				} else {
					panic(err)
				}
			}
			fmt.Printf("Insert thread %d completed\n", i)
		}(i)
	}
	// read BENCH2_SIZE * 2 documents
	for i := 0; i < numThreads; i++ {
		go func(i int) {
			fmt.Printf("Read thread %d starting\n", i)
			defer wp.Done()
			var doc interface{}
			for j := 0; j < BENCH2_SIZE/numThreads*2; j++ {
				col.Read(docs[uint64(rand.Intn(len(docs)))], &doc)
			}
			fmt.Printf("Read thread %d completed\n", i)
		}(i)
	}
	// query BENCH2_SIZE times
	for i := 0; i < numThreads; i++ {
		go func(i int) {
			fmt.Printf("Query thread %d starting\n", i)
			defer wp.Done()
			var query interface{}
			var err error
			for j := 0; j < BENCH2_SIZE/numThreads; j++ {
				if err = json.Unmarshal([]byte(`{"c": [{"eq": `+strconv.Itoa(rand.Intn(BENCH2_SIZE))+`, "in": ["a", "b", "c"], "limit": 1}, `+
					`{"eq": `+strconv.Itoa(rand.Intn(BENCH2_SIZE))+`, "in": ["c", "d"], "limit": 1}]}`), &query); err != nil {
					panic("json error")
				}
				result := make(map[uint64]struct{})
				if err = db.EvalQueryV2(query, col, &result); err != nil {
					panic("query error")
				}
			}
			fmt.Printf("Query thread %d completed\n", i)
		}(i)
	}
	// update BENCH2_SIZE documents
	for i := 0; i < numThreads; i++ {
		go func(i int) {
			fmt.Printf("Update thread %d starting\n", i)
			defer wp.Done()
			var updated interface{}
			var err error
			for j := 0; j < BENCH2_SIZE/numThreads; j++ {
				if err = json.Unmarshal([]byte(
					`{"a": {"b": {"c": `+strconv.Itoa(rand.Intn(BENCH2_SIZE))+`}},`+
						`"c": {"d": `+strconv.Itoa(rand.Intn(BENCH2_SIZE))+`},`+
						`"more": "abcdefghijklmnopqrstuvwxyz"}`), &updated); err != nil {
					panic(err)
				}
				if _, err = col.Update(docs[uint64(rand.Intn(len(docs)))], updated); err != nil {
					// "does not exist" indicates that a deleted document is being updated, it is safe to ignore
					if !strings.Contains(fmt.Sprint(err), "does not exist") {
						fmt.Println(err)
					}
				}
			}
			fmt.Printf("Update thread %d completed\n", i)
		}(i)
	}
	// delete BENCH2_SIZE documents
	for i := 0; i < numThreads; i++ {
		go func(i int) {
			fmt.Printf("Delete thread %d starting\n", i)
			defer wp.Done()
			for j := 0; j < BENCH2_SIZE/numThreads; j++ {
				col.Delete(docs[uint64(rand.Intn(len(docs)))])
			}
			fmt.Printf("Delete thread %d completed\n", i)
		}(i)
	}
	wp.Wait()
	end := float64(time.Now().UTC().UnixNano())
	fmt.Printf("Total operations %d: %d ns/iter, %d iter/sec\n", BENCH2_SIZE*7, int((end-start)/BENCH2_SIZE/7), int(1000000000/((end-start)/BENCH2_SIZE/7)))
}
<|endoftext|>


<commit_before>package core

import (
	"bytes"
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"

	"github.com/emicklei/melrose/notify"
)

// Note represents a musical note.
// Notations:
// 		½C♯.3 = half duration, pitch C, sharp, octave 3, velocity default (70)
//		D     = quarter duration, pitch D, octave 4, no accidental
//      ⅛B♭  = eigth duration, pitch B, octave 4, flat
//		=     = quarter rest
//      -/+   = velocity number
// http://en.wikipedia.org/wiki/Musical_Note
type Note struct {
	Name       string // {C D E F G A B = ^ < >}
	Octave     int    // [0 .. 9]
	Accidental int    // -1 Flat, +1 Sharp, 0 Normal
	Dotted     bool   // if true then fraction is increased by half
	Velocity   int    // 1..127

	fraction float32 // {0.0625,0.125,0.25,0.5,1}
}

func (n Note) Storex() string {
	return fmt.Sprintf("note('%s')", n.String())
}

// ToNote() is part of NoteConvertable
func (n Note) ToNote() Note {
	return n
}

func (n Note) ToRest() Note {
	return Note{
		Name:       "=",
		Octave:     n.Octave,
		Accidental: n.Accidental,
		Dotted:     n.Dotted,
		Velocity:   n.Velocity,
		fraction:   n.fraction,
	}
}

// Replaced is part of Replaceable
func (n Note) Replaced(from, to Sequenceable) Sequenceable {
	if IsIdenticalTo(from, n) {
		return to
	}
	return n
}

var (
	Rest4       = Note{Name: "=", fraction: 0.25}
	PedalUpDown = Note{Name: "^", fraction: 0}
	PedalDown   = Note{Name: ">", fraction: 0}
	PedalUp     = Note{Name: "<", fraction: 0}
)

const validNoteNames = "ABCDEFG=<^>"

func NewNote(name string, octave int, duration float32, accidental int, dot bool, velocity int) (Note, error) {
	if len(name) != 1 {
		return Rest4, fmt.Errorf("note must be one character, got [%s]", name)
	}
	// pedal check
	switch name {
	case "^":
		return PedalUpDown, nil
	case ">":
		return PedalDown, nil
	case "<":
		return PedalUp, nil
	}

	if !strings.Contains(validNoteNames, name) {
		return Rest4, fmt.Errorf("invalid note name [%s]:%s", validNoteNames, name)
	}
	if octave < 0 || octave > 9 {
		return Rest4, fmt.Errorf("invalid octave [0..9]: %d", octave)
	}
	switch duration {
	case 0.0625:
	case 0.125:
	case 0.25:
	case 0.5:
	case 1:
	default:
		return Rest4, fmt.Errorf("invalid fraction [1,0.5,0.25,0.125,0.0625]:%v", duration)
	}

	if accidental != 0 && accidental != -1 && accidental != 1 {
		return Rest4, fmt.Errorf("invalid accidental: %d", accidental)
	}

	return Note{Name: name, Octave: octave, fraction: duration, Accidental: accidental, Dotted: dot, Velocity: velocity}, nil
}

func (n Note) IsRest() bool        { return Rest4.Name == n.Name }
func (n Note) IsPedalUp() bool     { return PedalUp.Name == n.Name }
func (n Note) IsPedalDown() bool   { return PedalDown.Name == n.Name }
func (n Note) IsPedalUpDown() bool { return PedalUpDown.Name == n.Name }

// DurationFactor is the actual duration time factor
func (n Note) DurationFactor() float32 {
	if n.Dotted {
		return n.fraction * 1.5
	}
	return n.fraction
}

func (n Note) S() Sequence {
	return BuildSequence([]Note{n})
}

func (n Note) WithDynamic(emphasis string) Note {
	n.Velocity = ParseVelocity(emphasis)
	return n
}

func (n Note) WithVelocity(v int) Note {
	n.Velocity = v
	return n
}

func (n Note) WithFraction(f float64, dotted bool) Note {
	var fraction float32
	switch f {
	case 16:
		fraction = 0.0625
	case 8:
		fraction = 0.125
	case 4:
		fraction = 0.25
	case 2:
		fraction = 0.5
	case 1:
		fraction = 1
	case 0.5, 0.25, 0.125, 0.0625:
		fraction = float32(f)
	default:
		notify.Panic(fmt.Errorf("cannot create note with fraction [%f]", f))
	}
	// shortest
	if fraction < 0.0625 {
		fraction = 0.0625
	}
	n.fraction = fraction
	return n
}

// Conversion

var noteRegexp = regexp.MustCompile("([1]?[½¼⅛12468]?)(\\.?)([CDEFGAB=<^>])([#♯_♭]?)([0-9]?)([-+]?[-+]?[-+]?)")

// MustParseNote returns a Note by parsing the input. Panic if it fails.
func MustParseNote(input string) Note {
	n, err := ParseNote(input)
	if err != nil {
		panic("MustParseNote failed:" + err.Error())
	}
	return n
}

var N = MustParseNote

// ParseNote reads the format  <(inverse-)duration?>[CDEFGA=<^>]<accidental?><dot?><octave?>
func ParseNote(input string) (Note, error) {
	matches := noteRegexp.FindStringSubmatch(strings.ToUpper(input))
	if matches == nil {
		return Note{}, fmt.Errorf("illegal note: [%s]", input)
	}

	var duration float32
	switch matches[1] {
	case "16":
		duration = 0.0625
	case "⅛", "8":
		duration = 0.125
	case "¼", "4":
		duration = 0.25
	case "½", "2":
		duration = 0.5
	case "1":
		duration = 1
	default:
		duration = 0.25 // quarter
	}

	dotted := matches[2] == "."

	// pedal
	switch matches[3] {
	case "^":
		return PedalUpDown, nil
	case "<":
		return PedalUp, nil
	case ">":
		return PedalDown, nil
	}

	var accidental int
	switch matches[4] {
	case "#":
		accidental = 1
	case "♯":
		accidental = 1
	case "♭":
		accidental = -1
	case "_":
		accidental = -1
	default:
		accidental = 0
	}

	octave := 4
	if len(matches[5]) > 0 {
		i, err := strconv.Atoi(matches[5])
		if err != nil {
			return Note{}, fmt.Errorf("illegal octave: %s", matches[5])

		}
		octave = i
	}
	var velocity = Normal
	if len(matches[6]) > 0 {
		velocity = ParseVelocity(matches[6])
	}
	return NewNote(matches[3], octave, duration, accidental, dotted, velocity)
}

func ParseVelocity(plusmin string) (velocity int) {
	switch plusmin {
	case "-":
		velocity = MezzoPiano
	case "--":
		velocity = Piano
	case "---":
		velocity = Pianissimo
	case "----":
		velocity = Pianississimo
	case "+":
		velocity = MezzoForte
	case "++":
		velocity = Forte
	case "+++":
		velocity = Fortissimo
	case "++++":
		velocity = Fortississimo
	default:
		// 0
		velocity = 72
	}
	return
}

// Formatting

func (n Note) accidentalf(encoded bool) string {
	if n.Accidental == -1 {
		if encoded {
			return "b"
		} else {
			return "♭"
		}
	}
	if n.Accidental == 1 {
		if encoded {
			return "#"
		} else {
			return "♯"
		}
	}
	return ""
}

func (n Note) durationf(encoded bool) string {
	switch n.fraction {
	case 0.0625:
		return "16"
	case 0.125:
		if encoded {
			return "8"
		} else {
			return "⅛"
		}
	case 0.25:
		if encoded {
			return "4"
		} else {
			return "¼"
		}
	case 0.5:
		if encoded {
			return "2"
		} else {
			return "½"
		}
	case 1.0:
		return "1"
	}
	return ""
}

func (n Note) Inspect(i Inspection) {
	i.Properties["length"] = n.DurationFactor()
	i.Properties["midi"] = n.MIDI()
	i.Properties["velocity"] = n.Velocity
}

func (n Note) String() string {
	return n.PrintString(PrintAsSpecified)
}

func (n Note) PrintString(sharpOrFlatKey int) string {
	var buf bytes.Buffer
	n.printOn(&buf, sharpOrFlatKey)
	return buf.String()
}

func (n Note) printOn(buf *bytes.Buffer, sharpOrFlatKey int) {
	if n.IsPedalUp() {
		buf.WriteString(PedalUp.Name)
		return
	}
	if n.IsPedalDown() {
		buf.WriteString(PedalDown.Name)
		return
	}
	if n.IsPedalUpDown() {
		buf.WriteString(PedalUpDown.Name)
		return
	}

	if n.fraction != 0.25 {
		buf.WriteString(n.durationf(false))
	}

	if n.Dotted {
		buf.WriteString(".")
	}

	if n.IsRest() {
		buf.WriteString(n.Name)
		return
	}
	if Sharp == sharpOrFlatKey && n.Accidental == -1 { // want Sharp, specified in Flat
		buf.WriteString(n.Pitched(-1).Name)
		buf.WriteString("♯")
	} else if Flat == sharpOrFlatKey && n.Accidental == 1 { // want Flat, specified in Sharp
		buf.WriteString(n.Pitched(1).Name)
		buf.WriteString("♭")
	} else { // PrintAsSpecified
		buf.WriteString(n.Name)
		if n.Accidental != 0 {
			buf.WriteString(n.accidentalf(false))
		}
	}
	if n.Octave != 4 {
		fmt.Fprintf(buf, "%d", n.Octave)
	}
	if n.Velocity != 72 {
		switch n.Velocity {
		case Pianissimo:
			io.WriteString(buf, "---")
		case Piano:
			io.WriteString(buf, "--")
		case MezzoPiano:
			io.WriteString(buf, "-")
		case MezzoForte:
			io.WriteString(buf, "+")
		case Forte:
			io.WriteString(buf, "++")
		case Fortissimo:
			io.WriteString(buf, "+++")
		}
	}
}
<commit_msg>fix comment<commit_after>package core

import (
	"bytes"
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"

	"github.com/emicklei/melrose/notify"
)

// Note represents a musical note.
// Notations:
// 		½.C♯3 = half duration, pitch C, sharp, octave 3, velocity default (70)
//		D     = quarter duration, pitch D, octave 4, no accidental
//      ⅛B♭  = eigth duration, pitch B, octave 4, flat
//		=     = quarter rest
//      -/+   = velocity number
// http://en.wikipedia.org/wiki/Musical_Note
type Note struct {
	Name       string // {C D E F G A B = ^ < >}
	Octave     int    // [0 .. 9]
	Accidental int    // -1 Flat, +1 Sharp, 0 Normal
	Dotted     bool   // if true then fraction is increased by half
	Velocity   int    // 1..127

	fraction float32 // {0.0625,0.125,0.25,0.5,1}
}

func (n Note) Storex() string {
	return fmt.Sprintf("note('%s')", n.String())
}

// ToNote() is part of NoteConvertable
func (n Note) ToNote() Note {
	return n
}

func (n Note) ToRest() Note {
	return Note{
		Name:       "=",
		Octave:     n.Octave,
		Accidental: n.Accidental,
		Dotted:     n.Dotted,
		Velocity:   n.Velocity,
		fraction:   n.fraction,
	}
}

// Replaced is part of Replaceable
func (n Note) Replaced(from, to Sequenceable) Sequenceable {
	if IsIdenticalTo(from, n) {
		return to
	}
	return n
}

var (
	Rest4       = Note{Name: "=", fraction: 0.25}
	PedalUpDown = Note{Name: "^", fraction: 0}
	PedalDown   = Note{Name: ">", fraction: 0}
	PedalUp     = Note{Name: "<", fraction: 0}
)

const validNoteNames = "ABCDEFG=<^>"

func NewNote(name string, octave int, duration float32, accidental int, dot bool, velocity int) (Note, error) {
	if len(name) != 1 {
		return Rest4, fmt.Errorf("note must be one character, got [%s]", name)
	}
	// pedal check
	switch name {
	case "^":
		return PedalUpDown, nil
	case ">":
		return PedalDown, nil
	case "<":
		return PedalUp, nil
	}

	if !strings.Contains(validNoteNames, name) {
		return Rest4, fmt.Errorf("invalid note name [%s]:%s", validNoteNames, name)
	}
	if octave < 0 || octave > 9 {
		return Rest4, fmt.Errorf("invalid octave [0..9]: %d", octave)
	}
	switch duration {
	case 0.0625:
	case 0.125:
	case 0.25:
	case 0.5:
	case 1:
	default:
		return Rest4, fmt.Errorf("invalid fraction [1,0.5,0.25,0.125,0.0625]:%v", duration)
	}

	if accidental != 0 && accidental != -1 && accidental != 1 {
		return Rest4, fmt.Errorf("invalid accidental: %d", accidental)
	}

	return Note{Name: name, Octave: octave, fraction: duration, Accidental: accidental, Dotted: dot, Velocity: velocity}, nil
}

func (n Note) IsRest() bool        { return Rest4.Name == n.Name }
func (n Note) IsPedalUp() bool     { return PedalUp.Name == n.Name }
func (n Note) IsPedalDown() bool   { return PedalDown.Name == n.Name }
func (n Note) IsPedalUpDown() bool { return PedalUpDown.Name == n.Name }

// DurationFactor is the actual duration time factor
func (n Note) DurationFactor() float32 {
	if n.Dotted {
		return n.fraction * 1.5
	}
	return n.fraction
}

func (n Note) S() Sequence {
	return BuildSequence([]Note{n})
}

func (n Note) WithDynamic(emphasis string) Note {
	n.Velocity = ParseVelocity(emphasis)
	return n
}

func (n Note) WithVelocity(v int) Note {
	n.Velocity = v
	return n
}

func (n Note) WithFraction(f float64, dotted bool) Note {
	var fraction float32
	switch f {
	case 16:
		fraction = 0.0625
	case 8:
		fraction = 0.125
	case 4:
		fraction = 0.25
	case 2:
		fraction = 0.5
	case 1:
		fraction = 1
	case 0.5, 0.25, 0.125, 0.0625:
		fraction = float32(f)
	default:
		notify.Panic(fmt.Errorf("cannot create note with fraction [%f]", f))
	}
	// shortest
	if fraction < 0.0625 {
		fraction = 0.0625
	}
	n.fraction = fraction
	return n
}

// Conversion
// https://regoio.herokuapp.com/
var noteRegexp = regexp.MustCompile("([\\d+\\*]?)([1]?[½¼⅛12468]?)(\\.?)([CDEFGAB=<^>])([#♯_♭]?)([0-9]?)([-+]?[-+]?[-+]?)")

// MustParseNote returns a Note by parsing the input. Panic if it fails.
func MustParseNote(input string) Note {
	n, err := ParseNote(input)
	if err != nil {
		panic("MustParseNote failed:" + err.Error())
	}
	return n
}

var N = MustParseNote

// ParseNote reads the format  <(inverse-)duration?>[CDEFGA=<^>]<accidental?><dot?><octave?>
func ParseNote(input string) (Note, error) {
	matches := noteRegexp.FindStringSubmatch(strings.ToUpper(input))
	if matches == nil {
		return Note{}, fmt.Errorf("illegal note: [%s]", input)
	}

	var duration float32
	switch matches[1] {
	case "16":
		duration = 0.0625
	case "⅛", "8":
		duration = 0.125
	case "¼", "4":
		duration = 0.25
	case "½", "2":
		duration = 0.5
	case "1":
		duration = 1
	default:
		duration = 0.25 // quarter
	}

	dotted := matches[2] == "."

	// pedal
	switch matches[3] {
	case "^":
		return PedalUpDown, nil
	case "<":
		return PedalUp, nil
	case ">":
		return PedalDown, nil
	}

	var accidental int
	switch matches[4] {
	case "#":
		accidental = 1
	case "♯":
		accidental = 1
	case "♭":
		accidental = -1
	case "_":
		accidental = -1
	default:
		accidental = 0
	}

	octave := 4
	if len(matches[5]) > 0 {
		i, err := strconv.Atoi(matches[5])
		if err != nil {
			return Note{}, fmt.Errorf("illegal octave: %s", matches[5])

		}
		octave = i
	}
	var velocity = Normal
	if len(matches[6]) > 0 {
		velocity = ParseVelocity(matches[6])
	}
	return NewNote(matches[3], octave, duration, accidental, dotted, velocity)
}

func ParseVelocity(plusmin string) (velocity int) {
	switch plusmin {
	case "-":
		velocity = MezzoPiano
	case "--":
		velocity = Piano
	case "---":
		velocity = Pianissimo
	case "----":
		velocity = Pianississimo
	case "+":
		velocity = MezzoForte
	case "++":
		velocity = Forte
	case "+++":
		velocity = Fortissimo
	case "++++":
		velocity = Fortississimo
	default:
		// 0
		velocity = 72
	}
	return
}

// Formatting

func (n Note) accidentalf(encoded bool) string {
	if n.Accidental == -1 {
		if encoded {
			return "b"
		} else {
			return "♭"
		}
	}
	if n.Accidental == 1 {
		if encoded {
			return "#"
		} else {
			return "♯"
		}
	}
	return ""
}

func (n Note) durationf(encoded bool) string {
	switch n.fraction {
	case 0.0625:
		return "16"
	case 0.125:
		if encoded {
			return "8"
		} else {
			return "⅛"
		}
	case 0.25:
		if encoded {
			return "4"
		} else {
			return "¼"
		}
	case 0.5:
		if encoded {
			return "2"
		} else {
			return "½"
		}
	case 1.0:
		return "1"
	}
	return ""
}

func (n Note) Inspect(i Inspection) {
	i.Properties["length"] = n.DurationFactor()
	i.Properties["midi"] = n.MIDI()
	i.Properties["velocity"] = n.Velocity
}

func (n Note) String() string {
	return n.PrintString(PrintAsSpecified)
}

func (n Note) PrintString(sharpOrFlatKey int) string {
	var buf bytes.Buffer
	n.printOn(&buf, sharpOrFlatKey)
	return buf.String()
}

func (n Note) printOn(buf *bytes.Buffer, sharpOrFlatKey int) {
	if n.IsPedalUp() {
		buf.WriteString(PedalUp.Name)
		return
	}
	if n.IsPedalDown() {
		buf.WriteString(PedalDown.Name)
		return
	}
	if n.IsPedalUpDown() {
		buf.WriteString(PedalUpDown.Name)
		return
	}

	if n.fraction != 0.25 {
		buf.WriteString(n.durationf(false))
	}

	if n.Dotted {
		buf.WriteString(".")
	}

	if n.IsRest() {
		buf.WriteString(n.Name)
		return
	}
	if Sharp == sharpOrFlatKey && n.Accidental == -1 { // want Sharp, specified in Flat
		buf.WriteString(n.Pitched(-1).Name)
		buf.WriteString("♯")
	} else if Flat == sharpOrFlatKey && n.Accidental == 1 { // want Flat, specified in Sharp
		buf.WriteString(n.Pitched(1).Name)
		buf.WriteString("♭")
	} else { // PrintAsSpecified
		buf.WriteString(n.Name)
		if n.Accidental != 0 {
			buf.WriteString(n.accidentalf(false))
		}
	}
	if n.Octave != 4 {
		fmt.Fprintf(buf, "%d", n.Octave)
	}
	if n.Velocity != 72 {
		switch n.Velocity {
		case Pianissimo:
			io.WriteString(buf, "---")
		case Piano:
			io.WriteString(buf, "--")
		case MezzoPiano:
			io.WriteString(buf, "-")
		case MezzoForte:
			io.WriteString(buf, "+")
		case Forte:
			io.WriteString(buf, "++")
		case Fortissimo:
			io.WriteString(buf, "+++")
		}
	}
}
<|endoftext|>


<commit_before>package aerospike

import (
	"errors"
	"log"
	"net"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/plugins/inputs"

	as "github.com/aerospike/aerospike-client-go"
)

type Aerospike struct {
	Servers []string
}

var sampleConfig = `
  ## Aerospike servers to connect to (with port)
  ## This plugin will query all namespaces the aerospike
  ## server has configured and get stats for them.
  servers = ["localhost:3000"]
 `

func (a *Aerospike) SampleConfig() string {
	return sampleConfig
}

func (a *Aerospike) Description() string {
	return "Read stats from aerospike server(s)"
}

func (a *Aerospike) Gather(acc telegraf.Accumulator) error {
	if len(a.Servers) == 0 {
		return a.gatherServer("127.0.0.1:3000", acc)
	}

	var wg sync.WaitGroup
	wg.Add(len(a.Servers))
	for _, server := range a.Servers {
		go func(serv string) {
			defer wg.Done()
			acc.AddError(a.gatherServer(serv, acc))
		}(server)
	}

	wg.Wait()
	return nil
}

func (a *Aerospike) gatherServer(hostport string, acc telegraf.Accumulator) error {
	host, port, err := net.SplitHostPort(hostport)
	if err != nil {
		return err
	}

	iport, err := strconv.Atoi(port)
	if err != nil {
		iport = 3000
	}

	c, err := as.NewClient(host, iport)
	if err != nil {
		return err
	}
	defer c.Close()

	nodes := c.GetNodes()
	for _, n := range nodes {
		tags := map[string]string{
			"aerospike_host": hostport,
			"node_name":      n.GetName(),
		}
		fields := make(map[string]interface{})
		stats, err := as.RequestNodeStats(n)
		if err != nil {
			return err
		}
		for k, v := range stats {
			val, err := parseValue(v)
			if err == nil {
				fields[strings.Replace(k, "-", "_", -1)] = val
			} else {
				log.Printf("I! skipping aerospike field %v with int64 overflow", k)
			}
		}
		acc.AddFields("aerospike_node", fields, tags, time.Now())

		info, err := as.RequestNodeInfo(n, "namespaces")
		if err != nil {
			return err
		}
		namespaces := strings.Split(info["namespaces"], ";")

		for _, namespace := range namespaces {
			nTags := map[string]string{
				"aerospike_host": hostport,
				"node_name":      n.GetName(),
			}
			nTags["namespace"] = namespace
			nFields := make(map[string]interface{})
			info, err := as.RequestNodeInfo(n, "namespace/"+namespace)
			if err != nil {
				continue
			}
			stats := strings.Split(info["namespace/"+namespace], ";")
			for _, stat := range stats {
				parts := strings.Split(stat, "=")
				if len(parts) < 2 {
					continue
				}
				val, err := parseValue(parts[1])
				if err == nil {
					nFields[strings.Replace(parts[0], "-", "_", -1)] = val
				} else {
					log.Printf("I! skipping aerospike field %v with int64 overflow", parts[0])
				}
			}
			acc.AddFields("aerospike_namespace", nFields, nTags, time.Now())
		}
	}
	return nil
}

func parseValue(v string) (interface{}, error) {
	if parsed, err := strconv.ParseInt(v, 10, 64); err == nil {
		return parsed, nil
	} else if _, err := strconv.ParseUint(v, 10, 64); err == nil {
		// int64 overflow, yet valid uint64
		return nil, errors.New("Number is too large")
	} else if parsed, err := strconv.ParseBool(v); err == nil {
		return parsed, nil
	} else {
		return v, nil
	}
}

func copyTags(m map[string]string) map[string]string {
	out := make(map[string]string)
	for k, v := range m {
		out[k] = v
	}
	return out
}

func init() {
	inputs.Add("aerospike", func() telegraf.Input {
		return &Aerospike{}
	})
}
<commit_msg>Log aerospike field value on error<commit_after>package aerospike

import (
	"errors"
	"log"
	"net"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/plugins/inputs"

	as "github.com/aerospike/aerospike-client-go"
)

type Aerospike struct {
	Servers []string
}

var sampleConfig = `
  ## Aerospike servers to connect to (with port)
  ## This plugin will query all namespaces the aerospike
  ## server has configured and get stats for them.
  servers = ["localhost:3000"]
 `

func (a *Aerospike) SampleConfig() string {
	return sampleConfig
}

func (a *Aerospike) Description() string {
	return "Read stats from aerospike server(s)"
}

func (a *Aerospike) Gather(acc telegraf.Accumulator) error {
	if len(a.Servers) == 0 {
		return a.gatherServer("127.0.0.1:3000", acc)
	}

	var wg sync.WaitGroup
	wg.Add(len(a.Servers))
	for _, server := range a.Servers {
		go func(serv string) {
			defer wg.Done()
			acc.AddError(a.gatherServer(serv, acc))
		}(server)
	}

	wg.Wait()
	return nil
}

func (a *Aerospike) gatherServer(hostport string, acc telegraf.Accumulator) error {
	host, port, err := net.SplitHostPort(hostport)
	if err != nil {
		return err
	}

	iport, err := strconv.Atoi(port)
	if err != nil {
		iport = 3000
	}

	c, err := as.NewClient(host, iport)
	if err != nil {
		return err
	}
	defer c.Close()

	nodes := c.GetNodes()
	for _, n := range nodes {
		tags := map[string]string{
			"aerospike_host": hostport,
			"node_name":      n.GetName(),
		}
		fields := make(map[string]interface{})
		stats, err := as.RequestNodeStats(n)
		if err != nil {
			return err
		}
		for k, v := range stats {
			val, err := parseValue(v)
			if err == nil {
				fields[strings.Replace(k, "-", "_", -1)] = val
			} else {
				log.Printf("I! skipping aerospike field %v with int64 overflow: %q", k, v)
			}
		}
		acc.AddFields("aerospike_node", fields, tags, time.Now())

		info, err := as.RequestNodeInfo(n, "namespaces")
		if err != nil {
			return err
		}
		namespaces := strings.Split(info["namespaces"], ";")

		for _, namespace := range namespaces {
			nTags := map[string]string{
				"aerospike_host": hostport,
				"node_name":      n.GetName(),
			}
			nTags["namespace"] = namespace
			nFields := make(map[string]interface{})
			info, err := as.RequestNodeInfo(n, "namespace/"+namespace)
			if err != nil {
				continue
			}
			stats := strings.Split(info["namespace/"+namespace], ";")
			for _, stat := range stats {
				parts := strings.Split(stat, "=")
				if len(parts) < 2 {
					continue
				}
				val, err := parseValue(parts[1])
				if err == nil {
					nFields[strings.Replace(parts[0], "-", "_", -1)] = val
				} else {
					log.Printf("I! skipping aerospike field %v with int64 overflow: %q", parts[0], parts[1])
				}
			}
			acc.AddFields("aerospike_namespace", nFields, nTags, time.Now())
		}
	}
	return nil
}

func parseValue(v string) (interface{}, error) {
	if parsed, err := strconv.ParseInt(v, 10, 64); err == nil {
		return parsed, nil
	} else if _, err := strconv.ParseUint(v, 10, 64); err == nil {
		// int64 overflow, yet valid uint64
		return nil, errors.New("Number is too large")
	} else if parsed, err := strconv.ParseBool(v); err == nil {
		return parsed, nil
	} else {
		return v, nil
	}
}

func copyTags(m map[string]string) map[string]string {
	out := make(map[string]string)
	for k, v := range m {
		out[k] = v
	}
	return out
}

func init() {
	inputs.Add("aerospike", func() telegraf.Input {
		return &Aerospike{}
	})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"image"
	"log"
	"os"

	"github.com/ninjasphere/sphere-go-led-controller/model"
	"github.com/ninjasphere/sphere-go-led-controller/ui"
	"github.com/ninjasphere/sphere-go-led-controller/util"
)

// Implements the pairing UI with a direct connection to the LED matrix.
type directPairingUI struct {
	layout *ui.PairingLayout
}

func newDirectPairingUI() (*directPairingUI, error) {

	pairingUI := &directPairingUI{
		layout: ui.NewPairingLayout(),
	}

	led, err := util.GetLEDConnection()

	if err != nil {
		log.Fatalf("Failed to get connection to LED matrix: %s", err)
	}

	go func() {

		s, err := util.GetLEDConnection()

		if err != nil {
			log.Fatalf("Failed to get connection to LED matrix: %s", err)
		}

		// Send a blank image to the led matrix
		util.WriteLEDMatrix(image.NewRGBA(image.Rect(0, 0, 16, 16)), s)

		// Main drawing loop
		for {
			image, err := pairingUI.layout.Render()
			if err != nil {
				log.Fatalf("Unable to render to led: %s", err)
			}
			util.WriteLEDMatrix(image, led)
		}

	}()

	return pairingUI, nil
}

func (u *directPairingUI) DisplayColorHint(color string) error {
	fmt.Fprintf(os.Stderr, "color hint %s\n", color)
	return u.DisplayColorHint(color)
}

func (u *directPairingUI) DisplayPairingCode(code string) error {
	fmt.Fprintf(os.Stderr, "pairing code %d\n", code)
	return u.DisplayPairingCode(code)
}

func (u *directPairingUI) EnableControl() error {
	return fmt.Errorf("Control is not available in reset mode.")
}

func (u *directPairingUI) DisplayIcon(icon string) error {
	return u.DisplayIcon(icon)
}

func (u *directPairingUI) DisplayResetMode(m *model.ResetMode) error {
	return u.DisplayResetMode(m)
}
<commit_msg>Fix the direct pairing ui<commit_after>package main

import (
	"fmt"
	"image"
	"log"
	"os"

	"github.com/lucasb-eyer/go-colorful"
	"github.com/ninjasphere/sphere-go-led-controller/model"
	"github.com/ninjasphere/sphere-go-led-controller/ui"
	"github.com/ninjasphere/sphere-go-led-controller/util"
)

// Implements the pairing UI with a direct connection to the LED matrix.
type directPairingUI struct {
	layout *ui.PairingLayout
}

func newDirectPairingUI() (*directPairingUI, error) {

	pairingUI := &directPairingUI{
		layout: ui.NewPairingLayout(),
	}

	led, err := util.GetLEDConnection()

	if err != nil {
		log.Fatalf("Failed to get connection to LED matrix: %s", err)
	}

	go func() {

		s, err := util.GetLEDConnection()

		if err != nil {
			log.Fatalf("Failed to get connection to LED matrix: %s", err)
		}

		// Send a blank image to the led matrix
		util.WriteLEDMatrix(image.NewRGBA(image.Rect(0, 0, 16, 16)), s)

		// Main drawing loop
		for {
			image, err := pairingUI.layout.Render()
			if err != nil {
				log.Fatalf("Unable to render to led: %s", err)
			}
			util.WriteLEDMatrix(image, led)
		}

	}()

	return pairingUI, nil
}

func (u *directPairingUI) DisplayColorHint(color string) error {
	fmt.Fprintf(os.Stderr, "color hint %s\n", color)
	col, err := colorful.Hex(color)

	if err != nil {
		return err
	}

	u.layout.ShowColor(col)
	return nil
}

func (u *directPairingUI) DisplayPairingCode(code string) error {
	fmt.Fprintf(os.Stderr, "pairing code %d\n", code)
	u.layout.ShowCode(code)
	return nil
}

func (u *directPairingUI) EnableControl() error {
	return fmt.Errorf("Control is not available in reset mode.")
}

func (u *directPairingUI) DisplayIcon(icon string) error {
	u.layout.ShowIcon(icon)
	return nil
}

func (u *directPairingUI) DisplayResetMode(m *model.ResetMode) error {
	return fmt.Errorf("Reset mode ui is not available in reset mode")
}
<|endoftext|>


<commit_before>package signalfx

import (
	"encoding/json"
	"net"
	"net/http"
	"sync"
	"sync/atomic"
	"time"

	"bytes"
	"context"

	"github.com/gorilla/mux"
	"github.com/signalfx/com_signalfx_metrics_protobuf"
	"github.com/signalfx/gateway/logkey"
	"github.com/signalfx/gateway/protocol"
	"github.com/signalfx/gateway/protocol/collectd"
	"github.com/signalfx/gateway/protocol/signalfx/additionalspantags"
	"github.com/signalfx/gateway/protocol/signalfx/spanobfuscation"
	"github.com/signalfx/gateway/protocol/signalfx/tagreplace"
	"github.com/signalfx/gateway/protocol/zipper"
	"github.com/signalfx/golib/datapoint"
	"github.com/signalfx/golib/datapoint/dpsink"
	"github.com/signalfx/golib/errors"
	"github.com/signalfx/golib/log"
	"github.com/signalfx/golib/pointer"
	"github.com/signalfx/golib/sfxclient"
	"github.com/signalfx/golib/web"
)

// ListenerServer controls listening on a socket for SignalFx connections
type ListenerServer struct {
	protocol.CloseableHealthCheck
	listener net.Listener
	logger   log.Logger

	internalCollectors sfxclient.Collector
	metricHandler      metricHandler
}

// Close the exposed socket listening for new connections
func (streamer *ListenerServer) Close() error {
	return streamer.listener.Close()
}

// Addr returns the currently listening address
func (streamer *ListenerServer) Addr() net.Addr {
	return streamer.listener.Addr()
}

// Datapoints returns the datapoints about various internal endpoints
func (streamer *ListenerServer) Datapoints() []*datapoint.Datapoint {
	return append(streamer.internalCollectors.Datapoints(), streamer.HealthDatapoints()...)
}

// MericTypeGetter is an old metric interface that returns the type of a metric name
type MericTypeGetter interface {
	GetMetricTypeFromMap(metricName string) com_signalfx_metrics_protobuf.MetricType
}

// ErrorReader are datapoint streamers that read from a HTTP request and return errors if
// the stream is invalid
type ErrorReader interface {
	Read(ctx context.Context, req *http.Request) error
}

// ErrorTrackerHandler behaves like a http handler, but tracks error returns from a ErrorReader
type ErrorTrackerHandler struct {
	TotalErrors int64
	reader      ErrorReader
	Logger      log.Logger
}

// Datapoints gets TotalErrors stats
func (e *ErrorTrackerHandler) Datapoints() []*datapoint.Datapoint {
	return []*datapoint.Datapoint{
		sfxclient.Cumulative("total_errors", nil, atomic.LoadInt64(&e.TotalErrors)),
	}
}

// ServeHTTPC will serve the wrapped ErrorReader and return the error (if any) to rw if ErrorReader
// fails
func (e *ErrorTrackerHandler) ServeHTTPC(ctx context.Context, rw http.ResponseWriter, req *http.Request) {
	if err := e.reader.Read(ctx, req); err != nil {
		atomic.AddInt64(&e.TotalErrors, 1)
		rw.WriteHeader(http.StatusBadRequest)
		_, err = rw.Write([]byte(err.Error()))
		log.IfErr(e.Logger, err)
		return
	}
	_, err := rw.Write([]byte(`"OK"`))
	log.IfErr(e.Logger, err)
}

// ListenerConfig controls optional parameters for the listener
type ListenerConfig struct {
	ListenAddr                         *string
	HealthCheck                        *string
	Timeout                            *time.Duration
	Logger                             log.Logger
	RootContext                        context.Context
	JSONMarshal                        func(v interface{}) ([]byte, error)
	DebugContext                       *web.HeaderCtxFlag
	HTTPChain                          web.NextConstructor
	SpanNameReplacementRules           []string
	SpanNameReplacementBreakAfterMatch *bool
	AdditionalSpanTags                 map[string]string
	RemoveSpanTags                     []*spanobfuscation.TagMatchRuleConfig
	ObfuscateSpanTags                  []*spanobfuscation.TagMatchRuleConfig
}

var defaultListenerConfig = &ListenerConfig{
	ListenAddr:                         pointer.String("127.0.0.1:12345"),
	HealthCheck:                        pointer.String("/healthz"),
	Timeout:                            pointer.Duration(time.Second * 30),
	Logger:                             log.Discard,
	RootContext:                        context.Background(),
	JSONMarshal:                        json.Marshal,
	SpanNameReplacementRules:           []string{},
	SpanNameReplacementBreakAfterMatch: pointer.Bool(true),
	AdditionalSpanTags:                 make(map[string]string),
	RemoveSpanTags:                     []*spanobfuscation.TagMatchRuleConfig{},
	ObfuscateSpanTags:                  []*spanobfuscation.TagMatchRuleConfig{},
}

type metricHandler struct {
	metricCreationsMapMutex sync.Mutex
	metricCreationsMap      map[string]com_signalfx_metrics_protobuf.MetricType
	jsonMarshal             func(v interface{}) ([]byte, error)
	logger                  log.Logger
}

func (handler *metricHandler) ServeHTTP(writter http.ResponseWriter, req *http.Request) {
	dec := json.NewDecoder(req.Body)
	var d []MetricCreationStruct
	if err := dec.Decode(&d); err != nil {
		handler.logger.Log(log.Err, err, "Invalid metric creation request")
		writter.WriteHeader(http.StatusBadRequest)
		_, err = writter.Write([]byte(`{msg:"Invalid creation request"}`))
		log.IfErr(handler.logger, err)
		return
	}
	handler.metricCreationsMapMutex.Lock()
	defer handler.metricCreationsMapMutex.Unlock()
	ret := []MetricCreationResponse{}
	for _, m := range d {
		metricType, ok := com_signalfx_metrics_protobuf.MetricType_value[m.MetricType]
		if !ok {
			writter.WriteHeader(http.StatusBadRequest)
			_, err := writter.Write([]byte(`{msg:"Invalid metric type"}`))
			log.IfErr(handler.logger, err)
			return
		}
		handler.metricCreationsMap[m.MetricName] = com_signalfx_metrics_protobuf.MetricType(metricType)
		ret = append(ret, MetricCreationResponse{Code: 409})
	}
	toWrite, err := handler.jsonMarshal(ret)
	if err != nil {
		handler.logger.Log(log.Err, err, "Unable to marshal json")
		writter.WriteHeader(http.StatusBadRequest)
		_, err = writter.Write([]byte(`{msg:"Unable to marshal json!"}`))
		log.IfErr(handler.logger, err)
		return
	}
	writter.WriteHeader(http.StatusOK)
	_, err = writter.Write(toWrite)
	log.IfErr(handler.logger, err)
}

func (handler *metricHandler) GetMetricTypeFromMap(metricName string) com_signalfx_metrics_protobuf.MetricType {
	handler.metricCreationsMapMutex.Lock()
	defer handler.metricCreationsMapMutex.Unlock()
	mt, ok := handler.metricCreationsMap[metricName]
	if !ok {
		return com_signalfx_metrics_protobuf.MetricType_GAUGE
	}
	return mt
}

// NewListener servers http requests for Signalfx datapoints
func NewListener(sink Sink, conf *ListenerConfig) (*ListenerServer, error) {
	conf = pointer.FillDefaultFrom(conf, defaultListenerConfig).(*ListenerConfig)

	listener, err := net.Listen("tcp", *conf.ListenAddr)
	if err != nil {
		return nil, errors.Annotatef(err, "cannot open listening address %s", *conf.ListenAddr)
	}
	r := mux.NewRouter()

	server := http.Server{
		Handler:      r,
		Addr:         *conf.ListenAddr,
		ReadTimeout:  *conf.Timeout,
		WriteTimeout: *conf.Timeout,
	}
	listenServer := ListenerServer{
		listener: listener,
		logger:   conf.Logger,
		metricHandler: metricHandler{
			metricCreationsMap: make(map[string]com_signalfx_metrics_protobuf.MetricType),
			logger:             log.NewContext(conf.Logger).With(logkey.Struct, "metricHandler"),
			jsonMarshal:        conf.JSONMarshal,
		},
	}
	listenServer.SetupHealthCheck(conf.HealthCheck, r, conf.Logger)

	r.Handle("/v1/metric", &listenServer.metricHandler)
	r.Handle("/metric", &listenServer.metricHandler)

	traceSink, err := createTraceSink(sink, conf)

	listenServer.internalCollectors = sfxclient.NewMultiCollector(
		setupNotFoundHandler(conf.RootContext, r),
		setupProtobufV1(conf.RootContext, r, sink, &listenServer.metricHandler, conf.Logger, conf.HTTPChain),
		setupJSONV1(conf.RootContext, r, sink, &listenServer.metricHandler, conf.Logger, conf.HTTPChain),
		setupProtobufV2(conf.RootContext, r, sink, conf.Logger, conf.DebugContext, conf.HTTPChain),
		setupProtobufEventV2(conf.RootContext, r, sink, conf.Logger, conf.DebugContext, conf.HTTPChain),
		setupJSONV2(conf.RootContext, r, sink, conf.Logger, conf.DebugContext, conf.HTTPChain),
		setupJSONEventV2(conf.RootContext, r, sink, conf.Logger, conf.DebugContext, conf.HTTPChain),
		setupCollectd(conf.RootContext, r, sink, conf.DebugContext, conf.HTTPChain, conf.Logger),
		setupThriftTraceV1(conf.RootContext, r, traceSink, conf.Logger, conf.HTTPChain),
		setupJSONTraceV1(conf.RootContext, r, traceSink, conf.Logger, conf.HTTPChain),
	)

	go func() {
		log.IfErr(conf.Logger, server.Serve(listener))
	}()
	return &listenServer, err
}

func setupNotFoundHandler(ctx context.Context, r *mux.Router) sfxclient.Collector {
	metricTracking := web.RequestCounter{}
	r.NotFoundHandler = web.NewHandler(ctx, web.FromHTTP(http.NotFoundHandler())).Add(web.NextHTTP(metricTracking.ServeHTTP))
	return &sfxclient.WithDimensions{
		Dimensions: map[string]string{"protocol": "http404"},
		Collector:  &metricTracking,
	}
}

func createTraceSink(sink Sink, conf *ListenerConfig) (Sink, error) {
	if len(conf.SpanNameReplacementRules) > 0 {
		var err1 error
		sink, err1 = tagreplace.New(conf.SpanNameReplacementRules, *conf.SpanNameReplacementBreakAfterMatch, sink)
		if err1 != nil {
			return nil, errors.Annotatef(err1, "cannot parse tag replacement rules %v", conf.SpanNameReplacementRules)
		}
	}
	if len(conf.AdditionalSpanTags) > 0 {
		sink = additionalspantags.New(conf.AdditionalSpanTags, sink)
	}
	if len(conf.ObfuscateSpanTags) > 0 {
		var err error
		sink, err = spanobfuscation.NewObf(conf.ObfuscateSpanTags, sink)
		if err != nil {
			return nil, errors.Annotatef(err, "cannot parse span tag obfuscation rules %v", conf.ObfuscateSpanTags)
		}
	}
	if len(conf.RemoveSpanTags) > 0 {
		var err error
		sink, err = spanobfuscation.NewRm(conf.RemoveSpanTags, sink)
		if err != nil {
			return nil, errors.Annotatef(err, "cannot parse span tag removal rules %v", conf.RemoveSpanTags)
		}
	}
	return sink, nil
}

// SetupChain wraps the reader returned by getReader in an http.Handler along
// with some middleware that calculates internal metrics about requests.
func SetupChain(ctx context.Context, sink Sink, chainType string, getReader func(Sink) ErrorReader, httpChain web.NextConstructor, logger log.Logger, moreConstructors ...web.Constructor) (http.Handler, sfxclient.Collector) {
	zippers := zipper.NewZipper()

	counter := &dpsink.Counter{
		Logger:        logger,
		DroppedReason: "mux",
	}

	ucount := UnifyNextSinkWrap(counter)
	finalSink := FromChain(sink, NextWrap(ucount))
	errReader := getReader(finalSink)
	errorTracker := ErrorTrackerHandler{
		reader: errReader,
		Logger: logger,
	}
	metricTracking := web.RequestCounter{}
	handler := web.NewHandler(ctx, &errorTracker).Add(web.NextHTTP(metricTracking.ServeHTTP)).Add(httpChain)
	for _, c := range moreConstructors {
		handler.Add(c)
	}
	st := &sfxclient.WithDimensions{
		Collector: sfxclient.NewMultiCollector(
			&metricTracking,
			&errorTracker,
			counter,
			zippers,
		),
		Dimensions: map[string]string{
			"protocol": "sfx_" + chainType,
		},
	}
	return zippers.GzipHandler(handler), st
}

// SetupJSONByPaths tells the router which paths the given handler (which should handle the given
// endpoint) should see
func SetupJSONByPaths(r *mux.Router, handler http.Handler, endpoint string) {
	r.Path(endpoint).Methods("POST").Headers("Content-Type", "application/json").Handler(handler)
	r.Path(endpoint).Methods("POST").Headers("Content-Type", "application/json; charset=UTF-8").Handler(handler)
	r.Path(endpoint).Methods("POST").Headers("Content-Type", "").HandlerFunc(web.InvalidContentType)
	r.Path(endpoint).Methods("POST").Handler(handler)
}

func setupCollectd(ctx context.Context, r *mux.Router, sink dpsink.Sink, debugContext *web.HeaderCtxFlag, httpChain web.NextConstructor, logger log.Logger) sfxclient.Collector {
	counter := &dpsink.Counter{
		Logger:        logger,
		DroppedReason: "mux",
	}
	finalSink := dpsink.FromChain(sink, dpsink.NextWrap(counter))
	decoder := collectd.JSONDecoder{
		Logger: logger,
		SendTo: finalSink,
	}
	metricTracking := &web.RequestCounter{}
	httpHandler := web.NewHandler(ctx, &decoder).Add(web.NextHTTP(metricTracking.ServeHTTP), debugContext, httpChain)
	collectd.SetupCollectdPaths(r, httpHandler, "/v1/collectd")
	return &sfxclient.WithDimensions{
		Collector: sfxclient.NewMultiCollector(
			metricTracking,
			counter,
			&decoder,
		),
		Dimensions: map[string]string{
			"type": "collectd",
		},
	}
}

func readFromRequest(jeff *bytes.Buffer, req *http.Request, logger log.Logger) error {
	// for compressed transactions, contentLength isn't trustworthy
	readLen, err := jeff.ReadFrom(req.Body)
	if err != nil {
		logger.Log(log.Err, err, logkey.ReadLen, readLen, logkey.ContentLength, req.ContentLength, "Unable to fully read from buffer")
		return err
	}
	return nil
}
<commit_msg>Update trace sink order of operations<commit_after>package signalfx

import (
	"encoding/json"
	"net"
	"net/http"
	"sync"
	"sync/atomic"
	"time"

	"bytes"
	"context"

	"github.com/gorilla/mux"
	"github.com/signalfx/com_signalfx_metrics_protobuf"
	"github.com/signalfx/gateway/logkey"
	"github.com/signalfx/gateway/protocol"
	"github.com/signalfx/gateway/protocol/collectd"
	"github.com/signalfx/gateway/protocol/signalfx/additionalspantags"
	"github.com/signalfx/gateway/protocol/signalfx/spanobfuscation"
	"github.com/signalfx/gateway/protocol/signalfx/tagreplace"
	"github.com/signalfx/gateway/protocol/zipper"
	"github.com/signalfx/golib/datapoint"
	"github.com/signalfx/golib/datapoint/dpsink"
	"github.com/signalfx/golib/errors"
	"github.com/signalfx/golib/log"
	"github.com/signalfx/golib/pointer"
	"github.com/signalfx/golib/sfxclient"
	"github.com/signalfx/golib/web"
)

// ListenerServer controls listening on a socket for SignalFx connections
type ListenerServer struct {
	protocol.CloseableHealthCheck
	listener net.Listener
	logger   log.Logger

	internalCollectors sfxclient.Collector
	metricHandler      metricHandler
}

// Close the exposed socket listening for new connections
func (streamer *ListenerServer) Close() error {
	return streamer.listener.Close()
}

// Addr returns the currently listening address
func (streamer *ListenerServer) Addr() net.Addr {
	return streamer.listener.Addr()
}

// Datapoints returns the datapoints about various internal endpoints
func (streamer *ListenerServer) Datapoints() []*datapoint.Datapoint {
	return append(streamer.internalCollectors.Datapoints(), streamer.HealthDatapoints()...)
}

// MericTypeGetter is an old metric interface that returns the type of a metric name
type MericTypeGetter interface {
	GetMetricTypeFromMap(metricName string) com_signalfx_metrics_protobuf.MetricType
}

// ErrorReader are datapoint streamers that read from a HTTP request and return errors if
// the stream is invalid
type ErrorReader interface {
	Read(ctx context.Context, req *http.Request) error
}

// ErrorTrackerHandler behaves like a http handler, but tracks error returns from a ErrorReader
type ErrorTrackerHandler struct {
	TotalErrors int64
	reader      ErrorReader
	Logger      log.Logger
}

// Datapoints gets TotalErrors stats
func (e *ErrorTrackerHandler) Datapoints() []*datapoint.Datapoint {
	return []*datapoint.Datapoint{
		sfxclient.Cumulative("total_errors", nil, atomic.LoadInt64(&e.TotalErrors)),
	}
}

// ServeHTTPC will serve the wrapped ErrorReader and return the error (if any) to rw if ErrorReader
// fails
func (e *ErrorTrackerHandler) ServeHTTPC(ctx context.Context, rw http.ResponseWriter, req *http.Request) {
	if err := e.reader.Read(ctx, req); err != nil {
		atomic.AddInt64(&e.TotalErrors, 1)
		rw.WriteHeader(http.StatusBadRequest)
		_, err = rw.Write([]byte(err.Error()))
		log.IfErr(e.Logger, err)
		return
	}
	_, err := rw.Write([]byte(`"OK"`))
	log.IfErr(e.Logger, err)
}

// ListenerConfig controls optional parameters for the listener
type ListenerConfig struct {
	ListenAddr                         *string
	HealthCheck                        *string
	Timeout                            *time.Duration
	Logger                             log.Logger
	RootContext                        context.Context
	JSONMarshal                        func(v interface{}) ([]byte, error)
	DebugContext                       *web.HeaderCtxFlag
	HTTPChain                          web.NextConstructor
	SpanNameReplacementRules           []string
	SpanNameReplacementBreakAfterMatch *bool
	AdditionalSpanTags                 map[string]string
	RemoveSpanTags                     []*spanobfuscation.TagMatchRuleConfig
	ObfuscateSpanTags                  []*spanobfuscation.TagMatchRuleConfig
}

var defaultListenerConfig = &ListenerConfig{
	ListenAddr:                         pointer.String("127.0.0.1:12345"),
	HealthCheck:                        pointer.String("/healthz"),
	Timeout:                            pointer.Duration(time.Second * 30),
	Logger:                             log.Discard,
	RootContext:                        context.Background(),
	JSONMarshal:                        json.Marshal,
	SpanNameReplacementRules:           []string{},
	SpanNameReplacementBreakAfterMatch: pointer.Bool(true),
	AdditionalSpanTags:                 make(map[string]string),
	RemoveSpanTags:                     []*spanobfuscation.TagMatchRuleConfig{},
	ObfuscateSpanTags:                  []*spanobfuscation.TagMatchRuleConfig{},
}

type metricHandler struct {
	metricCreationsMapMutex sync.Mutex
	metricCreationsMap      map[string]com_signalfx_metrics_protobuf.MetricType
	jsonMarshal             func(v interface{}) ([]byte, error)
	logger                  log.Logger
}

func (handler *metricHandler) ServeHTTP(writter http.ResponseWriter, req *http.Request) {
	dec := json.NewDecoder(req.Body)
	var d []MetricCreationStruct
	if err := dec.Decode(&d); err != nil {
		handler.logger.Log(log.Err, err, "Invalid metric creation request")
		writter.WriteHeader(http.StatusBadRequest)
		_, err = writter.Write([]byte(`{msg:"Invalid creation request"}`))
		log.IfErr(handler.logger, err)
		return
	}
	handler.metricCreationsMapMutex.Lock()
	defer handler.metricCreationsMapMutex.Unlock()
	ret := []MetricCreationResponse{}
	for _, m := range d {
		metricType, ok := com_signalfx_metrics_protobuf.MetricType_value[m.MetricType]
		if !ok {
			writter.WriteHeader(http.StatusBadRequest)
			_, err := writter.Write([]byte(`{msg:"Invalid metric type"}`))
			log.IfErr(handler.logger, err)
			return
		}
		handler.metricCreationsMap[m.MetricName] = com_signalfx_metrics_protobuf.MetricType(metricType)
		ret = append(ret, MetricCreationResponse{Code: 409})
	}
	toWrite, err := handler.jsonMarshal(ret)
	if err != nil {
		handler.logger.Log(log.Err, err, "Unable to marshal json")
		writter.WriteHeader(http.StatusBadRequest)
		_, err = writter.Write([]byte(`{msg:"Unable to marshal json!"}`))
		log.IfErr(handler.logger, err)
		return
	}
	writter.WriteHeader(http.StatusOK)
	_, err = writter.Write(toWrite)
	log.IfErr(handler.logger, err)
}

func (handler *metricHandler) GetMetricTypeFromMap(metricName string) com_signalfx_metrics_protobuf.MetricType {
	handler.metricCreationsMapMutex.Lock()
	defer handler.metricCreationsMapMutex.Unlock()
	mt, ok := handler.metricCreationsMap[metricName]
	if !ok {
		return com_signalfx_metrics_protobuf.MetricType_GAUGE
	}
	return mt
}

// NewListener servers http requests for Signalfx datapoints
func NewListener(sink Sink, conf *ListenerConfig) (*ListenerServer, error) {
	conf = pointer.FillDefaultFrom(conf, defaultListenerConfig).(*ListenerConfig)

	listener, err := net.Listen("tcp", *conf.ListenAddr)
	if err != nil {
		return nil, errors.Annotatef(err, "cannot open listening address %s", *conf.ListenAddr)
	}
	r := mux.NewRouter()

	server := http.Server{
		Handler:      r,
		Addr:         *conf.ListenAddr,
		ReadTimeout:  *conf.Timeout,
		WriteTimeout: *conf.Timeout,
	}
	listenServer := ListenerServer{
		listener: listener,
		logger:   conf.Logger,
		metricHandler: metricHandler{
			metricCreationsMap: make(map[string]com_signalfx_metrics_protobuf.MetricType),
			logger:             log.NewContext(conf.Logger).With(logkey.Struct, "metricHandler"),
			jsonMarshal:        conf.JSONMarshal,
		},
	}
	listenServer.SetupHealthCheck(conf.HealthCheck, r, conf.Logger)

	r.Handle("/v1/metric", &listenServer.metricHandler)
	r.Handle("/metric", &listenServer.metricHandler)

	traceSink, err := createTraceSink(sink, conf)

	listenServer.internalCollectors = sfxclient.NewMultiCollector(
		setupNotFoundHandler(conf.RootContext, r),
		setupProtobufV1(conf.RootContext, r, sink, &listenServer.metricHandler, conf.Logger, conf.HTTPChain),
		setupJSONV1(conf.RootContext, r, sink, &listenServer.metricHandler, conf.Logger, conf.HTTPChain),
		setupProtobufV2(conf.RootContext, r, sink, conf.Logger, conf.DebugContext, conf.HTTPChain),
		setupProtobufEventV2(conf.RootContext, r, sink, conf.Logger, conf.DebugContext, conf.HTTPChain),
		setupJSONV2(conf.RootContext, r, sink, conf.Logger, conf.DebugContext, conf.HTTPChain),
		setupJSONEventV2(conf.RootContext, r, sink, conf.Logger, conf.DebugContext, conf.HTTPChain),
		setupCollectd(conf.RootContext, r, sink, conf.DebugContext, conf.HTTPChain, conf.Logger),
		setupThriftTraceV1(conf.RootContext, r, traceSink, conf.Logger, conf.HTTPChain),
		setupJSONTraceV1(conf.RootContext, r, traceSink, conf.Logger, conf.HTTPChain),
	)

	go func() {
		log.IfErr(conf.Logger, server.Serve(listener))
	}()
	return &listenServer, err
}

func setupNotFoundHandler(ctx context.Context, r *mux.Router) sfxclient.Collector {
	metricTracking := web.RequestCounter{}
	r.NotFoundHandler = web.NewHandler(ctx, web.FromHTTP(http.NotFoundHandler())).Add(web.NextHTTP(metricTracking.ServeHTTP))
	return &sfxclient.WithDimensions{
		Dimensions: map[string]string{"protocol": "http404"},
		Collector:  &metricTracking,
	}
}

func createTraceSink(sink Sink, conf *ListenerConfig) (Sink, error) {
	if len(conf.RemoveSpanTags) > 0 {
		var err error
		sink, err = spanobfuscation.NewRm(conf.RemoveSpanTags, sink)
		if err != nil {
			return nil, errors.Annotatef(err, "cannot parse span tag removal rules %v", conf.RemoveSpanTags)
		}
	}
	if len(conf.ObfuscateSpanTags) > 0 {
		var err error
		sink, err = spanobfuscation.NewObf(conf.ObfuscateSpanTags, sink)
		if err != nil {
			return nil, errors.Annotatef(err, "cannot parse span tag obfuscation rules %v", conf.ObfuscateSpanTags)
		}
	}
	if len(conf.SpanNameReplacementRules) > 0 {
		var err1 error
		sink, err1 = tagreplace.New(conf.SpanNameReplacementRules, *conf.SpanNameReplacementBreakAfterMatch, sink)
		if err1 != nil {
			return nil, errors.Annotatef(err1, "cannot parse tag replacement rules %v", conf.SpanNameReplacementRules)
		}
	}
	if len(conf.AdditionalSpanTags) > 0 {
		sink = additionalspantags.New(conf.AdditionalSpanTags, sink)
	}
	return sink, nil
}

// SetupChain wraps the reader returned by getReader in an http.Handler along
// with some middleware that calculates internal metrics about requests.
func SetupChain(ctx context.Context, sink Sink, chainType string, getReader func(Sink) ErrorReader, httpChain web.NextConstructor, logger log.Logger, moreConstructors ...web.Constructor) (http.Handler, sfxclient.Collector) {
	zippers := zipper.NewZipper()

	counter := &dpsink.Counter{
		Logger:        logger,
		DroppedReason: "mux",
	}

	ucount := UnifyNextSinkWrap(counter)
	finalSink := FromChain(sink, NextWrap(ucount))
	errReader := getReader(finalSink)
	errorTracker := ErrorTrackerHandler{
		reader: errReader,
		Logger: logger,
	}
	metricTracking := web.RequestCounter{}
	handler := web.NewHandler(ctx, &errorTracker).Add(web.NextHTTP(metricTracking.ServeHTTP)).Add(httpChain)
	for _, c := range moreConstructors {
		handler.Add(c)
	}
	st := &sfxclient.WithDimensions{
		Collector: sfxclient.NewMultiCollector(
			&metricTracking,
			&errorTracker,
			counter,
			zippers,
		),
		Dimensions: map[string]string{
			"protocol": "sfx_" + chainType,
		},
	}
	return zippers.GzipHandler(handler), st
}

// SetupJSONByPaths tells the router which paths the given handler (which should handle the given
// endpoint) should see
func SetupJSONByPaths(r *mux.Router, handler http.Handler, endpoint string) {
	r.Path(endpoint).Methods("POST").Headers("Content-Type", "application/json").Handler(handler)
	r.Path(endpoint).Methods("POST").Headers("Content-Type", "application/json; charset=UTF-8").Handler(handler)
	r.Path(endpoint).Methods("POST").Headers("Content-Type", "").HandlerFunc(web.InvalidContentType)
	r.Path(endpoint).Methods("POST").Handler(handler)
}

func setupCollectd(ctx context.Context, r *mux.Router, sink dpsink.Sink, debugContext *web.HeaderCtxFlag, httpChain web.NextConstructor, logger log.Logger) sfxclient.Collector {
	counter := &dpsink.Counter{
		Logger:        logger,
		DroppedReason: "mux",
	}
	finalSink := dpsink.FromChain(sink, dpsink.NextWrap(counter))
	decoder := collectd.JSONDecoder{
		Logger: logger,
		SendTo: finalSink,
	}
	metricTracking := &web.RequestCounter{}
	httpHandler := web.NewHandler(ctx, &decoder).Add(web.NextHTTP(metricTracking.ServeHTTP), debugContext, httpChain)
	collectd.SetupCollectdPaths(r, httpHandler, "/v1/collectd")
	return &sfxclient.WithDimensions{
		Collector: sfxclient.NewMultiCollector(
			metricTracking,
			counter,
			&decoder,
		),
		Dimensions: map[string]string{
			"type": "collectd",
		},
	}
}

func readFromRequest(jeff *bytes.Buffer, req *http.Request, logger log.Logger) error {
	// for compressed transactions, contentLength isn't trustworthy
	readLen, err := jeff.ReadFrom(req.Body)
	if err != nil {
		logger.Log(log.Err, err, logkey.ReadLen, readLen, logkey.ContentLength, req.ContentLength, "Unable to fully read from buffer")
		return err
	}
	return nil
}
<|endoftext|>