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

// +build js

package oto

import (
	"encoding/base64"
	"errors"
	"fmt"
	"sync"
	"syscall/js"
)

type driver struct {
	sampleRate      int
	channelNum      int
	bitDepthInBytes int
	nextPos         float64
	tmp             []byte
	bufferSize      int
	context         js.Value
	ready           bool
	callbacks       map[string]js.Func

	// For Audio Worklet
	workletNode   js.Value
	messageArray  js.Value
	transferArray js.Value
	bufs          [][]js.Value
	cond          *sync.Cond
}

type warn struct {
	msg string
}

func (w *warn) Error() string {
	return w.msg
}

const audioBufferSamples = 3200

func tryAudioWorklet(context js.Value, channelNum int) (js.Value, error) {
	if !js.Global().Get("AudioWorkletNode").Truthy() {
		return js.Undefined(), nil
	}

	worklet := context.Get("audioWorklet")
	if !worklet.Truthy() {
		return js.Undefined(), &warn{
			msg: "AudioWorklet is not available due to the insecure context. See https://developer.mozilla.org/en-US/docs/Web/API/AudioWorklet",
		}
	}

	script := `
class EbitenAudioWorkletProcessor extends AudioWorkletProcessor {
  constructor() {
    super();

    this.buffers_ = [[], []];
    this.offsets_ = [0, 0];
    this.offsetsInArray_ = [0, 0];
    this.consumed_ = [];

    this.port.onmessage = (e) => {
      const bufs = e.data;
      for (let ch = 0; ch < bufs.length; ch++) {
        const buf = bufs[ch];
        this.buffers_[ch].push(new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4));
      }
    };
  }

  bufferTotalLength(ch) {
    const sum = this.buffers_[ch].reduce((total, buf) => total + buf.length, 0);
    return sum - this.offsetsInArray_[ch];
  }

  consume(ch, i) {
    while (this.buffers_[ch][0].length <= i - this.offsets_[ch]) {
      this.offsets_[ch] += this.buffers_[ch][0].length;
      this.offsetsInArray_[ch] = 0;
      const buf = this.buffers_[ch].shift();
      this.appendConsumedBuffer(ch, buf);
    }
    this.offsetsInArray_[ch]++;
    return this.buffers_[ch][0][i - this.offsets_[ch]];
  }

  appendConsumedBuffer(ch, buf) {
    let idx = this.consumed_.length - 1;
    if (idx < 0 || this.consumed_[idx][ch]) {
      this.consumed_.push([]);
      idx++;
    }
    this.consumed_[idx][ch] = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
  }

  process(inputs, outputs, parameters) {
    const out = outputs[0];

    if (this.bufferTotalLength(0) < out[0].length) {
      for (let ch = 0; ch < out.length; ch++) {
        for (let i = 0; i < out[ch].length; i++) {
          out[ch][i] = 0;
        }
      }
      return true;
    }

    for (let ch = 0; ch < out.length; ch++) {
      const offset = this.offsets_[ch] + this.offsetsInArray_[ch];
      for (let i = 0; i < out[ch].length; i++) {
        out[ch][i] = this.consume(ch, i + offset);
      }
    }

    for (let bufs of this.consumed_) {
      this.port.postMessage(bufs, bufs.map(buf => buf.buffer));
    }
    this.consumed_ = [];

    return true;
  }
}

registerProcessor('ebiten-audio-worklet-processor', EbitenAudioWorkletProcessor);`
	scriptURL := "data:application/javascript;base64," + base64.StdEncoding.EncodeToString([]byte(script))

	ch := make(chan error)
	worklet.Call("addModule", scriptURL).Call("then", js.FuncOf(func(js.Value, []js.Value) interface{} {
		close(ch)
		return nil
	})).Call("catch", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		err := args[0]
		ch <- fmt.Errorf("oto: error at addModule: %s: %s", err.Get("name").String(), err.Get("message").String())
		close(ch)
		return nil
	}))
	if err := <-ch; err != nil {
		return js.Undefined(), err
	}

	options := js.Global().Get("Object").New()
	arr := js.Global().Get("Array").New()
	arr.Call("push", channelNum)
	options.Set("outputChannelCount", arr)

	node := js.Global().Get("AudioWorkletNode").New(context, "ebiten-audio-worklet-processor", options)
	node.Call("connect", context.Get("destination"))

	return node, nil
}

func newDriver(sampleRate, channelNum, bitDepthInBytes, bufferSize int) (tryWriteCloser, error) {
	class := js.Global().Get("AudioContext")
	if !class.Truthy() {
		class = js.Global().Get("webkitAudioContext")
	}
	if !class.Truthy() {
		return nil, errors.New("oto: audio couldn't be initialized")
	}

	options := js.Global().Get("Object").New()
	options.Set("sampleRate", sampleRate)
	context := class.New(options)

	node, err := tryAudioWorklet(context, channelNum)
	if err != nil {
		w, ok := err.(*warn)
		if !ok {
			return nil, err
		}
		js.Global().Get("console").Call("warn", w.Error())
	}

	bs := bufferSize
	if !node.Truthy() {
		bs = max(bufferSize, audioBufferSamples*channelNum*bitDepthInBytes)
	} else {
		bs = max(bufferSize, 4096)
	}

	p := &driver{
		sampleRate:      sampleRate,
		channelNum:      channelNum,
		bitDepthInBytes: bitDepthInBytes,
		context:         context,
		workletNode:     node,
		bufferSize:      bs,
	}

	if node.Truthy() {
		p.messageArray = js.Global().Get("Array").New(2)
		p.transferArray = js.Global().Get("Array").New(2)
		p.cond = sync.NewCond(&sync.Mutex{})

		s := p.bufferSize / p.channelNum / p.bitDepthInBytes / 2 * 4
		p.bufs = [][]js.Value{
			{
				js.Global().Get("Uint8Array").New(s),
				js.Global().Get("Uint8Array").New(s),
			},
			{
				js.Global().Get("Uint8Array").New(s),
				js.Global().Get("Uint8Array").New(s),
			},
		}

		node.Get("port").Set("onmessage", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
			p.cond.L.Lock()
			defer p.cond.L.Unlock()

			bufs := args[0].Get("data")
			var arr []js.Value
			for i := 0; i < bufs.Length(); i++ {
				arr = append(arr, bufs.Index(i))
			}

			notify := len(p.bufs) == 0
			p.bufs = append(p.bufs, arr)
			if notify {
				p.cond.Signal()
			}

			return nil
		}))
	}

	setCallback := func(event string) js.Func {
		var f js.Func
		f = js.FuncOf(func(this js.Value, arguments []js.Value) interface{} {
			if !p.ready {
				p.context.Call("resume")
				p.ready = true
			}
			js.Global().Get("document").Call("removeEventListener", event, f)
			return nil
		})
		js.Global().Get("document").Call("addEventListener", event, f)
		p.callbacks[event] = f
		return f
	}

	// Browsers require user interaction to start the audio.
	// https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#webaudio
	p.callbacks = map[string]js.Func{}
	setCallback("touchend")
	setCallback("keyup")
	setCallback("mouseup")
	return p, nil
}

func toLR(data []byte) ([]float32, []float32) {
	const max = 1 << 15

	l := make([]float32, len(data)/4)
	r := make([]float32, len(data)/4)
	for i := 0; i < len(data)/4; i++ {
		l[i] = float32(int16(data[4*i])|int16(data[4*i+1])<<8) / max
		r[i] = float32(int16(data[4*i+2])|int16(data[4*i+3])<<8) / max
	}
	return l, r
}

func (p *driver) TryWrite(data []byte) (int, error) {
	if !p.ready {
		return 0, nil
	}

	if p.workletNode.Truthy() {
		p.cond.L.Lock()
		defer p.cond.L.Unlock()

		n := min(len(data), max(0, p.bufferSize-len(p.tmp)))
		p.tmp = append(p.tmp, data[:n]...)

		if len(p.tmp) < p.bufferSize/2 {
			return n, nil
		}

		for len(p.bufs) == 0 {
			p.cond.Wait()
		}

		l, r := toLR(p.tmp[:p.bufferSize/2])
		tl := p.bufs[0][0]
		tr := p.bufs[0][1]
		copyFloat32sToJS(tl, l)
		copyFloat32sToJS(tr, r)
		p.tmp = p.tmp[p.bufferSize/2:]

		bufs := p.messageArray
		bufs.SetIndex(0, tl)
		bufs.SetIndex(1, tr)
		transfers := p.transferArray
		transfers.SetIndex(0, tl.Get("buffer"))
		transfers.SetIndex(1, tr.Get("buffer"))

		p.workletNode.Get("port").Call("postMessage", bufs, transfers)

		p.bufs = p.bufs[1:]

		return n, nil
	}

	n := min(len(data), max(0, p.bufferSize-len(p.tmp)))
	p.tmp = append(p.tmp, data[:n]...)

	c := p.context.Get("currentTime").Float()

	if p.nextPos < c {
		p.nextPos = c
	}

	// It's too early to enqueue a buffer.
	// Highly likely, there are two playing buffers now.
	if c+float64(p.bufferSize/p.bitDepthInBytes/p.channelNum)/float64(p.sampleRate) < p.nextPos {
		return n, nil
	}

	le := audioBufferSamples * p.bitDepthInBytes * p.channelNum
	if len(p.tmp) < le {
		return n, nil
	}

	buf := p.context.Call("createBuffer", p.channelNum, audioBufferSamples, p.sampleRate)
	l, r := toLR(p.tmp[:le])
	tl, freel := float32SliceToTypedArray(l)
	tr, freer := float32SliceToTypedArray(r)
	if buf.Get("copyToChannel").Truthy() {
		buf.Call("copyToChannel", tl, 0, 0)
		buf.Call("copyToChannel", tr, 1, 0)
	} else {
		// copyToChannel is not defined on Safari 11
		buf.Call("getChannelData", 0).Call("set", tl)
		buf.Call("getChannelData", 1).Call("set", tr)
	}
	freel()
	freer()

	s := p.context.Call("createBufferSource")
	s.Set("buffer", buf)
	s.Call("connect", p.context.Get("destination"))
	s.Call("start", p.nextPos)
	p.nextPos += buf.Get("duration").Float()

	p.tmp = p.tmp[le:]
	return n, nil
}

func (p *driver) Close() error {
	for event, f := range p.callbacks {
		// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
		// "Calling removeEventListener() with arguments that do not identify any currently registered EventListener on the EventTarget has no effect."
		js.Global().Get("document").Call("removeEventListener", event, f)
		f.Release()
	}
	p.callbacks = nil
	return nil
}
<commit_msg>js: Replace Call with Invoke to reduce string conversion<commit_after>// Copyright 2015 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build js

package oto

import (
	"encoding/base64"
	"errors"
	"fmt"
	"sync"
	"syscall/js"
)

type driver struct {
	sampleRate      int
	channelNum      int
	bitDepthInBytes int
	nextPos         float64
	tmp             []byte
	bufferSize      int
	context         js.Value
	ready           bool
	callbacks       map[string]js.Func

	// For Audio Worklet
	workletNode     js.Value
	workletNodePost js.Value
	messageArray    js.Value
	transferArray   js.Value
	bufs            [][]js.Value
	cond            *sync.Cond
}

type warn struct {
	msg string
}

func (w *warn) Error() string {
	return w.msg
}

const audioBufferSamples = 3200

func tryAudioWorklet(context js.Value, channelNum int) (js.Value, error) {
	if !js.Global().Get("AudioWorkletNode").Truthy() {
		return js.Undefined(), nil
	}

	worklet := context.Get("audioWorklet")
	if !worklet.Truthy() {
		return js.Undefined(), &warn{
			msg: "AudioWorklet is not available due to the insecure context. See https://developer.mozilla.org/en-US/docs/Web/API/AudioWorklet",
		}
	}

	script := `
class EbitenAudioWorkletProcessor extends AudioWorkletProcessor {
  constructor() {
    super();

    this.buffers_ = [[], []];
    this.offsets_ = [0, 0];
    this.offsetsInArray_ = [0, 0];
    this.consumed_ = [];

    this.port.onmessage = (e) => {
      const bufs = e.data;
      for (let ch = 0; ch < bufs.length; ch++) {
        const buf = bufs[ch];
        this.buffers_[ch].push(new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4));
      }
    };
  }

  bufferTotalLength(ch) {
    const sum = this.buffers_[ch].reduce((total, buf) => total + buf.length, 0);
    return sum - this.offsetsInArray_[ch];
  }

  consume(ch, i) {
    while (this.buffers_[ch][0].length <= i - this.offsets_[ch]) {
      this.offsets_[ch] += this.buffers_[ch][0].length;
      this.offsetsInArray_[ch] = 0;
      const buf = this.buffers_[ch].shift();
      this.appendConsumedBuffer(ch, buf);
    }
    this.offsetsInArray_[ch]++;
    return this.buffers_[ch][0][i - this.offsets_[ch]];
  }

  appendConsumedBuffer(ch, buf) {
    let idx = this.consumed_.length - 1;
    if (idx < 0 || this.consumed_[idx][ch]) {
      this.consumed_.push([]);
      idx++;
    }
    this.consumed_[idx][ch] = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
  }

  process(inputs, outputs, parameters) {
    const out = outputs[0];

    if (this.bufferTotalLength(0) < out[0].length) {
      for (let ch = 0; ch < out.length; ch++) {
        for (let i = 0; i < out[ch].length; i++) {
          out[ch][i] = 0;
        }
      }
      return true;
    }

    for (let ch = 0; ch < out.length; ch++) {
      const offset = this.offsets_[ch] + this.offsetsInArray_[ch];
      for (let i = 0; i < out[ch].length; i++) {
        out[ch][i] = this.consume(ch, i + offset);
      }
    }

    for (let bufs of this.consumed_) {
      this.port.postMessage(bufs, bufs.map(buf => buf.buffer));
    }
    this.consumed_ = [];

    return true;
  }
}

registerProcessor('ebiten-audio-worklet-processor', EbitenAudioWorkletProcessor);`
	scriptURL := "data:application/javascript;base64," + base64.StdEncoding.EncodeToString([]byte(script))

	ch := make(chan error)
	worklet.Call("addModule", scriptURL).Call("then", js.FuncOf(func(js.Value, []js.Value) interface{} {
		close(ch)
		return nil
	})).Call("catch", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		err := args[0]
		ch <- fmt.Errorf("oto: error at addModule: %s: %s", err.Get("name").String(), err.Get("message").String())
		close(ch)
		return nil
	}))
	if err := <-ch; err != nil {
		return js.Undefined(), err
	}

	options := js.Global().Get("Object").New()
	arr := js.Global().Get("Array").New()
	arr.Call("push", channelNum)
	options.Set("outputChannelCount", arr)

	node := js.Global().Get("AudioWorkletNode").New(context, "ebiten-audio-worklet-processor", options)
	node.Call("connect", context.Get("destination"))

	return node, nil
}

func newDriver(sampleRate, channelNum, bitDepthInBytes, bufferSize int) (tryWriteCloser, error) {
	class := js.Global().Get("AudioContext")
	if !class.Truthy() {
		class = js.Global().Get("webkitAudioContext")
	}
	if !class.Truthy() {
		return nil, errors.New("oto: audio couldn't be initialized")
	}

	options := js.Global().Get("Object").New()
	options.Set("sampleRate", sampleRate)
	context := class.New(options)

	node, err := tryAudioWorklet(context, channelNum)
	if err != nil {
		w, ok := err.(*warn)
		if !ok {
			return nil, err
		}
		js.Global().Get("console").Call("warn", w.Error())
	}

	bs := bufferSize
	if !node.Truthy() {
		bs = max(bufferSize, audioBufferSamples*channelNum*bitDepthInBytes)
	} else {
		bs = max(bufferSize, 4096)
	}

	p := &driver{
		sampleRate:      sampleRate,
		channelNum:      channelNum,
		bitDepthInBytes: bitDepthInBytes,
		context:         context,
		workletNode:     node,
		bufferSize:      bs,
	}

	if node.Truthy() {
		port := node.Get("port")
		p.workletNodePost = port.Get("postMessage").Call("bind", port)
		p.messageArray = js.Global().Get("Array").New(2)
		p.transferArray = js.Global().Get("Array").New(2)
		p.cond = sync.NewCond(&sync.Mutex{})

		s := p.bufferSize / p.channelNum / p.bitDepthInBytes / 2 * 4
		p.bufs = [][]js.Value{
			{
				js.Global().Get("Uint8Array").New(s),
				js.Global().Get("Uint8Array").New(s),
			},
			{
				js.Global().Get("Uint8Array").New(s),
				js.Global().Get("Uint8Array").New(s),
			},
		}

		node.Get("port").Set("onmessage", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
			p.cond.L.Lock()
			defer p.cond.L.Unlock()

			bufs := args[0].Get("data")
			var arr []js.Value
			for i := 0; i < bufs.Length(); i++ {
				arr = append(arr, bufs.Index(i))
			}

			notify := len(p.bufs) == 0
			p.bufs = append(p.bufs, arr)
			if notify {
				p.cond.Signal()
			}

			return nil
		}))
	}

	setCallback := func(event string) js.Func {
		var f js.Func
		f = js.FuncOf(func(this js.Value, arguments []js.Value) interface{} {
			if !p.ready {
				p.context.Call("resume")
				p.ready = true
			}
			js.Global().Get("document").Call("removeEventListener", event, f)
			return nil
		})
		js.Global().Get("document").Call("addEventListener", event, f)
		p.callbacks[event] = f
		return f
	}

	// Browsers require user interaction to start the audio.
	// https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#webaudio
	p.callbacks = map[string]js.Func{}
	setCallback("touchend")
	setCallback("keyup")
	setCallback("mouseup")
	return p, nil
}

func toLR(data []byte) ([]float32, []float32) {
	const max = 1 << 15

	l := make([]float32, len(data)/4)
	r := make([]float32, len(data)/4)
	for i := 0; i < len(data)/4; i++ {
		l[i] = float32(int16(data[4*i])|int16(data[4*i+1])<<8) / max
		r[i] = float32(int16(data[4*i+2])|int16(data[4*i+3])<<8) / max
	}
	return l, r
}

func (p *driver) TryWrite(data []byte) (int, error) {
	if !p.ready {
		return 0, nil
	}

	if p.workletNode.Truthy() {
		p.cond.L.Lock()
		defer p.cond.L.Unlock()

		n := min(len(data), max(0, p.bufferSize-len(p.tmp)))
		p.tmp = append(p.tmp, data[:n]...)

		if len(p.tmp) < p.bufferSize/2 {
			return n, nil
		}

		for len(p.bufs) == 0 {
			p.cond.Wait()
		}

		l, r := toLR(p.tmp[:p.bufferSize/2])
		tl := p.bufs[0][0]
		tr := p.bufs[0][1]
		copyFloat32sToJS(tl, l)
		copyFloat32sToJS(tr, r)
		p.tmp = p.tmp[p.bufferSize/2:]

		bufs := p.messageArray
		bufs.SetIndex(0, tl)
		bufs.SetIndex(1, tr)
		transfers := p.transferArray
		transfers.SetIndex(0, tl.Get("buffer"))
		transfers.SetIndex(1, tr.Get("buffer"))

		p.workletNodePost.Invoke(bufs, transfers)

		p.bufs = p.bufs[1:]

		return n, nil
	}

	n := min(len(data), max(0, p.bufferSize-len(p.tmp)))
	p.tmp = append(p.tmp, data[:n]...)

	c := p.context.Get("currentTime").Float()

	if p.nextPos < c {
		p.nextPos = c
	}

	// It's too early to enqueue a buffer.
	// Highly likely, there are two playing buffers now.
	if c+float64(p.bufferSize/p.bitDepthInBytes/p.channelNum)/float64(p.sampleRate) < p.nextPos {
		return n, nil
	}

	le := audioBufferSamples * p.bitDepthInBytes * p.channelNum
	if len(p.tmp) < le {
		return n, nil
	}

	buf := p.context.Call("createBuffer", p.channelNum, audioBufferSamples, p.sampleRate)
	l, r := toLR(p.tmp[:le])
	tl, freel := float32SliceToTypedArray(l)
	tr, freer := float32SliceToTypedArray(r)
	if buf.Get("copyToChannel").Truthy() {
		buf.Call("copyToChannel", tl, 0, 0)
		buf.Call("copyToChannel", tr, 1, 0)
	} else {
		// copyToChannel is not defined on Safari 11
		buf.Call("getChannelData", 0).Call("set", tl)
		buf.Call("getChannelData", 1).Call("set", tr)
	}
	freel()
	freer()

	s := p.context.Call("createBufferSource")
	s.Set("buffer", buf)
	s.Call("connect", p.context.Get("destination"))
	s.Call("start", p.nextPos)
	p.nextPos += buf.Get("duration").Float()

	p.tmp = p.tmp[le:]
	return n, nil
}

func (p *driver) Close() error {
	for event, f := range p.callbacks {
		// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
		// "Calling removeEventListener() with arguments that do not identify any currently registered EventListener on the EventTarget has no effect."
		js.Global().Get("document").Call("removeEventListener", event, f)
		f.Release()
	}
	p.callbacks = nil
	return nil
}
<|endoftext|>


<commit_before>package checkers

import (
  "testing"
  "github.com/cl0ne/go-checkers/point"
)

func TestNewBoard(t *testing.T) {
  board_zero := NewBoard(0)
  if board_zero != nil {
    t.Error("Size: ", board_zero.Size(), ". Board's size shouldn't be zero.")
  }
  board_neg := NewBoard(-1)
  if board_neg != nil {
    t.Error("Size: ", board_neg.Size(), ". Board's size shouldn't be negative.")
  }
}

func TestIsEmpty(t *testing.T) {
  board := NewBoard(8)
  checker := newChecker(3, 3, true)
  point_check := point.Point{ X:3, Y:3 }
  board.placeChecker(3, 3, &checker)
  if board.IsEmpty(3, 3) {
    t.Error("Cell at ", point_check.String(), " is empty.")
  }
  point_check.X = 4
  point_check.Y = 6
  if !board.IsEmpty(4, 6) {
    t.Error("Cell at ", point_check.String(), " is not empty.")
  }
}

func TestContainsPos(t *testing.T) {
  board := NewBoard(6)
  point := point.Point{ X:7, Y:8 }
  ok_pos := board.ContainsPos(7, 8)
  if ok_pos {
    t.Error("Invalid position at ", point.String(), ". Out of range of board.")
  }
  point.X, point.Y = 3, 2
  ok_pos = board.ContainsPos(3, 2)
  if !ok_pos {
    t.Error("Valid position at ", point.String(), ". In range of board.")
  }
}

func TestIsBlackSquare(t *testing.T) {
  board := NewBoard(8)
  point_black := point.Point{ X:4, Y:0 }
  point_white := point.Point{ X:2, Y:1 }
  if !board.IsBlackSquare(point_black) {
    t.Error("Cell at ", point_black.String(), " must be black.")
  }
  if board.IsBlackSquare(point_white) {
    t.Error("Cell at ", point_white.String(), " must be white.")
  }
}

func TestIsWhiteSquare(t *testing.T) {
  board := NewBoard(8)
  point_white := point.Point{ X:2, Y:1 }
  point_black := point.Point{ X:4, Y:0 }
  if !board.IsWhiteSquare(point_white) {
    t.Error("Cell at ", point_white.String(), " must be white.")
  }
  if board.IsWhiteSquare(point_black) {
    t.Error("Cell at ", point_black.String(), " must be black.")
  }
}

func TestTakeChecker(t *testing.T) {
  board := NewBoard(6)
  checker := newChecker(2, 2, true)
  point := point.Point{ X:2, Y:2 }
  board.placeChecker(2, 2, &checker)
  c := board.takeChecker(2, 2)
  if c == nil {
    t.Error("Cell at ", point.String(), " was empty. Couldn't take checker.")
  }
}

func TestGetChecker(t *testing.T) {
  board := NewBoard(8)
  checker := newChecker(2, 4, false)
  point := point.Point { X:2, Y:4 }
  board.placeChecker(2, 4, &checker)
  c := board.GetChecker(2, 4)
  if c == nil {
    t.Error("Checker at ", point.String(), " is abcent.")
  }
  c = board.GetChecker(5, 5)
  point.X, point.Y = 5, 5
  if c != nil {
    t.Error("Checker at ", point.String(), " should be abcent.")
  }
}

func TestMoveChecker(t *testing.T) {
  board := NewBoard(8)
  point_from := point.Point{ X:3, Y:4 }
  point_to := point.Point{ X:4, Y:3 }
  board.moveChecker(point_from, point_to)
  c := board.GetChecker(3, 4)
  if c != nil {
    t.Error("Checker wasn't moved from ", point_from.String(), " to ", point_to.String())
  }
}
<commit_msg>Cleanup board tests, allow zero-sized board creation<commit_after>package checkers

import (
	"testing"

	"github.com/cl0ne/go-checkers/point"
)

func TestNewBoard(t *testing.T) {
	uselessBoard := NewBoard(0)
	if uselessBoard == nil {
		t.Error("Zero-sized boards aren't very useful, but got nil.")
	}
	if uselessBoard.Size() != 0 {
		t.Error("Zero-sized board after creation got size of", uselessBoard.Size())
	}

	invalidBoard := NewBoard(-1)
	if invalidBoard != nil {
		t.Error("Boards of negative size not allowed, got size of", invalidBoard.Size())
	}

	board8x8 := NewBoard(8)
	if board8x8 == nil {
		t.Error("Can't create 8x8 board got nil.")
	}
	if board8x8.Size() != 8 {
		t.Error("Size of created 8x8 board is", board8x8.Size())
	}
}

func TestBoardOperations(t *testing.T) {
	board := NewBoard(8)
	t.Run("IsEmpty", func(t *testing.T) {
		checker := newChecker(3, 3, true)
		p := point.Point{X: 3, Y: 3}
		board.placeChecker(p.X, p.Y, &checker)
		if board.IsEmpty(p.X, p.Y) {
			t.Error("Cell", p, "shouldn't be empty.")
		}
		p.X, p.Y = 4, 6
		if !board.IsEmpty(p.X, p.Y) {
			t.Error("Cell", p, "should be empty.")
		}
	})

	t.Run("ContainsPos", func(t *testing.T) {
		point := point.Point{X: 7, Y: 8}
		contains := board.ContainsPos(7, 8)
		if contains {
			t.Error("Cell", point, "is outside of the board.")
		}
		point.X, point.Y = 3, 2
		contains = board.ContainsPos(3, 2)
		if !contains {
			t.Error("Cell", point, "is within the board.")
		}
	})

	whiteCell := point.Point{X: 2, Y: 1}
	blackCell := point.Point{X: 4, Y: 0}

	t.Run("IsBlackSquare", func(t *testing.T) {
		if !board.IsBlackSquare(blackCell) {
			t.Error("Cell", blackCell, "must be black.")
		}
		if board.IsBlackSquare(whiteCell) {
			t.Error("Cell", whiteCell, "must be white.")
		}
	})

	t.Run("IsWhiteSquare", func(t *testing.T) {
		if !board.IsWhiteSquare(whiteCell) {
			t.Error("Cell", whiteCell, "must be white.")
		}
		if board.IsWhiteSquare(blackCell) {
			t.Error("Cell", blackCell, "must be black.")
		}
	})

	t.Run("TakeChecker", func(t *testing.T) {
		p := point.Point{X: 2, Y: 2}
		checker := newChecker(p.X, p.Y, true)
		board.placeChecker(p.X, p.Y, &checker)
		c := board.takeChecker(p.X, p.Y)
		if c == nil {
			t.Error("Can't take checker at", p, ": cell is empty.")
		}
	})

	t.Run("GetChecker", func(t *testing.T) {
		p := point.Point{X: 2, Y: 4}
		checker := newChecker(p.X, p.Y, false)
		board.placeChecker(p.X, p.Y, &checker)
		c := board.GetChecker(p.X, p.Y)
		if c == nil {
			t.Error("Checker at", p, "is missing.")
		}
		p.X, p.Y = 5, 5
		c = board.GetChecker(p.X, p.Y)
		if c != nil {
			t.Error("Cell", p, "should be empty")
		}
	})

	t.Run("MoveChecker", func(t *testing.T) {
		from := point.Point{X: 3, Y: 4}
		to := point.Point{X: 4, Y: 3}
		board.moveChecker(from, to)
		c := board.GetChecker(3, 4)
		if c != nil {
			t.Error("Checker wasn't moved from", from, "to", to)
		}
	})
}
<|endoftext|>


<commit_before>package main

import (
	"compress/gzip"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/gorilla/mux"
)

// Generic structure which should fit any notification to be sent the user
type gnDiffDatum []*gnDiffData

type gnDiffData struct {
	Repo    link       `json:"repo"`
	Changed bool       `json:"changed"`
	Data    []diffData `json:"data"`
	MadeFor string     `json:"made_for"`
}

type diffData struct {
	Title      link   `json:"title"`
	Error      string `json:"error"`
	ChangeType string `json:"change_type"`
	Changed    bool   `json:"changed"`
	Changes    []link `json:"changes"`
}

type link struct {
	Text  string `json:"text"`
	Href  string `json:"href"`
	Title string `json:"title"`
}

func listAllDiffs(w http.ResponseWriter, r *http.Request) {

	hc := &httpContext{w, r}
	// Redirect user if not logged in
	redirected := hc.redirectUnlessLoggedIn()
	if redirected {
		return
	}

	userInfo := hc.userLoggedinInfo()
	configFile := userInfo.getConfigFile()

	conf := new(Setting)
	conf.load(configFile)
	files := (&gnDiffDatum{}).ListUserChanges(conf)

	page := newPage(hc, "Changes for "+conf.Auth.UserName, "Recent Changes", files, nil)
	displayPage(w, "changes_list", page)

	// TODO render in Page
}

func renderThisDiff(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	entry := vars["diffentry"]
	if entry == "" {
		listAllDiffs(w, r)
		return
	}

	hc := &httpContext{w, r}
	// Redirect user if not logged in
	redirected := hc.redirectUnlessLoggedIn()
	if redirected {
		return
	}

	userInfo := hc.userLoggedinInfo()
	configFile := userInfo.getConfigFile()

	conf := new(Setting)
	conf.load(configFile)

	diffs := &gnDiffDatum{}
	if err := diffs.load(entry, conf); err != nil {
		http.NotFound(w, r)
		return
	}

	intFilename, _ := strconv.ParseInt(entry, 10, 64)
	reference := parseUnixTimeToString(intFilename, "02 Jan 2006 | 15 Hrs", conf.User.TimeZoneName)

	title := "New Updates from " + reference
	page := newPage(hc, "Changes for "+entry, title, diffs, nil)
	displayPage(w, "changes", page)
}

// check if atleast one of the diffs has changed
func (r *gnDiffDatum) hasChanges() bool {
	// check if eligible to send email
	for _, a := range *r {
		if a.Changed {
			return true
		}
	}
	return false
}

type changeDetail struct {
	Display   string
	Reference int64
}

type ByInt []*changeDetail

func (a ByInt) Len() int           { return len(a) }
func (a ByInt) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByInt) Less(i, j int) bool { return a[i].Display < a[j].Display }

// at a user/repo level
func (r *gnDiffDatum) ListUserChanges(conf *Setting) []*changeDetail {
	dir := strings.Join([]string{conf.Auth.getConfigDir(), "diff"}, string(os.PathSeparator))

	fis, err := ioutil.ReadDir(dir)
	if err != nil {
		log.Print(err)
		return []*changeDetail{}
	}
	files := make([]*changeDetail, 0, len(fis))
	for _, fi := range fis {
		fileName := strings.TrimRight(fi.Name(), ".json")
		intFilename, _ := strconv.ParseInt(fileName, 10, 64)
		reference := parseUnixTimeToString(intFilename, "02 Jan 2006 | 15 Hrs", conf.User.TimeZoneName)
		files = append(files, &changeDetail{reference, intFilename})
	}
	sort.Sort(sort.Reverse(ByInt(files)))

	return files
}

func parseUnixTimeToString(i int64, format string, tz string) string {
	loc, _ := time.LoadLocation(tz)
	ti := time.Unix(i, 0).In(loc)

	return ti.Format(format)
}

func (r *gnDiffDatum) save(conf *Setting) (string, error) {
	var out []byte
	var err error

	t := time.Now()

	dir := strings.Join([]string{conf.Auth.getConfigDir(), "diff"}, string(os.PathSeparator))
	os.MkdirAll(dir, 0700)

	filenamePrefix := fmt.Sprintf("%d", t.Unix())
	fileName := strings.Join([]string{conf.Auth.getConfigDir(), "diff", filenamePrefix + ".json"}, string(os.PathSeparator))

	if out, err = json.Marshal(r); err != nil {
		fmt.Println("Error saving diff ", err)
		return "", err
	}

	if err = saveCompressedFile(fileName, out); err != nil {
		return "", err
	}

	return filenamePrefix, nil
}

func (r *gnDiffDatum) load(fileNamePrefix string, conf *Setting) error {
	fileName := strings.Join([]string{conf.Auth.getConfigDir(), "diff", fileNamePrefix + ".json"}, string(os.PathSeparator))
	data, err := readCompressedFile(fileName)
	if err != nil {
		return err
	}
	json.Unmarshal(data, &r)
	return nil
}

func saveCompressedFile(fileName string, data []byte) error {
	file, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}
	defer file.Close()

	fileWriter := gzip.NewWriter(file)
	fileWriter.Write(data)
	fileWriter.Close()

	return nil
}

func readCompressedFile(fileName string) ([]byte, error) {
	file, err := os.OpenFile(fileName, os.O_RDONLY, 0600)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	fileReader, err := gzip.NewReader(file)
	if err != nil {
		return nil, err
	}
	defer fileReader.Close()

	data, err := ioutil.ReadAll(fileReader)
	if err != nil {
		return nil, err
	}

	return data, nil
}
<commit_msg>sort by time instead of filename<commit_after>package main

import (
	"compress/gzip"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/gorilla/mux"
)

// Generic structure which should fit any notification to be sent the user
type gnDiffDatum []*gnDiffData

type gnDiffData struct {
	Repo    link       `json:"repo"`
	Changed bool       `json:"changed"`
	Data    []diffData `json:"data"`
	MadeFor string     `json:"made_for"`
}

type diffData struct {
	Title      link   `json:"title"`
	Error      string `json:"error"`
	ChangeType string `json:"change_type"`
	Changed    bool   `json:"changed"`
	Changes    []link `json:"changes"`
}

type link struct {
	Text  string `json:"text"`
	Href  string `json:"href"`
	Title string `json:"title"`
}

func listAllDiffs(w http.ResponseWriter, r *http.Request) {

	hc := &httpContext{w, r}
	// Redirect user if not logged in
	redirected := hc.redirectUnlessLoggedIn()
	if redirected {
		return
	}

	userInfo := hc.userLoggedinInfo()
	configFile := userInfo.getConfigFile()

	conf := new(Setting)
	conf.load(configFile)
	files := (&gnDiffDatum{}).ListUserChanges(conf)

	page := newPage(hc, "Changes for "+conf.Auth.UserName, "Recent Changes", files, nil)
	displayPage(w, "changes_list", page)

	// TODO render in Page
}

func renderThisDiff(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	entry := vars["diffentry"]
	if entry == "" {
		listAllDiffs(w, r)
		return
	}

	hc := &httpContext{w, r}
	// Redirect user if not logged in
	redirected := hc.redirectUnlessLoggedIn()
	if redirected {
		return
	}

	userInfo := hc.userLoggedinInfo()
	configFile := userInfo.getConfigFile()

	conf := new(Setting)
	conf.load(configFile)

	diffs := &gnDiffDatum{}
	if err := diffs.load(entry, conf); err != nil {
		http.NotFound(w, r)
		return
	}

	intFilename, _ := strconv.ParseInt(entry, 10, 64)
	reference := parseUnixTimeToString(intFilename, "02 Jan 2006 | 15 Hrs", conf.User.TimeZoneName)

	title := "New Updates from " + reference
	page := newPage(hc, "Changes for "+entry, title, diffs, nil)
	displayPage(w, "changes", page)
}

// check if atleast one of the diffs has changed
func (r *gnDiffDatum) hasChanges() bool {
	// check if eligible to send email
	for _, a := range *r {
		if a.Changed {
			return true
		}
	}
	return false
}

type changeDetail struct {
	Display   string
	Reference int64
}

type ByInt []*changeDetail

func (a ByInt) Len() int           { return len(a) }
func (a ByInt) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByInt) Less(i, j int) bool { return a[i].Reference < a[j].Reference }

// at a user/repo level
func (r *gnDiffDatum) ListUserChanges(conf *Setting) []*changeDetail {
	dir := strings.Join([]string{conf.Auth.getConfigDir(), "diff"}, string(os.PathSeparator))

	fis, err := ioutil.ReadDir(dir)
	if err != nil {
		log.Print(err)
		return []*changeDetail{}
	}
	files := make([]*changeDetail, 0, len(fis))
	for _, fi := range fis {
		fileName := strings.TrimRight(fi.Name(), ".json")
		intFilename, _ := strconv.ParseInt(fileName, 10, 64)
		reference := parseUnixTimeToString(intFilename, "02 Jan 2006 | 15 Hrs", conf.User.TimeZoneName)
		files = append(files, &changeDetail{reference, intFilename})
	}
	sort.Sort(sort.Reverse(ByInt(files)))

	return files
}

func parseUnixTimeToString(i int64, format string, tz string) string {
	loc, _ := time.LoadLocation(tz)
	ti := time.Unix(i, 0).In(loc)

	return ti.Format(format)
}

func (r *gnDiffDatum) save(conf *Setting) (string, error) {
	var out []byte
	var err error

	t := time.Now()

	dir := strings.Join([]string{conf.Auth.getConfigDir(), "diff"}, string(os.PathSeparator))
	os.MkdirAll(dir, 0700)

	filenamePrefix := fmt.Sprintf("%d", t.Unix())
	fileName := strings.Join([]string{conf.Auth.getConfigDir(), "diff", filenamePrefix + ".json"}, string(os.PathSeparator))

	if out, err = json.Marshal(r); err != nil {
		fmt.Println("Error saving diff ", err)
		return "", err
	}

	if err = saveCompressedFile(fileName, out); err != nil {
		return "", err
	}

	return filenamePrefix, nil
}

func (r *gnDiffDatum) load(fileNamePrefix string, conf *Setting) error {
	fileName := strings.Join([]string{conf.Auth.getConfigDir(), "diff", fileNamePrefix + ".json"}, string(os.PathSeparator))
	data, err := readCompressedFile(fileName)
	if err != nil {
		return err
	}
	json.Unmarshal(data, &r)
	return nil
}

func saveCompressedFile(fileName string, data []byte) error {
	file, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}
	defer file.Close()

	fileWriter := gzip.NewWriter(file)
	fileWriter.Write(data)
	fileWriter.Close()

	return nil
}

func readCompressedFile(fileName string) ([]byte, error) {
	file, err := os.OpenFile(fileName, os.O_RDONLY, 0600)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	fileReader, err := gzip.NewReader(file)
	if err != nil {
		return nil, err
	}
	defer fileReader.Close()

	data, err := ioutil.ReadAll(fileReader)
	if err != nil {
		return nil, err
	}

	return data, nil
}
<|endoftext|>


<commit_before>package signalfx

import (
	"math"
	"sync/atomic"
	"time"

	"github.com/zvelo/go-signalfx/sfxproto"
)

type Counter struct {
	Metric     string
	Dimensions map[string]string
	value      uint64
}

var counterType = sfxproto.MetricType_COUNTER

func (c *Counter) Inc(delta uint64) uint64 {
	return atomic.AddUint64(&c.value, delta)
}

func (c *Counter) dataPoint() *dataPoint {
	value := atomic.LoadUint64(&c.value)
	if value > math.MaxInt64 {
		return nil
	}
	return &dataPoint{
		Metric:     c.Metric,
		Timestamp:  time.Now(),
		Type:       CounterType,
		Dimensions: c.Dimensions,
		Value:      int64(value),
	}
}

func (c *Counter) reset(v int64) {
	if v < 0 {
		panic("negative counter should be impossible")
	}
	vv := uint64(v)
	// this is the canonical way to subtract a value atomically;
	// c.f. godoc atomic
	atomic.AddUint64(&c.value, ^(vv - 1))
}

type SetterCounter struct {
	Metric     string
	Dimensions map[string]string
	Value      Subtracter
}

func (c *SetterCounter) dataPoint() *dataPoint {
	gottenValue, err := c.Value.Get()
	if err != nil {
		return nil
	}
	value, err := toInt64(gottenValue)
	if err != nil {
		return nil
	}
	return &dataPoint{
		Metric:     c.Metric,
		Timestamp:  time.Now(),
		Type:       CounterType,
		Dimensions: c.Dimensions,
		Value:      value,
	}
}

func (c *SetterCounter) reset(v int64) {
	if v < 0 {
		panic("negative counter should be impossible")
	}
	c.Value.Subtract(v)
}
<commit_msg>Rename SetterCounter to WrappedCounter<commit_after>package signalfx

import (
	"math"
	"sync/atomic"
	"time"

	"github.com/zvelo/go-signalfx/sfxproto"
)

type Counter struct {
	Metric     string
	Dimensions map[string]string
	value      uint64
}

var counterType = sfxproto.MetricType_COUNTER

func (c *Counter) Inc(delta uint64) uint64 {
	return atomic.AddUint64(&c.value, delta)
}

func (c *Counter) dataPoint() *dataPoint {
	value := atomic.LoadUint64(&c.value)
	if value > math.MaxInt64 {
		return nil
	}
	return &dataPoint{
		Metric:     c.Metric,
		Timestamp:  time.Now(),
		Type:       CounterType,
		Dimensions: c.Dimensions,
		Value:      int64(value),
	}
}

func (c *Counter) reset(v int64) {
	if v < 0 {
		panic("negative counter should be impossible")
	}
	vv := uint64(v)
	// this is the canonical way to subtract a value atomically;
	// c.f. godoc atomic
	atomic.AddUint64(&c.value, ^(vv - 1))
}

type WrappedCounter struct {
	Metric     string
	Dimensions map[string]string
	Value      Subtracter
}

func (c *WrappedCounter) dataPoint() *dataPoint {
	gottenValue, err := c.Value.Get()
	if err != nil {
		return nil
	}
	value, err := toInt64(gottenValue)
	if err != nil {
		return nil
	}
	return &dataPoint{
		Metric:     c.Metric,
		Timestamp:  time.Now(),
		Type:       CounterType,
		Dimensions: c.Dimensions,
		Value:      value,
	}
}

func (c *WrappedCounter) reset(v int64) {
	if v < 0 {
		panic("negative counter should be impossible")
	}
	c.Value.Subtract(v)
}
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 PayinTech
 *
 * 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 caddy_datadog

import (
	"container/list"
	"github.com/DataDog/datadog-go/statsd"
	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
	"reflect"
	"regexp"
	"strings"
	"sync/atomic"
	"time"
)

const (
	STATSD_SERVER    = "127.0.0.1:8125"
	STATSD_RATE      = 1.0
	STATSD_NAMESPACE = "caddy."
	TICKER_INTERVAL  = 10.0
)

type DatadogModule struct {
	Next    httpserver.Handler
	Metrics *DatadogMetrics
}

type DatadogMetrics struct {
	// Known Go bug: https://golang.org/pkg/sync/atomic/#pkg-note-BUG
	// must be first field for 64 bit alignment
	// on x86 and arm.
	index            int64
	response1xxCount uint64
	response2xxCount uint64
	response3xxCount uint64
	response4xxCount uint64
	response5xxCount uint64
	responseSize     uint64
	responseTime     uint64
	area             []string
}

// Handle to collected metrics.
var glDatadogMetrics list.List

// Handle to the statsd client.
var glStatsdClient *statsd.Client = nil

// Handle to the plugin ticker
var glTicker *time.Ticker

// Try to retrieve metrics for the given area. If no existing
// metrics was found, a newly created metrics structure will
// be returned.
func getOrCreateMetrics(area []string) *DatadogMetrics {
	if area != nil && len(area) == 0 {
		return getOrCreateMetrics(nil)
	}
	for e := glDatadogMetrics.Front(); e != nil; e = e.Next() {
		if reflect.DeepEqual(e.Value.(*DatadogMetrics).area, area) {
			return e.Value.(*DatadogMetrics)
		}
	}
	newMetrics := &DatadogMetrics{
		area: area,
	}
	glDatadogMetrics.PushBack(newMetrics)
	return newMetrics
}

// Reconfigure statsd client with the last know configuration.
// If statsd is already configured; the current connection will
// be closed and a statsd client will be reconfigured.
func reconfigureStatsdClient(server string, namespace string, tags []string) error {
	var err = error(nil)
	if server != "" {
		if glStatsdClient != nil {
			glStatsdClient.Close()
		}
		glStatsdClient, err = statsd.New(server)
	}
	if tags == nil || len(tags) == 0 {
		glStatsdClient.Tags = nil
	} else {
		glStatsdClient.Tags = tags
	}
	if namespace == "" {
		glStatsdClient.Namespace = STATSD_NAMESPACE
	} else {
		glStatsdClient.Namespace = namespace
	}
	return err
}

// Initialize the Datadog module by parsing the current Caddy
// configuration file.
func initializeDatadogHQ(controller *caddy.Controller) error {
	hostnameRegex := regexp.MustCompile(`^[0-9a-zA-Z\\._-]{1,60}:[0-9]{1,5}$`)
	tagRegex := regexp.MustCompile(`^[a-zA-Z0-9:-]{1,25}$`)
	namespaceRegex := regexp.MustCompile(`^[a-zA-Z0-9\\.\\-_]{2,25}$`)

	if glStatsdClient == nil {
		reconfigureStatsdClient(STATSD_SERVER, STATSD_NAMESPACE, nil)
	}

	currentDatadogModule := DatadogModule{}
	for controller.Next() {
		currentDatadogModule.Metrics = getOrCreateMetrics(controller.RemainingArgs())

		var statsdServer, statsdNamespace, statsdTags = "", glStatsdClient.Namespace, glStatsdClient.Tags
		for controller.NextBlock() {
			switch controller.Val() {
			case "statsd":
				var args = controller.RemainingArgs()
				if len(args) > 0 {
					statsdServer = args[0]
				} else {
					statsdServer = STATSD_SERVER
				}
				if !hostnameRegex.MatchString(statsdServer) {
					return controller.Err("datadog: not a valid address. Must be <hostname>:<port>")
				}
			case "tags":
				statsdTags = controller.RemainingArgs()
				for idx, tag := range statsdTags {
					if !tagRegex.MatchString(tag) {
						return controller.Errf("datadog: tag #%d is not valid", idx+1)
					}
				}
			case "namespace":
				var args = controller.RemainingArgs()
				if len(args) > 0 {
					statsdNamespace = args[0]
				} else {
					statsdNamespace = STATSD_NAMESPACE
				}
				if !strings.HasSuffix(statsdNamespace, ".") {
					statsdNamespace += "."
				}
				if !namespaceRegex.MatchString(statsdNamespace) ||
					strings.Contains(statsdNamespace, "..") ||
					strings.HasPrefix(statsdNamespace, ".") {
					return controller.Err("datadog: not a valid namespace")
				}
			}
		}
		reconfigureStatsdClient(statsdServer, statsdNamespace, statsdTags)
	}
	httpserver.
		GetConfig(controller).
		AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
			currentDatadogModule.Next = next
			return currentDatadogModule
		})

	if glTicker == nil {
		glTicker = time.NewTicker(time.Second * TICKER_INTERVAL)
		quit := make(chan struct{})
		go func() {
			for {
				select {
				case <-glTicker.C:
					for e := glDatadogMetrics.Front(); e != nil; e = e.Next() {
						var currentMetrics = e.Value.(*DatadogMetrics)

						totalResponsesPeriod := currentMetrics.response1xxCount +
							currentMetrics.response2xxCount +
							currentMetrics.response3xxCount +
							currentMetrics.response4xxCount +
							currentMetrics.response5xxCount

						glStatsdClient.Gauge(
							"requests.per_s",
							float64(totalResponsesPeriod)/TICKER_INTERVAL,
							currentMetrics.area,
							STATSD_RATE,
						)
						glStatsdClient.Incr(
							"responses.1xx",
							currentMetrics.area,
							float64(currentMetrics.response1xxCount),
						)
						glStatsdClient.Gauge(
							"responses.2xx",
							float64(currentMetrics.response2xxCount),
							currentMetrics.area,
							STATSD_RATE,
						)
						glStatsdClient.Gauge(
							"responses.3xx",
							float64(currentMetrics.response3xxCount),
							currentMetrics.area,
							STATSD_RATE,
						)
						glStatsdClient.Gauge(
							"responses.4xx",
							float64(currentMetrics.response4xxCount),
							currentMetrics.area,
							STATSD_RATE,
						)
						glStatsdClient.Gauge(
							"responses.5xx",
							float64(currentMetrics.response5xxCount),
							currentMetrics.area,
							STATSD_RATE,
						)
						glStatsdClient.Gauge(
							"responses.size_bytes",
							float64(currentMetrics.responseSize),
							currentMetrics.area,
							STATSD_RATE,
						)
						if totalResponsesPeriod == 0 { // Avoid div. per zero
							totalResponsesPeriod = 1
						}
						glStatsdClient.Gauge(
							"responses.duration",
							float64(currentMetrics.responseTime)/float64(totalResponsesPeriod),
							currentMetrics.area,
							STATSD_RATE,
						)

						atomic.AddUint64(&currentMetrics.response1xxCount, -currentMetrics.response1xxCount)
						atomic.AddUint64(&currentMetrics.response2xxCount, -currentMetrics.response2xxCount)
						atomic.AddUint64(&currentMetrics.response3xxCount, -currentMetrics.response3xxCount)
						atomic.AddUint64(&currentMetrics.response4xxCount, -currentMetrics.response4xxCount)
						atomic.AddUint64(&currentMetrics.response5xxCount, -currentMetrics.response5xxCount)
						atomic.AddUint64(&currentMetrics.responseSize, -currentMetrics.responseSize)
						currentMetrics.responseTime = 0
					}
				case <-quit:
					glTicker.Stop()
					return
				}
			}
		}()
	}

	return nil
}
<commit_msg>Allow longer tags and with underscores (#5)<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 PayinTech
 *
 * 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 caddy_datadog

import (
	"container/list"
	"github.com/DataDog/datadog-go/statsd"
	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
	"reflect"
	"regexp"
	"strings"
	"sync/atomic"
	"time"
)

const (
	STATSD_SERVER    = "127.0.0.1:8125"
	STATSD_RATE      = 1.0
	STATSD_NAMESPACE = "caddy."
	TICKER_INTERVAL  = 10.0
)

type DatadogModule struct {
	Next    httpserver.Handler
	Metrics *DatadogMetrics
}

type DatadogMetrics struct {
	// Known Go bug: https://golang.org/pkg/sync/atomic/#pkg-note-BUG
	// must be first field for 64 bit alignment
	// on x86 and arm.
	index            int64
	response1xxCount uint64
	response2xxCount uint64
	response3xxCount uint64
	response4xxCount uint64
	response5xxCount uint64
	responseSize     uint64
	responseTime     uint64
	area             []string
}

// Handle to collected metrics.
var glDatadogMetrics list.List

// Handle to the statsd client.
var glStatsdClient *statsd.Client = nil

// Handle to the plugin ticker
var glTicker *time.Ticker

// Try to retrieve metrics for the given area. If no existing
// metrics was found, a newly created metrics structure will
// be returned.
func getOrCreateMetrics(area []string) *DatadogMetrics {
	if area != nil && len(area) == 0 {
		return getOrCreateMetrics(nil)
	}
	for e := glDatadogMetrics.Front(); e != nil; e = e.Next() {
		if reflect.DeepEqual(e.Value.(*DatadogMetrics).area, area) {
			return e.Value.(*DatadogMetrics)
		}
	}
	newMetrics := &DatadogMetrics{
		area: area,
	}
	glDatadogMetrics.PushBack(newMetrics)
	return newMetrics
}

// Reconfigure statsd client with the last know configuration.
// If statsd is already configured; the current connection will
// be closed and a statsd client will be reconfigured.
func reconfigureStatsdClient(server string, namespace string, tags []string) error {
	var err = error(nil)
	if server != "" {
		if glStatsdClient != nil {
			glStatsdClient.Close()
		}
		glStatsdClient, err = statsd.New(server)
	}
	if tags == nil || len(tags) == 0 {
		glStatsdClient.Tags = nil
	} else {
		glStatsdClient.Tags = tags
	}
	if namespace == "" {
		glStatsdClient.Namespace = STATSD_NAMESPACE
	} else {
		glStatsdClient.Namespace = namespace
	}
	return err
}

// Initialize the Datadog module by parsing the current Caddy
// configuration file.
func initializeDatadogHQ(controller *caddy.Controller) error {
	hostnameRegex := regexp.MustCompile(`^[0-9a-zA-Z\\._-]{1,100}:[0-9]{1,5}$`)
	tagRegex := regexp.MustCompile(`^[a-zA-Z0-9:_-]{1,100}$`)
	namespaceRegex := regexp.MustCompile(`^[a-zA-Z0-9\\.\\-_]{2,25}$`)

	if glStatsdClient == nil {
		reconfigureStatsdClient(STATSD_SERVER, STATSD_NAMESPACE, nil)
	}

	currentDatadogModule := DatadogModule{}
	for controller.Next() {
		currentDatadogModule.Metrics = getOrCreateMetrics(controller.RemainingArgs())

		var statsdServer, statsdNamespace, statsdTags = "", glStatsdClient.Namespace, glStatsdClient.Tags
		for controller.NextBlock() {
			switch controller.Val() {
			case "statsd":
				var args = controller.RemainingArgs()
				if len(args) > 0 {
					statsdServer = args[0]
				} else {
					statsdServer = STATSD_SERVER
				}
				if !hostnameRegex.MatchString(statsdServer) {
					return controller.Err("datadog: not a valid address. Must be <hostname>:<port>")
				}
			case "tags":
				statsdTags = controller.RemainingArgs()
				for idx, tag := range statsdTags {
					if !tagRegex.MatchString(tag) {
						return controller.Errf("datadog: tag #%d is not valid", idx+1)
					}
				}
			case "namespace":
				var args = controller.RemainingArgs()
				if len(args) > 0 {
					statsdNamespace = args[0]
				} else {
					statsdNamespace = STATSD_NAMESPACE
				}
				if !strings.HasSuffix(statsdNamespace, ".") {
					statsdNamespace += "."
				}
				if !namespaceRegex.MatchString(statsdNamespace) ||
					strings.Contains(statsdNamespace, "..") ||
					strings.HasPrefix(statsdNamespace, ".") {
					return controller.Err("datadog: not a valid namespace")
				}
			}
		}
		reconfigureStatsdClient(statsdServer, statsdNamespace, statsdTags)
	}
	httpserver.
		GetConfig(controller).
		AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
			currentDatadogModule.Next = next
			return currentDatadogModule
		})

	if glTicker == nil {
		glTicker = time.NewTicker(time.Second * TICKER_INTERVAL)
		quit := make(chan struct{})
		go func() {
			for {
				select {
				case <-glTicker.C:
					for e := glDatadogMetrics.Front(); e != nil; e = e.Next() {
						var currentMetrics = e.Value.(*DatadogMetrics)

						totalResponsesPeriod := currentMetrics.response1xxCount +
							currentMetrics.response2xxCount +
							currentMetrics.response3xxCount +
							currentMetrics.response4xxCount +
							currentMetrics.response5xxCount

						glStatsdClient.Gauge(
							"requests.per_s",
							float64(totalResponsesPeriod)/TICKER_INTERVAL,
							currentMetrics.area,
							STATSD_RATE,
						)
						glStatsdClient.Incr(
							"responses.1xx",
							currentMetrics.area,
							float64(currentMetrics.response1xxCount),
						)
						glStatsdClient.Gauge(
							"responses.2xx",
							float64(currentMetrics.response2xxCount),
							currentMetrics.area,
							STATSD_RATE,
						)
						glStatsdClient.Gauge(
							"responses.3xx",
							float64(currentMetrics.response3xxCount),
							currentMetrics.area,
							STATSD_RATE,
						)
						glStatsdClient.Gauge(
							"responses.4xx",
							float64(currentMetrics.response4xxCount),
							currentMetrics.area,
							STATSD_RATE,
						)
						glStatsdClient.Gauge(
							"responses.5xx",
							float64(currentMetrics.response5xxCount),
							currentMetrics.area,
							STATSD_RATE,
						)
						glStatsdClient.Gauge(
							"responses.size_bytes",
							float64(currentMetrics.responseSize),
							currentMetrics.area,
							STATSD_RATE,
						)
						if totalResponsesPeriod == 0 { // Avoid div. per zero
							totalResponsesPeriod = 1
						}
						glStatsdClient.Gauge(
							"responses.duration",
							float64(currentMetrics.responseTime)/float64(totalResponsesPeriod),
							currentMetrics.area,
							STATSD_RATE,
						)

						atomic.AddUint64(&currentMetrics.response1xxCount, -currentMetrics.response1xxCount)
						atomic.AddUint64(&currentMetrics.response2xxCount, -currentMetrics.response2xxCount)
						atomic.AddUint64(&currentMetrics.response3xxCount, -currentMetrics.response3xxCount)
						atomic.AddUint64(&currentMetrics.response4xxCount, -currentMetrics.response4xxCount)
						atomic.AddUint64(&currentMetrics.response5xxCount, -currentMetrics.response5xxCount)
						atomic.AddUint64(&currentMetrics.responseSize, -currentMetrics.responseSize)
						currentMetrics.responseTime = 0
					}
				case <-quit:
					glTicker.Stop()
					return
				}
			}
		}()
	}

	return nil
}
<|endoftext|>


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

// This is an implementation of the game rock, paper, scissors.
package main

import (
	"fmt"
	"math/rand"
	"os"
	"os/exec"
	"os/signal"
	"time"
)

const (
	lose int = iota
	tie
	win
)

var compStr string
var err error
var compAnswer int
var givenAnswer int

// channel variables
var (
	answerChan = make(chan *int, 1)
	validChan  = make(chan *int, 1)      // channel to receive answer for validity check
	validResp  = make(chan bool, 1)      // channel to send validity answer on
	signalChan = make(chan os.Signal, 1) // channel to catch ctrl-c
)

// game holds the data collected during game play
type game struct {
	attempts int   // track how many rounds played
	pAnswer  *int  // pointer to given answer
	cAnswer  int   // computer's answer
	results  []int // array to hold wins, loses, and ties
}

// checkValidAnswer makes sure the given answer is valid
func checkValidAnswer() {
	for {
		select {
		case a := <-validChan:
			if *a == lose || *a == tie || *a == win {
				validResp <- true
			} else {
				validResp <- false
			}
		}
	}
}

// clearScreen runs a shell clear command
func clearScreen() {
	c := exec.Command("clear")
	c.Stdout = os.Stdout
	c.Run()
}

// genStats outputs the game play statistics
func (g *game) genStats() {
	var wins, loses, ties int
	for _, i := range g.results {
		switch {
		case i == win:
			wins++
		case i == lose:
			loses++
		case i == tie:
			ties++
		}
	}
	fmt.Printf("\n\nRounds: %d, Wins: %d, Loses: %d, Ties: %d\n\n", len(g.results), wins, loses, ties)
	os.Exit(1) // Since it was a ctrl-c, exit non-zero
}

// genComputerAnswer will randomly generate a number used as an answer
func genComputerAnswer() (int, string, error) {
	rand.Seed(time.Now().UTC().UnixNano())
	a := rand.Intn(3)
	switch {
	case a == 0:
		return a, "rock", nil
	case a == 1:
		return a, "paper", nil
	case a == 2:
		return a, "scissors", nil
	}
	return 127, "", fmt.Errorf("unknown value")
}

func main() {
	clearScreen()
	fmt.Print("+ Rock-Paper-Scissors (Enter 0 for ROCK, 1 for PAPER, and 2 for SCISSORS)\n")
	g := game{
		attempts: 0,
		results:  make([]int, 0),
	}
	signal.Notify(signalChan, os.Interrupt)
	// setup go routine to catch a ctrl-c
	go func() {
		for range signalChan {
			g.genStats()
		}
	}()
	go checkValidAnswer()
Loop:
	for {
		clearScreen()
		fmt.Print("+ Rock-Paper-Scissors (Enter 0 for ROCK, 1 for PAPER, and 2 for SCISSORS)\n")
		fmt.Print("\nEnter answer: ")
		fmt.Scanf("%d", &givenAnswer)
		validChan <- &givenAnswer
		for i := range validResp {
			if !i {
				fmt.Println("invalid answer, try again")
				continue Loop
			}
			break
		}
		g.attempts = g.attempts + 1
		g.pAnswer = &givenAnswer
		if compAnswer, compStr, err = genComputerAnswer(); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		g.cAnswer = compAnswer
		switch {
		case g.cAnswer%3+1 == *g.pAnswer:
			g.results = append(g.results, win)
			fmt.Printf("Computer: %s  WIN!\n", compStr)
		case *g.pAnswer%3+1 == g.cAnswer:
			g.results = append(g.results, lose)
			fmt.Printf("Computer: %s  LOSE\n", compStr)
		default:
			g.results = append(g.results, tie)
			fmt.Printf("Computer: %s  TIE\n", compStr)
		}
	}
}
<commit_msg>figuring out more output stuff<commit_after>// Copyright 2015 Brian J. Downs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// This is an implementation of the game rock, paper, scissors.
package main

import (
	"fmt"
	"math/rand"
	"os"
	"os/exec"
	"os/signal"
	"time"
)

const (
	lose int = iota
	tie
	win
)

var compStr string
var err error
var compAnswer int
var givenAnswer int

// channel variables
var (
	answerChan = make(chan *int, 1)
	validChan  = make(chan *int, 1)      // channel to receive answer for validity check
	validResp  = make(chan bool, 1)      // channel to send validity answer on
	signalChan = make(chan os.Signal, 1) // channel to catch ctrl-c
)

// game holds the data collected during game play
type game struct {
	attempts int   // track how many rounds played
	pAnswer  *int  // pointer to given answer
	cAnswer  int   // computer's answer
	results  []int // array to hold wins, loses, and ties
}

// checkValidAnswer makes sure the given answer is valid
func checkValidAnswer() {
	for {
		select {
		case a := <-validChan:
			if *a == lose || *a == tie || *a == win {
				validResp <- true
			} else {
				validResp <- false
			}
		}
	}
}

// clearScreen runs a shell clear command
func clearScreen() {
	c := exec.Command("clear")
	c.Stdout = os.Stdout
	c.Run()
}

// genStats outputs the game play statistics
func (g *game) genStats() {
	var wins, loses, ties int
	for _, i := range g.results {
		switch {
		case i == win:
			wins++
		case i == lose:
			loses++
		case i == tie:
			ties++
		}
	}
	fmt.Printf("\n\nRounds: %d, Wins: %d, Loses: %d, Ties: %d\n\n", len(g.results), wins, loses, ties)
	os.Exit(1) // Since it was a ctrl-c, exit non-zero
}

// genComputerAnswer will randomly generate a number used as an answer
func genComputerAnswer() (int, string, error) {
	rand.Seed(time.Now().UTC().UnixNano())
	a := rand.Intn(3)
	switch {
	case a == 0:
		return a, "rock", nil
	case a == 1:
		return a, "paper", nil
	case a == 2:
		return a, "scissors", nil
	}
	return 127, "", fmt.Errorf("unknown value")
}

func main() {
	clearScreen()
	fmt.Print("+ Rock-Paper-Scissors (Enter 0 for ROCK, 1 for PAPER, and 2 for SCISSORS)\n")
	g := game{
		attempts: 0,
		results:  make([]int, 0),
	}
	signal.Notify(signalChan, os.Interrupt)
	// setup go routine to catch a ctrl-c
	go func() {
		for range signalChan {
			g.genStats()
		}
	}()
	go checkValidAnswer()
Loop:
	for {
		clearScreen()
		fmt.Print("+ Rock-Paper-Scissors (Enter 0 for ROCK, 1 for PAPER, and 2 for SCISSORS)\n")
		fmt.Print("\nEnter answer: ")
		fmt.Scanf("%d", &givenAnswer)
		validChan <- &givenAnswer
		for i := range validResp {
			if !i {
				fmt.Println("invalid answer, try again")
				continue Loop
			}
			break
		}
		g.attempts = g.attempts + 1
		g.pAnswer = &givenAnswer
		if compAnswer, compStr, err = genComputerAnswer(); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		g.cAnswer = compAnswer
		switch {
		case g.cAnswer%3+1 == *g.pAnswer:
			g.results = append(g.results, win)
			fmt.Printf("Computer: %s  WIN!\n", compStr)
			time.Sleep(800 * time.Millisecond)
		case *g.pAnswer%3+1 == g.cAnswer:
			g.results = append(g.results, lose)
			fmt.Printf("Computer: %s  LOSE\n", compStr)
			time.Sleep(800 * time.Millisecond)
		default:
			g.results = append(g.results, tie)
			fmt.Printf("Computer: %s  TIE\n", compStr)
			time.Sleep(800 * time.Millisecond)
		}
	}
}
<|endoftext|>


<commit_before>package SimpleChatCommon

import (
	"fmt"
	"github.com/nsf/termbox-go"
	"github.com/gabriel-comeau/termbox-uikit"
	"strings"
)

// Separator string to split the message up
const MESSAGE_TOKEN string = "|%|"

// Converts a colorized string object to a string
func Pack(colorizedString *termbox-uikit.ColorizedString) string {
	colors := new(ColorList)
	colStr := colors.fromColor(colorizedString.Color)

	return fmt.Sprintf("%v%v%v\n", colorizedString.Text, MESSAGE_TOKEN, colStr)
}

// Converts a string to a ColorizedString object
func Unpack(raw string) *termbox-uikit.ColorizedString {
	cs := new(termbox-uikit.ColorizedString)
	trimmed := strings.Trim(raw, " \n")
	parts := strings.Split(trimmed, MESSAGE_TOKEN)

	cs.Color = termbox.ColorWhite

	for i, part := range parts {
		if i == 0 {
			cs.Text = part
		} else if i == 1 {
			colors := new(ColorList)
			col := colors.Get(part)
			cs.Color = col
		}
	}

	return cs
}

// Creates a new colorized string object, from a body string and color string
func Create(text, color string) *termbox-uikit.ColorizedString {
	cs := new(termbox-uikit.ColorizedString)
	cs.Text = text

	cs.Color = termbox.ColorWhite
	colors := new(ColorList)
	col := colors.Get(color)
	cs.Color = col

	return cs
}

// For convenience, takes a slice of strings and a single color and then
// converts them to slice of message objects.  Obviously, they'll all have
// the same color but in many cases this is desirable.
func ColStringSliceFromStringSlice(texts []string, color string) []*termbox-uikit.ColorizedString {
	cstrings := make([]*termbox-uikit.ColorizedString, 0)
	for _, t := range texts {
		cstrings = append(cstrings, Create(t, color))
	}

	return cstrings
}
<commit_msg>Using new package name<commit_after>package SimpleChatCommon

import (
	"fmt"
	"github.com/nsf/termbox-go"
	"github.com/gabriel-comeau/tbuikit"
	"strings"
)

// Separator string to split the message up
const MESSAGE_TOKEN string = "|%|"

// Converts a colorized string object to a string
func Pack(colorizedString *tbuikit.ColorizedString) string {
	colors := new(ColorList)
	colStr := colors.fromColor(colorizedString.Color)

	return fmt.Sprintf("%v%v%v\n", colorizedString.Text, MESSAGE_TOKEN, colStr)
}

// Converts a string to a ColorizedString object
func Unpack(raw string) *tbuikit.ColorizedString {
	cs := new(tbuikit.ColorizedString)
	trimmed := strings.Trim(raw, " \n")
	parts := strings.Split(trimmed, MESSAGE_TOKEN)

	cs.Color = termbox.ColorWhite

	for i, part := range parts {
		if i == 0 {
			cs.Text = part
		} else if i == 1 {
			colors := new(ColorList)
			col := colors.Get(part)
			cs.Color = col
		}
	}

	return cs
}

// Creates a new colorized string object, from a body string and color string
func Create(text, color string) *tbuikit.ColorizedString {
	cs := new(tbuikit.ColorizedString)
	cs.Text = text

	cs.Color = termbox.ColorWhite
	colors := new(ColorList)
	col := colors.Get(color)
	cs.Color = col

	return cs
}

// For convenience, takes a slice of strings and a single color and then
// converts them to slice of message objects.  Obviously, they'll all have
// the same color but in many cases this is desirable.
func ColStringSliceFromStringSlice(texts []string, color string) []*tbuikit.ColorizedString {
	cstrings := make([]*tbuikit.ColorizedString, 0)
	for _, t := range texts {
		cstrings = append(cstrings, Create(t, color))
	}

	return cstrings
}
<|endoftext|>


<commit_before>// package forwarder implements http handler that forwards requests to remote server
// and serves back the response
package forward

import (
	"io"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"time"

	"github.com/FoxComm/vulcand/Godeps/_workspace/src/github.com/mailgun/oxy/utils"
)

// ReqRewriter can alter request headers and body
type ReqRewriter interface {
	Rewrite(r *http.Request)
}

type optSetter func(f *Forwarder) error

func RoundTripper(r http.RoundTripper) optSetter {
	return func(f *Forwarder) error {
		f.roundTripper = r
		return nil
	}
}

func Rewriter(r ReqRewriter) optSetter {
	return func(f *Forwarder) error {
		f.rewriter = r
		return nil
	}
}

// ErrorHandler is a functional argument that sets error handler of the server
func ErrorHandler(h utils.ErrorHandler) optSetter {
	return func(f *Forwarder) error {
		f.errHandler = h
		return nil
	}
}

func Logger(l utils.Logger) optSetter {
	return func(f *Forwarder) error {
		f.log = l
		return nil
	}
}

type Forwarder struct {
	errHandler   utils.ErrorHandler
	roundTripper http.RoundTripper
	rewriter     ReqRewriter
	log          utils.Logger
}

func New(setters ...optSetter) (*Forwarder, error) {
	f := &Forwarder{}
	for _, s := range setters {
		if err := s(f); err != nil {
			return nil, err
		}
	}
	if f.roundTripper == nil {
		f.roundTripper = http.DefaultTransport
	}
	if f.rewriter == nil {
		h, err := os.Hostname()
		if err != nil {
			h = "localhost"
		}
		f.rewriter = &HeaderRewriter{TrustForwardHeader: true, Hostname: h}
	}
	if f.log == nil {
		f.log = utils.NullLogger
	}
	if f.errHandler == nil {
		f.errHandler = utils.DefaultHandler
	}
	return f, nil
}

func (f *Forwarder) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	start := time.Now().UTC()
	response, err := f.roundTripper.RoundTrip(f.copyRequest(req, req.URL))
	if err != nil {
		f.log.Errorf("Error forwarding to %v, err: %v", req.URL, err)
		f.errHandler.ServeHTTP(w, req, err)
		return
	}

	if req.TLS != nil {
		f.log.Infof("Round trip: %v, code: %v, duration: %v tls:version: %x, tls:resume:%t, tls:csuite:%x, tls:server:%v",
			req.URL, response.StatusCode, time.Now().UTC().Sub(start),
			req.TLS.Version,
			req.TLS.DidResume,
			req.TLS.CipherSuite,
			req.TLS.ServerName)
	} else {
		f.log.Infof("Round trip: %v, code: %v, duration: %v",
			req.URL, response.StatusCode, time.Now().UTC().Sub(start))
	}

	utils.CopyHeaders(w.Header(), response.Header)
	w.WriteHeader(response.StatusCode)
	written, _ := io.Copy(w, response.Body)
	if written != 0 {
		w.Header().Set(ContentLength, strconv.FormatInt(written, 10))
	}
	response.Body.Close()
}

func (f *Forwarder) copyRequest(req *http.Request, u *url.URL) *http.Request {
	outReq := new(http.Request)
	*outReq = *req // includes shallow copies of maps, but we handle this below

	outReq.URL = utils.CopyURL(req.URL)
	outReq.URL.Scheme = u.Scheme
	outReq.URL.Host = u.Host
	outReq.URL.Opaque = req.RequestURI
	// raw query is already included in RequestURI, so ignore it to avoid dupes
	outReq.URL.RawQuery = ""

	outReq.Proto = "HTTP/1.1"
	outReq.ProtoMajor = 1
	outReq.ProtoMinor = 1

	// Overwrite close flag so we can keep persistent connection for the backend servers
	outReq.Close = false

	outReq.Header = make(http.Header)
	utils.CopyHeaders(outReq.Header, req.Header)

	if f.rewriter != nil {
		f.rewriter.Rewrite(outReq)
	}
	return outReq
}
<commit_msg>Fix fwd requests starting with //<commit_after>// package forwarder implements http handler that forwards requests to remote server
// and serves back the response
package forward

import (
	"io"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"time"

	"github.com/FoxComm/vulcand/Godeps/_workspace/src/github.com/mailgun/oxy/utils"
)

// ReqRewriter can alter request headers and body
type ReqRewriter interface {
	Rewrite(r *http.Request)
}

type optSetter func(f *Forwarder) error

func RoundTripper(r http.RoundTripper) optSetter {
	return func(f *Forwarder) error {
		f.roundTripper = r
		return nil
	}
}

func Rewriter(r ReqRewriter) optSetter {
	return func(f *Forwarder) error {
		f.rewriter = r
		return nil
	}
}

// ErrorHandler is a functional argument that sets error handler of the server
func ErrorHandler(h utils.ErrorHandler) optSetter {
	return func(f *Forwarder) error {
		f.errHandler = h
		return nil
	}
}

func Logger(l utils.Logger) optSetter {
	return func(f *Forwarder) error {
		f.log = l
		return nil
	}
}

type Forwarder struct {
	errHandler   utils.ErrorHandler
	roundTripper http.RoundTripper
	rewriter     ReqRewriter
	log          utils.Logger
}

func New(setters ...optSetter) (*Forwarder, error) {
	f := &Forwarder{}
	for _, s := range setters {
		if err := s(f); err != nil {
			return nil, err
		}
	}
	if f.roundTripper == nil {
		f.roundTripper = http.DefaultTransport
	}
	if f.rewriter == nil {
		h, err := os.Hostname()
		if err != nil {
			h = "localhost"
		}
		f.rewriter = &HeaderRewriter{TrustForwardHeader: true, Hostname: h}
	}
	if f.log == nil {
		f.log = utils.NullLogger
	}
	if f.errHandler == nil {
		f.errHandler = utils.DefaultHandler
	}
	return f, nil
}

func (f *Forwarder) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	start := time.Now().UTC()
	response, err := f.roundTripper.RoundTrip(f.copyRequest(req, req.URL))
	if err != nil {
		f.log.Errorf("Error forwarding to %v, err: %v", req.URL, err)
		f.errHandler.ServeHTTP(w, req, err)
		return
	}

	if req.TLS != nil {
		f.log.Infof("Round trip: %v, code: %v, duration: %v tls:version: %x, tls:resume:%t, tls:csuite:%x, tls:server:%v",
			req.URL, response.StatusCode, time.Now().UTC().Sub(start),
			req.TLS.Version,
			req.TLS.DidResume,
			req.TLS.CipherSuite,
			req.TLS.ServerName)
	} else {
		f.log.Infof("Round trip: %v, code: %v, duration: %v",
			req.URL, response.StatusCode, time.Now().UTC().Sub(start))
	}

	utils.CopyHeaders(w.Header(), response.Header)
	w.WriteHeader(response.StatusCode)
	written, _ := io.Copy(w, response.Body)
	if written != 0 {
		w.Header().Set(ContentLength, strconv.FormatInt(written, 10))
	}
	response.Body.Close()
}

func (f *Forwarder) copyRequest(req *http.Request, u *url.URL) *http.Request {
	outReq := new(http.Request)
	*outReq = *req // includes shallow copies of maps, but we handle this below

	outReq.URL = utils.CopyURL(req.URL)
	outReq.URL.Scheme = u.Scheme
	outReq.URL.Host = u.Host
	// workaround for https://github.com/golang/go/issues/10433
	outReq.URL.Opaque = mergeStartingSlashes(req.RequestURI)
	// raw query is already included in RequestURI, so ignore it to avoid dupes
	outReq.URL.RawQuery = ""

	outReq.Proto = "HTTP/1.1"
	outReq.ProtoMajor = 1
	outReq.ProtoMinor = 1

	// Overwrite close flag so we can keep persistent connection for the backend servers
	outReq.Close = false

	outReq.Header = make(http.Header)
	utils.CopyHeaders(outReq.Header, req.Header)

	if f.rewriter != nil {
		f.rewriter.Rewrite(outReq)
	}
	return outReq
}

// faster than using strings or regexp packages
func mergeStartingSlashes(uri string) string {
	for len(uri) > 1 && uri[0] == '/' && uri[1] == '/' {
		uri = uri[1:]
	}
	return uri
}
<|endoftext|>


<commit_before>// peekaboo registers the current machine (or provided IP) to a Rackspace load
// balancer, enabling (or disabling) itself.

package main

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"strconv"
	"strings"

	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/pagination"
	"github.com/rackspace/gophercloud/rackspace"
	"github.com/rackspace/gophercloud/rackspace/lb/v1/lbs"
	"github.com/rackspace/gophercloud/rackspace/lb/v1/nodes"
)

// waitForReady waits for the load balancer with id loadBalancerID to become
// ACTIVE. It times out after 60 seconds and streamrolls on ahead.
func waitForReady(client *gophercloud.ServiceClient, loadBalancerID int) {

	// Ensure the load balancer is ready
	gophercloud.WaitFor(60, func() (bool, error) {
		lb, err := lbs.Get(client, loadBalancerID).Extract()
		if err != nil {
			return false, err
		}

		if lb.Status != lbs.ACTIVE {
			// It has not yet reached ACTIVE
			return false, nil
		}

		// It has reached ACTIVE
		return true, nil
	})

}

// findNodeByIPPort gets a load balancer node by IP and Port
func findNodeByIPPort(
	client *gophercloud.ServiceClient,
	loadBalancerID int,
	address string,
	port int,
) *nodes.Node {

	// nil until found
	var found *nodes.Node

	pager := nodes.List(client, loadBalancerID, nil)

	pager.EachPage(func(page pagination.Page) (bool, error) {
		lbNodes, err := nodes.ExtractNodes(page)
		if err != nil {
			log.Fatalf("Error during paging load balancer: %v", err)
		}

		for _, trialNode := range lbNodes {
			if trialNode.Address == address && trialNode.Port == port {
				found = &trialNode
				return false, nil
			}

		}

		return true, nil
	})

	return found
}

// getIP tries to best guess what IP to work with
//
// Precedence for IP is determined by
//  - ipPtr being non-nil (peekaboo's -ip flag)
//  - Service Net Environment variable $RAX_SERVICENET_IPV4
//  - Public Net Environment variable $RAX_PUBLICNET_IPV4
//  - Gleaning a 10 dot out of the network interfaces (likely service net)
//  - eth0
func getIP(ipPtr *string) (string, error) {

	serviceNetIPv4 := os.Getenv("RAX_SERVICENET_IPV4")
	publicNetIPv4 := os.Getenv("RAX_PUBLICNET_IPV4")

	switch {
	case *ipPtr != "":
		return *ipPtr, nil
	case serviceNetIPv4 != "":
		return serviceNetIPv4, nil
	case publicNetIPv4 != "":
		return publicNetIPv4, nil
	}

	addrs, err := net.InterfaceAddrs()

	if err != nil {
		return "", err
	}

	for _, addr := range addrs {
		cidr := addr.String()
		ip := strings.Split(cidr, "/")[0]

		if strings.HasPrefix(ip, "10.") {
			return ip, nil
		}
	}

	// Find eth0
	eth0, err := net.InterfaceByName("eth0")
	if err != nil {
		return "", fmt.Errorf("trouble finding eth0: %v", err)
	}

	addrs, err = eth0.Addrs()
	if err != nil {
		return "", err
	}

	for _, addr := range addrs {
		cidr := addr.String()
		ip := strings.Split(cidr, "/")[0]

		// Pick out IPv4
		if strings.ContainsRune(ip, '.') {
			return ip, err
		}
	}

	return "", errors.New("unable to determine an IP for load balancing")

}

func main() {
	var err error

	disabledPtr := flag.Bool("disable", false, "Disable the node on the load balancer")

	//NOTE: peekaboo allows setting the IP by using
	//        - environment variables: RAX_SERVICENET_IPV4 or RAX_PUBLICNET_IPV4
	//        - finding an ip on the system starting with 10. (service net)
	//        - locating the eth0 interface
	//        - providing an ip as a flag is fine too and will take precedence
	ipPtr := flag.String("ip", "", "IP address to register/deregister on the load balancer")
	flag.Parse()

	username := os.Getenv("OS_USERNAME")
	APIKey := os.Getenv("OS_PASSWORD")
	region := os.Getenv("OS_REGION_NAME")

	if username == "" || APIKey == "" || region == "" {
		log.Fatalf("One or more of $OS_USERNAME, $OS_PASSWORD, and $OS_REGION_NAME not set")
	}

	// These get converted into integers later
	strLoadBalancerID := os.Getenv("LOAD_BALANCER_ID")
	strAppPort := os.Getenv("APP_PORT")

	/**
	 * Retrieve port for load balancer's node, defaulting to 80
	 */
	var nodePort = 80

	if strAppPort == "" {
		log.Printf("$APP_PORT not set, defaulting to 80")
	} else {
		nodePort, err = strconv.Atoi(strAppPort)
		if err != nil {
			log.Fatalf("Unable to parse integer from $APP_PORT: %v\n", strAppPort)
		}
	}

	/**
	 * Determine the IP Address for the load balancer's node
	 */
	nodeAddress, err := getIP(ipPtr)
	if err != nil {
		log.Fatalln(err)
	}

	log.Printf("Determined IP: %v", nodeAddress)
	log.Printf("Determined Port: %v", nodePort)

	/**
	 * Retrive Load Balancer ID
	 */
	if strLoadBalancerID == "" {
		log.Fatalln("$LOAD_BALANCER_ID must be set")
	}
	loadBalancerID, err := strconv.Atoi(strLoadBalancerID)
	if err != nil {
		log.Fatalf("$LOAD_BALANCER_ID not an integer: %v\n", loadBalancerID)
	}

	provider, err := rackspace.AuthenticatedClient(gophercloud.AuthOptions{
		Username: username,
		APIKey:   APIKey,
	})

	if err != nil {
		log.Fatalf("Trouble authenticating to Rackspace: %v\n", err)
	}

	client, err := rackspace.NewLBV1(provider, gophercloud.EndpointOpts{
		Region: region,
	})

	if err != nil {
		log.Fatalf("Creating load balancer client in %v failed: %v\n", region, err)
	}

	log.Println("Client ready")

	waitForReady(client, loadBalancerID)

	nodePtr := findNodeByIPPort(client, loadBalancerID, nodeAddress, nodePort)

	condition := nodes.ENABLED
	if *disabledPtr {
		//TODO: Watch the interface on the right process/container to determine
		//      when connections have dropped, and set to DISABLED
		condition = nodes.DRAINING
	}

	log.Printf("Telling %v on port %v to be %v\n", nodeAddress, nodePort, condition)

	if nodePtr != nil {
		log.Printf("Found existing node %v", *nodePtr)

		opts := nodes.UpdateOpts{
			Condition: condition,
		}

		updateResult := nodes.Update(client, loadBalancerID, nodePtr.ID, opts)
		err = updateResult.ExtractErr()
		if err != nil {
			log.Panicf("Updating node failed: %v", err)
		}

	} else {
		log.Printf("Creating new node")
		opts := nodes.CreateOpts{
			nodes.CreateOpt{
				Address:   nodeAddress,
				Port:      nodePort,
				Condition: condition,
			},
		}

		nodePager := nodes.Create(client, loadBalancerID, opts)
		nodeList, err := nodePager.ExtractNodes()
		if err != nil || len(nodeList) != 1 {
			log.Panicf("Something went terribly wrong on node creation: %v\n", nodeList)
		}
		nodePtr = &nodeList[0]
	}

	waitForReady(client, loadBalancerID)

	// After update, get the version of the node Rackspace has
	result := nodes.Get(client, loadBalancerID, nodePtr.ID)
	nodePtr, err = result.Extract()
	if err != nil {
		log.Panicln(err)
	}

	log.Printf("Updated state for node: %v\n", *nodePtr)

}
<commit_msg>Slim down the logging<commit_after>// peekaboo registers the current machine (or provided IP) to a Rackspace load
// balancer, enabling (or disabling) itself.

package main

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"strconv"
	"strings"

	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/pagination"
	"github.com/rackspace/gophercloud/rackspace"
	"github.com/rackspace/gophercloud/rackspace/lb/v1/lbs"
	"github.com/rackspace/gophercloud/rackspace/lb/v1/nodes"
)

// waitForReady waits for the load balancer with id loadBalancerID to become
// ACTIVE. It times out after 60 seconds and streamrolls on ahead.
func waitForReady(client *gophercloud.ServiceClient, loadBalancerID int) {

	// Ensure the load balancer is ready
	gophercloud.WaitFor(60, func() (bool, error) {
		lb, err := lbs.Get(client, loadBalancerID).Extract()
		if err != nil {
			return false, err
		}

		if lb.Status != lbs.ACTIVE {
			// It has not yet reached ACTIVE
			return false, nil
		}

		// It has reached ACTIVE
		return true, nil
	})

}

// findNodeByIPPort gets a load balancer node by IP and Port
func findNodeByIPPort(
	client *gophercloud.ServiceClient,
	loadBalancerID int,
	address string,
	port int,
) *nodes.Node {

	// nil until found
	var found *nodes.Node

	pager := nodes.List(client, loadBalancerID, nil)

	pager.EachPage(func(page pagination.Page) (bool, error) {
		lbNodes, err := nodes.ExtractNodes(page)
		if err != nil {
			log.Fatalf("Error during paging load balancer: %v", err)
		}

		for _, trialNode := range lbNodes {
			if trialNode.Address == address && trialNode.Port == port {
				found = &trialNode
				return false, nil
			}

		}

		return true, nil
	})

	return found
}

// getIP tries to best guess what IP to work with
//
// Precedence for IP is determined by
//  - ipPtr being non-nil (peekaboo's -ip flag)
//  - Service Net Environment variable $RAX_SERVICENET_IPV4
//  - Public Net Environment variable $RAX_PUBLICNET_IPV4
//  - Gleaning a 10 dot out of the network interfaces (likely service net)
//  - eth0
func getIP(ipPtr *string) (string, error) {

	serviceNetIPv4 := os.Getenv("RAX_SERVICENET_IPV4")
	publicNetIPv4 := os.Getenv("RAX_PUBLICNET_IPV4")

	switch {
	case *ipPtr != "":
		return *ipPtr, nil
	case serviceNetIPv4 != "":
		return serviceNetIPv4, nil
	case publicNetIPv4 != "":
		return publicNetIPv4, nil
	}

	addrs, err := net.InterfaceAddrs()

	if err != nil {
		return "", err
	}

	for _, addr := range addrs {
		cidr := addr.String()
		ip := strings.Split(cidr, "/")[0]

		if strings.HasPrefix(ip, "10.") {
			return ip, nil
		}
	}

	// Find eth0
	eth0, err := net.InterfaceByName("eth0")
	if err != nil {
		return "", fmt.Errorf("trouble finding eth0: %v", err)
	}

	addrs, err = eth0.Addrs()
	if err != nil {
		return "", err
	}

	for _, addr := range addrs {
		cidr := addr.String()
		ip := strings.Split(cidr, "/")[0]

		// Pick out IPv4
		if strings.ContainsRune(ip, '.') {
			return ip, err
		}
	}

	return "", errors.New("unable to determine an IP for load balancing")

}

func main() {
	var err error

	disabledPtr := flag.Bool("disable", false, "Disable the node on the load balancer")

	//NOTE: peekaboo allows setting the IP by using
	//        - environment variables: RAX_SERVICENET_IPV4 or RAX_PUBLICNET_IPV4
	//        - finding an ip on the system starting with 10. (service net)
	//        - locating the eth0 interface
	//        - providing an ip as a flag is fine too and will take precedence
	ipPtr := flag.String("ip", "", "IP address to register/deregister on the load balancer")
	flag.Parse()

	username := os.Getenv("OS_USERNAME")
	APIKey := os.Getenv("OS_PASSWORD")
	region := os.Getenv("OS_REGION_NAME")

	if username == "" || APIKey == "" || region == "" {
		log.Fatalf("One or more of $OS_USERNAME, $OS_PASSWORD, and $OS_REGION_NAME not set")
	}

	// These get converted into integers later
	strLoadBalancerID := os.Getenv("LOAD_BALANCER_ID")
	strAppPort := os.Getenv("APP_PORT")

	/**
	 * Retrieve port for load balancer's node, defaulting to 80
	 */
	var nodePort = 80

	if strAppPort == "" {
		log.Printf("$APP_PORT not set, defaulting to 80")
	} else {
		nodePort, err = strconv.Atoi(strAppPort)
		if err != nil {
			log.Fatalf("Unable to parse integer from $APP_PORT: %v\n", strAppPort)
		}
	}

	/**
	 * Determine the IP Address for the load balancer's node
	 */
	nodeAddress, err := getIP(ipPtr)
	if err != nil {
		log.Fatalln(err)
	}

	log.Printf("Enabling %v:%v", nodeAddress, nodePort)

	/**
	 * Retrive Load Balancer ID
	 */
	if strLoadBalancerID == "" {
		log.Fatalln("$LOAD_BALANCER_ID must be set")
	}
	loadBalancerID, err := strconv.Atoi(strLoadBalancerID)
	if err != nil {
		log.Fatalf("$LOAD_BALANCER_ID not an integer: %v\n", loadBalancerID)
	}

	provider, err := rackspace.AuthenticatedClient(gophercloud.AuthOptions{
		Username: username,
		APIKey:   APIKey,
	})

	if err != nil {
		log.Fatalf("Trouble authenticating to Rackspace: %v\n", err)
	}

	client, err := rackspace.NewLBV1(provider, gophercloud.EndpointOpts{
		Region: region,
	})

	if err != nil {
		log.Fatalf("Creating load balancer client in %v failed: %v\n", region, err)
	}

	waitForReady(client, loadBalancerID)

	nodePtr := findNodeByIPPort(client, loadBalancerID, nodeAddress, nodePort)

	condition := nodes.ENABLED
	if *disabledPtr {
		//TODO: Watch the interface on the right process/container to determine
		//      when connections have dropped, and set to DISABLED
		condition = nodes.DRAINING
	}

	log.Printf("Setting %v:%v to be %v on load balancer %v\n",
		nodeAddress, nodePort,
		condition,
		loadBalancerID)

	if nodePtr != nil {
		log.Printf("Updating existing node %v", *nodePtr)

		opts := nodes.UpdateOpts{
			Condition: condition,
		}

		updateResult := nodes.Update(client, loadBalancerID, nodePtr.ID, opts)
		err = updateResult.ExtractErr()
		if err != nil {
			log.Panicf("Updating node failed: %v", err)
		}

	} else {
		log.Printf("Creating new node")
		opts := nodes.CreateOpts{
			nodes.CreateOpt{
				Address:   nodeAddress,
				Port:      nodePort,
				Condition: condition,
			},
		}

		nodePager := nodes.Create(client, loadBalancerID, opts)
		nodeList, err := nodePager.ExtractNodes()
		if err != nil || len(nodeList) != 1 {
			log.Panicf("Something went terribly wrong on node creation: %v\n", nodeList)
		}
		nodePtr = &nodeList[0]
	}

	waitForReady(client, loadBalancerID)

	// After update, get the version of the node Rackspace has
	result := nodes.Get(client, loadBalancerID, nodePtr.ID)
	nodePtr, err = result.Extract()
	if err != nil {
		log.Panicln(err)
	}

	log.Printf("Final node state: %v\n", *nodePtr)

}
<|endoftext|>


<commit_before>//  Copyright 2016 The goscope Authors
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

package dummy

import (
	"math"

	"github.com/zagrodzki/goscope/scope"
)

type sinChan struct{}

func (sinChan) ID() scope.ChanID { return "sin" }
func (ch sinChan) data(offset int) []scope.Voltage {
	ret := make([]scope.Voltage, numSamples)
	for i := 0; i < numSamples; i++ {
		ret[i] = scope.Voltage(math.Sin(60*math.Pi*float64(i)/numSamples + float64(offset)))
	}
	return ret
}
<commit_msg>Add a benchmark. Do all float64 casting only once, before the loop.<commit_after>//  Copyright 2016 The goscope Authors
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

package dummy

import (
	"math"

	"github.com/zagrodzki/goscope/scope"
)

type sinChan struct{}

func (sinChan) ID() scope.ChanID { return "sin" }
func (ch sinChan) data(offset int) []scope.Voltage {
	ret := make([]scope.Voltage, numSamples)
	inc := 60 * math.Pi
	flof := float64(offset)
	var phase float64
	for i := 0; i < numSamples; i++ {
		ret[i] = scope.Voltage(math.Sin(phase/numSamples + flof))
		phase += inc
	}
	return ret
}
<|endoftext|>


<commit_before>// Package easyvk provides you simple way
// to work with VK API.
package easyvk

import (
	"net/url"
	"net/http"
	"io/ioutil"
	"fmt"
	"encoding/json"
	"golang.org/x/net/html"
	"net/http/cookiejar"
	"io"
	"errors"
)

const version = "5.63"
const apiURL = "https://api.vk.com/method/"
const authURL = "https://oauth.vk.com/authorize?" +
	"client_id=%s" +
	"&scope=%s" +
	"&redirect_uri=https://oauth.vk.com/blank.html" +
	"&display=wap" +
	"&v=%s" +
	"&response_type=token"

// VK defines a set of functions for
// working with VK API.
type VK struct {
	AccessToken string
	Version     string
	Account     Account
	Fave        Fave
	Photos      Photos
	Status      Status
	Upload      Upload
	Wall        Wall
}

// WithToken helps to initialize your
// VK object with token.
func WithToken(token string) VK {
	vk := VK{}
	vk.AccessToken = token
	vk.Version = version
	vk.Account = Account{&vk }
	vk.Fave = Fave{&vk }
	vk.Photos = Photos{&vk }
	vk.Status = Status{&vk }
	vk.Upload = Upload{}
	vk.Wall = Wall{&vk }
	return vk
}

// WithAuth helps to initialize your VK object
// with signing in by login, password, client id and scope
// Scope must be a string like "friends,wall"
func WithAuth(login, password, clientID, scope string) (VK, error) {
	u := fmt.Sprintf(authURL, clientID, scope, version)
	jar, _ := cookiejar.New(nil)
	client := &http.Client{
		Jar: jar,
	}

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

	args, u := parseForm(resp.Body)

	args.Add("email", login)
	args.Add("pass", password)

	resp, err = client.PostForm(u, args)
	if err != nil {
		return VK{}, err
	}

	if resp.Request.URL.Path != "/blank.html" {
		args, u := parseForm(resp.Body)
		resp, err := client.PostForm(u, args)
		if err != nil {
			return VK{}, err
		}
		defer resp.Body.Close()

		if resp.Request.URL.Path != "/blank.html" {
			return VK{}, errors.New("can't log in")
		}
	}

	urlArgs, err := url.ParseQuery(resp.Request.URL.Fragment)
	if err != nil {
		return VK{}, err
	}

	return WithToken(urlArgs["access_token"][0]), nil
}

func parseForm(body io.ReadCloser) (url.Values, string) {
	tokenizer := html.NewTokenizer(body)

	u := ""
	formData := map[string]string{}

	end := false
	for !end {
		tag := tokenizer.Next()

		switch tag {
		case html.ErrorToken:
			end = true
		case html.StartTagToken:
			switch token := tokenizer.Token(); token.Data {
			case "form":
				for _, attr := range token.Attr {
					if attr.Key == "action" {
						u = attr.Val
					}
				}
			case "input":
				if token.Attr[1].Val == "_origin" {
					formData["_origin"] = token.Attr[2].Val
				}
				if token.Attr[1].Val == "to" {
					formData["to"] = token.Attr[2].Val
				}
			}
		case html.SelfClosingTagToken:
			switch token := tokenizer.Token(); token.Data {
			case "input":
				if token.Attr[1].Val == "ip_h" {
					formData["ip_h"] = token.Attr[2].Val
				}
				if token.Attr[1].Val == "lg_h" {
					formData["lg_h"] = token.Attr[2].Val
				}
			}
		}
	}

	args := url.Values{}

	for key, val := range formData {
		args.Add(key, val)
	}

	return args, u
}

// Request provides access to VK API methods.
func (vk *VK) Request(method string, params map[string]string) ([]byte, error) {
	u, err := url.Parse(apiURL + method)
	if err != nil {
		return nil, err
	}

	query := url.Values{}
	for k, v := range params {
		query.Set(k, v)
	}
	query.Set("access_token", vk.AccessToken)
	query.Set("v", vk.Version)
	u.RawQuery = query.Encode()

	resp, err := http.Get(u.String())
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

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

	var handler struct {
		Error    *Error
		Response json.RawMessage
	}
	err = json.Unmarshal(body, &handler)

	if handler.Error != nil {
		return nil, handler.Error
	}

	return handler.Response, nil
}
<commit_msg>Const reformat<commit_after>// Package easyvk provides you simple way
// to work with VK API.
package easyvk

import (
	"net/url"
	"net/http"
	"io/ioutil"
	"fmt"
	"encoding/json"
	"golang.org/x/net/html"
	"net/http/cookiejar"
	"io"
	"errors"
)

const (
	version = "5.63"
	apiURL  = "https://api.vk.com/method/"
	authURL   = "https://oauth.vk.com/authorize?" +
		"client_id=%s" +
		"&scope=%s" +
		"&redirect_uri=https://oauth.vk.com/blank.html" +
		"&display=wap" +
		"&v=%s" +
		"&response_type=token"
)

// VK defines a set of functions for
// working with VK API.
type VK struct {
	AccessToken string
	Version     string
	Account     Account
	Fave        Fave
	Photos      Photos
	Status      Status
	Upload      Upload
	Wall        Wall
}

// WithToken helps to initialize your
// VK object with token.
func WithToken(token string) VK {
	vk := VK{}
	vk.AccessToken = token
	vk.Version = version
	vk.Account = Account{&vk }
	vk.Fave = Fave{&vk }
	vk.Photos = Photos{&vk }
	vk.Status = Status{&vk }
	vk.Upload = Upload{}
	vk.Wall = Wall{&vk }
	return vk
}

// WithAuth helps to initialize your VK object
// with signing in by login, password, client id and scope
// Scope must be a string like "friends,wall"
func WithAuth(login, password, clientID, scope string) (VK, error) {
	u := fmt.Sprintf(authURL, clientID, scope, version)
	jar, _ := cookiejar.New(nil)
	client := &http.Client{
		Jar: jar,
	}

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

	args, u := parseForm(resp.Body)

	args.Add("email", login)
	args.Add("pass", password)

	resp, err = client.PostForm(u, args)
	if err != nil {
		return VK{}, err
	}

	if resp.Request.URL.Path != "/blank.html" {
		args, u := parseForm(resp.Body)
		resp, err := client.PostForm(u, args)
		if err != nil {
			return VK{}, err
		}
		defer resp.Body.Close()

		if resp.Request.URL.Path != "/blank.html" {
			return VK{}, errors.New("can't log in")
		}
	}

	urlArgs, err := url.ParseQuery(resp.Request.URL.Fragment)
	if err != nil {
		return VK{}, err
	}

	return WithToken(urlArgs["access_token"][0]), nil
}

func parseForm(body io.ReadCloser) (url.Values, string) {
	tokenizer := html.NewTokenizer(body)

	u := ""
	formData := map[string]string{}

	end := false
	for !end {
		tag := tokenizer.Next()

		switch tag {
		case html.ErrorToken:
			end = true
		case html.StartTagToken:
			switch token := tokenizer.Token(); token.Data {
			case "form":
				for _, attr := range token.Attr {
					if attr.Key == "action" {
						u = attr.Val
					}
				}
			case "input":
				if token.Attr[1].Val == "_origin" {
					formData["_origin"] = token.Attr[2].Val
				}
				if token.Attr[1].Val == "to" {
					formData["to"] = token.Attr[2].Val
				}
			}
		case html.SelfClosingTagToken:
			switch token := tokenizer.Token(); token.Data {
			case "input":
				if token.Attr[1].Val == "ip_h" {
					formData["ip_h"] = token.Attr[2].Val
				}
				if token.Attr[1].Val == "lg_h" {
					formData["lg_h"] = token.Attr[2].Val
				}
			}
		}
	}

	args := url.Values{}

	for key, val := range formData {
		args.Add(key, val)
	}

	return args, u
}

// Request provides access to VK API methods.
func (vk *VK) Request(method string, params map[string]string) ([]byte, error) {
	u, err := url.Parse(apiURL + method)
	if err != nil {
		return nil, err
	}

	query := url.Values{}
	for k, v := range params {
		query.Set(k, v)
	}
	query.Set("access_token", vk.AccessToken)
	query.Set("v", vk.Version)
	u.RawQuery = query.Encode()

	resp, err := http.Get(u.String())
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

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

	var handler struct {
		Error    *Error
		Response json.RawMessage
	}
	err = json.Unmarshal(body, &handler)

	if handler.Error != nil {
		return nil, handler.Error
	}

	return handler.Response, nil
}
<|endoftext|>


<commit_before><commit_msg>Fix failing simulator test<commit_after><|endoftext|>