<commit_before>// Package play implements a simple mp3 player.
package play

/*
#include <stdlib.h>
#include "play.h"

#cgo LDFLAGS: -lmpg123 -lao -lasound -lid3
*/
import "C"

import (
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"time"
	"unsafe"
)

// For development
var _ = fmt.Println

const debug = true

// Package init function
func init() {
	C.play_init()
}

// Play the specified file. Return when playback is complete.
func Play(filename string) {
	n := C.CString(filename)
	C.play_play(n)
	C.free(unsafe.Pointer(n))
}

// Set the volume as a percentage between 0 and 100 inclusive.
// This method sets the volume on the default ALSA card.
func SetVolume(pct byte) {
	C.play_setvolume(C.uchar(pct), C.CString("default"))
}

// Set the volume as a percentage between 0 and 100 inclusive.
// This method sets the volume on all ALSA cards.
func SetVolumeAll(pct byte) {
	C.play_setvolume_all(C.uchar(pct))
}

// Get the volume as a percentage between 0 and 100 inclusive.
func GetVolume() byte {
	v := int8(C.play_getvolume())
	if v >= 0 {
		return byte(v)
	} else {
		// Error occurred. Return 0.
		return 0
	}
}

// Metadata is information about an mp3 stored in id3 tags.
type Metadata struct {
	Title  string
	Artist string
	Album  string
}

// Information about an mp3 determined once the mp3 is loaded.
type Info struct {
	// The bitrate of the mp3, in kbps
	BitRate int

	// Sampling rate in Hz
	Rate int
}

// GetMetadata extracts the id3 information from the mp3 file `filename`.
func GetMetadata(filename string) Metadata {
	meta := C.play_meta(C.CString(filename))
	r := Metadata{
		Title:  strings.Trim(C.GoString(meta.title), " "),
		Artist: strings.Trim(C.GoString(meta.artist), " "),
		Album:  strings.Trim(C.GoString(meta.album), " "),
	}
	C.play_delete_meta(meta)
	return r
}

// Print debugging information about the metadata in `filename` to stdout.
func DebugMetadata(filename string) {
	C.play_debug_meta(C.CString(filename))
}

// PlayerState represents the current state of a Player. Must be one of Empty, Playing, or Paused.
type PlayerState int

const (
	// The player has no mp3 loaded
	Empty PlayerState = iota
	// The player has an mp3 loaded and is playing it
	Playing
	// The player has an mp3 loaded and it is paused
	Paused
)

func (s PlayerState) String() string {
	switch s {
	case Empty:
		return "empty"
	case Playing:
		return "playing"
	case Paused:
		return "paused"
	default:
		panic("PlayerState.String() needs to be updated. Missing case for " + strconv.Itoa(int(s)))
	}
}

// Player is an mp3 player.
type Player struct {
	cmds   chan interface{}
	Events chan Event
}

// Internal command used by the Player
type loadCmd struct {
	path string
	err  chan error
	// on load, this channel is written the size of the mp3 in samples
	size chan int
}

// Internal command used by the Player
type playCmd chan error

// Internal command used by the Player
type pauseCmd bool

// Internal command used by the Player
type stopCmd bool

// The offset to seek to
type seekCmd int

// The percentage to set to
type setVolumeCmd byte

// Internal command used by the Player
type setVolumeAllCmd byte

// Internal command used by the Player
type getStatusCmd chan PlayerStatus

// Internal command used by the Player
type getInfoCmd chan *Info

// Event represents events sent by the Player.
type Event int

const (
	OffsetChange Event = iota
	StateChange
	VolumeChange
)

func (e Event) String() string {
	switch e {
	case OffsetChange:
		return "OffsetChange"
	case StateChange:
		return "StateChange"
	case VolumeChange:
		return "VolumeChange"
	default:
		return "Unknown"
	}
}

// PlayerStatus contains information about the current status of the player
type PlayerStatus struct {
	// Offset is the offset within the current track in samples
	Offset int
	// Size of the current track in samples
	Size int
	// State of the Player
	State PlayerState
	// Volume
	Volume byte
}

// Create a new Player.
func NewPlayer() (p Player) {

	p.cmds = make(chan interface{})
	p.Events = make(chan Event, 1000)

	// This goroutine implements the player.
	go func() {
		var reader *C.play_reader_t
		var writer *C.ao_device
		var lastofftime time.Time
		lastoff := -1

		var state PlayerState = Empty
		var states [3]func()

		debugCmd := func(cmd interface{}) {
			if debug {
				fmt.Printf("player: In state %s, got command %s\n", state, reflect.TypeOf(cmd))
			}
		}

		makeWriter := func() error {
			if reader == nil {
				return errors.New("Creating writer failed: reader was nil")
			}
			writer = C.play_new_writer(reader)
			if writer == nil {
				return errors.New("Creating writer failed")
			}
			return nil
		}

		deleteWriter := func() {
			if writer != nil {
				C.play_delete_writer(writer)
				writer = nil
			}
		}

		sendEvent := func(e Event) {
			// Do not block when sending events. We buffer some and then drop the rest.
			// This is to prevent deadlocks: users of the client need to read events, but
			// they also send commands to the player. If the player is sending an event
			// while the event reader is sending a command to the player we have a deadlock.
			select {
			case p.Events <- e:
			default:
			}
		}

		// Stop the currently playing mp3 if it's playing, and unload it.
		stop := func() {
			if state != Empty {
				C.play_delete_reader(reader)
				reader = nil
				deleteWriter()

				state = Empty
			}
		}

		// Load a new mp3. Returns true if the state has changed.
		load := func(cmd loadCmd) {
			if state != Empty {
				stop()
			}

			lastoff = -1

			reader = C.play_new_reader(C.CString(cmd.path))
			if reader == nil {
				close(cmd.size)
				cmd.err <- errors.New("Creating reader failed")
				return
			}

			err := makeWriter()
			if err != nil {
				close(cmd.size)
				C.play_delete_reader(reader)
				reader = nil
				cmd.err <- err
				return
			}

			cmd.size <- int(C.play_length(reader))

			cmd.err <- nil
			state = Paused
		}

		// Play the loaded mp3.
		play := func(cmd playCmd) {
			if state != Empty {
				if writer == nil {
					// If we were paused we need to recreate the writer.
					err := makeWriter()
					if err != nil {
						cmd <- err
						return
					}
				}
				state = Playing
			}
			cmd <- nil
		}

		// Pause the playing mp3.
		pause := func() {
			if state != Empty {
				state = Paused

				// We delete the writer here to release libao. This is so we don't have
				// the audio device locked so that other applications that require sound
				// can play sound.
				if writer != nil {
					deleteWriter()
				}
			}
		}

		// Seek to a position in the loaded mp3.
		seek := func(cmd seekCmd) {
			if state == Empty {
				return
			}

			C.play_seek(reader, C.int(cmd))
			// Zero out lastofftime
			var zero time.Time
			lastofftime = zero

			sendEvent(OffsetChange)
		}

		handleCommonCmds := func(cmd interface{}) bool {
			switch cmd.(type) {
			case setVolumeCmd:
				SetVolume(byte(cmd.(setVolumeCmd)))
				sendEvent(VolumeChange)
				if debug {
					fmt.Println("player: sending VolumeChange event")
				}
				return true
			case setVolumeAllCmd:
				SetVolumeAll(byte(cmd.(setVolumeAllCmd)))
				sendEvent(VolumeChange)
				if debug {
					fmt.Println("player: sending VolumeChange event")
				}
				return true
			case getStatusCmd:
				timer := time.Now()
				offset := 0
				size := 0
				if state != Empty {
					offset = int(C.play_offset(reader))
					size = int(C.play_length(reader))
				}
				cmd.(getStatusCmd) <- PlayerStatus{Offset: offset, Size: size, State: state, Volume: GetVolume()}
				if debug {
					fmt.Printf("player: Generating status took %v\n", time.Now().Sub(timer))
				}
				return true
			case getInfoCmd:
				if state != Empty {
					i, err := C.play_getinfo(reader)
					if err != nil {
						cmd.(getInfoCmd) <- nil
					} else {
						cmd.(getInfoCmd) <- &Info{BitRate: int(i.bitrate), Rate: int(i.rate)}
					}
				} else {
					cmd.(getInfoCmd) <- nil
				}
				return true
			}
			return false
		}

		states[Empty] = func() {
			// Only the load command is not ignored.
			for {
				select {
				case cmd := <-p.cmds:
					debugCmd(cmd)
					if handleCommonCmds(cmd) {
						break
					}

					switch cmd.(type) {
					case loadCmd:
						load(cmd.(loadCmd))
					}
				}

				if state != Empty {
					break
				}
			}
		}

		states[Paused] = func() {
			for {
				select {
				case cmd := <-p.cmds:
					debugCmd(cmd)
					if handleCommonCmds(cmd) {
						break
					}

					switch cmd.(type) {
					case loadCmd:
						load(cmd.(loadCmd))
					case playCmd:
						play(cmd.(playCmd))
					case stopCmd:
						stop()
					case seekCmd:
						seek(cmd.(seekCmd))
					}
				}

				if state != Paused {
					break
				}
			}
		}

		states[Playing] = func() {
		outer:
			for {
				// Handle all commands before copying song frames since copying is slow.
				wasCmd := true
				for {
					select {
					case cmd := <-p.cmds:
						debugCmd(cmd)
						if handleCommonCmds(cmd) {
							break
						}

						switch cmd.(type) {
						case loadCmd:
							load(cmd.(loadCmd))
						case pauseCmd:
							pause()
						case stopCmd:
							stop()
						case seekCmd:
							seek(cmd.(seekCmd))
						}
					default:
						wasCmd = false
					}

					if state != Playing {
						break outer
					}

					if !wasCmd {
						break
					}
				}

				timer := time.Now()

				// Copy a buffer of data to the output device
				n, err := C.play_read(reader)
				if err != nil {
					// We're done
					stop()
					break
				}

				C.play_write(writer, reader.buffer, n)

				if debug {
					fmt.Printf("player: copying a buffer of data took %v\n", time.Now().Sub(timer))
				}

				if lastofftime.IsZero() || time.Now().Sub(lastofftime) > time.Millisecond*250 {
					if o := int(C.play_offset(reader)); o != lastoff {
						timer = time.Now()
						sendEvent(OffsetChange)
						if debug {
							fmt.Printf("player: sending offsetchange event took %v\n", time.Now().Sub(timer))
						}
					}
				}
			}
		}

		// Player state machine:
		for {
			if debug {
				fmt.Println("player: Entering state", state)
			}
			lastState := state
			states[state]()
			if lastState != state {
				sendEvent(StateChange)
			}
		}

	}()

	return p
}

// Load the specified file into the Player. Call Play to play the file.
// Returns the size of the file in samples. On failure, err is non nil.
// On success, the player will write the current playing offset (in samples) to offchan
// periodically.
func (p Player) Load(filename string) (size int, err error) {
	ch := make(chan error)
	sizech := make(chan int)

	p.cmds <- loadCmd{path: filename, err: ch, size: sizech}

	size = <-sizech
	err = <-ch

	return
}

// Play the currently loaded file. The player must have an mp3 loaded and not currently playing.
func (p Player) Play() (err error) {
	playCmd := make(playCmd)

	p.cmds <- playCmd

	err = <-playCmd

	return
}

// Pause the currently playing file.
func (p Player) Pause() {
	p.cmds <- pauseCmd(true)
}

// Stop the currently playing file if it's playing, and unload the file from the mp3 player.
func (p Player) Stop() {
	p.cmds <- stopCmd(true)
}

// Seek to the specified sample in the loaded mp3 file.
func (p Player) Seek(offset int) {
	p.cmds <- seekCmd(offset)
}

// SetVolume calls the regular SetVolume function but also writes a VolumeChange event to the
// Player's Event channel.
func (p Player) SetVolume(pct byte) {
	p.cmds <- setVolumeCmd(pct)
}

// SetVolumeAll calls the regular SetVolumeAll function but also writes a VolumeChange event to the
// Player's Event channel.
func (p Player) SetVolumeAll(pct byte) {
	p.cmds <- setVolumeAllCmd(pct)
}

// GetStatus gets the player status
func (p Player) GetStatus() PlayerStatus {
	cmd := make(chan PlayerStatus)
	p.cmds <- getStatusCmd(cmd)

	return <-cmd
}

// GetInfo returns an Info object about the loaded mp3. If an mp3 is not loaded, nil is returned
func (p Player) GetInfo() *Info {
	cmd := make(chan *Info)
	p.cmds <- getInfoCmd(cmd)

	return <-cmd
}
<commit_msg>Updated comments<commit_after>// Package play implements a simple mp3 player. In general, to use this package first create a Player using NewPlayer,
// start a goroutine that listens for events on Player.Event, Load an mp3 using Player.Load, and play it using Player.Play().
// GetMetadata can be used to retrieve ID3 information from mp3 files.
package play

/*
#include <stdlib.h>
#include "play.h"

#cgo LDFLAGS: -lmpg123 -lao -lasound -lid3
*/
import "C"

import (
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"time"
	"unsafe"
)

// For development
var _ = fmt.Println

const debug = true

// Package init function
func init() {
	C.play_init()
}

// Play the specified file. Return when playback is complete.
func Play(filename string) {
	n := C.CString(filename)
	C.play_play(n)
	C.free(unsafe.Pointer(n))
}

// Set the volume as a percentage between 0 and 100 inclusive.
// This method sets the volume on the default ALSA card.
func SetVolume(pct byte) {
	C.play_setvolume(C.uchar(pct), C.CString("default"))
}

// Set the volume as a percentage between 0 and 100 inclusive.
// This method sets the volume on all ALSA cards.
func SetVolumeAll(pct byte) {
	C.play_setvolume_all(C.uchar(pct))
}

// Get the volume as a percentage between 0 and 100 inclusive.
func GetVolume() byte {
	v := int8(C.play_getvolume())
	if v >= 0 {
		return byte(v)
	} else {
		// Error occurred. Return 0.
		return 0
	}
}

// Metadata is information about an mp3 stored in id3 tags.
type Metadata struct {
	Title  string
	Artist string
	Album  string
}

// Information about an mp3 determined once the mp3 is loaded.
type Info struct {
	// The bitrate of the mp3, in kbps
	BitRate int

	// Sampling rate in Hz
	Rate int
}

// GetMetadata extracts the id3 information from the mp3 file `filename`.
func GetMetadata(filename string) Metadata {
	meta := C.play_meta(C.CString(filename))
	r := Metadata{
		Title:  strings.Trim(C.GoString(meta.title), " "),
		Artist: strings.Trim(C.GoString(meta.artist), " "),
		Album:  strings.Trim(C.GoString(meta.album), " "),
	}
	C.play_delete_meta(meta)
	return r
}

// Print debugging information about the metadata in `filename` to stdout.
func DebugMetadata(filename string) {
	C.play_debug_meta(C.CString(filename))
}

// PlayerState represents the current state of a Player. Must be one of Empty, Playing, or Paused.
type PlayerState int

const (
	// The player has no mp3 loaded
	Empty PlayerState = iota
	// The player has an mp3 loaded and is playing it
	Playing
	// The player has an mp3 loaded and it is paused
	Paused
)

func (s PlayerState) String() string {
	switch s {
	case Empty:
		return "empty"
	case Playing:
		return "playing"
	case Paused:
		return "paused"
	default:
		panic("PlayerState.String() needs to be updated. Missing case for " + strconv.Itoa(int(s)))
	}
}

// Player is an mp3 player.
type Player struct {
	cmds chan interface{}
	// Events is a channel to which Player events are written, such as state changes, volume changes, or offset changes.
	// When an event is recieved the user should call GetStatus() to get the current player information.
	Events chan Event
}

// Internal command used by the Player
type loadCmd struct {
	path string
	err  chan error
	// on load, this channel is written the size of the mp3 in samples
	size chan int
}

// Internal command used by the Player
type playCmd chan error

// Internal command used by the Player
type pauseCmd bool

// Internal command used by the Player
type stopCmd bool

// The offset to seek to
type seekCmd int

// The percentage to set to
type setVolumeCmd byte

// Internal command used by the Player
type setVolumeAllCmd byte

// Internal command used by the Player
type getStatusCmd chan PlayerStatus

// Internal command used by the Player
type getInfoCmd chan *Info

// Event represents events sent by the Player.
type Event int

const (
	OffsetChange Event = iota
	StateChange
	VolumeChange
)

func (e Event) String() string {
	switch e {
	case OffsetChange:
		return "OffsetChange"
	case StateChange:
		return "StateChange"
	case VolumeChange:
		return "VolumeChange"
	default:
		return "Unknown"
	}
}

// PlayerStatus contains information about the current status of the player
type PlayerStatus struct {
	// Offset is the offset within the current track in samples
	Offset int
	// Size of the current track in samples
	Size int
	// State of the Player
	State PlayerState
	// Volume
	Volume byte
}

// Create a new Player.
func NewPlayer() (p Player) {

	p.cmds = make(chan interface{})
	p.Events = make(chan Event, 1000)

	// This goroutine implements the player.
	go func() {
		var reader *C.play_reader_t
		var writer *C.ao_device
		var lastofftime time.Time
		lastoff := -1

		var state PlayerState = Empty
		var states [3]func()

		debugCmd := func(cmd interface{}) {
			if debug {
				fmt.Printf("player: In state %s, got command %s\n", state, reflect.TypeOf(cmd))
			}
		}

		makeWriter := func() error {
			if reader == nil {
				return errors.New("Creating writer failed: reader was nil")
			}
			writer = C.play_new_writer(reader)
			if writer == nil {
				return errors.New("Creating writer failed")
			}
			return nil
		}

		deleteWriter := func() {
			if writer != nil {
				C.play_delete_writer(writer)
				writer = nil
			}
		}

		sendEvent := func(e Event) {
			// Do not block when sending events. We buffer some and then drop the rest.
			// This is to prevent deadlocks: users of the client need to read events, but
			// they also send commands to the player. If the player is sending an event
			// while the event reader is sending a command to the player we have a deadlock.
			select {
			case p.Events <- e:
			default:
			}
		}

		// Stop the currently playing mp3 if it's playing, and unload it.
		stop := func() {
			if state != Empty {
				C.play_delete_reader(reader)
				reader = nil
				deleteWriter()

				state = Empty
			}
		}

		// Load a new mp3. Returns true if the state has changed.
		load := func(cmd loadCmd) {
			if state != Empty {
				stop()
			}

			lastoff = -1

			reader = C.play_new_reader(C.CString(cmd.path))
			if reader == nil {
				close(cmd.size)
				cmd.err <- errors.New("Creating reader failed")
				return
			}

			err := makeWriter()
			if err != nil {
				close(cmd.size)
				C.play_delete_reader(reader)
				reader = nil
				cmd.err <- err
				return
			}

			cmd.size <- int(C.play_length(reader))

			cmd.err <- nil
			state = Paused
		}

		// Play the loaded mp3.
		play := func(cmd playCmd) {
			if state != Empty {
				if writer == nil {
					// If we were paused we need to recreate the writer.
					err := makeWriter()
					if err != nil {
						cmd <- err
						return
					}
				}
				state = Playing
			}
			cmd <- nil
		}

		// Pause the playing mp3.
		pause := func() {
			if state != Empty {
				state = Paused

				// We delete the writer here to release libao. This is so we don't have
				// the audio device locked so that other applications that require sound
				// can play sound.
				if writer != nil {
					deleteWriter()
				}
			}
		}

		// Seek to a position in the loaded mp3.
		seek := func(cmd seekCmd) {
			if state == Empty {
				return
			}

			C.play_seek(reader, C.int(cmd))
			// Zero out lastofftime
			var zero time.Time
			lastofftime = zero

			sendEvent(OffsetChange)
		}

		handleCommonCmds := func(cmd interface{}) bool {
			switch cmd.(type) {
			case setVolumeCmd:
				SetVolume(byte(cmd.(setVolumeCmd)))
				sendEvent(VolumeChange)
				if debug {
					fmt.Println("player: sending VolumeChange event")
				}
				return true
			case setVolumeAllCmd:
				SetVolumeAll(byte(cmd.(setVolumeAllCmd)))
				sendEvent(VolumeChange)
				if debug {
					fmt.Println("player: sending VolumeChange event")
				}
				return true
			case getStatusCmd:
				timer := time.Now()
				offset := 0
				size := 0
				if state != Empty {
					offset = int(C.play_offset(reader))
					size = int(C.play_length(reader))
				}
				cmd.(getStatusCmd) <- PlayerStatus{Offset: offset, Size: size, State: state, Volume: GetVolume()}
				if debug {
					fmt.Printf("player: Generating status took %v\n", time.Now().Sub(timer))
				}
				return true
			case getInfoCmd:
				if state != Empty {
					i, err := C.play_getinfo(reader)
					if err != nil {
						cmd.(getInfoCmd) <- nil
					} else {
						cmd.(getInfoCmd) <- &Info{BitRate: int(i.bitrate), Rate: int(i.rate)}
					}
				} else {
					cmd.(getInfoCmd) <- nil
				}
				return true
			}
			return false
		}

		states[Empty] = func() {
			// Only the load command is not ignored.
			for {
				select {
				case cmd := <-p.cmds:
					debugCmd(cmd)
					if handleCommonCmds(cmd) {
						break
					}

					switch cmd.(type) {
					case loadCmd:
						load(cmd.(loadCmd))
					}
				}

				if state != Empty {
					break
				}
			}
		}

		states[Paused] = func() {
			for {
				select {
				case cmd := <-p.cmds:
					debugCmd(cmd)
					if handleCommonCmds(cmd) {
						break
					}

					switch cmd.(type) {
					case loadCmd:
						load(cmd.(loadCmd))
					case playCmd:
						play(cmd.(playCmd))
					case stopCmd:
						stop()
					case seekCmd:
						seek(cmd.(seekCmd))
					}
				}

				if state != Paused {
					break
				}
			}
		}

		states[Playing] = func() {
		outer:
			for {
				// Handle all commands before copying song frames since copying is slow.
				wasCmd := true
				for {
					select {
					case cmd := <-p.cmds:
						debugCmd(cmd)
						if handleCommonCmds(cmd) {
							break
						}

						switch cmd.(type) {
						case loadCmd:
							load(cmd.(loadCmd))
						case pauseCmd:
							pause()
						case stopCmd:
							stop()
						case seekCmd:
							seek(cmd.(seekCmd))
						}
					default:
						wasCmd = false
					}

					if state != Playing {
						break outer
					}

					if !wasCmd {
						break
					}
				}

				timer := time.Now()

				// Copy a buffer of data to the output device
				n, err := C.play_read(reader)
				if err != nil {
					// We're done
					stop()
					break
				}

				C.play_write(writer, reader.buffer, n)

				if debug {
					fmt.Printf("player: copying a buffer of data took %v\n", time.Now().Sub(timer))
				}

				if lastofftime.IsZero() || time.Now().Sub(lastofftime) > time.Millisecond*250 {
					if o := int(C.play_offset(reader)); o != lastoff {
						timer = time.Now()
						sendEvent(OffsetChange)
						if debug {
							fmt.Printf("player: sending offsetchange event took %v\n", time.Now().Sub(timer))
						}
					}
				}
			}
		}

		// Player state machine:
		for {
			if debug {
				fmt.Println("player: Entering state", state)
			}
			lastState := state
			states[state]()
			if lastState != state {
				sendEvent(StateChange)
			}
		}

	}()

	return p
}

// Load the specified file into the Player. Call Play to play the file.
// Returns the size of the file in samples. On failure, err is non nil.
// On success, the player will write the current playing offset (in samples) to offchan
// periodically.
func (p Player) Load(filename string) (size int, err error) {
	ch := make(chan error)
	sizech := make(chan int)

	p.cmds <- loadCmd{path: filename, err: ch, size: sizech}

	size = <-sizech
	err = <-ch

	return
}

// Play the currently loaded file. The player must have an mp3 loaded and not currently playing.
func (p Player) Play() (err error) {
	playCmd := make(playCmd)

	p.cmds <- playCmd

	err = <-playCmd

	return
}

// Pause the currently playing file.
func (p Player) Pause() {
	p.cmds <- pauseCmd(true)
}

// Stop the currently playing file if it's playing, and unload the file from the mp3 player.
func (p Player) Stop() {
	p.cmds <- stopCmd(true)
}

// Seek to the specified sample in the loaded mp3 file.
func (p Player) Seek(offset int) {
	p.cmds <- seekCmd(offset)
}

// SetVolume calls the regular SetVolume function but also writes a VolumeChange event to the
// Player's Event channel.
func (p Player) SetVolume(pct byte) {
	p.cmds <- setVolumeCmd(pct)
}

// SetVolumeAll calls the regular SetVolumeAll function but also writes a VolumeChange event to the
// Player's Event channel.
func (p Player) SetVolumeAll(pct byte) {
	p.cmds <- setVolumeAllCmd(pct)
}

// GetStatus gets the player status
func (p Player) GetStatus() PlayerStatus {
	cmd := make(chan PlayerStatus)
	p.cmds <- getStatusCmd(cmd)

	return <-cmd
}

// GetInfo returns an Info object about the loaded mp3. If an mp3 is not loaded, nil is returned
func (p Player) GetInfo() *Info {
	cmd := make(chan *Info)
	p.cmds <- getInfoCmd(cmd)

	return <-cmd
}
<|endoftext|>


<commit_before>package wkb

const (
	wkbXDR = 0
	wkbNDR = 1
)

const (
	wkbPoint                = 1
	wkbLineString           = 2
	wkbPolygon              = 3
	wkbTriangle             = 17
	wkbMultiPoint           = 4
	wkbMultiLineString      = 5
	wkbMultiPolygon         = 6
	wkbGeometryCollection   = 7
	wkbPolyhedralSurface    = 15
	wkbTIN                  = 16
	wkbPointZ               = 1001
	wkbLineStringZ          = 1002
	wkbPolygonZ             = 1003
	wkbTriangleZ            = 1017
	wkbMultiPointZ          = 1004
	wkbMultiLineStringZ     = 1005
	wkbMultiPolygonZ        = 1006
	wkbGeometryCollectionZ  = 1007
	wkbPolyhedralSurfaceZ   = 1015
	wkbTINZ                 = 1016
	wkbPointM               = 2001
	wkbLineStringM          = 2002
	wkbPolygonM             = 2003
	wkbTriangleM            = 2017
	wkbMultiPointM          = 2004
	wkbMultiLineStringM     = 2005
	wkbMultiPolygonM        = 2006
	wkbGeometryCollectionM  = 2007
	wkbPolyhedralSurfaceM   = 2015
	wkbTINM                 = 2016
	wkbPointZM              = 3001
	wkbLineStringZM         = 3002
	wkbPolygonZM            = 3003
	wkbTriangleZM           = 3017
	wkbMultiPointZM         = 3004
	wkbMultiLineStringZM    = 3005
	wkbMultiPolygonZM       = 3006
	wkbGeometryCollectionZM = 3007
	wkbPolyhedralSurfaceZM  = 3015
	wkbTINZM                = 3016
)
<commit_msg>Add XDR and NDR<commit_after>package wkb

import (
	"encoding/binary"
)

const (
	wkbXDR = 0
	wkbNDR = 1
)

const (
	wkbPoint                = 1
	wkbLineString           = 2
	wkbPolygon              = 3
	wkbTriangle             = 17
	wkbMultiPoint           = 4
	wkbMultiLineString      = 5
	wkbMultiPolygon         = 6
	wkbGeometryCollection   = 7
	wkbPolyhedralSurface    = 15
	wkbTIN                  = 16
	wkbPointZ               = 1001
	wkbLineStringZ          = 1002
	wkbPolygonZ             = 1003
	wkbTriangleZ            = 1017
	wkbMultiPointZ          = 1004
	wkbMultiLineStringZ     = 1005
	wkbMultiPolygonZ        = 1006
	wkbGeometryCollectionZ  = 1007
	wkbPolyhedralSurfaceZ   = 1015
	wkbTINZ                 = 1016
	wkbPointM               = 2001
	wkbLineStringM          = 2002
	wkbPolygonM             = 2003
	wkbTriangleM            = 2017
	wkbMultiPointM          = 2004
	wkbMultiLineStringM     = 2005
	wkbMultiPolygonM        = 2006
	wkbGeometryCollectionM  = 2007
	wkbPolyhedralSurfaceM   = 2015
	wkbTINM                 = 2016
	wkbPointZM              = 3001
	wkbLineStringZM         = 3002
	wkbPolygonZM            = 3003
	wkbTriangleZM           = 3017
	wkbMultiPointZM         = 3004
	wkbMultiLineStringZM    = 3005
	wkbMultiPolygonZM       = 3006
	wkbGeometryCollectionZM = 3007
	wkbPolyhedralSurfaceZM  = 3015
	wkbTINZM                = 3016
)

var (
	XDR = binary.BigEndian
	NDR = binary.LittleEndian
)
<|endoftext|>


<commit_before>package backend

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"time"

	"github.com/influxdata/flux"
	"github.com/influxdata/flux/lang"
	"github.com/influxdata/influxdb"
	platform "github.com/influxdata/influxdb"
	pctx "github.com/influxdata/influxdb/context"
	"github.com/influxdata/influxdb/models"
	"github.com/influxdata/influxdb/query"
	"github.com/influxdata/influxdb/storage"
	"github.com/influxdata/influxdb/tsdb"
	"go.uber.org/zap"
)

const (
	runIDField        = "runID"
	scheduledForField = "scheduledFor"
	startedAtField    = "startedAt"
	finishedAtField   = "finishedAt"
	requestedAtField  = "requestedAt"
	statusField       = "status"
	logField          = "logs"

	taskIDTag = "taskID"

	// Fixed system bucket ID for task and run logs.
	taskSystemBucketID platform.ID = 10
)

// NewAnalyticalStorage creates a new analytical store with access to the necessary systems for storing data and to act as a middleware
func NewAnalyticalStorage(logger *zap.Logger, ts influxdb.TaskService, tcs TaskControlService, pw storage.PointsWriter, qs query.QueryService) *AnalyticalStorage {
	return &AnalyticalStorage{
		logger:             logger,
		TaskService:        ts,
		TaskControlService: tcs,
		pw:                 pw,
		qs:                 qs,
	}
}

type AnalyticalStorage struct {
	influxdb.TaskService
	TaskControlService

	pw     storage.PointsWriter
	qs     query.QueryService
	logger *zap.Logger
}

func (as *AnalyticalStorage) FinishRun(ctx context.Context, taskID, runID influxdb.ID) (*influxdb.Run, error) {
	run, err := as.TaskControlService.FinishRun(ctx, taskID, runID)
	if run != nil && run.ID.String() != "" {
		task, err := as.TaskService.FindTaskByID(ctx, run.TaskID)
		if err != nil {
			return run, err
		}

		tags := models.Tags{
			models.NewTag([]byte(taskIDTag), []byte(run.TaskID.String())),
			models.NewTag([]byte(statusField), []byte(run.Status)),
		}

		// log an error if we have incomplete data on finish
		if !run.ID.Valid() ||
			run.ScheduledFor == "" ||
			run.StartedAt == "" ||
			run.FinishedAt == "" ||
			run.Status == "" {
			as.logger.Error("Run missing critical fields", zap.String("run", fmt.Sprintf("%+v", run)), zap.String("runID", run.ID.String()))
		}

		fields := map[string]interface{}{}
		fields[statusField] = run.Status
		fields[runIDField] = run.ID.String()
		fields[startedAtField] = run.StartedAt
		fields[finishedAtField] = run.FinishedAt
		fields[scheduledForField] = run.ScheduledFor
		if run.RequestedAt != "" {
			fields[requestedAtField] = run.RequestedAt
		}

		startedAt, err := run.StartedAtTime()
		if err != nil {
			startedAt = time.Now()
		}

		logBytes, err := json.Marshal(run.Log)
		if err != nil {
			return run, err
		}
		fields[logField] = string(logBytes)

		point, err := models.NewPoint("runs", tags, fields, startedAt)
		if err != nil {
			return run, err
		}

		// use the tsdb explode points to convert to the new style.
		// We could split this on our own but its quite possible this could change.
		points, err := tsdb.ExplodePoints(task.OrganizationID, taskSystemBucketID, models.Points{point})
		if err != nil {
			return run, err
		}
		return run, as.pw.WritePoints(ctx, points)
	}
	return run, err
}

// FindLogs returns logs for a run.
// First attempt to use the TaskService, then append additional analytical's logs to the list
func (as *AnalyticalStorage) FindLogs(ctx context.Context, filter influxdb.LogFilter) ([]*influxdb.Log, int, error) {
	var logs []*influxdb.Log
	if filter.Run != nil {
		run, err := as.FindRunByID(ctx, filter.Task, *filter.Run)
		if err != nil {
			return nil, 0, err
		}
		for i := 0; i < len(run.Log); i++ {
			logs = append(logs, &run.Log[i])
		}
		return logs, len(logs), nil
	}

	// add historical logs to the transactional logs.
	runs, n, err := as.FindRuns(ctx, influxdb.RunFilter{Task: filter.Task})
	if err != nil {
		return nil, 0, err
	}

	for _, run := range runs {
		for i := 0; i < len(run.Log); i++ {
			logs = append(logs, &run.Log[i])
		}
	}

	return logs, n, err
}

// FindRuns returns a list of runs that match a filter and the total count of returned runs.
// First attempt to use the TaskService, then append additional analytical's runs to the list
func (as *AnalyticalStorage) FindRuns(ctx context.Context, filter influxdb.RunFilter) ([]*influxdb.Run, int, error) {
	if filter.Limit == 0 {
		filter.Limit = influxdb.TaskDefaultPageSize
	}

	if filter.Limit < 0 || filter.Limit > influxdb.TaskMaxPageSize {
		return nil, 0, influxdb.ErrOutOfBoundsLimit
	}

	runs, n, err := as.TaskService.FindRuns(ctx, filter)
	if err != nil {
		return runs, n, err
	}

	// if we reached the limit lets stop here
	if len(runs) >= filter.Limit {
		return runs, n, err
	}

	task, err := as.TaskService.FindTaskByID(ctx, filter.Task)
	if err != nil {
		return runs, n, err
	}

	filterPart := ""
	if filter.After != nil {
		filterPart = fmt.Sprintf(`|> filter(fn: (r) => r.runID > %q)`, filter.After.String())
	}

	// the data will be stored for 7 days in the system bucket so pulling 14d's is sufficient.
	runsScript := fmt.Sprintf(`from(bucketID: "000000000000000a")
	  |> range(start: -14d)
	  |> filter(fn: (r) => r._measurement == "runs" and r.taskID == %q)
	  %s
	  |> group(columns: ["taskID"])
	  |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")

	  `, filter.Task.String(), filterPart)

	auth, err := pctx.GetAuthorizer(ctx)
	if err != nil {
		return nil, 0, err
	}
	if auth.Kind() != "authorization" {
		return nil, 0, influxdb.ErrAuthorizerNotSupported
	}
	request := &query.Request{Authorization: auth.(*influxdb.Authorization), OrganizationID: task.OrganizationID, Compiler: lang.FluxCompiler{Query: runsScript}}

	ittr, err := as.qs.Query(ctx, request)
	if err != nil {
		return nil, 0, err
	}
	defer ittr.Release()

	re := &runReader{logger: as.logger.With(zap.String("component", "run-reader"), zap.String("taskID", filter.Task.String()))}
	for ittr.More() {
		err := ittr.Next().Tables().Do(re.readTable)
		if err != nil {
			return runs, n, err
		}
	}

	runs = append(runs, re.runs...)

	return runs, n, err
}

// FindRunByID returns a single run.
// First see if it is in the existing TaskService. If not pull it from analytical storage.
func (as *AnalyticalStorage) FindRunByID(ctx context.Context, taskID, runID influxdb.ID) (*influxdb.Run, error) {
	// check the taskService to see if the run is on its list
	run, err := as.TaskService.FindRunByID(ctx, taskID, runID)
	if err != nil {
		if err, ok := err.(*influxdb.Error); !ok || err.Msg != "run not found" {
			return run, err
		}
	}
	if run != nil {
		return run, err
	}

	task, err := as.TaskService.FindTaskByID(ctx, taskID)
	if err != nil {
		return run, err
	}

	// the data will be stored for 7 days in the system bucket so pulling 14d's is sufficient.
	findRunScript := fmt.Sprintf(`from(bucketID: "000000000000000a")
	|> range(start: -14d)
	|> filter(fn: (r) => r._measurement == "runs" and r.taskID == %q)
	|> group(columns: ["taskID"])
	|> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
	|> filter(fn: (r) => r.runID == %q)
	  `, taskID.String(), runID.String())

	auth, err := pctx.GetAuthorizer(ctx)
	if err != nil {
		return nil, err
	}
	if auth.Kind() != "authorization" {
		return nil, influxdb.ErrAuthorizerNotSupported
	}
	request := &query.Request{Authorization: auth.(*influxdb.Authorization), OrganizationID: task.OrganizationID, Compiler: lang.FluxCompiler{Query: findRunScript}}

	ittr, err := as.qs.Query(ctx, request)
	if err != nil {
		return nil, err
	}
	defer ittr.Release()

	re := &runReader{}
	for ittr.More() {
		err := ittr.Next().Tables().Do(re.readTable)
		if err != nil {
			return nil, err
		}
	}

	if len(re.runs) == 0 {
		return nil, platform.ErrRunNotFound

	}

	if len(re.runs) != 1 {
		return nil, &influxdb.Error{
			Msg:  "found multiple runs with id " + runID.String(),
			Code: influxdb.EInternal,
		}
	}

	return re.runs[0], err
}

func (as *AnalyticalStorage) RetryRun(ctx context.Context, taskID, runID influxdb.ID) (*influxdb.Run, error) {
	run, err := as.TaskService.RetryRun(ctx, taskID, runID)
	if err != nil {
		if err, ok := err.(*influxdb.Error); !ok || err.Msg != "run not found" {
			return run, err
		}
	}

	if run != nil {
		return run, err
	}

	// try finding the run (in our system or underlieing)
	run, err = as.FindRunByID(ctx, taskID, runID)
	if err != nil {
		return run, err
	}

	sf, err := run.ScheduledForTime()
	if err != nil {
		return run, err
	}

	return as.ForceRun(ctx, taskID, sf.Unix())
}

type runReader struct {
	runs   []*influxdb.Run
	logger *zap.Logger
}

func (re *runReader) readTable(tbl flux.Table) error {
	return tbl.Do(re.readRuns)
}

func (re *runReader) readRuns(cr flux.ColReader) error {
	for i := 0; i < cr.Len(); i++ {
		var r influxdb.Run
		for j, col := range cr.Cols() {
			switch col.Label {
			case "runID":
				if cr.Strings(j).ValueString(i) != "" {
					id, err := influxdb.IDFromString(cr.Strings(j).ValueString(i))
					if err != nil {
						re.logger.Info("failed to parse runID", zap.Error(err))
						continue
					}
					r.ID = *id
				}
			case "taskID":
				if cr.Strings(j).ValueString(i) != "" {
					id, err := influxdb.IDFromString(cr.Strings(j).ValueString(i))
					if err != nil {
						re.logger.Info("failed to parse taskID", zap.Error(err))
						continue
					}
					r.TaskID = *id
				}
			case startedAtField:
				r.StartedAt = cr.Strings(j).ValueString(i)
			case requestedAtField:
				r.RequestedAt = cr.Strings(j).ValueString(i)
			case scheduledForField:
				r.ScheduledFor = cr.Strings(j).ValueString(i)
			case statusField:
				r.Status = cr.Strings(j).ValueString(i)
			case finishedAtField:
				r.FinishedAt = cr.Strings(j).ValueString(i)
			case logField:
				logBytes := bytes.TrimSpace(cr.Strings(j).Value(i))
				if len(logBytes) != 0 {
					err := json.Unmarshal(logBytes, &r.Log)
					if err != nil {
						re.logger.Info("failed to parse log data", zap.Error(err), zap.ByteString("log_bytes", logBytes))
					}
				}
			}
		}

		// if we dont have a full enough data set we fail here.
		if r.ID.Valid() {
			re.runs = append(re.runs, &r)
		}

	}

	return nil
}
<commit_msg>fix(task): eliminate unneccesary auth checking when communicating to internal systems (#14411)<commit_after>package backend

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"time"

	"github.com/influxdata/flux"
	"github.com/influxdata/flux/lang"
	"github.com/influxdata/influxdb"
	platform "github.com/influxdata/influxdb"
	"github.com/influxdata/influxdb/models"
	"github.com/influxdata/influxdb/query"
	"github.com/influxdata/influxdb/storage"
	"github.com/influxdata/influxdb/tsdb"
	"go.uber.org/zap"
)

const (
	runIDField        = "runID"
	scheduledForField = "scheduledFor"
	startedAtField    = "startedAt"
	finishedAtField   = "finishedAt"
	requestedAtField  = "requestedAt"
	statusField       = "status"
	logField          = "logs"

	taskIDTag = "taskID"

	// Fixed system bucket ID for task and run logs.
	taskSystemBucketID platform.ID = 10
)

// NewAnalyticalStorage creates a new analytical store with access to the necessary systems for storing data and to act as a middleware
func NewAnalyticalStorage(logger *zap.Logger, ts influxdb.TaskService, tcs TaskControlService, pw storage.PointsWriter, qs query.QueryService) *AnalyticalStorage {
	return &AnalyticalStorage{
		logger:             logger,
		TaskService:        ts,
		TaskControlService: tcs,
		pw:                 pw,
		qs:                 qs,
	}
}

type AnalyticalStorage struct {
	influxdb.TaskService
	TaskControlService

	pw     storage.PointsWriter
	qs     query.QueryService
	logger *zap.Logger
}

func (as *AnalyticalStorage) FinishRun(ctx context.Context, taskID, runID influxdb.ID) (*influxdb.Run, error) {
	run, err := as.TaskControlService.FinishRun(ctx, taskID, runID)
	if run != nil && run.ID.String() != "" {
		task, err := as.TaskService.FindTaskByID(ctx, run.TaskID)
		if err != nil {
			return run, err
		}

		tags := models.Tags{
			models.NewTag([]byte(taskIDTag), []byte(run.TaskID.String())),
			models.NewTag([]byte(statusField), []byte(run.Status)),
		}

		// log an error if we have incomplete data on finish
		if !run.ID.Valid() ||
			run.ScheduledFor == "" ||
			run.StartedAt == "" ||
			run.FinishedAt == "" ||
			run.Status == "" {
			as.logger.Error("Run missing critical fields", zap.String("run", fmt.Sprintf("%+v", run)), zap.String("runID", run.ID.String()))
		}

		fields := map[string]interface{}{}
		fields[statusField] = run.Status
		fields[runIDField] = run.ID.String()
		fields[startedAtField] = run.StartedAt
		fields[finishedAtField] = run.FinishedAt
		fields[scheduledForField] = run.ScheduledFor
		if run.RequestedAt != "" {
			fields[requestedAtField] = run.RequestedAt
		}

		startedAt, err := run.StartedAtTime()
		if err != nil {
			startedAt = time.Now()
		}

		logBytes, err := json.Marshal(run.Log)
		if err != nil {
			return run, err
		}
		fields[logField] = string(logBytes)

		point, err := models.NewPoint("runs", tags, fields, startedAt)
		if err != nil {
			return run, err
		}

		// use the tsdb explode points to convert to the new style.
		// We could split this on our own but its quite possible this could change.
		points, err := tsdb.ExplodePoints(task.OrganizationID, taskSystemBucketID, models.Points{point})
		if err != nil {
			return run, err
		}
		return run, as.pw.WritePoints(ctx, points)
	}
	return run, err
}

// FindLogs returns logs for a run.
// First attempt to use the TaskService, then append additional analytical's logs to the list
func (as *AnalyticalStorage) FindLogs(ctx context.Context, filter influxdb.LogFilter) ([]*influxdb.Log, int, error) {
	var logs []*influxdb.Log
	if filter.Run != nil {
		run, err := as.FindRunByID(ctx, filter.Task, *filter.Run)
		if err != nil {
			return nil, 0, err
		}
		for i := 0; i < len(run.Log); i++ {
			logs = append(logs, &run.Log[i])
		}
		return logs, len(logs), nil
	}

	// add historical logs to the transactional logs.
	runs, n, err := as.FindRuns(ctx, influxdb.RunFilter{Task: filter.Task})
	if err != nil {
		return nil, 0, err
	}

	for _, run := range runs {
		for i := 0; i < len(run.Log); i++ {
			logs = append(logs, &run.Log[i])
		}
	}

	return logs, n, err
}

// FindRuns returns a list of runs that match a filter and the total count of returned runs.
// First attempt to use the TaskService, then append additional analytical's runs to the list
func (as *AnalyticalStorage) FindRuns(ctx context.Context, filter influxdb.RunFilter) ([]*influxdb.Run, int, error) {
	if filter.Limit == 0 {
		filter.Limit = influxdb.TaskDefaultPageSize
	}

	if filter.Limit < 0 || filter.Limit > influxdb.TaskMaxPageSize {
		return nil, 0, influxdb.ErrOutOfBoundsLimit
	}

	runs, n, err := as.TaskService.FindRuns(ctx, filter)
	if err != nil {
		return runs, n, err
	}

	// if we reached the limit lets stop here
	if len(runs) >= filter.Limit {
		return runs, n, err
	}

	task, err := as.TaskService.FindTaskByID(ctx, filter.Task)
	if err != nil {
		return runs, n, err
	}

	filterPart := ""
	if filter.After != nil {
		filterPart = fmt.Sprintf(`|> filter(fn: (r) => r.runID > %q)`, filter.After.String())
	}

	// the data will be stored for 7 days in the system bucket so pulling 14d's is sufficient.
	runsScript := fmt.Sprintf(`from(bucketID: "000000000000000a")
	  |> range(start: -14d)
	  |> filter(fn: (r) => r._measurement == "runs" and r.taskID == %q)
	  %s
	  |> group(columns: ["taskID"])
	  |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")

	  `, filter.Task.String(), filterPart)

	// At this point we are behind authorization
	// so we are faking a read only permission to the org's system bucket
	runSystemBucketID := influxdb.ID(10)
	runAuth := &influxdb.Authorization{
		OrgID: task.OrganizationID,
		Permissions: []influxdb.Permission{
			influxdb.Permission{
				Action: influxdb.ReadAction,
				Resource: influxdb.Resource{
					Type:  influxdb.BucketsResourceType,
					OrgID: &task.OrganizationID,
					ID:    &runSystemBucketID,
				},
			},
		},
	}
	request := &query.Request{Authorization: runAuth, OrganizationID: task.OrganizationID, Compiler: lang.FluxCompiler{Query: runsScript}}

	ittr, err := as.qs.Query(ctx, request)
	if err != nil {
		return nil, 0, err
	}
	defer ittr.Release()

	re := &runReader{logger: as.logger.With(zap.String("component", "run-reader"), zap.String("taskID", filter.Task.String()))}
	for ittr.More() {
		err := ittr.Next().Tables().Do(re.readTable)
		if err != nil {
			return runs, n, err
		}
	}

	runs = append(runs, re.runs...)

	return runs, n, err
}

// FindRunByID returns a single run.
// First see if it is in the existing TaskService. If not pull it from analytical storage.
func (as *AnalyticalStorage) FindRunByID(ctx context.Context, taskID, runID influxdb.ID) (*influxdb.Run, error) {
	// check the taskService to see if the run is on its list
	run, err := as.TaskService.FindRunByID(ctx, taskID, runID)
	if err != nil {
		if err, ok := err.(*influxdb.Error); !ok || err.Msg != "run not found" {
			return run, err
		}
	}
	if run != nil {
		return run, err
	}

	task, err := as.TaskService.FindTaskByID(ctx, taskID)
	if err != nil {
		return run, err
	}

	// the data will be stored for 7 days in the system bucket so pulling 14d's is sufficient.
	findRunScript := fmt.Sprintf(`from(bucketID: "000000000000000a")
	|> range(start: -14d)
	|> filter(fn: (r) => r._measurement == "runs" and r.taskID == %q)
	|> group(columns: ["taskID"])
	|> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
	|> filter(fn: (r) => r.runID == %q)
	  `, taskID.String(), runID.String())

	// At this point we are behind authorization
	// so we are faking a read only permission to the org's system bucket
	runSystemBucketID := influxdb.ID(10)
	runAuth := &influxdb.Authorization{
		OrgID: task.OrganizationID,
		Permissions: []influxdb.Permission{
			influxdb.Permission{
				Action: influxdb.ReadAction,
				Resource: influxdb.Resource{
					Type:  influxdb.BucketsResourceType,
					OrgID: &task.OrganizationID,
					ID:    &runSystemBucketID,
				},
			},
		},
	}
	request := &query.Request{Authorization: runAuth, OrganizationID: task.OrganizationID, Compiler: lang.FluxCompiler{Query: findRunScript}}

	ittr, err := as.qs.Query(ctx, request)
	if err != nil {
		return nil, err
	}
	defer ittr.Release()

	re := &runReader{}
	for ittr.More() {
		err := ittr.Next().Tables().Do(re.readTable)
		if err != nil {
			return nil, err
		}
	}

	if len(re.runs) == 0 {
		return nil, platform.ErrRunNotFound

	}

	if len(re.runs) != 1 {
		return nil, &influxdb.Error{
			Msg:  "found multiple runs with id " + runID.String(),
			Code: influxdb.EInternal,
		}
	}

	return re.runs[0], err
}

func (as *AnalyticalStorage) RetryRun(ctx context.Context, taskID, runID influxdb.ID) (*influxdb.Run, error) {
	run, err := as.TaskService.RetryRun(ctx, taskID, runID)
	if err != nil {
		if err, ok := err.(*influxdb.Error); !ok || err.Msg != "run not found" {
			return run, err
		}
	}

	if run != nil {
		return run, err
	}

	// try finding the run (in our system or underlieing)
	run, err = as.FindRunByID(ctx, taskID, runID)
	if err != nil {
		return run, err
	}

	sf, err := run.ScheduledForTime()
	if err != nil {
		return run, err
	}

	return as.ForceRun(ctx, taskID, sf.Unix())
}

type runReader struct {
	runs   []*influxdb.Run
	logger *zap.Logger
}

func (re *runReader) readTable(tbl flux.Table) error {
	return tbl.Do(re.readRuns)
}

func (re *runReader) readRuns(cr flux.ColReader) error {
	for i := 0; i < cr.Len(); i++ {
		var r influxdb.Run
		for j, col := range cr.Cols() {
			switch col.Label {
			case "runID":
				if cr.Strings(j).ValueString(i) != "" {
					id, err := influxdb.IDFromString(cr.Strings(j).ValueString(i))
					if err != nil {
						re.logger.Info("failed to parse runID", zap.Error(err))
						continue
					}
					r.ID = *id
				}
			case "taskID":
				if cr.Strings(j).ValueString(i) != "" {
					id, err := influxdb.IDFromString(cr.Strings(j).ValueString(i))
					if err != nil {
						re.logger.Info("failed to parse taskID", zap.Error(err))
						continue
					}
					r.TaskID = *id
				}
			case startedAtField:
				r.StartedAt = cr.Strings(j).ValueString(i)
			case requestedAtField:
				r.RequestedAt = cr.Strings(j).ValueString(i)
			case scheduledForField:
				r.ScheduledFor = cr.Strings(j).ValueString(i)
			case statusField:
				r.Status = cr.Strings(j).ValueString(i)
			case finishedAtField:
				r.FinishedAt = cr.Strings(j).ValueString(i)
			case logField:
				logBytes := bytes.TrimSpace(cr.Strings(j).Value(i))
				if len(logBytes) != 0 {
					err := json.Unmarshal(logBytes, &r.Log)
					if err != nil {
						re.logger.Info("failed to parse log data", zap.Error(err), zap.ByteString("log_bytes", logBytes))
					}
				}
			}
		}

		// if we dont have a full enough data set we fail here.
		if r.ID.Valid() {
			re.runs = append(re.runs, &r)
		}

	}

	return nil
}
<|endoftext|>


<commit_before>package triangle

/*
#cgo CFLAGS: -I. -w
#cgo LDFLAGS: -L${SRCDIR} -ltriangle
#include "triangle.h"
#include <stdlib.h>
*/
import "C"
import "unsafe"

type triangulateIO struct {
	ct *C.struct_triangulateio
}

func newTriangulateIO() *triangulateIO {
	t := triangulateIO{}
	t.ct = (*C.struct_triangulateio)(C.malloc(C.sizeof_struct_triangulateio))
	if t.ct == nil {
		panic("Unable to allocate memory")
	}
	return &t
}

func freeTriangulateIO(t *triangulateIO) {
	C.free(unsafe.Pointer(t.ct))
}

func (t *triangulateIO) NumberOfEdges() int {
	return int(t.ct.numberofedges)
}

func (t *triangulateIO) NumberOfPoints() int {
	return int(t.ct.numberofpoints)
}

func (t *triangulateIO) NumberOfSegments() int {
	return int(t.ct.numberofsegments)
}

func (t *triangulateIO) NumberOfTriangles() int {
	return int(t.ct.numberoftriangles)
}

func (t *triangulateIO) Normals() [][2]float64 {
	num := t.NumberOfEdges()
	sz := num * 2
	slice := (*[1 << 30]C.double)(unsafe.Pointer(t.ct.normlist))[:sz:sz]

	result := make([][2]float64, num)
	for i := 0; i < num; i++ {
		j := i * 2
		result[i] = [2]float64{float64(slice[j]), float64(slice[j+1])}
	}
	return result
}

func (t *triangulateIO) Edges() [][2]int {
	numEdges := t.NumberOfEdges()
	sz := numEdges * 2
	slice := (*[1 << 30]C.int)(unsafe.Pointer(t.ct.edgelist))[:sz:sz]

	result := make([][2]int, numEdges)
	for i := 0; i < numEdges; i++ {
		j := i * 2
		result[i] = [2]int{int(slice[j]), int(slice[j+1])}
	}
	return result
}

func (t *triangulateIO) Points() [][2]float64 {
	numPoints := t.NumberOfPoints()
	sz := numPoints * 2
	slice := (*[1 << 30]C.double)(unsafe.Pointer(t.ct.pointlist))[:sz:sz]

	result := make([][2]float64, numPoints)
	for i := 0; i < numPoints; i++ {
		j := i * 2
		result[i] = [2]float64{float64(slice[j]), float64(slice[j+1])}
	}
	return result
}

func (t *triangulateIO) PointMarkers() []int {
	sz := t.NumberOfPoints()
	slice := (*[1 << 30]C.int)(unsafe.Pointer(t.ct.pointmarkerlist))[:sz:sz]

	result := make([]int, sz)
	for i := 0; i < sz; i++ {
		result[i] = int(slice[i])
	}
	return result
}

func (t *triangulateIO) SetEdges(edges [][2]int) {
	t.ct.edgelist = (*C.int)(unsafe.Pointer(&edges[0][0]))
	t.ct.numberofedges = C.int(len(edges))
}

func (t *triangulateIO) SetPoints(pts [][2]float64) {
	t.ct.pointlist = (*C.double)(unsafe.Pointer(&pts[0][0]))
	t.ct.numberofpoints = C.int(len(pts))
}

func (t *triangulateIO) SetPointMarkers(markers []int) {
	t.ct.pointmarkerlist = (*C.int)(unsafe.Pointer(&markers[0]))
}

func (t *triangulateIO) SetSegments(segments [][2]int) {
	t.ct.segmentlist = (*C.int)(unsafe.Pointer(&segments[0]))
}

func (t *triangulateIO) SetSegmentMarkers(markers [][2]int) {
	t.ct.segmentmarkerlist = (*C.int)(unsafe.Pointer(&markers[0]))
}

func (t *triangulateIO) SetTriangles(tri [][3]int) {
	t.ct.trianglelist = (*C.int)(unsafe.Pointer(&tri[0][0]))
}

func (t *triangulateIO) SetTriangleAreas(areas []float64) {
	t.ct.trianglearealist = (*C.double)(unsafe.Pointer(&areas[0]))
}

func (t *triangulateIO) Triangles() [][3]int {
	numTriangles := t.NumberOfTriangles()
	numCorners := int(t.ct.numberofcorners)
	sz := numTriangles * numCorners
	slice := (*[1 << 30]C.int)(unsafe.Pointer(t.ct.trianglelist))[:sz:sz]

	result := make([][3]int, numTriangles)
	for i := 0; i < numTriangles; i++ {
		j := i * numCorners
		result[i] = [3]int{int(slice[j]), int(slice[j+1]), int(slice[j+2])}
	}
	return result
}

func triang(opt string, in, out, vorout *triangulateIO) {
	copt := C.CString(opt)
	defer C.free(unsafe.Pointer(copt))
	if vorout == nil {
		C.triangulate(copt, in.ct, out.ct, nil)
	} else {
		C.triangulate(copt, in.ct, out.ct, vorout.ct)
	}
}
<commit_msg>Add helper functions to convert C array to Go slice<commit_after>package triangle

/*
#cgo CFLAGS: -I.
#cgo LDFLAGS: ${SRCDIR}/libtriangle.a
#include "triangle.h"
#include <stdlib.h>
*/
import "C"
import "unsafe"

type triangulateIO struct {
	ct *C.struct_triangulateio
}

func newTriangulateIO() *triangulateIO {
	t := triangulateIO{}
	t.ct = (*C.struct_triangulateio)(C.malloc(C.sizeof_struct_triangulateio))
	if t.ct == nil {
		panic("Unable to allocate memory")
	}
	return &t
}

func freeTriangulateIO(t *triangulateIO) {
	C.free(unsafe.Pointer(t.ct))
}

func (t *triangulateIO) NumberOfEdges() int {
	return int(t.ct.numberofedges)
}

func (t *triangulateIO) NumberOfPoints() int {
	return int(t.ct.numberofpoints)
}

func (t *triangulateIO) NumberOfSegments() int {
	return int(t.ct.numberofsegments)
}

func (t *triangulateIO) NumberOfTriangles() int {
	return int(t.ct.numberoftriangles)
}

func (t *triangulateIO) Normals() [][2]float64 {
	return cArrToFlt64Slice2D(unsafe.Pointer(t.ct.normlist), t.NumberOfEdges())
}

func (t *triangulateIO) Edges() [][2]int {
	return cArrToIntSlice2D(unsafe.Pointer(t.ct.edgelist), t.NumberOfEdges())
}

func (t *triangulateIO) Points() [][2]float64 {
	return cArrToFlt64Slice2D(unsafe.Pointer(t.ct.pointlist), t.NumberOfPoints())
}

func (t *triangulateIO) PointMarkers() []int {
	return cArrToIntSlice(unsafe.Pointer(t.ct.pointmarkerlist), t.NumberOfPoints())
}

func (t *triangulateIO) SetEdges(edges [][2]int) {
	t.ct.edgelist = (*C.int)(unsafe.Pointer(&edges[0][0]))
	t.ct.numberofedges = C.int(len(edges))
}

func (t *triangulateIO) SetPoints(pts [][2]float64) {
	t.ct.pointlist = (*C.double)(unsafe.Pointer(&pts[0][0]))
	t.ct.numberofpoints = C.int(len(pts))
}

func (t *triangulateIO) SetPointMarkers(markers []int) {
	t.ct.pointmarkerlist = (*C.int)(unsafe.Pointer(&markers[0]))
}

func (t *triangulateIO) SetSegments(segments [][2]int) {
	t.ct.segmentlist = (*C.int)(unsafe.Pointer(&segments[0]))
}

func (t *triangulateIO) SetSegmentMarkers(markers [][2]int) {
	t.ct.segmentmarkerlist = (*C.int)(unsafe.Pointer(&markers[0]))
}

func (t *triangulateIO) SetTriangles(tri [][3]int) {
	t.ct.trianglelist = (*C.int)(unsafe.Pointer(&tri[0][0]))
}

func (t *triangulateIO) SetTriangleAreas(areas []float64) {
	t.ct.trianglearealist = (*C.double)(unsafe.Pointer(&areas[0]))
}

func (t *triangulateIO) Triangles() [][3]int {
	return cArrToIntSlice3D(unsafe.Pointer(t.ct.trianglelist), t.NumberOfTriangles())
}

func triang(opt string, in, out, vorout *triangulateIO) {
	copt := C.CString(opt)
	defer C.free(unsafe.Pointer(copt))
	if vorout == nil {
		C.triangulate(copt, in.ct, out.ct, nil)
	} else {
		C.triangulate(copt, in.ct, out.ct, vorout.ct)
	}
}

func cArrToIntSlice(ptr unsafe.Pointer, length int) []int {
	slice := (*[1 << 30]C.int)(ptr)[:length:length]
	result := make([]int, length)
	for i := 0; i < length; i++ {
		result[i] = int(slice[i])
	}
	return result
}

func cArrToIntSlice2D(ptr unsafe.Pointer, length int) [][2]int {
	sz := length * 2
	slice := (*[1 << 30]C.int)(ptr)[:sz:sz]
	result := make([][2]int, length)
	for i := 0; i < length; i++ {
		j := i * 2
		result[i] = [2]int{int(slice[j]), int(slice[j+1])}
	}
	return result
}

func cArrToIntSlice3D(ptr unsafe.Pointer, length int) [][3]int {
	sz := length * 3
	slice := (*[1 << 30]C.int)(ptr)[:sz:sz]
	result := make([][3]int, length)
	for i := 0; i < length; i++ {
		j := i * 3
		result[i] = [3]int{int(slice[j]), int(slice[j+1]), int(slice[j+2])}
	}
	return result
}

func cArrToFlt64Slice(ptr unsafe.Pointer, length int) []float64 {
	slice := (*[1 << 30]C.double)(ptr)[:length:length]
	result := make([]float64, length)
	for i := 0; i < length; i++ {
		result[i] = float64(slice[i])
	}
	return result
}

func cArrToFlt64Slice2D(ptr unsafe.Pointer, length int) [][2]float64 {
	sz := length * 2
	slice := (*[1 << 30]C.double)(ptr)[:sz:sz]
	result := make([][2]float64, length)
	for i := 0; i < length; i++ {
		j := i * 2
		result[i] = [2]float64{float64(slice[j]), float64(slice[j+1])}
	}
	return result
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/csv"
	"encoding/json"
	"encoding/xml"
	"flag"
	"fmt"
	"github.com/robertkrimen/otto"
	"gopkg.in/xmlpath.v2"
	"io"
	"os"
	"strings"
)

type transformExpression struct {
	XPath string `json:"xpath"`
	Eval  string `json:"eval"`

	xpath *xmlpath.Path
}

type transformColumn struct {
	Name string `json:"name"`
	transformExpression
}

type transform struct {
	Path    string                `json:"path"`
	Include []transformExpression `json:"include"`
	Exclude []transformExpression `json:"exclude"`
	Columns []transformColumn     `json:"columns"`

	path []string
}

func run(in, out, transformPath string) error {
	var err error

	switch {
	case in == "":
		return fmt.Errorf("missing required parameter -i")
	case out == "":
		return fmt.Errorf("missing required parameter -o")
	case transformPath == "":
		return fmt.Errorf("missing required parameter -t")
	}

	transformFile, err := os.Open(transformPath)
	if err != nil {
		return fmt.Errorf("failed to open %v: %v", transformPath, err)
	}
	defer transformFile.Close()

	var t transform
	err = json.NewDecoder(transformFile).Decode(&t)
	if err != nil {
		return fmt.Errorf("failed to parse json in %v: %v", transformPath, err)
	}

	t.path = strings.Split(t.Path, "/")
	if len(t.path) < 2 && t.path[0] != "" {
		return fmt.Errorf("Path field is invalid")
	}
	t.path = t.path[1:]

	if t.path[len(t.path)-1] == "" {
		t.path = t.path[:len(t.path)-1]
		if len(t.path) == 0 {
			return fmt.Errorf("parameter -path is invalid")
		}
	}

	for i := 0; i < len(t.Include); i++ {
		if err = compile(&t.Include[i]); err != nil {
			return err
		}
	}
	for i := 0; i < len(t.Exclude); i++ {
		if err = compile(&t.Exclude[i]); err != nil {
			return err
		}
	}
	for i := 0; i < len(t.Columns); i++ {
		if err = compile(&t.Columns[i].transformExpression); err != nil {
			return err
		}
	}

	fmt.Println(t)

	xmlFile, err := os.Open(in)
	if err != nil {
		return fmt.Errorf("failed to open %v: %v", in, err)
	}
	defer xmlFile.Close()

	var writer io.Writer
	if out != "" {
		outFile, err := os.Create(out)
		if err != nil {
			return fmt.Errorf("failed to create %v: %v", out, err)
		}
		defer outFile.Close()
		writer = outFile
	} else {
		writer = os.Stdout
	}

	headers := make([]string, len(t.Columns))
	for i := 0; i < len(headers); i++ {
		headers[i] = t.Columns[i].Name
	}

	jsvm := otto.New()

	csvWriter := csv.NewWriter(writer)
	csvWriter.Write(headers)

	err = parse(xmlFile, t.path, func(xml []byte) error {
		node, err := xmlpath.Parse(bytes.NewReader(xml))
		if err != nil {
			return fmt.Errorf("failed to parse xml: %v", err)
		}

		include := len(t.Include) == 0
		for i := 0; i < len(t.Include); i++ {
			if include, err = evalBool(jsvm, node, &t.Include[i]); err != nil {
				return fmt.Errorf("failed to eval include expression: %v", err)
			}
			if include {
				break
			}
		}
		if !include {
			return nil
		}

		for i := 0; i < len(t.Exclude); i++ {
			exclude, err := evalBool(jsvm, node, &t.Exclude[i])
			if err != nil {
				return fmt.Errorf("failed to eval exclude expression: %v", err)
			}
			if exclude {
				return nil
			}
		}

		values := make([]string, len(t.Columns))
		for i := 0; i < len(t.Columns); i++ {
			value, err := evalString(jsvm, node, &t.Columns[i].transformExpression)
			if err != nil {
				return fmt.Errorf("failed to eval column expression: %v", err)
			}
			values[i] = strings.TrimSpace(value)
		}

		err = csvWriter.Write(values)
		if err != nil {
			return fmt.Errorf("failed to write to csv file: %v", err)
		}
		return nil
	})

	csvWriter.Flush()

	if err != nil {
		return err
	}

	return nil
}

func compile(expr *transformExpression) error {
	var err error
	expr.xpath, err = xmlpath.Compile(expr.XPath)
	if err != nil {
		return fmt.Errorf("Failed to compile xpath expression '%v': %v", expr.XPath, err)
	}
	return nil
}

func eval(jsvm *otto.Otto, node *xmlpath.Node, expr *transformExpression) (otto.Value, error) {
	emptyString, _ := otto.ToValue("")

	nodeValue, ok := expr.xpath.String(node)
	if !ok {
		return emptyString, nil
	}

	if expr.Eval == "" {
		stringVal, _ := otto.ToValue(nodeValue)
		return stringVal, nil
	}

	jsvm.Set("v", nodeValue)
	exprValue, err := jsvm.Run(expr.Eval)
	if err != nil {
		return emptyString,
			fmt.Errorf("failed to evaluate javascript expr '%v': %v", expr.Eval, err)
	}
	return exprValue, nil
}

func evalBool(jsvm *otto.Otto, node *xmlpath.Node, expr *transformExpression) (bool, error) {
	if expr.Eval == "" {
		return expr.xpath.Exists(node), nil
	}

	exprValue, err := eval(jsvm, node, expr)
	if err != nil {
		return false, err
	}

	boolValue, err := exprValue.ToBoolean()
	if err != nil {
		return false,
			fmt.Errorf("result of javascript expr '%v' doesn't evaluate to bool", expr.Eval)
	}

	return boolValue, nil
}

func evalString(jsvm *otto.Otto, node *xmlpath.Node, expr *transformExpression) (string, error) {
	exprValue, err := eval(jsvm, node, expr)
	if err != nil {
		return "", err
	}

	stringValue, err := exprValue.ToString()
	if err != nil {
		return "",
			fmt.Errorf("result of javascript expr '%v' doesn't evaluate to string", expr.Eval)
	}

	return stringValue, nil
}

func parse(xmlFile io.Reader, path []string, emit func(xml []byte) error) error {
	buffer := &bytes.Buffer{}
	decoder := xml.NewDecoder(xmlFile)
	encoder := xml.NewEncoder(buffer)
	stack := make([]string, 0, 10)
	matching := false

	for {
		t, err := decoder.Token()
		if err == io.EOF {
			break
		} else if err != nil {
			return fmt.Errorf("failed to parse xml: %v", err)
		}

		switch t := t.(type) {
		case xml.StartElement:
			stack = append(stack, t.Name.Local)
			if !matching {
				matching = sliceEq(stack, path)
			}
			if matching {
				encoder.EncodeToken(t)
			}

		case xml.EndElement:
			stack = stack[:len(stack)-1]
			if matching {
				encoder.EncodeToken(t)
				if len(stack) < len(path) {
					matching = false
					encoder.Flush()
					err := emit(buffer.Bytes())
					if err != nil {
						return fmt.Errorf("failed to process xml node: %v", err)
					}
					buffer.Reset()
				}
			}

		default:
			if matching {
				encoder.EncodeToken(t)
			}
		}
	}
	return nil
}

func sliceEq(a, b []string) bool {
	if len(a) != len(b) {
		return false
	}
	for i := 0; i < len(a); i++ {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

func main() {
	var (
		in        = flag.String("i", "", "")
		out       = flag.String("o", "", "")
		transform = flag.String("t", "", "")
	)

	flag.Parse()

	err := run(*in, *out, *transform)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}
}
<commit_msg>More improvements. Checkpoint.<commit_after>package main

import (
	"bytes"
	"encoding/csv"
	"encoding/json"
	"encoding/xml"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"runtime"
	"runtime/pprof"
	"strings"
	"sync"
	"time"

	"github.com/robertkrimen/otto"
	"gopkg.in/xmlpath.v2"
)

type transformExpression struct {
	XPath string `json:"xpath"`
	Eval  string `json:"eval"`

	xpath *xmlpath.Path
}

type transformColumn struct {
	Name string `json:"name"`
	transformExpression
}

type transform struct {
	Path    string                `json:"path"`
	Include []transformExpression `json:"include"`
	Exclude []transformExpression `json:"exclude"`
	Columns []transformColumn     `json:"columns"`

	path []string
}

func run(in, out, transformPath string, append bool) error {
	var err error

	switch {
	case in == "":
		return fmt.Errorf("missing required parameter -i")
	case out == "":
		return fmt.Errorf("missing required parameter -o")
	case transformPath == "":
		return fmt.Errorf("missing required parameter -t")
	}

	transformFile, err := os.Open(transformPath)
	if err != nil {
		return fmt.Errorf("failed to open %v: %v", transformPath, err)
	}
	defer transformFile.Close()

	t := &transform{}
	err = json.NewDecoder(transformFile).Decode(t)
	if err != nil {
		return fmt.Errorf("failed to parse json in %v: %v", transformPath, err)
	}

	t.path = strings.Split(t.Path, "/")
	if len(t.path) < 2 && t.path[0] != "" {
		return fmt.Errorf("Path field is invalid")
	}
	t.path = t.path[1:]

	if t.path[len(t.path)-1] == "" {
		t.path = t.path[:len(t.path)-1]
		if len(t.path) == 0 {
			return fmt.Errorf("parameter -path is invalid")
		}
	}

	for i := 0; i < len(t.Include); i++ {
		if err = compile(&t.Include[i]); err != nil {
			return err
		}
	}
	for i := 0; i < len(t.Exclude); i++ {
		if err = compile(&t.Exclude[i]); err != nil {
			return err
		}
	}
	for i := 0; i < len(t.Columns); i++ {
		if err = compile(&t.Columns[i].transformExpression); err != nil {
			return err
		}
	}

	xmlFile, err := os.Open(in)
	if err != nil {
		return fmt.Errorf("failed to open %v: %v", in, err)
	}
	defer xmlFile.Close()

	var writer io.Writer
	if out != "" {
		outFileFlags := os.O_CREATE
		if append {
			outFileFlags |= os.O_APPEND
		} else {
			outFileFlags |= os.O_TRUNC
		}
		outFile, err := os.OpenFile(out, outFileFlags, 644)
		if err != nil {
			return fmt.Errorf("failed to create %v: %v", out, err)
		}
		defer outFile.Close()
		writer = outFile
	} else {
		writer = os.Stdout
	}

	headers := make([]string, len(t.Columns))
	for i := 0; i < len(headers); i++ {
		headers[i] = t.Columns[i].Name
	}

	csvWriter := csv.NewWriter(writer)

	if !append {
		csvWriter.Write(headers)
	}

	wg := &sync.WaitGroup{}

	processorCount := runtime.NumCPU()
	xmlChan := make(chan []byte, processorCount)
	csvChan := make(chan []string)
	errChan := make(chan error)

	cancelChan := make(chan struct{})

	wg.Add(processorCount)
	for i := 0; i < processorCount; i++ {
		go process(t, xmlChan, csvChan, errChan, wg)
	}

	wg.Add(1)
	go func() {
		defer wg.Done()
		err = parse(xmlFile, t.path, func(xml []byte) error {
			xmlCopy := make([]byte, len(xml))
			copy(xmlCopy, xml)

			xmlChan <- xmlCopy

			select {
			case <-cancelChan:
				return fmt.Errorf("cancelled")
			default:
				return nil
			}
		})

		if err != nil {
			errChan <- err
		}
		close(xmlChan)
	}()

	go func() {
		wg.Wait()
		close(csvChan)
	}()

loop:
	for {
		select {
		case err = <-errChan:
			close(cancelChan)
			break loop

		case csvLine, ok := <-csvChan:
			if !ok {
				break loop
			}
			err = csvWriter.Write(csvLine)
			if err != nil {
				break loop
			}
		}
	}

	if err != nil {
		return err
	}

	csvWriter.Flush()

	return nil
}

func process(t *transform, xmlChan <-chan []byte, csvChan chan<- []string, errChan chan error, wg *sync.WaitGroup) {
	defer wg.Done()

	jsvm := otto.New()

loop:
	for xml := range xmlChan {
		node, err := xmlpath.Parse(bytes.NewReader(xml))
		if err != nil {
			errChan <- fmt.Errorf("failed to parse xml: %v", err)
			return
		}

		include := len(t.Include) == 0
		for i := 0; i < len(t.Include); i++ {
			if include, err = evalBool(jsvm, node, &t.Include[i]); err != nil {
				errChan <- fmt.Errorf("failed to eval include expression: %v", err)
				return
			}
			if include {
				break
			}
		}
		if !include {
			continue loop
		}

		for i := 0; i < len(t.Exclude); i++ {
			exclude, err := evalBool(jsvm, node, &t.Exclude[i])
			if err != nil {
				errChan <- fmt.Errorf("failed to eval exclude expression: %v", err)
				return
			}
			if exclude {
				continue loop
			}
		}

		values := make([]string, len(t.Columns))
		for i := 0; i < len(t.Columns); i++ {
			value, err := evalString(jsvm, node, &t.Columns[i].transformExpression)
			if err != nil {
				errChan <- fmt.Errorf("failed to eval column expression: %v", err)
				return
			}
			values[i] = strings.TrimSpace(value)
		}

		csvChan <- values
	}
}

func compile(expr *transformExpression) error {
	var err error
	expr.xpath, err = xmlpath.Compile(expr.XPath)
	if err != nil {
		return fmt.Errorf("Failed to compile xpath expression '%v': %v", expr.XPath, err)
	}
	return nil
}

func eval(jsvm *otto.Otto, node *xmlpath.Node, expr *transformExpression) (otto.Value, error) {
	emptyString, _ := otto.ToValue("")

	nodeValue, ok := expr.xpath.String(node)
	if !ok {
		return emptyString, nil
	}

	if expr.Eval == "" {
		stringVal, _ := otto.ToValue(nodeValue)
		return stringVal, nil
	}

	jsvm.Set("v", nodeValue)
	exprValue, err := jsvm.Run(expr.Eval)
	if err != nil {
		return emptyString,
			fmt.Errorf("failed to evaluate javascript expr '%v': %v", expr.Eval, err)
	}
	return exprValue, nil
}

func evalBool(jsvm *otto.Otto, node *xmlpath.Node, expr *transformExpression) (bool, error) {
	if expr.Eval == "" {
		return expr.xpath.Exists(node), nil
	}

	exprValue, err := eval(jsvm, node, expr)
	if err != nil {
		return false, err
	}

	boolValue, err := exprValue.ToBoolean()
	if err != nil {
		return false,
			fmt.Errorf("result of javascript expr '%v' doesn't evaluate to bool", expr.Eval)
	}

	return boolValue, nil
}

func evalString(jsvm *otto.Otto, node *xmlpath.Node, expr *transformExpression) (string, error) {
	exprValue, err := eval(jsvm, node, expr)
	if err != nil {
		return "", err
	}

	stringValue, err := exprValue.ToString()
	if err != nil {
		return "",
			fmt.Errorf("result of javascript expr '%v' doesn't evaluate to string", expr.Eval)
	}

	return stringValue, nil
}

func parse(xmlFile io.Reader, path []string, emit func(xml []byte) error) error {
	buffer := &bytes.Buffer{}
	decoder := xml.NewDecoder(xmlFile)
	encoder := xml.NewEncoder(buffer)
	stack := make([]string, 0, 10)
	matching := false

	for {
		t, err := decoder.Token()
		if err == io.EOF {
			break
		} else if err != nil {
			return fmt.Errorf("failed to parse xml: %v", err)
		}

		switch t := t.(type) {
		case xml.StartElement:
			stack = append(stack, t.Name.Local)
			if !matching {
				matching = sliceEq(stack, path)
			}
			if matching {
				encoder.EncodeToken(t)
			}

		case xml.EndElement:
			stack = stack[:len(stack)-1]
			if matching {
				encoder.EncodeToken(t)
				if len(stack) < len(path) {
					matching = false
					encoder.Flush()
					err := emit(buffer.Bytes())
					if err != nil {
						return fmt.Errorf("failed to process xml node: %v", err)
					}
					buffer.Reset()
				}
			}

		default:
			if matching {
				encoder.EncodeToken(t)
			}
		}
	}
	return nil
}

func sliceEq(a, b []string) bool {
	if len(a) != len(b) {
		return false
	}
	for i := 0; i < len(a); i++ {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

func main() {
	var (
		in         = flag.String("i", "", "")
		out        = flag.String("o", "", "")
		transform  = flag.String("t", "", "")
		append     = flag.Bool("a", false, "")
		cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
		memprofile = flag.String("memprofile", "", "write memory profile to file")
	)

	flag.Parse()

	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		defer f.Close()
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	if *memprofile != "" {
		go func() {
			time.Sleep(5 * time.Second)
			f, err := os.Create(*memprofile)
			if err != nil {
				log.Fatal(err)
			}
			defer f.Close()
			pprof.Lookup("heap").WriteTo(f, 0)
		}()
	}

	err := run(*in, *out, *transform, *append)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package goutube

import (
	"fmt"
	"net/http"
	"io/ioutil"
	"strings"
	"encoding/json"
)

type YoutubeArgs struct {
	VideoID string
	VideoFormats string
	VideoAdaptFormats string
	VideoManifestURL string
	PlayerSource string
}

func getYoutubeArgs (url string) YoutubeArgs {
	resp, _ := http.Get(url)
	page, _ := ioutil.ReadAll(resp.Body)

	bodyTxt := string(page[:])
	startIndex := strings.Index(bodyTxt, "ytplayer.config = ") + 18

	openCount := 0
	closeCount := 0
	endIndex := startIndex
	for i, c := range bodyTxt[startIndex:] {
		if c == '{' {
			openCount++
		} else if c == '}' {
			closeCount++
		}
		if openCount == closeCount {
			endIndex = startIndex + i + 1
			break
		}
	}

	var videoOptions map[string]interface{}
	videoOptionsJSON := bodyTxt[startIndex: endIndex]
	json.Unmarshal([]byte(videoOptionsJSON), &videoOptions)

	args := videoOptions["args"]
	videoID := args.(map[string]interface{})["video_id"].(string)
	videoFormats := args.(map[string]interface{})["url_encoded_fmt_stream_map"].(string)
	videoAdaptFormats := args.(map[string]interface{})["adaptive_fmts"].(string)
	videoManifestURL := args.(map[string]interface{})["dashmpd"].(string)
	jsURL := videoOptions["assets"].(map[string]interface{})["js"].(string)

	if strings.Index(jsURL, "//") != -1 {
		jsURL = "http:" + jsURL
	}
	resp, _ = http.Get(jsURL)
	page, _ = ioutil.ReadAll(resp.Body)
	jsScriptBody := string(page[:])

	return YoutubeArgs{
		VideoID: videoID, VideoFormats: videoFormats,
		VideoAdaptFormats: videoAdaptFormats, VideoManifestURL: videoManifestURL,
		ScriptBody: jsScriptBody}
}

func getYoutubeDownloadLink(args YoutubeArgs) string {

}

<commit_msg>Reordered some code and made it working<commit_after>package main

import (
	"fmt"
	"net/http"
	"net/url"
	"io/ioutil"
	"strings"
	"regexp"
	"encoding/json"
	"github.com/robertkrimen/otto"
)

type YoutubeConfigArgs struct {
	VideoID           string
	VideoFormats      string
	VideoAdaptFormats string
	VideoManifestURL  string
	PlayerSource      string
}

type Video struct {
	URL          string
	Signature    string
	Quality      string
	Format       string
}

func MakeDownloadLink(link string, signature string) string {
	downloadLink, _ := url.QueryUnescape(link + "&signature=" + signature)
	fmt.Printf("%T", downloadLink)
	return downloadLink
}

func ExtractVideoFormats(args YoutubeConfigArgs) []Video {
	text := args.VideoFormats + "," + args.VideoAdaptFormats

	videos := make([]Video, 0)
	for _, a := range strings.Split(text, ",") {
		v := make(map[string]string)
		for _, b := range strings.Split(a, "&") {
			pair := strings.Split(b, "=")
			if len(pair) >= 2 {
				v[pair[0]] = pair[1]
			}
		}
		signature := DecryptSignature(args, v["s"])
		format, _ := url.QueryUnescape(v["type"])

		videos = append(videos,
			Video{ URL: MakeDownloadLink(v["url"], signature), Signature: signature,
				Quality: v["quality"], Format: format })
	}
	return videos
}

func GetYoutubeConfigArgs(url string) YoutubeConfigArgs {
	resp, _ := http.Get(url)
	page, _ := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()

	bodyTxt := string(page[:])
	startIndex := strings.Index(bodyTxt, "ytplayer.config = ") + 18

	openCount := 0
	closeCount := 0
	endIndex := startIndex
	for i, c := range bodyTxt[startIndex:] {
		if c == '{' {
			openCount++
		} else if c == '}' {
			closeCount++
		}
		if openCount == closeCount {
			endIndex = startIndex + i + 1
			break
		}
	}

	var videoOptions map[string]interface{}
	videoOptionsJSON := bodyTxt[startIndex: endIndex]
	json.Unmarshal([]byte(videoOptionsJSON), &videoOptions)

	args := videoOptions["args"].(map[string]interface{})
	videoID := args["video_id"].(string)
	videoFormats := args["url_encoded_fmt_stream_map"].(string)
	videoAdaptFormats := args["adaptive_fmts"].(string)
	videoManifestURL := args["dashmpd"].(string)

	jsURL := videoOptions["assets"].(map[string]interface{})["js"].(string)

	if strings.Index(jsURL, "//") != -1 {
		jsURL = "http:" + jsURL
	}
	resp, _ = http.Get(jsURL)
	page, _ = ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	jsScriptBody := string(page[:])

	return YoutubeConfigArgs{
		VideoID: videoID, VideoFormats: videoFormats,
		VideoAdaptFormats: videoAdaptFormats, VideoManifestURL: videoManifestURL,
		PlayerSource: jsScriptBody}
}

func ExtractSignatureFunc(args YoutubeConfigArgs) string {
	// In the script body, the `foo(bar)` in .set("signature", foo(bar)) is the
	// signature function
	funcNameRegex1 := regexp.MustCompile(`.set\("signature"\s*,\s*(.*)\(`)
	funcNameRegex2 := regexp.MustCompile(`.set\("sig"\s*,\s*(.*)\(`)

	funcName := funcNameRegex1.FindStringSubmatch(args.PlayerSource)[1]
	if len(funcName) == 0 {
		funcName = funcNameRegex2.FindStringSubmatch(args.PlayerSource)[1]
	}

	// We extract the function body
	bodyRegex := regexp.MustCompile("function \\s*" + funcName +
		"\\s*\\([\\w$]*\\)\\s*{[\\w$]*=[\\w$]*\\.split\\(\"\"\\);(.+);return [\\w$]*\\.join")
	body := bodyRegex.FindStringSubmatch(args.PlayerSource)[1]

	return body
}

func ExtractHelperObject(args YoutubeConfigArgs, sigBody string) string {
	// We need to extract whole of the helper object (`fG` in eg above).
	funcNameRegex := regexp.MustCompile(`(\w*).\.*`)
	firstFuncCall := strings.Split(sigBody, ";")[0]
	objName := funcNameRegex.FindStringSubmatch(firstFuncCall)[1]

	objRegex := regexp.MustCompile("var \\s*" + objName);
	startIndex := objRegex.FindStringIndex(args.PlayerSource)[0]

	endIndex := startIndex
	expectSemicolon := false
	for i, c := range args.PlayerSource[startIndex:] {
		if c == '}' {
			expectSemicolon = true
			continue
		}
		if expectSemicolon {
			if c == ';' {
				endIndex = startIndex + i + 1
				break
			}
			expectSemicolon = false
		}
	}
	objCode := args.PlayerSource[startIndex: endIndex]

	return objCode
}

func DecryptSignature(args YoutubeConfigArgs, signature string) string {
	sigBody := ExtractSignatureFunc(args)
	helperObj := ExtractHelperObject(args, sigBody)

	jsvm := otto.New()
	jsvm.Set("a", signature)
	// TODO: Handle error
	_, err := jsvm.Run(helperObj + ";a = a.split(\"\");" + sigBody + ";a = a.join(\"\");")

	fmt.Println(err)

	js_a, _ := jsvm.Get("a")
	value, _ := js_a.ToString()

	return value
}
<|endoftext|>


<commit_before>package handler

import (
	"database/sql"
	"encoding/json"
	"errors"
	"net/http"
	"strconv"

	"code.google.com/p/go.crypto/bcrypt"
	"github.com/gorilla/mux"
	"github.com/hackedu/backend/database"
	"github.com/hackedu/backend/model"
)

// Authenticate checks the provided user information against the information
// in the database. If it all checks out, then a JWT is generated and
// returned.
func Authenticate(w http.ResponseWriter, r *http.Request,
	u *model.User) *AppError {
	defer r.Body.Close()

	var requestUser model.RequestUser
	err := json.NewDecoder(r.Body).Decode(&requestUser)
	if err != nil {
		return &AppError{err, "bad request", http.StatusBadRequest}
	}

	userFromDB, err := database.GetUserByEmail(requestUser.Email)
	if err != nil {
		if err == sql.ErrNoRows {
			return &AppError{err, "user not found", http.StatusNotFound}
		}

		return &AppError{err, "error retrieving user",
			http.StatusInternalServerError}
	}

	err = userFromDB.ComparePassword(requestUser.Password)
	if err == bcrypt.ErrMismatchedHashAndPassword {
		return &AppError{err, "invalid password", http.StatusBadRequest}
	} else if err != nil {
		return &AppError{err, "error checking password",
			http.StatusInternalServerError}
	}

	token, err := model.NewToken(userFromDB)
	if err != nil {
		return &AppError{err, "problem creating jwt token",
			http.StatusInternalServerError}
	}

	return renderJSON(w, token, http.StatusOK)
}

// CreateUser creates a new user from JSON in the request body.
func CreateUser(w http.ResponseWriter, r *http.Request,
	u *model.User) *AppError {
	defer r.Body.Close()
	user, err := model.NewUser(r.Body)
	if err != nil {
		return &AppError{err, err.Error(), http.StatusBadRequest}
	}

	err = database.SaveUser(user)
	if err != nil {
		if err == model.ErrInvalidEmail {
			return &AppError{err, err.Error(), http.StatusBadRequest}
		}
		return &AppError{err, "error saving user", http.StatusInternalServerError}
	}

	return renderJSON(w, user, http.StatusOK)
}

// GetUser gets the user specified by ID in the url.
func GetUser(w http.ResponseWriter, r *http.Request, u *model.User) *AppError {
	vars := mux.Vars(r)
	id, err := strconv.ParseInt(vars["id"], 10, 64)
	if err != nil {
		return &AppError{err, "invalid id", http.StatusBadRequest}
	}

	if id == u.ID {
		return renderJSON(w, u, http.StatusOK)
	}

	return &AppError{err, "unauthorized", http.StatusBadRequest}
}

// GetCurrentUser gets the current authenticated user.
func GetCurrentUser(w http.ResponseWriter, r *http.Request,
	u *model.User) *AppError {
	if u == nil {
		return &AppError{errors.New("user not authorized"), "not authorized",
			http.StatusUnauthorized}
	}

	return renderJSON(w, u, http.StatusOK)
}
<commit_msg>User must be an admin to create new users. Closes #7.<commit_after>package handler

import (
	"database/sql"
	"encoding/json"
	"errors"
	"net/http"
	"strconv"

	"code.google.com/p/go.crypto/bcrypt"
	"github.com/gorilla/mux"
	"github.com/hackedu/backend/database"
	"github.com/hackedu/backend/model"
)

// Authenticate checks the provided user information against the information
// in the database. If it all checks out, then a JWT is generated and
// returned.
func Authenticate(w http.ResponseWriter, r *http.Request,
	u *model.User) *AppError {
	defer r.Body.Close()

	var requestUser model.RequestUser
	err := json.NewDecoder(r.Body).Decode(&requestUser)
	if err != nil {
		return &AppError{err, "bad request", http.StatusBadRequest}
	}

	userFromDB, err := database.GetUserByEmail(requestUser.Email)
	if err != nil {
		if err == sql.ErrNoRows {
			return &AppError{err, "user not found", http.StatusNotFound}
		}

		return &AppError{err, "error retrieving user",
			http.StatusInternalServerError}
	}

	err = userFromDB.ComparePassword(requestUser.Password)
	if err == bcrypt.ErrMismatchedHashAndPassword {
		return &AppError{err, "invalid password", http.StatusBadRequest}
	} else if err != nil {
		return &AppError{err, "error checking password",
			http.StatusInternalServerError}
	}

	token, err := model.NewToken(userFromDB)
	if err != nil {
		return &AppError{err, "problem creating jwt token",
			http.StatusInternalServerError}
	}

	return renderJSON(w, token, http.StatusOK)
}

// CreateUser creates a new user from JSON in the request body.
func CreateUser(w http.ResponseWriter, r *http.Request,
	u *model.User) *AppError {
	if u == nil || u.Type != model.UserAdmin {
		err := errors.New("not authorized")
		return &AppError{err, err.Error(), http.StatusUnauthorized}
	}

	defer r.Body.Close()
	user, err := model.NewUser(r.Body)
	if err != nil {
		return &AppError{err, err.Error(), http.StatusBadRequest}
	}

	err = database.SaveUser(user)
	if err != nil {
		if err == model.ErrInvalidEmail {
			return &AppError{err, err.Error(), http.StatusBadRequest}
		}
		return &AppError{err, "error saving user", http.StatusInternalServerError}
	}

	return renderJSON(w, user, http.StatusOK)
}

// GetUser gets the user specified by ID in the url.
func GetUser(w http.ResponseWriter, r *http.Request, u *model.User) *AppError {
	vars := mux.Vars(r)
	id, err := strconv.ParseInt(vars["id"], 10, 64)
	if err != nil {
		return &AppError{err, "invalid id", http.StatusBadRequest}
	}

	if id == u.ID {
		return renderJSON(w, u, http.StatusOK)
	}

	return &AppError{err, "unauthorized", http.StatusBadRequest}
}

// GetCurrentUser gets the current authenticated user.
func GetCurrentUser(w http.ResponseWriter, r *http.Request,
	u *model.User) *AppError {
	if u == nil {
		return &AppError{errors.New("user not authorized"), "not authorized",
			http.StatusUnauthorized}
	}

	return renderJSON(w, u, http.StatusOK)
}
<|endoftext|>


<commit_before>package handlers

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strings"

	"github.com/byuoitav/sony-control-microservice/helpers"
	"github.com/labstack/echo"
)

func PowerOn(context echo.Context) error {
	log.Printf("Powering on %s...", context.Param("address"))

	err := helpers.SetPower(context.Param("address"), true)
	if err != nil {
		log.Printf("Error: %v", err.Error())
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func Standby(context echo.Context) error {
	log.Printf("Powering off %s...", context.Param("address"))

	err := helpers.SetPower(context.Param("address"), false)
	if err != nil {
		log.Printf("Error: %v", err.Error())
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func GetPower(context echo.Context) error {
	log.Printf("Getting power status of %s...", context.Param("address"))

	response, err := helpers.GetPower(context.Param("address"))
	if err != nil {
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	return context.JSON(http.StatusOK, response)
}

func SwitchInput(context echo.Context) error {
	log.Printf("Switching input for %s to %s ...", context.Param("address"), context.Param("port"))
	address := context.Param("address")
	port := context.Param("port")

	splitPort := strings.Split(port, "!")

	params := make(map[string]interface{})
	params["uri"] = fmt.Sprintf("extInput:%s?port=%s", splitPort[0], splitPort[1])

	err := helpers.BuildAndSendPayload(address, "avContent", "setPlayContent", params)
	if err != nil {
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func SetVolume(context echo.Context) error {
	address := context.Param("address")
	value := context.Param("value")

	log.Printf("Setting volume for %s to %v...", address, value)

	params := make(map[string]interface{})
	params["target"] = "speaker"
	params["volume"] = value

	err := helpers.BuildAndSendPayload(address, "audio", "setAudioVolume", params)
	if err != nil {
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func VolumeUnmute(context echo.Context) error {
	address := context.Param("address")
	log.Printf("Unmuting %s...", address)

	err := setMute(address, false)
	if err != nil {
		log.Printf("Error: %v", err.Error())
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func setMute(address string, status bool) error {
	params := make(map[string]interface{})
	params["status"] = status

	return helpers.BuildAndSendPayload(address, "audio", "setAudioMute", params)
}

func VolumeMute(context echo.Context) error {
	log.Printf("Muting %s...", context.Param("address"))

	err := setMute(context.Param("address"), true)
	if err != nil {
		log.Printf("Error: %v", err.Error())
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func BlankDisplay(context echo.Context) error {
	return Standby(context)
}

func UnblankDisplay(context echo.Context) error {
	return PowerOn(context)
}

func GetVolume(context echo.Context) error {
	log.Printf("Getting volume for %s...", context.Param("address"))

	payload := helpers.SonyTVRequest{
		Params:  []map[string]interface{}{},
		Method:  "getVolumeInformation",
		Version: "1.0",
		ID:      1,
	}

	log.Printf("%+v", payload)

	resp, err := helpers.PostHTTP(context.Param("address"), payload, "audio")

	parentResponse := helpers.SonyAudioResponse{}

	log.Printf("%s", resp)

	err = json.Unmarshal(resp, &parentResponse)
	if err != nil {
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("%+v", parentResponse)

	b, err := json.Marshal(parentResponse.Result[0])
	if err != nil {
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	context.Response().Write(b)

	log.Printf("Done")
	return nil
}

func GetInput(context echo.Context) error {
	return nil
}

func GetInputList(context echo.Context) error {
	return nil
}

func GetMute(context echo.Context) error {
	return nil
}

func GetBlank(context echo.Context) error {
	return nil
}
<commit_msg>I have no idea what's going on<commit_after>package handlers

import (
	"fmt"
	"log"
	"net/http"
	"strings"

	"github.com/byuoitav/sony-control-microservice/helpers"
	"github.com/labstack/echo"
)

func PowerOn(context echo.Context) error {
	log.Printf("Powering on %s...", context.Param("address"))

	err := helpers.SetPower(context.Param("address"), true)
	if err != nil {
		log.Printf("Error: %v", err.Error())
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func Standby(context echo.Context) error {
	log.Printf("Powering off %s...", context.Param("address"))

	err := helpers.SetPower(context.Param("address"), false)
	if err != nil {
		log.Printf("Error: %v", err.Error())
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func GetPower(context echo.Context) error {
	log.Printf("Getting power status of %s...", context.Param("address"))

	response, err := helpers.GetPower(context.Param("address"))
	if err != nil {
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	return context.JSON(http.StatusOK, response)
}

func SwitchInput(context echo.Context) error {
	log.Printf("Switching input for %s to %s ...", context.Param("address"), context.Param("port"))
	address := context.Param("address")
	port := context.Param("port")

	splitPort := strings.Split(port, "!")

	params := make(map[string]interface{})
	params["uri"] = fmt.Sprintf("extInput:%s?port=%s", splitPort[0], splitPort[1])

	err := helpers.BuildAndSendPayload(address, "avContent", "setPlayContent", params)
	if err != nil {
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func SetVolume(context echo.Context) error {
	address := context.Param("address")
	value := context.Param("value")

	log.Printf("Setting volume for %s to %v...", address, value)

	params := make(map[string]interface{})
	params["target"] = "speaker"
	params["volume"] = value

	err := helpers.BuildAndSendPayload(address, "audio", "setAudioVolume", params)
	if err != nil {
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func VolumeUnmute(context echo.Context) error {
	address := context.Param("address")
	log.Printf("Unmuting %s...", address)

	err := setMute(address, false)
	if err != nil {
		log.Printf("Error: %v", err.Error())
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func setMute(address string, status bool) error {
	params := make(map[string]interface{})
	params["status"] = status

	return helpers.BuildAndSendPayload(address, "audio", "setAudioMute", params)
}

func VolumeMute(context echo.Context) error {
	log.Printf("Muting %s...", context.Param("address"))

	err := setMute(context.Param("address"), true)
	if err != nil {
		log.Printf("Error: %v", err.Error())
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func BlankDisplay(context echo.Context) error {
	return Standby(context)
}

func UnblankDisplay(context echo.Context) error {
	return PowerOn(context)
}

func GetVolume(context echo.Context) error {
	log.Printf("Getting volume for %s...", context.Param("address"))

	return nil
}

func GetInput(context echo.Context) error {
	return nil
}

func GetInputList(context echo.Context) error {
	return nil
}

func GetMute(context echo.Context) error {
	return nil
}

func GetBlank(context echo.Context) error {
	return nil
}
<|endoftext|>


<commit_before>package benchmark

import (
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"hash"
	"testing"

	"github.com/dchest/siphash"
	blake2bsimd "github.com/minio/blake2b-simd"
	"github.com/spaolacci/murmur3"
	"golang.org/x/crypto/blake2b"
	"golang.org/x/crypto/ripemd160"
	"golang.org/x/crypto/sha3"
)

const hashBufferSize = 8

func benchmarkHash(b *testing.B, hash func() hash.Hash, length int64) {
	data := make([]byte, length)
	b.SetBytes(length)

	for i := 0; i < b.N; i++ {
		h := hash()
		_, err := h.Write(data[:])
		if err != nil {
			panic(err)
		}
		h.Sum(nil)
	}
}

func benchmarkHash64(b *testing.B, hash func() hash.Hash64, length int64) {
	data := make([]byte, length)
	b.SetBytes(length)

	for i := 0; i < b.N; i++ {
		h := hash()
		_, err := h.Write(data[:])
		if err != nil {
			panic(err)
		}
		h.Sum(nil)
	}
}

func benchmarkHashKeyError(b *testing.B, hash func([]byte) (hash.Hash, error), length int64) {
	data := make([]byte, length)
	b.SetBytes(length)
	key := make([]byte, 16)

	for i := 0; i < b.N; i++ {
		h, _ := hash(key)
		_, err := h.Write(data[:])
		if err != nil {
			panic(err)
		}
		h.Sum(nil)
	}
}

func benchmarkHashKey64(b *testing.B, hash func([]byte) hash.Hash64, length int64) {
	data := make([]byte, length)
	b.SetBytes(length)
	key := make([]byte, 16)

	for i := 0; i < b.N; i++ {
		h := hash(key)
		_, err := h.Write(data[:])
		if err != nil {
			panic(err)
		}
		h.Sum(nil)
	}
}

func BenchmarkComparisonMD5(b *testing.B) {
	benchmarkHash(b, md5.New, hashBufferSize)
}

func BenchmarkComparisonSHA1(b *testing.B) {
	benchmarkHash(b, sha1.New, hashBufferSize)
}

func BenchmarkComparisonSHA256(b *testing.B) {
	benchmarkHash(b, sha256.New, hashBufferSize)
}

func BenchmarkComparisonSHA3B224(b *testing.B) {
	benchmarkHash(b, sha3.New224, hashBufferSize)
}

func BenchmarkComparisonSHA3B256(b *testing.B) {
	benchmarkHash(b, sha3.New256, hashBufferSize)
}

func BenchmarkComparisonRIPEMD160(b *testing.B) {
	benchmarkHash(b, ripemd160.New, hashBufferSize)
}

func BenchmarkComparisonBlake2B(b *testing.B) {
	benchmarkHashKeyError(b, blake2b.New256, hashBufferSize)
}

func BenchmarkComparisonBlake2BSimd(b *testing.B) {
	benchmarkHash(b, blake2bsimd.New256, hashBufferSize)
}

func BenchmarkComparisonMurmur3(b *testing.B) {
	benchmarkHash64(b, murmur3.New64, hashBufferSize)
}
func BenchmarkComparisonSipHash(b *testing.B) {
	benchmarkHashKey64(b, siphash.New, hashBufferSize)
}
<commit_msg>added xxhash benchmark<commit_after>package benchmark

import (
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"hash"
	"testing"

	"github.com/cespare/xxhash"
	"github.com/dchest/siphash"
	blake2bsimd "github.com/minio/blake2b-simd"
	"github.com/spaolacci/murmur3"
	"golang.org/x/crypto/blake2b"
	"golang.org/x/crypto/ripemd160"
	"golang.org/x/crypto/sha3"
)

const hashBufferSize = 8

func benchmarkHash(b *testing.B, hash func() hash.Hash, length int64) {
	data := make([]byte, length)
	b.SetBytes(length)

	for i := 0; i < b.N; i++ {
		h := hash()
		_, err := h.Write(data[:])
		if err != nil {
			panic(err)
		}
		h.Sum(nil)
	}
}

func benchmarkHash64(b *testing.B, hash func() hash.Hash64, length int64) {
	data := make([]byte, length)
	b.SetBytes(length)

	for i := 0; i < b.N; i++ {
		h := hash()
		_, err := h.Write(data[:])
		if err != nil {
			panic(err)
		}
		h.Sum(nil)
	}
}

func benchmarkHashKeyError(b *testing.B, hash func([]byte) (hash.Hash, error), length int64) {
	data := make([]byte, length)
	b.SetBytes(length)
	key := make([]byte, 16)

	for i := 0; i < b.N; i++ {
		h, _ := hash(key)
		_, err := h.Write(data[:])
		if err != nil {
			panic(err)
		}
		h.Sum(nil)
	}
}

func benchmarkHashKey64(b *testing.B, hash func([]byte) hash.Hash64, length int64) {
	data := make([]byte, length)
	b.SetBytes(length)
	key := make([]byte, 16)

	for i := 0; i < b.N; i++ {
		h := hash(key)
		_, err := h.Write(data[:])
		if err != nil {
			panic(err)
		}
		h.Sum(nil)
	}
}

func BenchmarkComparisonMD5(b *testing.B) {
	benchmarkHash(b, md5.New, hashBufferSize)
}

func BenchmarkComparisonSHA1(b *testing.B) {
	benchmarkHash(b, sha1.New, hashBufferSize)
}

func BenchmarkComparisonSHA256(b *testing.B) {
	benchmarkHash(b, sha256.New, hashBufferSize)
}

func BenchmarkComparisonSHA3B224(b *testing.B) {
	benchmarkHash(b, sha3.New224, hashBufferSize)
}

func BenchmarkComparisonSHA3B256(b *testing.B) {
	benchmarkHash(b, sha3.New256, hashBufferSize)
}

func BenchmarkComparisonRIPEMD160(b *testing.B) {
	benchmarkHash(b, ripemd160.New, hashBufferSize)
}

func BenchmarkComparisonBlake2B(b *testing.B) {
	benchmarkHashKeyError(b, blake2b.New256, hashBufferSize)
}

func BenchmarkComparisonBlake2BSimd(b *testing.B) {
	benchmarkHash(b, blake2bsimd.New256, hashBufferSize)
}

func BenchmarkComparisonMurmur3(b *testing.B) {
	benchmarkHash64(b, murmur3.New64, hashBufferSize)
}
func BenchmarkComparisonSipHash(b *testing.B) {
	benchmarkHashKey64(b, siphash.New, hashBufferSize)
}
func BenchmarkComparisonXXHash(b *testing.B) {
	benchmarkHash64(b, xxhash.New, hashBufferSize)
}
<|endoftext|>


<commit_before><commit_msg>test(azure::cache): deflake TestCacheNoConcurrentGet<commit_after><|endoftext|>