<commit_before>// Warning! This example destroys all data on your SD card.
package main

import (
	"delay"
	"encoding/binary/be"
	"fmt"
	"rtos"

	"sdcard"

	"stm32/hal/dma"
	"stm32/hal/exti"
	"stm32/hal/gpio"
	"stm32/hal/irq"
	"stm32/hal/sdmmc"
	"stm32/hal/system"
	"stm32/hal/system/timer/systick"
)

//var sddrv *sdmmc.Driver
var sddrv *sdmmc.DriverDMA

func init() {
	system.Setup96(8) // Setups USB/SDIO/RNG clock to 48 MHz
	systick.Setup(2e6)

	// GPIO

	gpio.A.EnableClock(true)
	// irq := gpio.A.Pin(0)
	cmd := gpio.A.Pin(6)
	d1 := gpio.A.Pin(8)
	d2 := gpio.A.Pin(9)

	gpio.B.EnableClock(true)
	d3 := gpio.B.Pin(5)
	d0 := gpio.B.Pin(7)
	clk := gpio.B.Pin(15)

	cfg := gpio.Config{Mode: gpio.Alt, Speed: gpio.VeryHigh, Pull: gpio.PullUp}
	for _, pin := range []gpio.Pin{clk, cmd, d0, d1, d2, d3} {
		pin.Setup(&cfg)
		pin.SetAltFunc(gpio.SDIO)
	}

	d := dma.DMA2
	d.EnableClock(true)
	sddrv = sdmmc.NewDriverDMA(sdmmc.SDIO, d.Channel(6, 4), d0)
	//sddrv = sdmmc.NewDriver(sdmmc.SDIO, d0)
	sddrv.Periph().EnableClock(true)
	sddrv.Periph().Enable()

	rtos.IRQ(irq.SDIO).Enable()
	rtos.IRQ(irq.EXTI9_5).Enable()
}

func main() {
	delay.Millisec(200) // For SWO output

	ocr := sdcard.V31 | sdcard.V32 | sdcard.V33 | sdcard.HCXC
	v2 := true
	sd := sdcard.Host(sddrv)

	// Set SDIO_CK to no more than 400 kHz (max. open-drain freq)..
	sd.SetClock(400e3, false)

	// SD card power-up takes maximum of 1 ms or 74 SDIO_CK cycles.
	delay.Millisec(1)

	// Reset.
	sd.SendCmd(sdcard.CMD0())
	checkErr("CMD0", sd.Err(true), 0)

	// CMD0 may require up to 8 SDIO_CK cycles to reset the card.
	delay.Millisec(1)

	// Verify card interface operating condition.
	vhs, pattern := sd.SendCmd(sdcard.CMD8(sdcard.V27_36, 0xAC)).R7()
	if err := sd.Err(true); err != nil {
		if err == sdcard.ErrCmdTimeout {
			ocr &^= sdcard.HCXC
			v2 = false
		} else {
			checkErr("CMD8", err, 0)
		}
	} else if vhs != sdcard.V27_36 || pattern != 0xAC {
		fmt.Printf("CMD8 bad response: %x, %x\n", vhs, pattern)
		for {
		}
	}
	fmt.Printf("\nPhysical layer version 2.00+: %t\n", v2)

	fmt.Printf("Initializing SD card ")
	var oca sdcard.OCR
	for i := 0; oca&sdcard.PWUP == 0 && i < 20; i++ {
		sd.SendCmd(sdcard.CMD55(0))
		oca = sd.SendCmd(sdcard.ACMD41(ocr)).R3()
		checkErr("ACMD41", sd.Err(true), 0)
		fmt.Printf(".")
		delay.Millisec(50)
	}
	if oca&sdcard.PWUP == 0 {
		fmt.Printf(" timeout\n")
		for {
		}
	}
	fmt.Printf(" OK\n\n")
	fmt.Printf("Operation Conditions Register: 0x%08X\n\n", oca)

	// Read Card Identification Register.
	cid := sd.SendCmd(sdcard.CMD2()).R2CID()
	checkErr("CMD2", sd.Err(true), 0)

	printCID(cid)

	// Generate new Relative Card Address.
	rca, _ := sd.SendCmd(sdcard.CMD3()).R6()
	checkErr("CMD3", sd.Err(true), 0)

	fmt.Printf("Relative Card Address: 0x%04X\n\n", rca)

	printStatus(sd.SendCmd(sdcard.CMD13(rca, sdcard.Status)).R1())

	// After CMD3 card is in Data Transfer Mode (Standby State) and SDIO_CK can
	// be set to no more than 25 MHz (max. push-pull freq). Clock power save
	// mode can be enabled.
	sd.SetClock(25e6, false)

	// Read Card Specific Data register.
	csd := sd.SendCmd(sdcard.CMD9(rca)).R2CSD()
	checkErr("CMD9", sd.Err(true), 0)

	printCSD(csd)

	// Select card (put into Transfer State).
	st := sd.SendCmd(sdcard.CMD7(rca)).R1()
	checkErr("CMD7", sd.Err(true), st)

	// Now the card is in the Transfer State.
	printStatus(sd.SendCmd(sdcard.CMD13(rca, sdcard.Status)).R1())

	buf := sdcard.MakeDataBlocks(8)

	// Read SD Configuration Register.
	sd.SendCmd(sdcard.CMD55(rca))
	sd.SetupData(sdcard.Recv|sdcard.Block8, buf.Words()[:1], 8)
	st = sd.SendCmd(sdcard.ACMD51()).R1()
	checkErr("ACMD51", sd.Err(true), st)

	scr := sdcard.SCR(be.Decode64(buf.Bytes()))
	printSCR(scr)

	// Disable 50k pull-up resistor on D3/CD.
	sd.SendCmd(sdcard.CMD55(rca))
	st = sd.SendCmd(sdcard.ACMD42(false)).R1()
	checkErr("ACMD42", sd.Err(true), st)

	if scr.SD_BUS_WIDTHS()&sdcard.SDBus4 != 0 {
		fmt.Printf("Enable 4-bit data bus... ")
		sd.SendCmd(sdcard.CMD55(rca))
		st = sd.SendCmd(sdcard.ACMD6(sdcard.Bus4)).R1()
		checkErr("ACMD6", sd.Err(true), st)
		sd.SetBusWidth(sdcard.Bus4)
		fmt.Printf("OK\n")
	}

	// Enable High Speed.
	if scr.SD_SPEC() > 0 {
		sd.SetupData(sdcard.Recv|sdcard.Block64, buf.Words()[:8], 64)
		st = sd.SendCmd(sdcard.CMD6(sdcard.ModeSwitch | sdcard.HighSpeed)).R1()
		checkErr("CMD6", sd.Err(true), st)

		sel := printCMD6Status(buf.Bytes()[:64])

		if sel&sdcard.AccessMode == sdcard.HighSpeed && false {
			fmt.Printf("Card supports High Speed: set clock to 50 MHz.\n\n")
			delay.Millisec(1) // Function switch takes max. 8 SDIO_CK cycles.
			sd.SetClock(50e6, false)
		}
	}

	// Set block size to 512 B (required for protocol version < 2 or SDSC card).
	if oca&sdcard.HCXC == 0 {
		st = sd.SendCmd(sdcard.CMD16(512)).R1()
		checkErr("CMD16", sd.Err(true), st)
	}

	block := buf.Block(0)
	for i := range block.Bytes() {
		block.Bytes()[i] = byte(i)
	}

	fmt.Printf("Write block of data...\n")
	sd.SetupData(sdcard.Send|sdcard.Block512, block.Words(), 512)

	st = sd.SendCmd(sdcard.CMD24(512)).R1()
	checkErr("CMD24", sd.Err(true), st)

	for i := range block.Words() {
		block.Words()[i] = 0
	}

	fmt.Printf("Read block of data...\n")
	sd.SetupData(sdcard.Recv|sdcard.Block512, block.Words(), 512)
	st = sd.SendCmd(sdcard.CMD17(512)).R1()
	checkErr("CMD17", sd.Err(true), st)

	for i, d := 0, block.Bytes(); i < len(d); i += 16 {
		fmt.Printf("%02x\n", d[i:i+16])
	}

	for i := range buf.Words() {
		buf.Words()[i] = 0
	}

	bufSize := len(buf.Bytes())
	step := bufSize / 512
	testLen := uint(1e3)

	fmt.Printf(
		"Write %d blocks (%d KiB) using %d B buffer: ",
		testLen, testLen/2, bufSize,
	)
	t := rtos.Nanosec()
	for n := uint(0); n < testLen; n += uint(step) {
		addr := n
		if oca&sdcard.HCXC == 0 {
			addr *= 512
		}
		sd.SetupData(
			sdcard.Send|sdcard.Block512, buf.Words(), len(buf.Words())*8,
		)
		if step > 1 {
			st = sd.SendCmd(sdcard.CMD25(addr)).R1()
			checkErr("CMD25", sd.Err(true), st)
			st = sd.SendCmd(sdcard.CMD12()).R1()
			checkErr("CMD12", sd.Err(true), st)
		} else {
			st = sd.SendCmd(sdcard.CMD24(addr)).R1()
			checkErr("CMD24", sd.Err(true), st)
		}
	}
	dt := rtos.Nanosec() - t
	fmt.Printf("%d KiB/s\n", 1e9/2*int64(testLen)/dt)

	fmt.Printf(
		"Read %d blocks (%d KiB) using %d B buffer: ",
		testLen, testLen/2, bufSize,
	)

	t = rtos.Nanosec()
	for n := uint(0); n < testLen; n += uint(step) {
		addr := n
		if oca&sdcard.HCXC == 0 {
			addr *= 512
		}
		sd.SetupData(
			sdcard.Recv|sdcard.Block512, buf.Words(), len(buf.Words())*8,
		)
		if step > 1 {
			st = sd.SendCmd(sdcard.CMD18(addr)).R1()
			checkErr("CMD18", sd.Err(true), st)
			st = sd.SendCmd(sdcard.CMD12()).R1()
			checkErr("CMD12", sd.Err(true), st)
		} else {
			st = sd.SendCmd(sdcard.CMD17(addr)).R1()
			checkErr("CMD17", sd.Err(true), st)
		}
	}
	dt = rtos.Nanosec() - t
	fmt.Printf("%d KiB/s\n", 1e9/2*int64(testLen)/dt)
	fmt.Printf("End\n")
}

func sdioISR() {
	sddrv.ISR()
}

func exti9_5ISR() {
	pending := exti.Pending() & 0x3E0
	pending.ClearPending()
	if pending&sddrv.BusyLine() != 0 {
		sddrv.BusyISR()
	}
}

//c:__attribute__((section(".ISRs")))
var ISRs = [...]func(){
	irq.SDIO:    sdioISR,
	irq.EXTI9_5: exti9_5ISR,
}
<commit_msg>examples/nucleo-f411re/sdmc-raw: Adjust to new deffinition of sdcard.Data.<commit_after>// Warning! This example destroys all data on your SD card.
package main

import (
	"delay"
	"encoding/binary/be"
	"fmt"
	"rtos"

	"sdcard"

	"stm32/hal/dma"
	"stm32/hal/exti"
	"stm32/hal/gpio"
	"stm32/hal/irq"
	"stm32/hal/sdmmc"
	"stm32/hal/system"
	"stm32/hal/system/timer/systick"
)

//var sddrv *sdmmc.Driver
var sddrv *sdmmc.DriverDMA

func init() {
	system.Setup96(8) // Setups USB/SDIO/RNG clock to 48 MHz
	systick.Setup(2e6)

	// GPIO

	gpio.A.EnableClock(true)
	// irq := gpio.A.Pin(0)
	cmd := gpio.A.Pin(6)
	d1 := gpio.A.Pin(8)
	d2 := gpio.A.Pin(9)

	gpio.B.EnableClock(true)
	d3 := gpio.B.Pin(5)
	d0 := gpio.B.Pin(7)
	clk := gpio.B.Pin(15)

	cfg := gpio.Config{Mode: gpio.Alt, Speed: gpio.VeryHigh, Pull: gpio.PullUp}
	for _, pin := range []gpio.Pin{clk, cmd, d0, d1, d2, d3} {
		pin.Setup(&cfg)
		pin.SetAltFunc(gpio.SDIO)
	}

	d := dma.DMA2
	d.EnableClock(true)
	sddrv = sdmmc.NewDriverDMA(sdmmc.SDIO, d.Channel(6, 4), d0)
	//sddrv = sdmmc.NewDriver(sdmmc.SDIO, d0)
	sddrv.Periph().EnableClock(true)
	sddrv.Periph().Enable()

	rtos.IRQ(irq.SDIO).Enable()
	rtos.IRQ(irq.EXTI9_5).Enable()
}

func main() {
	delay.Millisec(200) // For SWO output

	ocr := sdcard.V31 | sdcard.V32 | sdcard.V33 | sdcard.HCXC
	v2 := true
	sd := sdcard.Host(sddrv)

	// Set SDIO_CK to no more than 400 kHz (max. open-drain freq)..
	sd.SetClock(400e3, false)

	// SD card power-up takes maximum of 1 ms or 74 SDIO_CK cycles.
	delay.Millisec(1)

	// Reset.
	sd.SendCmd(sdcard.CMD0())
	checkErr("CMD0", sd.Err(true), 0)

	// CMD0 may require up to 8 SDIO_CK cycles to reset the card.
	delay.Millisec(1)

	// Verify card interface operating condition.
	vhs, pattern := sd.SendCmd(sdcard.CMD8(sdcard.V27_36, 0xAC)).R7()
	if err := sd.Err(true); err != nil {
		if err == sdcard.ErrCmdTimeout {
			ocr &^= sdcard.HCXC
			v2 = false
		} else {
			checkErr("CMD8", err, 0)
		}
	} else if vhs != sdcard.V27_36 || pattern != 0xAC {
		fmt.Printf("CMD8 bad response: %x, %x\n", vhs, pattern)
		for {
		}
	}
	fmt.Printf("\nPhysical layer version 2.00+: %t\n", v2)

	fmt.Printf("Initializing SD card ")
	var oca sdcard.OCR
	for i := 0; oca&sdcard.PWUP == 0 && i < 20; i++ {
		sd.SendCmd(sdcard.CMD55(0))
		oca = sd.SendCmd(sdcard.ACMD41(ocr)).R3()
		checkErr("ACMD41", sd.Err(true), 0)
		fmt.Printf(".")
		delay.Millisec(50)
	}
	if oca&sdcard.PWUP == 0 {
		fmt.Printf(" timeout\n")
		for {
		}
	}
	fmt.Printf(" OK\n\n")
	fmt.Printf("Operation Conditions Register: 0x%08X\n\n", oca)

	// Read Card Identification Register.
	cid := sd.SendCmd(sdcard.CMD2()).R2CID()
	checkErr("CMD2", sd.Err(true), 0)

	printCID(cid)

	// Generate new Relative Card Address.
	rca, _ := sd.SendCmd(sdcard.CMD3()).R6()
	checkErr("CMD3", sd.Err(true), 0)

	fmt.Printf("Relative Card Address: 0x%04X\n\n", rca)

	printStatus(sd.SendCmd(sdcard.CMD13(rca, sdcard.Status)).R1())

	// After CMD3 card is in Data Transfer Mode (Standby State) and SDIO_CK can
	// be set to no more than 25 MHz (max. push-pull freq). Clock power save
	// mode can be enabled.
	sd.SetClock(25e6, false)

	// Read Card Specific Data register.
	csd := sd.SendCmd(sdcard.CMD9(rca)).R2CSD()
	checkErr("CMD9", sd.Err(true), 0)

	printCSD(csd)

	// Select card (put into Transfer State).
	st := sd.SendCmd(sdcard.CMD7(rca)).R1()
	checkErr("CMD7", sd.Err(true), st)

	// Now the card is in the Transfer State.
	printStatus(sd.SendCmd(sdcard.CMD13(rca, sdcard.Status)).R1())

	buf := sdcard.MakeDataBlocks(8)

	// Read SD Configuration Register.
	sd.SendCmd(sdcard.CMD55(rca))
	sd.SetupData(sdcard.Recv|sdcard.Block8, buf[:1], 8)
	st = sd.SendCmd(sdcard.ACMD51()).R1()
	checkErr("ACMD51", sd.Err(true), st)

	scr := sdcard.SCR(be.Decode64(buf.Bytes()))
	printSCR(scr)

	// Disable 50k pull-up resistor on D3/CD.
	sd.SendCmd(sdcard.CMD55(rca))
	st = sd.SendCmd(sdcard.ACMD42(false)).R1()
	checkErr("ACMD42", sd.Err(true), st)

	if scr.SD_BUS_WIDTHS()&sdcard.SDBus4 != 0 {
		fmt.Printf("Enable 4-bit data bus... ")
		sd.SendCmd(sdcard.CMD55(rca))
		st = sd.SendCmd(sdcard.ACMD6(sdcard.Bus4)).R1()
		checkErr("ACMD6", sd.Err(true), st)
		sd.SetBusWidth(sdcard.Bus4)
		fmt.Printf("OK\n")
	}

	// Enable High Speed.
	if scr.SD_SPEC() > 0 {
		sd.SetupData(sdcard.Recv|sdcard.Block64, buf[:8], 64)
		st = sd.SendCmd(sdcard.CMD6(sdcard.ModeSwitch | sdcard.HighSpeed)).R1()
		checkErr("CMD6", sd.Err(true), st)

		sel := printCMD6Status(buf.Bytes()[:64])

		if sel&sdcard.AccessMode == sdcard.HighSpeed && false {
			fmt.Printf("Card supports High Speed: set clock to 50 MHz.\n\n")
			delay.Millisec(1) // Function switch takes max. 8 SDIO_CK cycles.
			sd.SetClock(50e6, false)
		}
	}

	// Set block size to 512 B (required for protocol version < 2 or SDSC card).
	if oca&sdcard.HCXC == 0 {
		st = sd.SendCmd(sdcard.CMD16(512)).R1()
		checkErr("CMD16", sd.Err(true), st)
	}

	block := buf.Block(0)
	for i := range block.Bytes() {
		block.Bytes()[i] = byte(i)
	}

	fmt.Printf("Write block of data...\n")
	sd.SetupData(sdcard.Send|sdcard.Block512, block, 512)

	st = sd.SendCmd(sdcard.CMD24(512)).R1()
	checkErr("CMD24", sd.Err(true), st)

	for i := range block {
		block[i] = 0
	}

	fmt.Printf("Read block of data...\n")
	sd.SetupData(sdcard.Recv|sdcard.Block512, block, 512)
	st = sd.SendCmd(sdcard.CMD17(512)).R1()
	checkErr("CMD17", sd.Err(true), st)

	for i, d := 0, block.Bytes(); i < len(d); i += 16 {
		fmt.Printf("%02x\n", d[i:i+16])
	}

	for i := range buf {
		buf[i] = 0
	}

	bufSize := len(buf.Bytes())
	step := bufSize / 512
	testLen := uint(1e3)

	fmt.Printf(
		"Write %d blocks (%d KiB) using %d B buffer: ",
		testLen, testLen/2, bufSize,
	)
	t := rtos.Nanosec()
	for n := uint(0); n < testLen; n += uint(step) {
		addr := n
		if oca&sdcard.HCXC == 0 {
			addr *= 512
		}
		sd.SetupData(
			sdcard.Send|sdcard.Block512, buf, len(buf)*8,
		)
		if step > 1 {
			st = sd.SendCmd(sdcard.CMD25(addr)).R1()
			checkErr("CMD25", sd.Err(true), st)
			st = sd.SendCmd(sdcard.CMD12()).R1()
			checkErr("CMD12", sd.Err(true), st)
		} else {
			st = sd.SendCmd(sdcard.CMD24(addr)).R1()
			checkErr("CMD24", sd.Err(true), st)
		}
	}
	dt := rtos.Nanosec() - t
	fmt.Printf("%d KiB/s\n", 1e9/2*int64(testLen)/dt)

	fmt.Printf(
		"Read %d blocks (%d KiB) using %d B buffer: ",
		testLen, testLen/2, bufSize,
	)

	t = rtos.Nanosec()
	for n := uint(0); n < testLen; n += uint(step) {
		addr := n
		if oca&sdcard.HCXC == 0 {
			addr *= 512
		}
		sd.SetupData(
			sdcard.Recv|sdcard.Block512, buf, len(buf)*8,
		)
		if step > 1 {
			st = sd.SendCmd(sdcard.CMD18(addr)).R1()
			checkErr("CMD18", sd.Err(true), st)
			st = sd.SendCmd(sdcard.CMD12()).R1()
			checkErr("CMD12", sd.Err(true), st)
		} else {
			st = sd.SendCmd(sdcard.CMD17(addr)).R1()
			checkErr("CMD17", sd.Err(true), st)
		}
	}
	dt = rtos.Nanosec() - t
	fmt.Printf("%d KiB/s\n", 1e9/2*int64(testLen)/dt)
	fmt.Printf("End\n")
}

func sdioISR() {
	sddrv.ISR()
}

func exti9_5ISR() {
	pending := exti.Pending() & 0x3E0
	pending.ClearPending()
	if pending&sddrv.BusyLine() != 0 {
		sddrv.BusyISR()
	}
}

//c:__attribute__((section(".ISRs")))
var ISRs = [...]func(){
	irq.SDIO:    sdioISR,
	irq.EXTI9_5: exti9_5ISR,
}
<|endoftext|>


<commit_before>package expr

import (
	"math"
	"strconv"
	"testing"

	"github.com/grafana/metrictank/api/models"
	"github.com/grafana/metrictank/schema"
	"github.com/grafana/metrictank/test"
)

func getNewMinMax(in []models.Series) *FuncMinMax {
	f := NewMinMax()
	ps := f.(*FuncMinMax)
	ps.in = NewMock(in)
	return ps
}

const interval = 10

var basic = []schema.Point{
	{Val: 0, Ts: 10},
	{Val: 10, Ts: 20},
	{Val: 20, Ts: 30},
}

var basicR = []schema.Point{
	{Val: 0, Ts: 10},
	{Val: float64(10) / 20, Ts: 20},
	{Val: 1, Ts: 30},
}

var nan = []schema.Point{
	{Val: 0, Ts: 10},
	{Val: math.NaN(), Ts: 20},
	{Val: 20, Ts: 30},
}

var nanR = []schema.Point{
	{Val: 0, Ts: 10},
	{Val: math.NaN() / 20, Ts: 20},
	{Val: 1, Ts: 30},
}

var minMaxSame = []schema.Point{
	{Val: 20, Ts: 10},
	{Val: 20, Ts: 20},
	{Val: 20, Ts: 30},
}

var minMaxSameR = []schema.Point{
	{Val: 0, Ts: 10},
	{Val: 0, Ts: 20},
	{Val: 0, Ts: 30},
}

var infinity = []schema.Point{
	{Val: 100, Ts: 10},
	{Val: 1000, Ts: 20},
	{Val: math.Inf(0), Ts: 30},
}

// infinity / infinity is undefined
var infinityR = []schema.Point{
	{Val: 0, Ts: 10},
	{Val: 0, Ts: 20},
	{Val: math.NaN(), Ts: 30},
}

func TestMinMaxBasic(t *testing.T) {
	f := getNewMinMax(
		[]models.Series{
			{
				Interval:   interval,
				QueryPatt:  "queryPattHere",
				Target:     "targetHere",
				Datapoints: getCopy(basic),
			},
		},
	)
	out := []models.Series{
		{
			Interval:   interval,
			QueryPatt:  "minMax(queryPattHere)",
			Target:     "minMax(targetHere)",
			Datapoints: getCopy(basicR),
		},
	}

	got, err := f.Exec(make(map[Req][]models.Series))
	if err := equalOutput(out, got, nil, err); err != nil {
		t.Fatal(err)
	}
}

func TestMinMaxNaN(t *testing.T) {
	f := getNewMinMax(
		[]models.Series{
			{
				Interval:   interval,
				QueryPatt:  "queryPattHere",
				Target:     "targetHere",
				Datapoints: getCopy(nan),
			},
		},
	)
	out := []models.Series{
		{
			Interval:   interval,
			QueryPatt:  "minMax(queryPattHere)",
			Target:     "minMax(targetHere)",
			Datapoints: getCopy(nanR),
		},
	}

	got, err := f.Exec(make(map[Req][]models.Series))
	if err := equalOutput(out, got, nil, err); err != nil {
		t.Fatal(err)
	}
}

func TestMinMaxSame(t *testing.T) {
	f := getNewMinMax(
		[]models.Series{
			{
				Interval:   interval,
				QueryPatt:  "queryPattHere",
				Target:     "targetHere",
				Datapoints: getCopy(minMaxSame),
			},
		},
	)
	out := []models.Series{
		{
			Interval:   interval,
			QueryPatt:  "minMax(queryPattHere)",
			Target:     "minMax(targetHere)",
			Datapoints: getCopy(minMaxSameR),
		},
	}

	got, err := f.Exec(make(map[Req][]models.Series))
	if err := equalOutput(out, got, nil, err); err != nil {
		t.Fatal(err)
	}
}

func TestMinMaxInputUnchanged(t *testing.T) {
	in := []models.Series{
		{
			Interval:   interval,
			QueryPatt:  "queryPattHere",
			Target:     "targetHere",
			Datapoints: []schema.Point{},
		},
	}

	// store copy of the original input
	inCopy := make([]models.Series, len(in))
	copy(inCopy, in)

	f := getNewMinMax(in)
	_, err := f.Exec(make(map[Req][]models.Series))

	// make sure input hasn't changed after call to minMax
	if err := equalOutput(in, inCopy, nil, err); err != nil {
		t.Fatal(err)
	}
}

func TestMinMaxInfinity(t *testing.T) {
	f := getNewMinMax(
		[]models.Series{
			{
				Interval:   interval,
				QueryPatt:  "queryPattHere",
				Target:     "targetHere",
				Datapoints: getCopy(infinity),
			},
		},
	)
	out := []models.Series{
		{
			Interval:   interval,
			QueryPatt:  "minMax(queryPattHere)",
			Target:     "minMax(targetHere)",
			Datapoints: getCopy(infinityR),
		},
	}

	got, err := f.Exec(make(map[Req][]models.Series))
	if err := equalOutput(out, got, nil, err); err != nil {
		t.Fatal(err)
	}
}

func TestMinMaxZero(t *testing.T) {
	f := getNewMinMax(
		[]models.Series{
			{
				Interval:   interval,
				QueryPatt:  "queryPattHere",
				Target:     "targetHere",
				Datapoints: []schema.Point{},
			},
		},
	)
	out := []models.Series{
		{
			Interval:   interval,
			QueryPatt:  "minMax(queryPattHere)",
			Target:     "minMax(targetHere)",
			Datapoints: []schema.Point{},
		},
	}

	got, err := f.Exec(make(map[Req][]models.Series))
	if err := equalOutput(out, got, nil, err); err != nil {
		t.Fatal(err)
	}
}

func TestMinMaxMultiple(t *testing.T) {
	f := getNewMinMax(
		[]models.Series{
			{
				Interval:   interval,
				QueryPatt:  "queryPattHere",
				Target:     "targetHere",
				Datapoints: getCopy(basic),
			},
			{
				Interval:   20,
				QueryPatt:  "queryPattHere2",
				Target:     "targetHere2",
				Datapoints: getCopy(basic),
			},
		},
	)
	out := []models.Series{
		{
			Interval:   interval,
			QueryPatt:  "minMax(queryPattHere)",
			Target:     "minMax(targetHere)",
			Datapoints: getCopy(basicR),
		},
		{
			Interval:   20,
			QueryPatt:  "minMax(queryPattHere2)",
			Target:     "minMax(targetHere2)",
			Datapoints: getCopy(basicR),
		},
	}

	got, err := f.Exec(make(map[Req][]models.Series))
	if err := equalOutput(out, got, nil, err); err != nil {
		t.Fatal(err)
	}
}

func BenchmarkMinMax10k_1NoNulls(b *testing.B) {
	benchmarkMinMax(b, 1, test.RandFloats10k, test.RandFloats10k)
}
func BenchmarkMinMax10k_10NoNulls(b *testing.B) {
	benchmarkMinMax(b, 10, test.RandFloats10k, test.RandFloats10k)
}
func BenchmarkMinMax10k_100NoNulls(b *testing.B) {
	benchmarkMinMax(b, 100, test.RandFloats10k, test.RandFloats10k)
}
func BenchmarkMinMax10k_1000NoNulls(b *testing.B) {
	benchmarkMinMax(b, 1000, test.RandFloats10k, test.RandFloats10k)
}
func BenchmarkMinMax10k_1SomeSeriesHalfNulls(b *testing.B) {
	benchmarkMinMax(b, 1, test.RandFloats10k, test.RandFloatsWithNulls10k)
}
func BenchmarkMinMax10k_10SomeSeriesHalfNulls(b *testing.B) {
	benchmarkMinMax(b, 10, test.RandFloats10k, test.RandFloatsWithNulls10k)
}
func BenchmarkMinMax10k_100SomeSeriesHalfNulls(b *testing.B) {
	benchmarkMinMax(b, 100, test.RandFloats10k, test.RandFloatsWithNulls10k)
}
func BenchmarkMinMax10k_1000SomeSeriesHalfNulls(b *testing.B) {
	benchmarkMinMax(b, 1000, test.RandFloats10k, test.RandFloatsWithNulls10k)
}
func BenchmarkMinMax10k_1AllSeriesHalfNulls(b *testing.B) {
	benchmarkMinMax(b, 1, test.RandFloatsWithNulls10k, test.RandFloatsWithNulls10k)
}
func BenchmarkMinMax10k_10AllSeriesHalfNulls(b *testing.B) {
	benchmarkMinMax(b, 10, test.RandFloatsWithNulls10k, test.RandFloatsWithNulls10k)
}
func BenchmarkMinMax10k_100AllSeriesHalfNulls(b *testing.B) {
	benchmarkMinMax(b, 100, test.RandFloatsWithNulls10k, test.RandFloatsWithNulls10k)
}
func BenchmarkMinMax10k_1000AllSeriesHalfNulls(b *testing.B) {
	benchmarkMinMax(b, 1000, test.RandFloatsWithNulls10k, test.RandFloatsWithNulls10k)
}

func benchmarkMinMax(b *testing.B, numSeries int, fn0, fn1 func() []schema.Point) {
	var input []models.Series
	for i := 0; i < numSeries; i++ {
		series := models.Series{
			QueryPatt: strconv.Itoa(i),
		}
		if i%2 == 0 {
			series.Datapoints = fn0()
		} else {
			series.Datapoints = fn1()
		}
		input = append(input, series)
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		f := NewMinMax()
		f.(*FuncMinMax).in = NewMock(input)
		got, err := f.Exec(make(map[Req][]models.Series))
		if err != nil {
			b.Fatalf("%s", err)
		}
		results = got
	}
}
<commit_msg>Normalize minmax tests<commit_after>package expr

import (
	"math"
	"strconv"
	"testing"

	"github.com/grafana/metrictank/api/models"
	"github.com/grafana/metrictank/schema"
	"github.com/grafana/metrictank/test"
)

var basic = []schema.Point{
	{Val: 0, Ts: 10},
	{Val: 10, Ts: 20},
	{Val: 20, Ts: 30},
}

var basicR = []schema.Point{
	{Val: 0, Ts: 10},
	{Val: float64(10) / 20, Ts: 20},
	{Val: 1, Ts: 30},
}

var nan = []schema.Point{
	{Val: 0, Ts: 10},
	{Val: math.NaN(), Ts: 20},
	{Val: 20, Ts: 30},
}

var nanR = []schema.Point{
	{Val: 0, Ts: 10},
	{Val: math.NaN() / 20, Ts: 20},
	{Val: 1, Ts: 30},
}

var minMaxSame = []schema.Point{
	{Val: 20, Ts: 10},
	{Val: 20, Ts: 20},
	{Val: 20, Ts: 30},
}

var minMaxSameR = []schema.Point{
	{Val: 0, Ts: 10},
	{Val: 0, Ts: 20},
	{Val: 0, Ts: 30},
}

var infinity = []schema.Point{
	{Val: 100, Ts: 10},
	{Val: 1000, Ts: 20},
	{Val: math.Inf(0), Ts: 30},
}

// infinity / infinity is undefined
var infinityR = []schema.Point{
	{Val: 0, Ts: 10},
	{Val: 0, Ts: 20},
	{Val: math.NaN(), Ts: 30},
}

func TestMinMaxBasic(t *testing.T) {
	testMinMax("NaN",
		[]models.Series{
			getSeries("targetHere", "queryPattHere", basic),
		},
		[]models.Series{
			getSeries("minMax(targetHere)", "minMax(queryPattHere)", basicR),
		},
		t)
}

func TestMinMaxNaN(t *testing.T) {
	testMinMax("NaN",
		[]models.Series{
			getSeries("targetHere", "queryPattHere", nan),
		},
		[]models.Series{
			getSeries("minMax(targetHere)", "minMax(queryPattHere)", nanR),
		},
		t)
}

func TestMinMaxSame(t *testing.T) {
	testMinMax("SameMinMax",
		[]models.Series{
			getSeries("targetHere", "queryPattHere", minMaxSame),
		},
		[]models.Series{
			getSeries("minMax(targetHere)", "minMax(queryPattHere)", minMaxSameR),
		},
		t)
}

func TestMinMaxInfinity(t *testing.T) {
	testMinMax("Infinity",
		[]models.Series{
			getSeries("targetHere", "queryPattHere", infinity),
		},
		[]models.Series{
			getSeries("minMax(targetHere)", "minMax(queryPattHere)", infinityR),
		},
		t)
}

func TestMinMaxZero(t *testing.T) {
	testMinMax("Zero",
		[]models.Series{
			getSeries("targetHere", "queryPattHere", []schema.Point{}),
		},
		[]models.Series{
			getSeries("minMax(targetHere)", "minMax(queryPattHere)", []schema.Point{}),
		},
		t)
}

func TestMinMaxMultiple(t *testing.T) {
	testMinMax("MultipleSeries",
		[]models.Series{
			getSeries("targetHere", "queryPattHere", basic),
			getSeries("targetHere2", "queryPattHere2", basic),
		}, []models.Series{
			getSeries("minMax(targetHere)", "minMax(queryPattHere)", basicR),
			getSeries("minMax(targetHere2)", "minMax(queryPattHere2)", basicR),
		},
		t)
}

func getNewMinMax(in []models.Series) *FuncMinMax {
	f := NewMinMax()
	ps := f.(*FuncMinMax)
	ps.in = NewMock(in)
	return ps
}

func testMinMax(name string, in []models.Series, out []models.Series, t *testing.T) {
	f := getNewMinMax(in)

	// Copy input to check that it is unchanged later
	inputCopy := make([]models.Series, len(in))
	copy(inputCopy, in)

	dataMap := DataMap(make(map[Req][]models.Series))

	got, err := f.Exec(dataMap)
	if err := equalOutput(out, got, nil, err); err != nil {
		t.Fatalf("Case %s: %s", name, err)
	}

	t.Run("DidNotModifyInput", func(t *testing.T) {
		if err := equalOutput(inputCopy, in, nil, nil); err != nil {
			t.Fatalf("Case %s: Input was modified, err = %s", name, err)
		}
	})

	t.Run("DoesNotDoubleReturnPoints", func(t *testing.T) {
		if err := dataMap.CheckForOverlappingPoints(); err != nil {
			t.Fatalf("Case %s: Point slices in datamap overlap, err = %s", name, err)
		}
	})
}

func BenchmarkMinMax10k_1NoNulls(b *testing.B) {
	benchmarkMinMax(b, 1, test.RandFloats10k, test.RandFloats10k)
}
func BenchmarkMinMax10k_10NoNulls(b *testing.B) {
	benchmarkMinMax(b, 10, test.RandFloats10k, test.RandFloats10k)
}
func BenchmarkMinMax10k_100NoNulls(b *testing.B) {
	benchmarkMinMax(b, 100, test.RandFloats10k, test.RandFloats10k)
}
func BenchmarkMinMax10k_1000NoNulls(b *testing.B) {
	benchmarkMinMax(b, 1000, test.RandFloats10k, test.RandFloats10k)
}
func BenchmarkMinMax10k_1SomeSeriesHalfNulls(b *testing.B) {
	benchmarkMinMax(b, 1, test.RandFloats10k, test.RandFloatsWithNulls10k)
}
func BenchmarkMinMax10k_10SomeSeriesHalfNulls(b *testing.B) {
	benchmarkMinMax(b, 10, test.RandFloats10k, test.RandFloatsWithNulls10k)
}
func BenchmarkMinMax10k_100SomeSeriesHalfNulls(b *testing.B) {
	benchmarkMinMax(b, 100, test.RandFloats10k, test.RandFloatsWithNulls10k)
}
func BenchmarkMinMax10k_1000SomeSeriesHalfNulls(b *testing.B) {
	benchmarkMinMax(b, 1000, test.RandFloats10k, test.RandFloatsWithNulls10k)
}
func BenchmarkMinMax10k_1AllSeriesHalfNulls(b *testing.B) {
	benchmarkMinMax(b, 1, test.RandFloatsWithNulls10k, test.RandFloatsWithNulls10k)
}
func BenchmarkMinMax10k_10AllSeriesHalfNulls(b *testing.B) {
	benchmarkMinMax(b, 10, test.RandFloatsWithNulls10k, test.RandFloatsWithNulls10k)
}
func BenchmarkMinMax10k_100AllSeriesHalfNulls(b *testing.B) {
	benchmarkMinMax(b, 100, test.RandFloatsWithNulls10k, test.RandFloatsWithNulls10k)
}
func BenchmarkMinMax10k_1000AllSeriesHalfNulls(b *testing.B) {
	benchmarkMinMax(b, 1000, test.RandFloatsWithNulls10k, test.RandFloatsWithNulls10k)
}

func benchmarkMinMax(b *testing.B, numSeries int, fn0, fn1 func() []schema.Point) {
	var input []models.Series
	for i := 0; i < numSeries; i++ {
		series := models.Series{
			QueryPatt: strconv.Itoa(i),
		}
		if i%2 == 0 {
			series.Datapoints = fn0()
		} else {
			series.Datapoints = fn1()
		}
		input = append(input, series)
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		f := NewMinMax()
		f.(*FuncMinMax).in = NewMock(input)
		got, err := f.Exec(make(map[Req][]models.Series))
		if err != nil {
			b.Fatalf("%s", err)
		}
		results = got
	}
}
<|endoftext|>


<commit_before>package confl

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"sync"

	"github.com/kelseyhightower/envconfig"
	"github.com/teambition/confl/etcd"
	"github.com/teambition/confl/vault"
)

var (
	DefaultChangeChan = 10
	ConfPathEnv       = "CONFL_CONF_PATH"
	ErrorNoConfPath   = fmt.Errorf("required env %s missing value", ConfPathEnv)
)

var (
	defautlOnError = func(err error) {
		if err != nil {
			log.Println(err)
		}
	}
)

type Hook func(config interface{})

// Watcher manage the watch states
type Watcher struct {
	sync.Mutex
	confPath string
	// c the config struct user defined
	c        interface{}
	etcd     *etcd.Client
	changeCh chan struct{}
	hooks    []Hook
	onError  func(error)
}

// NewFromEnv create a config watcher from env
func NewFromEnv(c interface{}, onError func(error)) (*Watcher, error) {
	if onError == nil {
		onError = defautlOnError
	}

	confPath, _ := os.LookupEnv(ConfPathEnv)
	var (
		err       error
		etcdConf  = &etcd.Config{}
		vaultConf = &vault.Config{}
	)

	if err = envconfig.Process("", etcdConf); err != nil {
		return nil, err
	}

	if err = envconfig.Process("", vaultConf); err != nil {
		return nil, err
	}

	return New(c, confPath, etcdConf, vaultConf, onError)
}

func New(c interface{}, confPath string, etcdConf *etcd.Config, vaultConf *vault.Config, onError func(error)) (*Watcher, error) {
	var err error
	if confPath == "" {
		return nil, ErrorNoConfPath
	}

	if onError == nil {
		onError = defautlOnError
	}

	if etcdConf.OnError == nil {
		etcdConf.OnError = onError
	}

	if vaultConf.OnError == nil {
		vaultConf.OnError = onError
	}

	w := &Watcher{
		c:        c,
		confPath: confPath,
		changeCh: make(chan struct{}, DefaultChangeChan),
		hooks:    []Hook{},
		onError:  onError,
	}

	if w.etcd, err = etcd.NewClient(etcdConf); err != nil {
		return nil, err
	}

	vaultConf.ChangeCh = w.changeCh
	if err = vault.Init(vaultConf); err != nil {
		return nil, err
	}

	if err = w.loadConfig(); err != nil {
		return nil, err
	}

	return w, nil
}

func (w *Watcher) AddHook(hooks ...Hook) {
	w.Lock()
	w.hooks = append(w.hooks, hooks...)
	w.Unlock()
}

func (w *Watcher) GoWatch() {
	go w.etcd.WatchKey(w.confPath, w.changeCh)
	w.runReloaders()
}

func (w *Watcher) Close() error {
	w.etcd.Close()
	vault.Close()
	close(w.changeCh)
	return nil
}

func (w *Watcher) loadConfig() error {
	v, err := w.etcd.Key(w.confPath)
	if err != nil {
		return err
	}

	return json.Unmarshal([]byte(v), w.c)
}

// runReloaders run reloaders when the value changes
// which contained etcd and vault background storage
func (w *Watcher) runReloaders() {
	for range w.changeCh {
		if err := w.loadConfig(); err != nil {
			w.onError(err)
			continue
		}

		// reloaders have dependency order
		// need run reload one by one
		for _, hook := range w.hooks {
			hook(w.c)
		}
	}
}
<commit_msg>rename functions<commit_after>package confl

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"sync"

	"github.com/kelseyhightower/envconfig"
	"github.com/teambition/confl/etcd"
	"github.com/teambition/confl/vault"
)

var (
	DefaultChangeChan = 10
	ConfPathEnv       = "CONFL_CONF_PATH"
	ErrorNoConfPath   = fmt.Errorf("required env %s missing value", ConfPathEnv)
)

var (
	defautlOnError = func(err error) {
		if err != nil {
			log.Println(err)
		}
	}
)

type Hook func(config interface{})

// Watcher manage the watch states
type Watcher struct {
	sync.Mutex
	confPath string
	// c the config struct user defined
	c        interface{}
	etcd     *etcd.Client
	changeCh chan struct{}
	hooks    []Hook
	onError  func(error)
}

// NewFromEnv create a config watcher from env
func NewFromEnv(c interface{}, onError func(error)) (*Watcher, error) {
	if onError == nil {
		onError = defautlOnError
	}

	confPath, _ := os.LookupEnv(ConfPathEnv)
	var (
		err       error
		etcdConf  = &etcd.Config{}
		vaultConf = &vault.Config{}
	)

	if err = envconfig.Process("", etcdConf); err != nil {
		return nil, err
	}

	if err = envconfig.Process("", vaultConf); err != nil {
		return nil, err
	}

	return New(c, confPath, etcdConf, vaultConf, onError)
}

func New(c interface{}, confPath string, etcdConf *etcd.Config, vaultConf *vault.Config, onError func(error)) (*Watcher, error) {
	var err error
	if confPath == "" {
		return nil, ErrorNoConfPath
	}

	if onError == nil {
		onError = defautlOnError
	}

	if etcdConf.OnError == nil {
		etcdConf.OnError = onError
	}

	if vaultConf.OnError == nil {
		vaultConf.OnError = onError
	}

	w := &Watcher{
		c:        c,
		confPath: confPath,
		changeCh: make(chan struct{}, DefaultChangeChan),
		hooks:    []Hook{},
		onError:  onError,
	}

	if w.etcd, err = etcd.NewClient(etcdConf); err != nil {
		return nil, err
	}

	vaultConf.ChangeCh = w.changeCh
	if err = vault.Init(vaultConf); err != nil {
		return nil, err
	}

	if err = w.loadConfig(); err != nil {
		return nil, err
	}

	return w, nil
}

func (w *Watcher) AddHook(hooks ...Hook) {
	w.Lock()
	w.hooks = append(w.hooks, hooks...)
	w.Unlock()
}

func (w *Watcher) GoWatch() {
	go w.etcd.WatchKey(w.confPath, w.changeCh)
	w.procHooks()
}

func (w *Watcher) Close() error {
	w.etcd.Close()
	vault.Close()
	close(w.changeCh)
	return nil
}

func (w *Watcher) loadConfig() error {
	v, err := w.etcd.Key(w.confPath)
	if err != nil {
		return err
	}

	return json.Unmarshal([]byte(v), w.c)
}

// procHooks reloads config and runs the hooks when the watched value has changed
func (w *Watcher) procHooks() {
	for range w.changeCh {
		if err := w.loadConfig(); err != nil {
			w.onError(err)
			continue
		}

		// hooks must be called one by one
		// bcs there may be dependencies
		for _, hook := range w.hooks {
			hook(w.c)
		}
	}
}
<|endoftext|>


<commit_before>package openweathermap

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

type Coordinates struct {
	Longitude float64 `json:"lon"`
	Latitude  float64 `json:"lat"`
}

type Sys struct {
	Type    int     `json:"type"`
	Id      int     `json:"id"`
	Message float64 `json:"message"`
	Country string  `json:"country"`
	Sunrise int     `json:"sunrise"`
	Sunset  int     `json:"sunset"`
}

type Wind struct {
	Speed float64 `json:"speed"`
	Deg   int     `json:"deg"`
}

type Weather struct {
	Id          int    `json:"id"`
	Main        string `json:"main"`
	Description string `json:"description"`
	Icon        string `json:"icon"`
}

type Main struct {
	Temp     float64 `json:"temp"`
	TempMin  float64 `json:"temp_min"`
	TempMax  float64 `json:"temp_max"`
	Pressure int     `json:"pressure"`
	Humidity int     `json:"humidity"`
}

type Clouds struct {
	All int `json:"all"`
}

type WeatherData struct {
	GeoPos  Coordinates `json:"coord"`
	Sys     Sys         `json:"sys"`
	Base    string      `json:"base"`
	Weather []Weather   `json:"weather"`
	Main    Main        `json:"main"`
	Wind    Wind        `json:"wind"`
	Clouds  Clouds      `json:"clouds"`
	Dt      int         `json:"dt"`
	Id      int         `json:"id"`
	Name    string      `json:"name"`
	Cod     int         `json:"cod"`
}

func (w *WeatherData) GetByName(location string) {
	response, err := http.Get(fmt.Sprintf(cityUrl, location))
	if err != nil {
		log.Fatalln(err)
	}
	defer response.Body.Close()

	result, err := ioutil.ReadAll(response.Body)
	if err != nil {
		fmt.Println("Error reading data", err)
	}

	err = json.Unmarshal(result, &w)
	if err != nil {
		log.Fatalln(err)
	}
}

func (w *WeatherData) GetByCoordinates(location string) {}

func (w *WeatherData) GetByID() {}

func (w *WeatherData) GetByArea() {}
<commit_msg>added coordinat based query function<commit_after>package openweathermap

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

type Coordinates struct {
	Longitude float64 `json:"lon"`
	Latitude  float64 `json:"lat"`
}

type Sys struct {
	Type    int     `json:"type"`
	Id      int     `json:"id"`
	Message float64 `json:"message"`
	Country string  `json:"country"`
	Sunrise int     `json:"sunrise"`
	Sunset  int     `json:"sunset"`
}

type Wind struct {
	Speed float64 `json:"speed"`
	Deg   int     `json:"deg"`
}

type Weather struct {
	Id          int    `json:"id"`
	Main        string `json:"main"`
	Description string `json:"description"`
	Icon        string `json:"icon"`
}

type Main struct {
	Temp     float64 `json:"temp"`
	TempMin  float64 `json:"temp_min"`
	TempMax  float64 `json:"temp_max"`
	Pressure int     `json:"pressure"`
	Humidity int     `json:"humidity"`
}

type Clouds struct {
	All int `json:"all"`
}

type WeatherData struct {
	GeoPos  Coordinates `json:"coord"`
	Sys     Sys         `json:"sys"`
	Base    string      `json:"base"`
	Weather []Weather   `json:"weather"`
	Main    Main        `json:"main"`
	Wind    Wind        `json:"wind"`
	Clouds  Clouds      `json:"clouds"`
	Dt      int         `json:"dt"`
	Id      int         `json:"id"`
	Name    string      `json:"name"`
	Cod     int         `json:"cod"`
}

func (w *WeatherData) GetByName(location string) {
	response, err := http.Get(fmt.Sprintf(cityUrl, location))
	if err != nil {
		log.Fatalln(err)
	}
	defer response.Body.Close()

	result, err := ioutil.ReadAll(response.Body)
	if err != nil {
		fmt.Println("Error reading data", err)
	}

	err = json.Unmarshal(result, &w)
	if err != nil {
		log.Fatalln(err)
	}
}

func (w *WeatherData) GetByCoordinates(location *Coordinates) {
	response, err := http.Get(fmt.Sprintf(coordUrl, location.Latitude, location.Longitude))
	if err != nil {
		log.Fatalln(err)
	}
	defer response.Body.Close()

	result, err := ioutil.ReadAll(response.Body)
	if err != nil {
		fmt.Println("Error reading data", err)
	}

	err = json.Unmarshal(result, &w)
	if err != nil {
		log.Fatalln(err)
	}
}

func (w *WeatherData) GetByID() {}

func (w *WeatherData) GetByArea() {}
<|endoftext|>


<commit_before>package radix

import (
	"net"
	"strings"
	"sync"
	"time"

	"golang.org/x/xerrors"

	"github.com/mediocregopher/radix/v3/resp"
)

var blockingCmds = map[string]bool{
	"WAIT": true,

	// taken from https://github.com/joomcode/redispipe#limitations
	"BLPOP":      true,
	"BRPOP":      true,
	"BRPOPLPUSH": true,

	"BZPOPMIN": true,
	"BZPOPMAX": true,

	"XREAD":      true,
	"XREADGROUP": true,

	"SAVE": true,
}

type pipeliner struct {
	c Client

	limit  int
	window time.Duration

	// reqsBufCh contains buffers for collecting commands and acts as a semaphore
	// to limit the number of concurrent flushes.
	reqsBufCh chan []CmdAction

	reqCh chan *pipelinerCmd
	reqWG sync.WaitGroup

	l      sync.RWMutex
	closed bool
}

var _ Client = (*pipeliner)(nil)

func newPipeliner(c Client, concurrency, limit int, window time.Duration) *pipeliner {
	if concurrency < 1 {
		concurrency = 1
	}

	p := &pipeliner{
		c: c,

		limit:  limit,
		window: window,

		reqsBufCh: make(chan []CmdAction, concurrency),

		reqCh: make(chan *pipelinerCmd, 32), // https://xkcd.com/221/
	}

	p.reqWG.Add(1)
	go func() {
		defer p.reqWG.Done()
		p.reqLoop()
	}()

	for i := 0; i < cap(p.reqsBufCh); i++ {
		if p.limit > 0 {
			p.reqsBufCh <- make([]CmdAction, 0, limit)
		} else {
			p.reqsBufCh <- nil
		}
	}

	return p
}

// CanDo checks if the given Action can be executed / passed to p.Do.
//
// If CanDo returns false, the Action must not be given to Do.
func (p *pipeliner) CanDo(a Action) bool {
	// there is currently no way to get the command for CmdAction implementations
	// from outside the radix package so we can not multiplex those commands. User
	// defined pipelines are not pipelined to let the user better control them.
	if cmdA, ok := a.(*cmdAction); ok {
		return !blockingCmds[strings.ToUpper(cmdA.cmd)]
	}
	return false
}

// Do executes the given Action as part of the pipeline.
//
// If a is not a CmdAction, Do panics.
func (p *pipeliner) Do(a Action) error {
	req := getPipelinerCmd(a.(CmdAction)) // get this outside the lock to avoid

	p.l.RLock()
	if p.closed {
		p.l.RUnlock()
		return errClientClosed
	}
	p.reqCh <- req
	p.l.RUnlock()

	err := <-req.resCh
	poolPipelinerCmd(req)
	return err
}

// Close closes the pipeliner and makes sure that all background goroutines
// are stopped before returning.
//
// Close does *not* close the underlying Client.
func (p *pipeliner) Close() error {
	p.l.Lock()
	defer p.l.Unlock()

	if p.closed {
		return nil
	}

	close(p.reqCh)
	p.reqWG.Wait()

	for i := 0; i < cap(p.reqsBufCh); i++ {
		<-p.reqsBufCh
	}

	p.c, p.closed = nil, true
	return nil
}

func (p *pipeliner) reqLoop() {
	t := getTimer(time.Hour)
	defer putTimer(t)

	t.Stop()

	reqs := <-p.reqsBufCh
	defer func() {
		p.reqsBufCh <- reqs
	}()

	for {
		select {
		case req, ok := <-p.reqCh:
			if !ok {
				reqs = p.flush(reqs)
				return
			}

			reqs = append(reqs, req)

			if p.limit > 0 && len(reqs) == p.limit {
				// if we reached the pipeline limit, execute now to avoid unnecessary waiting
				t.Stop()

				reqs = p.flush(reqs)
			} else if len(reqs) == 1 {
				t.Reset(p.window)
			}
		case <-t.C:
			reqs = p.flush(reqs)
		}
	}
}

func (p *pipeliner) flush(reqs []CmdAction) []CmdAction {
	if len(reqs) == 0 {
		return reqs
	}

	go func() {
		defer func() {
			p.reqsBufCh <- reqs[:0]
		}()

		pipe := pipelinerPipeline{
			pipeline: pipeline(reqs),
		}

		if err := p.c.Do(pipe); err != nil {
			for _, req := range reqs {
				select {
				case req.(*pipelinerCmd).resCh <- err:
				default:
				}
			}
		}
	}()

	return <-p.reqsBufCh
}

type pipelinerCmd struct {
	CmdAction
	resCh chan error
}

var pipelinerCmdPool sync.Pool

func getPipelinerCmd(cmd CmdAction) *pipelinerCmd {
	req, _ := pipelinerCmdPool.Get().(*pipelinerCmd)
	if req != nil {
		req.CmdAction = cmd
		return req
	}
	return &pipelinerCmd{
		CmdAction: cmd,
		// using a buffer of 1 is faster than no buffer in most cases
		resCh: make(chan error, 1),
	}
}

func poolPipelinerCmd(req *pipelinerCmd) {
	req.CmdAction = nil
	pipelinerCmdPool.Put(req)
}

type pipelinerPipeline struct {
	pipeline
}

func (p pipelinerPipeline) Run(c Conn) error {
	if err := c.Encode(p); err != nil {
		return err
	}
	for _, req := range p.pipeline {
		err := c.Decode(req)
		if _, ok := err.(net.Error); ok {
			return err
		} else if err != nil && !xerrors.As(err, new(resp.ErrDiscarded)) {
			return err
		}
		req.(*pipelinerCmd).resCh <- err
	}
	return nil
}
<commit_msg>Use ioErrConn to simplify pipeliner error handling<commit_after>package radix

import (
	"strings"
	"sync"
	"time"
)

var blockingCmds = map[string]bool{
	"WAIT": true,

	// taken from https://github.com/joomcode/redispipe#limitations
	"BLPOP":      true,
	"BRPOP":      true,
	"BRPOPLPUSH": true,

	"BZPOPMIN": true,
	"BZPOPMAX": true,

	"XREAD":      true,
	"XREADGROUP": true,

	"SAVE": true,
}

type pipeliner struct {
	c Client

	limit  int
	window time.Duration

	// reqsBufCh contains buffers for collecting commands and acts as a semaphore
	// to limit the number of concurrent flushes.
	reqsBufCh chan []CmdAction

	reqCh chan *pipelinerCmd
	reqWG sync.WaitGroup

	l      sync.RWMutex
	closed bool
}

var _ Client = (*pipeliner)(nil)

func newPipeliner(c Client, concurrency, limit int, window time.Duration) *pipeliner {
	if concurrency < 1 {
		concurrency = 1
	}

	p := &pipeliner{
		c: c,

		limit:  limit,
		window: window,

		reqsBufCh: make(chan []CmdAction, concurrency),

		reqCh: make(chan *pipelinerCmd, 32), // https://xkcd.com/221/
	}

	p.reqWG.Add(1)
	go func() {
		defer p.reqWG.Done()
		p.reqLoop()
	}()

	for i := 0; i < cap(p.reqsBufCh); i++ {
		if p.limit > 0 {
			p.reqsBufCh <- make([]CmdAction, 0, limit)
		} else {
			p.reqsBufCh <- nil
		}
	}

	return p
}

// CanDo checks if the given Action can be executed / passed to p.Do.
//
// If CanDo returns false, the Action must not be given to Do.
func (p *pipeliner) CanDo(a Action) bool {
	// there is currently no way to get the command for CmdAction implementations
	// from outside the radix package so we can not multiplex those commands. User
	// defined pipelines are not pipelined to let the user better control them.
	if cmdA, ok := a.(*cmdAction); ok {
		return !blockingCmds[strings.ToUpper(cmdA.cmd)]
	}
	return false
}

// Do executes the given Action as part of the pipeline.
//
// If a is not a CmdAction, Do panics.
func (p *pipeliner) Do(a Action) error {
	req := getPipelinerCmd(a.(CmdAction)) // get this outside the lock to avoid

	p.l.RLock()
	if p.closed {
		p.l.RUnlock()
		return errClientClosed
	}
	p.reqCh <- req
	p.l.RUnlock()

	err := <-req.resCh
	poolPipelinerCmd(req)
	return err
}

// Close closes the pipeliner and makes sure that all background goroutines
// are stopped before returning.
//
// Close does *not* close the underlying Client.
func (p *pipeliner) Close() error {
	p.l.Lock()
	defer p.l.Unlock()

	if p.closed {
		return nil
	}

	close(p.reqCh)
	p.reqWG.Wait()

	for i := 0; i < cap(p.reqsBufCh); i++ {
		<-p.reqsBufCh
	}

	p.c, p.closed = nil, true
	return nil
}

func (p *pipeliner) reqLoop() {
	t := getTimer(time.Hour)
	defer putTimer(t)

	t.Stop()

	reqs := <-p.reqsBufCh
	defer func() {
		p.reqsBufCh <- reqs
	}()

	for {
		select {
		case req, ok := <-p.reqCh:
			if !ok {
				reqs = p.flush(reqs)
				return
			}

			reqs = append(reqs, req)

			if p.limit > 0 && len(reqs) == p.limit {
				// if we reached the pipeline limit, execute now to avoid unnecessary waiting
				t.Stop()

				reqs = p.flush(reqs)
			} else if len(reqs) == 1 {
				t.Reset(p.window)
			}
		case <-t.C:
			reqs = p.flush(reqs)
		}
	}
}

func (p *pipeliner) flush(reqs []CmdAction) []CmdAction {
	if len(reqs) == 0 {
		return reqs
	}

	go func() {
		defer func() {
			p.reqsBufCh <- reqs[:0]
		}()

		pipe := pipelinerPipeline{
			pipeline: pipeline(reqs),
		}

		if err := p.c.Do(pipe); err != nil {
			for _, req := range reqs {
				select {
				case req.(*pipelinerCmd).resCh <- err:
				default:
				}
			}
		}
	}()

	return <-p.reqsBufCh
}

type pipelinerCmd struct {
	CmdAction
	resCh chan error
}

var pipelinerCmdPool sync.Pool

func getPipelinerCmd(cmd CmdAction) *pipelinerCmd {
	req, _ := pipelinerCmdPool.Get().(*pipelinerCmd)
	if req != nil {
		req.CmdAction = cmd
		return req
	}
	return &pipelinerCmd{
		CmdAction: cmd,
		// using a buffer of 1 is faster than no buffer in most cases
		resCh: make(chan error, 1),
	}
}

func poolPipelinerCmd(req *pipelinerCmd) {
	req.CmdAction = nil
	pipelinerCmdPool.Put(req)
}

type pipelinerPipeline struct {
	pipeline
}

func (p pipelinerPipeline) Run(c Conn) error {
	if err := c.Encode(p); err != nil {
		return err
	}
	errConn := ioErrConn{Conn: c}
	for _, req := range p.pipeline {
		req.(*pipelinerCmd).resCh <- errConn.Decode(req)
		if errConn.lastIOErr != nil {
			return errConn.lastIOErr
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package webconn

import (
	"errors"
	"net/http"
	"io/ioutil"
	"bytes"
	"runtime"
)

const (
	WEBCONN_SERVER = 1
	WEBCONN_CLIENT = 2
)

type RecvHandler func([]byte) error

type Webconn struct {
	ip string
	port int

	mode int
	url string

	client *http.Client

	done chan bool
	running bool

	writeChan chan []byte
	handler RecvHandler
}

func (w *Webconn) getHttp() error {
	for w.running {
		resp, err := w.client.Get(w.url)

		if err != nil {
			return err
		}
		defer resp.Body.Close()

		content, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		if w.handler != nil {
			return w.handler(content)
		}
	}
	return nil
}

func getReader(list [][]byte) *bytes.Reader {
	b := bytes.Join(list, []byte(","))
	l := make([][]byte, 3)
	l[0] = []byte("[")
	l[1] = b
	l[2] = []byte("[")

	res := bytes.Join(l, []byte(""))
	return bytes.NewReader(res)
}

func (w *Webconn) postHttp() error {
	list := make([][]byte, 0)

	for w.running {
		select {
			case <-w.writeChan:
				buf := <-w.writeChan
				list = append(list, buf)
			default:
				if len(list) > 0 {
					r := getReader(list)
					resp, err := w.client.Post(w.url, "application/json", r)
					if err != nil {
						return err
					}
					defer resp.Body.Close()

					_, err = ioutil.ReadAll(resp.Body)
					if err != nil {
						return err
					}
					list = make([][]byte, 0)
				} else {
					runtime.Gosched()
				}
		}
	}

	return nil
}

func Client(url string) *Webconn {
	w := new(Webconn)

	w.mode = WEBCONN_CLIENT
	w.url = url

	w.init()

	return w
}

func Server(ip string, port int) *Webconn {
	w := new(Webconn)

	w.mode = WEBCONN_SERVER
	w.ip = ip
	w.port = port

	w.init()

	return w
}

func (w *Webconn) init() {
	w.done = make(chan bool, 1)
	w.writeChan = make(chan []byte, 100)
}

func (w *Webconn) Stop() {
	w.done <- true
}

func (w *Webconn) Register() error {
	if w.mode == WEBCONN_SERVER {
		return errors.New("Unsupported action")
	}

	return nil
}

func (w *Webconn) AddHandler(handler RecvHandler) {
	w.handler = handler
}

func (w *Webconn) runClient() error {
	go w.getHttp()
	go w.postHttp()

	<-w.done
	return nil
}

func (w *Webconn) runServer() error {
	w.running = false
	<-w.done
	return nil
}

func (w *Webconn) Run() error {
	w.running = true

	switch (w.mode) {
	case WEBCONN_SERVER:
		return w.runServer()
	case WEBCONN_CLIENT:
		return w.runClient()
	}
	return errors.New("Unsupported mode")
}<commit_msg>done with second draft<commit_after>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Edward Kim <edward@webconn.me>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package webconn

import (
	"errors"
	"net/http"
	"io/ioutil"
	"bytes"
	"runtime"
    "encoding/json"
)

const (
	WEBCONN_SERVER = 1
	WEBCONN_CLIENT = 2
)

type RecvHandler func([]byte) error

type Webconn struct {
	ip string
	port int

	mode int
	url string

	client *http.Client

	done chan bool
	running bool

    channelMap map[string]chan []byte
	handlerMap map[string]RecvHandler
}

type Message struct {
    Command string `json:"command"`
    Data string `json:"data"`
}

func (w *Webconn) getMessages() ([]Message, error) {
    var messages []Message

    resp, err := w.client.Get(w.url)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    content, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }

    err = json.Unmarshal(content, &messages)
    if err != nil {
        return nil, err
    }

    return messages, nil
}

func (w *Webconn) receiver() error {
	for w.running {
        messages, err := w.getMessages()
        if err != nil {
            continue
        }
        for _, m := range messages {
            if h, ok := w.handlerMap[m.Command]; ok {
                err := h([]byte(m.Data))
                if err != nil {
                    return err
                }
            }
        }
	}
	return nil
}

func (w *Webconn) postMessages(cmd string, ch <-chan []byte) (bool, error) {
    buf := []byte{}
    for {
        select {
            case <-ch:
                b := <-ch
                buf = append(buf, b...)
            default:
                if len(buf) > 0 {
                    msg := []Message{Message{cmd, string(buf)}}
                    json_data, err := json.Marshal(msg)
                    if err != nil {
                        return false, err
                    }
                    r := bytes.NewReader(json_data)
                    resp, err := w.client.Post(w.url, "application/json", r)
                    if err != nil {
                        return false, err
                    }
                    defer resp.Body.Close()

                    _, err = ioutil.ReadAll(resp.Body)
                    if err != nil {
                        return false, err
                    }
                    return true, nil
                } else {
                    return false, nil
                }
        }
    }

    return false, nil
}

func (w *Webconn) sender() error {
	for w.running {
        for k, v := range w.channelMap {
            w.postMessages(k, v)
        }
        runtime.Gosched()
    }

    return nil
}

func Client(url string) *Webconn {
	w := new(Webconn)

	w.mode = WEBCONN_CLIENT
	w.url = url

	w.init()

	return w
}

func Server(ip string, port int) *Webconn {
	w := new(Webconn)

	w.mode = WEBCONN_SERVER
	w.ip = ip
	w.port = port

	w.init()

	return w
}

func (w *Webconn) init() {
	w.done = make(chan bool, 1)
    w.handlerMap = make(map[string]RecvHandler)
	w.channelMap = make(map[string]chan []byte)
}

func (w *Webconn) Stop() {
	w.done <- true
}

func (w *Webconn) Register() error {
	if w.mode == WEBCONN_SERVER {
		return errors.New("Unsupported action")
	}

	return nil
}

func (w *Webconn) AddHandler(cmd string, handler RecvHandler) {
    w.handlerMap[cmd] = handler
}

func (w *Webconn) Write(cmd string, data []byte) {
    c, ok := w.channelMap[cmd]
    if !ok {
        w.channelMap[cmd] = make(chan []byte, 100)
        c, ok = w.channelMap[cmd]
    }

    c <- data
}

func (w *Webconn) runClient() error {
	go w.receiver()
	go w.sender()

	<-w.done
	return nil
}

func (w *Webconn) runServer() error {
	w.running = false
	<-w.done
	return nil
}

func (w *Webconn) Run() error {
	w.running = true

	switch (w.mode) {
	case WEBCONN_SERVER:
		return w.runServer()
	case WEBCONN_CLIENT:
		return w.runClient()
	}
	return errors.New("Unsupported mode")
}<|endoftext|>


<commit_before>package pkg

import (
	"context"
	"fmt"
	"go/ast"
	"go/types"
	"io/ioutil"
	"path/filepath"
	"sort"
	"strings"

	"github.com/rs/zerolog"
	"github.com/vektra/mockery/v2/pkg/logging"
	"golang.org/x/tools/go/packages"
)

type parserEntry struct {
	fileName   string
	pkg        *packages.Package
	syntax     *ast.File
	interfaces []string
}

type packageLoadEntry struct {
	pkgs []*packages.Package
	err  error
}

type Parser struct {
	entries           []*parserEntry
	entriesByFileName map[string]*parserEntry
	parserPackages    []*types.Package
	conf              packages.Config
	packageLoadCache  map[string]packageLoadEntry
}

func NewParser(buildTags []string) *Parser {
	var conf packages.Config
	conf.Mode = packages.LoadSyntax
	if len(buildTags) > 0 {
		conf.BuildFlags = []string{"-tags", strings.Join(buildTags, ",")}
	}
	return &Parser{
		parserPackages:    make([]*types.Package, 0),
		entriesByFileName: map[string]*parserEntry{},
		conf:              conf,
		packageLoadCache:  map[string]packageLoadEntry{},
	}
}

func (p *Parser) loadPackages(fpath string) ([]*packages.Package, error) {
	if result, ok := p.packageLoadCache[fpath]; ok {
		return result.pkgs, result.err
	}
	pkgs, err := packages.Load(&p.conf, "file="+fpath)
	p.packageLoadCache[fpath] = packageLoadEntry{pkgs, err}
	return pkgs, err
}

func (p *Parser) Parse(ctx context.Context, path string) error {
	// To support relative paths to mock targets w/ vendor deps, we need to provide eventual
	// calls to build.Context.Import with an absolute path. It needs to be absolute because
	// Import will only find the vendor directory if our target path for parsing is under
	// a "root" (GOROOT or a GOPATH). Only absolute paths will pass the prefix-based validation.
	//
	// For example, if our parse target is "./ifaces", Import will check if any "roots" are a
	// prefix of "ifaces" and decide to skip the vendor search.
	path, err := filepath.Abs(path)
	if err != nil {
		return err
	}

	dir := filepath.Dir(path)

	files, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}

	for _, fi := range files {
		log := zerolog.Ctx(ctx).With().
			Str(logging.LogKeyDir, dir).
			Str(logging.LogKeyFile, fi.Name()).
			Logger()
		ctx = log.WithContext(ctx)

		if filepath.Ext(fi.Name()) != ".go" || strings.HasSuffix(fi.Name(), "_test.go") {
			continue
		}

		log.Debug().Msgf("parsing")

		fname := fi.Name()
		fpath := filepath.Join(dir, fname)
		if _, ok := p.entriesByFileName[fpath]; ok {
			continue
		}

		pkgs, err := p.loadPackages(fpath)
		if err != nil {
			return err
		}
		if len(pkgs) == 0 {
			continue
		}
		if len(pkgs) > 1 {
			names := make([]string, len(pkgs))
			for i, p := range pkgs {
				names[i] = p.Name
			}
			panic(fmt.Sprintf("file %s resolves to multiple packages: %s", fpath, strings.Join(names, ", ")))
		}

		pkg := pkgs[0]
		if len(pkg.Errors) > 0 {
			return pkg.Errors[0]
		}
		if len(pkg.GoFiles) == 0 {
			continue
		}

		for idx, f := range pkg.GoFiles {
			if _, ok := p.entriesByFileName[f]; ok {
				continue
			}
			entry := parserEntry{
				fileName: f,
				pkg:      pkg,
				syntax:   pkg.Syntax[idx],
			}
			p.entries = append(p.entries, &entry)
			p.entriesByFileName[f] = &entry
		}
	}

	return nil
}

type NodeVisitor struct {
	declaredInterfaces []string
}

func NewNodeVisitor() *NodeVisitor {
	return &NodeVisitor{
		declaredInterfaces: make([]string, 0),
	}
}

func (nv *NodeVisitor) DeclaredInterfaces() []string {
	return nv.declaredInterfaces
}

func (nv *NodeVisitor) Visit(node ast.Node) ast.Visitor {
	switch n := node.(type) {
	case *ast.TypeSpec:
		switch n.Type.(type) {
		case *ast.InterfaceType, *ast.FuncType:
			nv.declaredInterfaces = append(nv.declaredInterfaces, n.Name.Name)
		}
	}
	return nv
}

func (p *Parser) Load() error {
	for _, entry := range p.entries {
		nv := NewNodeVisitor()
		ast.Walk(nv, entry.syntax)
		entry.interfaces = nv.DeclaredInterfaces()
	}
	return nil
}

func (p *Parser) Find(name string) (*Interface, error) {
	for _, entry := range p.entries {
		for _, iface := range entry.interfaces {
			if iface == name {
				list := p.packageInterfaces(entry.pkg.Types, entry.fileName, []string{name}, nil)
				if len(list) > 0 {
					return list[0], nil
				}
			}
		}
	}
	return nil, ErrNotInterface
}

type Method struct {
	Name      string
	Signature *types.Signature
}

// Interface type represents the target type that we will generate a mock for.
// It could be an interface, or a function type.
// Function type emulates: an interface it has 1 method with the function signature
// and a general name, e.g. "Execute".
type Interface struct {
	Name            string // Name of the type to be mocked.
	QualifiedName   string // Path to the package of the target type.
	FileName        string
	File            *ast.File
	Pkg             *types.Package
	NamedType       *types.Named
	IsFunction      bool             // If true, this instance represents a function, otherwise it's an interface.
	ActualInterface *types.Interface // Holds the actual interface type, in case it's an interface.
	SingleFunction  *Method          // Holds the function type information, in case it's a function type.
}

func (iface *Interface) Methods() []*Method {
	if iface.IsFunction {
		return []*Method{iface.SingleFunction}
	}
	methods := make([]*Method, iface.ActualInterface.NumMethods())
	for i := 0; i < iface.ActualInterface.NumMethods(); i++ {
		fn := iface.ActualInterface.Method(i)
		methods[i] = &Method{Name: fn.Name(), Signature: fn.Type().(*types.Signature)}
	}
	return methods
}

type sortableIFaceList []*Interface

func (s sortableIFaceList) Len() int {
	return len(s)
}

func (s sortableIFaceList) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func (s sortableIFaceList) Less(i, j int) bool {
	return strings.Compare(s[i].Name, s[j].Name) == -1
}

func (p *Parser) Interfaces() []*Interface {
	ifaces := make(sortableIFaceList, 0)
	for _, entry := range p.entries {
		declaredIfaces := entry.interfaces
		ifaces = p.packageInterfaces(entry.pkg.Types, entry.fileName, declaredIfaces, ifaces)
	}

	sort.Sort(ifaces)
	return ifaces
}

func (p *Parser) packageInterfaces(
	pkg *types.Package,
	fileName string,
	declaredInterfaces []string,
	ifaces []*Interface) []*Interface {
	scope := pkg.Scope()
	for _, name := range declaredInterfaces {
		obj := scope.Lookup(name)
		if obj == nil {
			continue
		}

		typ, ok := obj.Type().(*types.Named)
		if !ok {
			continue
		}

		name = typ.Obj().Name()

		if typ.Obj().Pkg() == nil {
			continue
		}

		elem := &Interface{
			Name:          name,
			Pkg:           pkg,
			QualifiedName: pkg.Path(),
			FileName:      fileName,
			NamedType:     typ,
		}

		iface, ok := typ.Underlying().(*types.Interface)
		if ok {
			elem.IsFunction = false
			elem.ActualInterface = iface
		} else {
			sig, ok := typ.Underlying().(*types.Signature)
			if !ok {
				continue
			}
			elem.IsFunction = true
			elem.SingleFunction = &Method{Name: "Execute", Signature: sig}
		}

		ifaces = append(ifaces, elem)
	}

	return ifaces
}
<commit_msg>Skip mock_ files during parsing<commit_after>package pkg

import (
	"context"
	"fmt"
	"go/ast"
	"go/types"
	"io/ioutil"
	"path/filepath"
	"sort"
	"strings"

	"github.com/rs/zerolog"
	"github.com/vektra/mockery/v2/pkg/logging"
	"golang.org/x/tools/go/packages"
)

type parserEntry struct {
	fileName   string
	pkg        *packages.Package
	syntax     *ast.File
	interfaces []string
}

type packageLoadEntry struct {
	pkgs []*packages.Package
	err  error
}

type Parser struct {
	entries           []*parserEntry
	entriesByFileName map[string]*parserEntry
	parserPackages    []*types.Package
	conf              packages.Config
	packageLoadCache  map[string]packageLoadEntry
}

func NewParser(buildTags []string) *Parser {
	var conf packages.Config
	conf.Mode = packages.LoadSyntax
	if len(buildTags) > 0 {
		conf.BuildFlags = []string{"-tags", strings.Join(buildTags, ",")}
	}
	return &Parser{
		parserPackages:    make([]*types.Package, 0),
		entriesByFileName: map[string]*parserEntry{},
		conf:              conf,
		packageLoadCache:  map[string]packageLoadEntry{},
	}
}

func (p *Parser) loadPackages(fpath string) ([]*packages.Package, error) {
	if result, ok := p.packageLoadCache[fpath]; ok {
		return result.pkgs, result.err
	}
	pkgs, err := packages.Load(&p.conf, "file="+fpath)
	p.packageLoadCache[fpath] = packageLoadEntry{pkgs, err}
	return pkgs, err
}

func (p *Parser) Parse(ctx context.Context, path string) error {
	// To support relative paths to mock targets w/ vendor deps, we need to provide eventual
	// calls to build.Context.Import with an absolute path. It needs to be absolute because
	// Import will only find the vendor directory if our target path for parsing is under
	// a "root" (GOROOT or a GOPATH). Only absolute paths will pass the prefix-based validation.
	//
	// For example, if our parse target is "./ifaces", Import will check if any "roots" are a
	// prefix of "ifaces" and decide to skip the vendor search.
	path, err := filepath.Abs(path)
	if err != nil {
		return err
	}

	dir := filepath.Dir(path)

	files, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}

	for _, fi := range files {
		log := zerolog.Ctx(ctx).With().
			Str(logging.LogKeyDir, dir).
			Str(logging.LogKeyFile, fi.Name()).
			Logger()
		ctx = log.WithContext(ctx)

		if filepath.Ext(fi.Name()) != ".go" || strings.HasSuffix(fi.Name(), "_test.go") || strings.HasPrefix(fi.Name(), "mock_") {
			continue
		}

		log.Debug().Msgf("parsing")

		fname := fi.Name()
		fpath := filepath.Join(dir, fname)
		if _, ok := p.entriesByFileName[fpath]; ok {
			continue
		}

		pkgs, err := p.loadPackages(fpath)
		if err != nil {
			return err
		}
		if len(pkgs) == 0 {
			continue
		}
		if len(pkgs) > 1 {
			names := make([]string, len(pkgs))
			for i, p := range pkgs {
				names[i] = p.Name
			}
			panic(fmt.Sprintf("file %s resolves to multiple packages: %s", fpath, strings.Join(names, ", ")))
		}

		pkg := pkgs[0]
		if len(pkg.Errors) > 0 {
			return pkg.Errors[0]
		}
		if len(pkg.GoFiles) == 0 {
			continue
		}

		for idx, f := range pkg.GoFiles {
			if _, ok := p.entriesByFileName[f]; ok {
				continue
			}
			entry := parserEntry{
				fileName: f,
				pkg:      pkg,
				syntax:   pkg.Syntax[idx],
			}
			p.entries = append(p.entries, &entry)
			p.entriesByFileName[f] = &entry
		}
	}

	return nil
}

type NodeVisitor struct {
	declaredInterfaces []string
}

func NewNodeVisitor() *NodeVisitor {
	return &NodeVisitor{
		declaredInterfaces: make([]string, 0),
	}
}

func (nv *NodeVisitor) DeclaredInterfaces() []string {
	return nv.declaredInterfaces
}

func (nv *NodeVisitor) Visit(node ast.Node) ast.Visitor {
	switch n := node.(type) {
	case *ast.TypeSpec:
		switch n.Type.(type) {
		case *ast.InterfaceType, *ast.FuncType:
			nv.declaredInterfaces = append(nv.declaredInterfaces, n.Name.Name)
		}
	}
	return nv
}

func (p *Parser) Load() error {
	for _, entry := range p.entries {
		nv := NewNodeVisitor()
		ast.Walk(nv, entry.syntax)
		entry.interfaces = nv.DeclaredInterfaces()
	}
	return nil
}

func (p *Parser) Find(name string) (*Interface, error) {
	for _, entry := range p.entries {
		for _, iface := range entry.interfaces {
			if iface == name {
				list := p.packageInterfaces(entry.pkg.Types, entry.fileName, []string{name}, nil)
				if len(list) > 0 {
					return list[0], nil
				}
			}
		}
	}
	return nil, ErrNotInterface
}

type Method struct {
	Name      string
	Signature *types.Signature
}

// Interface type represents the target type that we will generate a mock for.
// It could be an interface, or a function type.
// Function type emulates: an interface it has 1 method with the function signature
// and a general name, e.g. "Execute".
type Interface struct {
	Name            string // Name of the type to be mocked.
	QualifiedName   string // Path to the package of the target type.
	FileName        string
	File            *ast.File
	Pkg             *types.Package
	NamedType       *types.Named
	IsFunction      bool             // If true, this instance represents a function, otherwise it's an interface.
	ActualInterface *types.Interface // Holds the actual interface type, in case it's an interface.
	SingleFunction  *Method          // Holds the function type information, in case it's a function type.
}

func (iface *Interface) Methods() []*Method {
	if iface.IsFunction {
		return []*Method{iface.SingleFunction}
	}
	methods := make([]*Method, iface.ActualInterface.NumMethods())
	for i := 0; i < iface.ActualInterface.NumMethods(); i++ {
		fn := iface.ActualInterface.Method(i)
		methods[i] = &Method{Name: fn.Name(), Signature: fn.Type().(*types.Signature)}
	}
	return methods
}

type sortableIFaceList []*Interface

func (s sortableIFaceList) Len() int {
	return len(s)
}

func (s sortableIFaceList) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func (s sortableIFaceList) Less(i, j int) bool {
	return strings.Compare(s[i].Name, s[j].Name) == -1
}

func (p *Parser) Interfaces() []*Interface {
	ifaces := make(sortableIFaceList, 0)
	for _, entry := range p.entries {
		declaredIfaces := entry.interfaces
		ifaces = p.packageInterfaces(entry.pkg.Types, entry.fileName, declaredIfaces, ifaces)
	}

	sort.Sort(ifaces)
	return ifaces
}

func (p *Parser) packageInterfaces(
	pkg *types.Package,
	fileName string,
	declaredInterfaces []string,
	ifaces []*Interface) []*Interface {
	scope := pkg.Scope()
	for _, name := range declaredInterfaces {
		obj := scope.Lookup(name)
		if obj == nil {
			continue
		}

		typ, ok := obj.Type().(*types.Named)
		if !ok {
			continue
		}

		name = typ.Obj().Name()

		if typ.Obj().Pkg() == nil {
			continue
		}

		elem := &Interface{
			Name:          name,
			Pkg:           pkg,
			QualifiedName: pkg.Path(),
			FileName:      fileName,
			NamedType:     typ,
		}

		iface, ok := typ.Underlying().(*types.Interface)
		if ok {
			elem.IsFunction = false
			elem.ActualInterface = iface
		} else {
			sig, ok := typ.Underlying().(*types.Signature)
			if !ok {
				continue
			}
			elem.IsFunction = true
			elem.SingleFunction = &Method{Name: "Execute", Signature: sig}
		}

		ifaces = append(ifaces, elem)
	}

	return ifaces
}
<|endoftext|>


<commit_before>package plane

import (
	"V-switch/crypt"
	"V-switch/tools"
	"log"
	"net"
	"strings"
)

func init() {

	go TLVInterpreter()

}

func TLVInterpreter() {

	var my_tlv_enc []byte

	log.Println("[PLANE][TLV][INTERPRETER] Thread starts")

	for {

		my_tlv_enc = <-UdpToPlane
		log.Printf("[PLANE][TLV][INTERPRETER] Read %d bytes from UdpToPlane", len(my_tlv_enc))

		my_tlv := crypt.FrameDecrypt([]byte(VSwitch.SwID), my_tlv_enc)
		if my_tlv == nil {
			log.Printf("[PLANE][TLV][ERROR] Invalid KEY(%d): %s", len(VSwitch.SwID), VSwitch.SwID)
			continue
		}

		typ, ln, payload := tools.UnPackTLV(my_tlv)

		if ln == 0 {
			continue
		}

		log.Println("[PLANE][TLV][INTERPRETER] Received valid payload, type [", typ, "]")

		switch typ {

		// it is a frame
		case "F":
			PlaneToTap <- payload
			// someone is announging itself
		case "A":
			announce := string(payload)
			if strings.Count(announce, "|") == 2 {
				fields := strings.Split(announce, "|")
				VSwitch.AddMac(fields[0], fields[1], fields[2])
			}
		case "Q":
			sourcemac := string(payload)
			for alienmac, _ := range VSwitch.SPlane {

				AnnounceAlien(alienmac, sourcemac)
			}

		default:
			log.Println("[PLANE][TLV][INTERPRETER] Unknown type, discarded: [ ", typ, " ]")

		}

	}

}

func DispatchTLV(mytlv []byte, mac string) {

	mac = strings.ToUpper(mac)

	if VSwitch.macIsKnown(mac) {

		DispatchUDP(mytlv, VSwitch.SPlane[mac].EndPoint.String())

	}
}

func DispatchUDP(mytlv []byte, remote string) {

	var neterr error
	var RemoteAddr *net.UDPAddr

	var n int

	RemoteAddr, neterr = net.ResolveUDPAddr("udp", remote)
	if neterr != nil {
		log.Println("[PLANE][TLV][DispatchUDP] Remote address invalid :", neterr.Error())
		return
	}

	n, neterr = VSwitch.Server.WriteToUDP(mytlv, RemoteAddr) // we use the server IP and port as origin.
	if neterr != nil {
		log.Println("[PLANE][TLV][DispatchUDP] Error Writing to [", remote, "]:", neterr.Error())
		return
	} else {
		log.Printf("[PLANE][TLV][DispatchUDP] Written %d BYTES of %d to %s : %t", n, len(mytlv), remote, neterr == nil)
	}

}

func AnnounceLocal(mac string) {

	mac = strings.ToUpper(mac)

	myannounce := VSwitch.HAddr + "|" + VSwitch.Fqdn + "|" + VSwitch.IPAdd

	log.Printf("[PLANE][ANNOUNCELOCAL] Announcing [%s] ", myannounce)

	tlv := tools.CreateTLV("A", []byte(myannounce))

	tlv_enc := crypt.FrameEncrypt([]byte(VSwitch.SwID), tlv)

	DispatchTLV(tlv_enc, mac)

}

// Announces  port which is not ours
func AnnounceAlien(alien_mac string, mac string) {

	mac = strings.ToUpper(mac)
	alien_mac = strings.ToUpper(alien_mac)

	tmp_endpoint := VSwitch.SPlane[alien_mac].EndPoint
	tmp_ethIP := VSwitch.SPlane[alien_mac].EthIP

	myannounce := alien_mac + "|" + tmp_endpoint.String() + "|" + tmp_ethIP.String()

	log.Printf("[PLANE][ANNOUNCEALIEN] Announcing [%s] ", myannounce)

	tlv := tools.CreateTLV("A", []byte(myannounce))

	tlv_enc := crypt.FrameEncrypt([]byte(VSwitch.SwID), tlv)

	DispatchTLV(tlv_enc, mac)

}

func SendQueryToMac(mac string) {

	mac = strings.ToUpper(mac)

	myannounce := VSwitch.HAddr

	tlv := tools.CreateTLV("Q", []byte(VSwitch.HAddr))

	tlv_enc := crypt.FrameEncrypt([]byte(VSwitch.SwID), tlv)

	log.Printf("[PLANE][ANNOUNCEALIEN] Querying %s with our mac %s ", mac, myannounce)

	DispatchTLV(tlv_enc, mac)

}
<commit_msg>Thu Jun 22 22:47:59 CEST 2017<commit_after>package plane

import (
	"V-switch/crypt"
	"V-switch/tools"
	"log"
	"net"
	"strings"
)

func init() {

	go TLVInterpreter()

}

func TLVInterpreter() {

	var my_tlv_enc []byte

	log.Println("[PLANE][TLV][INTERPRETER] Thread starts")

	for {

		my_tlv_enc = <-UdpToPlane
		log.Printf("[PLANE][TLV][INTERPRETER] Read %d bytes from UdpToPlane", len(my_tlv_enc))

		my_tlv := crypt.FrameDecrypt([]byte(VSwitch.SwID), my_tlv_enc)
		if my_tlv == nil {
			log.Printf("[PLANE][TLV][ERROR] Invalid KEY(%d): %s", len(VSwitch.SwID), VSwitch.SwID)
			continue
		} else {
			log.Printf("[PLANE][TLV][INTERPRETER] Decrypted GOB %d BYTES long", len(my_tlv))
		}

		typ, ln, payload := tools.UnPackTLV(my_tlv)

		if ln == 0 {
			log.Printf("[PLANE][TLV][ERROR] Payload was empty, nothing to do")
			continue
		}

		log.Println("[PLANE][TLV][INTERPRETER] Received valid payload, type [", typ, "]")

		switch typ {

		// it is a frame
		case "F":
			PlaneToTap <- payload
			// someone is announging itself
		case "A":
			announce := string(payload)
			if strings.Count(announce, "|") == 2 {
				fields := strings.Split(announce, "|")
				VSwitch.AddMac(fields[0], fields[1], fields[2])
			}
		case "Q":
			sourcemac := string(payload)
			for alienmac, _ := range VSwitch.SPlane {

				AnnounceAlien(alienmac, sourcemac)
			}

		default:
			log.Println("[PLANE][TLV][INTERPRETER] Unknown type, discarded: [ ", typ, " ]")

		}

	}

}

func DispatchTLV(mytlv []byte, mac string) {

	mac = strings.ToUpper(mac)

	if VSwitch.macIsKnown(mac) {

		DispatchUDP(mytlv, VSwitch.SPlane[mac].EndPoint.String())

	}
}

func DispatchUDP(mytlv []byte, remote string) {

	var neterr error
	var RemoteAddr *net.UDPAddr

	var n int

	RemoteAddr, neterr = net.ResolveUDPAddr("udp", remote)
	if neterr != nil {
		log.Println("[PLANE][TLV][DispatchUDP] Remote address invalid :", neterr.Error())
		return
	}

	n, neterr = VSwitch.Server.WriteToUDP(mytlv, RemoteAddr) // we use the server IP and port as origin.
	if neterr != nil {
		log.Println("[PLANE][TLV][DispatchUDP] Error Writing to [", remote, "]:", neterr.Error())
		return
	} else {
		log.Printf("[PLANE][TLV][DispatchUDP] Written %d BYTES of %d to %s : %t", n, len(mytlv), remote, neterr == nil)
	}

}

func AnnounceLocal(mac string) {

	mac = strings.ToUpper(mac)

	myannounce := VSwitch.HAddr + "|" + VSwitch.Fqdn + "|" + VSwitch.IPAdd

	log.Printf("[PLANE][ANNOUNCELOCAL] Announcing [%s] ", myannounce)

	tlv := tools.CreateTLV("A", []byte(myannounce))

	tlv_enc := crypt.FrameEncrypt([]byte(VSwitch.SwID), tlv)

	DispatchTLV(tlv_enc, mac)

}

// Announces  port which is not ours
func AnnounceAlien(alien_mac string, mac string) {

	mac = strings.ToUpper(mac)
	alien_mac = strings.ToUpper(alien_mac)

	tmp_endpoint := VSwitch.SPlane[alien_mac].EndPoint
	tmp_ethIP := VSwitch.SPlane[alien_mac].EthIP

	myannounce := alien_mac + "|" + tmp_endpoint.String() + "|" + tmp_ethIP.String()

	log.Printf("[PLANE][ANNOUNCEALIEN] Announcing [%s] ", myannounce)

	tlv := tools.CreateTLV("A", []byte(myannounce))

	tlv_enc := crypt.FrameEncrypt([]byte(VSwitch.SwID), tlv)

	DispatchTLV(tlv_enc, mac)

}

func SendQueryToMac(mac string) {

	mac = strings.ToUpper(mac)

	myannounce := VSwitch.HAddr

	tlv := tools.CreateTLV("Q", []byte(VSwitch.HAddr))

	tlv_enc := crypt.FrameEncrypt([]byte(VSwitch.SwID), tlv)

	log.Printf("[PLANE][ANNOUNCEALIEN] Querying %s with our mac %s ", mac, myannounce)

	DispatchTLV(tlv_enc, mac)

}
<|endoftext|>


<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"
	"strings"
	"time"
	"unsafe"
)

// For development
var _ = fmt.Println

// 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 {
	return byte(C.play_getvolume())
}

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

// 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))
}

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

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

// Player is an mp3 player.
type Player chan command

const (
	cmdLoad = iota
	cmdPlay
	cmdPause
	cmdStop
	cmdSeek
)

type command struct {
	id     int
	path   string
	offset int
	err    chan error
	// The player writes offsets to this channel periodically, if it is not nil
	offchan chan int
	// on load, this channel is written the size of the mp3 in samples
	size chan int
}

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

	p := make(chan command)

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

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

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

				if offchan != nil {
					close(offchan)
				}

				state = Empty
			}
		}

    // Load a new mp3. Returns true if the state has changed.
		load := func(cmd command) {
			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
			}

			writer = C.play_new_writer(reader)
			if writer == nil {
				close(cmd.size)
				cmd.err <- errors.New("Creating writer failed")
				C.play_delete_reader(reader)
				reader = nil
				return
			}

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

			cmd.err <- nil
			state = Paused
		}

    // Play the loaded mp3.
		play := func() {
			if state != Empty {
				state = Playing
			}
		}

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

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

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

    states[Empty] = func(){
      // Only the load command is not ignored.
      for {
			  select {
        case cmd := <-p:
          switch cmd.id {
          case cmdLoad:
					  load(cmd)
          }
        }

        if state != Empty {
          break
        }
      }
    }

    states[Paused] = func(){
      for {
			  select {
        case cmd := <-p:
          switch cmd.id {
          case cmdLoad:
					  load(cmd)
          case cmdPlay:
            play()
          case cmdStop:
            stop()
          case cmdSeek:
            seek(cmd)
          }
        }

        if state != Paused {
          break
        }
      }
    }

    states[Playing] = func(){
      for {
			  select {
        case cmd := <-p:
          switch cmd.id {
          case cmdLoad:
					  load(cmd)
				  case cmdPause:
					  pause()
          case cmdStop:
            stop()
          case cmdSeek:
            seek(cmd)
          }
          default:
        }

        if state != Playing {
          break
        }

        // 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 offchan != nil {
          if lastofftime.IsZero() || time.Now().Sub(lastofftime) > time.Millisecond*100 {
            if o := int(C.play_offset(reader)); o != lastoff {
              select {
              case offchan <- o:
                lastofftime = time.Now()
              default:
              }
            }
          }
        }
      }
    }

    // Player state machine:
		for {
      states[state]()
		}

	}()

	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, offchan chan int) (size int, err error) {
	ch := make(chan error)
	sizech := make(chan int)

	p <- command{id: cmdLoad, path: filename, err: ch, offchan: offchan, 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() {
	p <- command{id: cmdPlay}
}

// Pause the currently playing file.
func (p Player) Pause() {
	p <- command{id: cmdPause}
}

// Stop the currently playing file if it's playing, and unload the file from the mp3 player.
func (p Player) Stop() {
	p <- command{id: cmdStop}
}

// Seek to the specified sample in the loaded mp3 file.
func (p Player) Seek(offset int) {
	p <- command{id: cmdSeek, offset: offset}
}
<commit_msg>Play nicely with other audio apps that want to use the audio card.<commit_after>// 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"
	"strings"
	"time"
	"unsafe"
)

// For development
var _ = fmt.Println

// 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 {
	return byte(C.play_getvolume())
}

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

// 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))
}

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

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

// Player is an mp3 player.
type Player chan command

const (
	cmdLoad = iota
	cmdPlay
	cmdPause
	cmdStop
	cmdSeek
)

type command struct {
	id     int
	path   string
	offset int
	err    chan error
	// The player writes offsets to this channel periodically, if it is not nil
	offchan chan int
	// on load, this channel is written the size of the mp3 in samples
	size chan int
}

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

	p := make(chan command)

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

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

		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
			}
		}

		// 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()

				if offchan != nil {
					close(offchan)
				}

				state = Empty
			}
		}

		// Load a new mp3. Returns true if the state has changed.
		load := func(cmd command) {
			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))
			offchan = cmd.offchan

			cmd.err <- nil
			state = Paused
		}

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

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

		states[Empty] = func() {
			// Only the load command is not ignored.
			for {
				select {
				case cmd := <-p:
					switch cmd.id {
					case cmdLoad:
						load(cmd)
					}
				}

				if state != Empty {
					break
				}
			}
		}

		states[Paused] = func() {
			for {
				select {
				case cmd := <-p:
					switch cmd.id {
					case cmdLoad:
						load(cmd)
					case cmdPlay:
						play(cmd)
					case cmdStop:
						stop()
					case cmdSeek:
						seek(cmd)
					}
				}

				if state != Paused {
					break
				}
			}
		}

		states[Playing] = func() {
			for {
				select {
				case cmd := <-p:
					switch cmd.id {
					case cmdLoad:
						load(cmd)
					case cmdPause:
						pause()
					case cmdStop:
						stop()
					case cmdSeek:
						seek(cmd)
					}
				default:
				}

				if state != Playing {
					break
				}

				// 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 offchan != nil {
					if lastofftime.IsZero() || time.Now().Sub(lastofftime) > time.Millisecond*100 {
						if o := int(C.play_offset(reader)); o != lastoff {
							select {
							case offchan <- o:
								lastofftime = time.Now()
							default:
							}
						}
					}
				}
			}
		}

		// Player state machine:
		for {
			states[state]()
		}

	}()

	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, offchan chan int) (size int, err error) {
	ch := make(chan error)
	sizech := make(chan int)

	p <- command{id: cmdLoad, path: filename, err: ch, offchan: offchan, 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) {
	ch := make(chan error)

	p <- command{id: cmdPlay, err: ch}

	err = <-ch

	return
}

// Pause the currently playing file.
func (p Player) Pause() {
	p <- command{id: cmdPause}
}

// Stop the currently playing file if it's playing, and unload the file from the mp3 player.
func (p Player) Stop() {
	p <- command{id: cmdStop}
}

// Seek to the specified sample in the loaded mp3 file.
func (p Player) Seek(offset int) {
	p <- command{id: cmdSeek, offset: offset}
}
<|endoftext|>