<commit_before>package main

import (
	"gl"
	"image"
	"image/png"
	"os"
	"io/ioutil"
	"bytes"
	"io"
	"encoding/binary"
	"unsafe"
)

func uploadTexture_NRGBA32(img *image.NRGBA) gl.GLuint {
	data := make([]uint8, img.Width() * img.Height() * 4)
	for y := 0; y < img.Height(); y++ {
		for x := 0; x < img.Width(); x++ {
			p := &img.Pixel[y][x]
			offset := y * img.Width() * 4 + x * 4
			data[offset+0] = p.R;
			data[offset+1] = p.G;
			data[offset+2] = p.B;
			data[offset+3] = p.A;
		}
	}

	var id gl.GLuint

	gl.GenTextures(1, &id)
	gl.BindTexture(gl.TEXTURE_2D, id)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE)
	gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.GLsizei(img.Width()), gl.GLsizei(img.Height()), 0, gl.RGBA,
		      gl.UNSIGNED_BYTE, unsafe.Pointer(&data[0]))

	if gl.GetError() != gl.NO_ERROR {
		gl.DeleteTextures(1, &id)
		panic(os.NewError("Failed to load a texture"))
		return 0
	}
	return id
}

//-------------------------------------------------------------------------
// FontGlyph
//-------------------------------------------------------------------------

type FontGlyph struct {
	OffsetX int32
	OffsetY int32
	Width uint32
	Height uint32

	// texture coords
	TX float32
	TY float32
	TX2 float32
	TY2 float32

	XAdvance uint32
}

type FontEncoding struct {
	Unicode uint32
	Index uint32
}

type Font struct {
	Glyphs []FontGlyph

	// I'm keeping it here because original font implementation
	// uses binary search lookups in that array, but here in Go I will
	// simply use a map for that
	Encoding []FontEncoding
	Texture gl.GLuint
	YAdvance uint32

	EncodingMap map[int]int
}

func LoadFontFromFile(filename string) (*Font, os.Error) {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}

	return LoadFont(data)
}

func readLittleEndian(r io.Reader, data interface{}) {
	err := binary.Read(r, binary.LittleEndian, data)
	if err != nil {
		panic(err)
	}
}

func LoadFont(data []byte) (fontOut *Font, errOut os.Error) {
	defer func() {
		if err := recover(); err != nil {
			var ok bool
			fontOut = nil
			errOut, ok = err.(os.Error)
			if !ok {
				panic(err)
			}
		}
	}()

	font := new(Font)
	buf := bytes.NewBuffer(data[4:]) // skip magic

	var glyphsNum uint32
	readLittleEndian(buf, &glyphsNum)
	readLittleEndian(buf, &font.YAdvance)

	font.Glyphs = make([]FontGlyph, glyphsNum)
	for i := 0; i < int(glyphsNum); i++ {
		readLittleEndian(buf, &font.Glyphs[i].OffsetX)
		readLittleEndian(buf, &font.Glyphs[i].OffsetY)
		readLittleEndian(buf, &font.Glyphs[i].Width)
		readLittleEndian(buf, &font.Glyphs[i].Height)
		readLittleEndian(buf, &font.Glyphs[i].TX)
		readLittleEndian(buf, &font.Glyphs[i].TY)
		readLittleEndian(buf, &font.Glyphs[i].TX2)
		readLittleEndian(buf, &font.Glyphs[i].TY2)
		readLittleEndian(buf, &font.Glyphs[i].XAdvance)
	}

	font.Encoding = make([]FontEncoding, glyphsNum)
	font.EncodingMap = make(map[int]int, glyphsNum)
	for i := 0; i < int(glyphsNum); i++ {
		readLittleEndian(buf, &font.Encoding[i].Unicode)
		readLittleEndian(buf, &font.Encoding[i].Index)

		font.EncodingMap[int(font.Encoding[i].Unicode)] =
			int(font.Encoding[i].Index)
	}

	img, err := png.Decode(buf)
	if err != nil {
		return nil, err
	}

	nrgba, ok := img.(*image.NRGBA)
	if !ok {
		return nil, os.NewError("Wrong image format")
	}

	font.Texture = uploadTexture_NRGBA32(nrgba)
	return font, nil
}

func drawQuad(x, y, w, h int, u, v, u2, v2 float) {
	gl.Begin(gl.QUADS)

	gl.TexCoord2f(gl.GLfloat(u), gl.GLfloat(v))
	gl.Vertex2i(gl.GLint(x), gl.GLint(y))

	gl.TexCoord2f(gl.GLfloat(u2), gl.GLfloat(v))
	gl.Vertex2i(gl.GLint(x+w), gl.GLint(y))

	gl.TexCoord2f(gl.GLfloat(u2), gl.GLfloat(v2))
	gl.Vertex2i(gl.GLint(x+w), gl.GLint(y+h))

	gl.TexCoord2f(gl.GLfloat(u), gl.GLfloat(v2))
	gl.Vertex2i(gl.GLint(x), gl.GLint(y+h))

	gl.End()
}

func drawGlyph(x, y int, g *FontGlyph) {
	drawQuad(x + int(g.OffsetX), y + int(g.OffsetY), int(g.Width), int(g.Height),
		 float(g.TX), float(g.TY), float(g.TX2), float(g.TY2))
}

func (self *Font) Draw(x, y int, text string) {
	gl.BindTexture(gl.TEXTURE_2D, gl.GLuint(self.Texture))
	for _, rune := range text {
		index, ok := self.EncodingMap[rune]
		if !ok {
			continue
		}

		g := &self.Glyphs[index - 1]
		drawGlyph(x, y, g)
		x += int(g.XAdvance)
	}
	gl.BindTexture(gl.TEXTURE_2D, 0)
}

func (self *Font) Width(text string) int {
	x := 0
	for _, rune := range text {
		index, ok := self.EncodingMap[rune]
		if !ok {
			continue
		}

		x += int(self.Glyphs[index - 1].XAdvance)
	}
	return x
}
<commit_msg>Update image API usage according to Go release 2010-08-11.<commit_after>package main

import (
	"gl"
	"image"
	"image/png"
	"os"
	"io/ioutil"
	"bytes"
	"io"
	"encoding/binary"
	"unsafe"
)

func uploadTexture_NRGBA32(img *image.NRGBA) gl.GLuint {
	b := img.Bounds()
	data := make([]uint8, b.Max.X * b.Max.Y * 4)
	for y := 0; y < b.Max.Y; y++ {
		for x := 0; x < b.Max.X; x++ {
			p := &img.Pix[y * img.Stride + x]
			offset := y * b.Max.X * 4 + x * 4
			data[offset+0] = p.R;
			data[offset+1] = p.G;
			data[offset+2] = p.B;
			data[offset+3] = p.A;
		}
	}

	var id gl.GLuint

	gl.GenTextures(1, &id)
	gl.BindTexture(gl.TEXTURE_2D, id)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE)
	gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.GLsizei(b.Max.X), gl.GLsizei(b.Max.Y), 0, gl.RGBA,
		      gl.UNSIGNED_BYTE, unsafe.Pointer(&data[0]))

	if gl.GetError() != gl.NO_ERROR {
		gl.DeleteTextures(1, &id)
		panic(os.NewError("Failed to load a texture"))
		return 0
	}
	return id
}

//-------------------------------------------------------------------------
// FontGlyph
//-------------------------------------------------------------------------

type FontGlyph struct {
	OffsetX int32
	OffsetY int32
	Width uint32
	Height uint32

	// texture coords
	TX float32
	TY float32
	TX2 float32
	TY2 float32

	XAdvance uint32
}

type FontEncoding struct {
	Unicode uint32
	Index uint32
}

type Font struct {
	Glyphs []FontGlyph

	// I'm keeping it here because original font implementation
	// uses binary search lookups in that array, but here in Go I will
	// simply use a map for that
	Encoding []FontEncoding
	Texture gl.GLuint
	YAdvance uint32

	EncodingMap map[int]int
}

func LoadFontFromFile(filename string) (*Font, os.Error) {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}

	return LoadFont(data)
}

func readLittleEndian(r io.Reader, data interface{}) {
	err := binary.Read(r, binary.LittleEndian, data)
	if err != nil {
		panic(err)
	}
}

func LoadFont(data []byte) (fontOut *Font, errOut os.Error) {
	defer func() {
		if err := recover(); err != nil {
			var ok bool
			fontOut = nil
			errOut, ok = err.(os.Error)
			if !ok {
				panic(err)
			}
		}
	}()

	font := new(Font)
	buf := bytes.NewBuffer(data[4:]) // skip magic

	var glyphsNum uint32
	readLittleEndian(buf, &glyphsNum)
	readLittleEndian(buf, &font.YAdvance)

	font.Glyphs = make([]FontGlyph, glyphsNum)
	for i := 0; i < int(glyphsNum); i++ {
		readLittleEndian(buf, &font.Glyphs[i].OffsetX)
		readLittleEndian(buf, &font.Glyphs[i].OffsetY)
		readLittleEndian(buf, &font.Glyphs[i].Width)
		readLittleEndian(buf, &font.Glyphs[i].Height)
		readLittleEndian(buf, &font.Glyphs[i].TX)
		readLittleEndian(buf, &font.Glyphs[i].TY)
		readLittleEndian(buf, &font.Glyphs[i].TX2)
		readLittleEndian(buf, &font.Glyphs[i].TY2)
		readLittleEndian(buf, &font.Glyphs[i].XAdvance)
	}

	font.Encoding = make([]FontEncoding, glyphsNum)
	font.EncodingMap = make(map[int]int, glyphsNum)
	for i := 0; i < int(glyphsNum); i++ {
		readLittleEndian(buf, &font.Encoding[i].Unicode)
		readLittleEndian(buf, &font.Encoding[i].Index)

		font.EncodingMap[int(font.Encoding[i].Unicode)] =
			int(font.Encoding[i].Index)
	}

	img, err := png.Decode(buf)
	if err != nil {
		return nil, err
	}

	nrgba, ok := img.(*image.NRGBA)
	if !ok {
		return nil, os.NewError("Wrong image format")
	}

	font.Texture = uploadTexture_NRGBA32(nrgba)
	return font, nil
}

func drawQuad(x, y, w, h int, u, v, u2, v2 float) {
	gl.Begin(gl.QUADS)

	gl.TexCoord2f(gl.GLfloat(u), gl.GLfloat(v))
	gl.Vertex2i(gl.GLint(x), gl.GLint(y))

	gl.TexCoord2f(gl.GLfloat(u2), gl.GLfloat(v))
	gl.Vertex2i(gl.GLint(x+w), gl.GLint(y))

	gl.TexCoord2f(gl.GLfloat(u2), gl.GLfloat(v2))
	gl.Vertex2i(gl.GLint(x+w), gl.GLint(y+h))

	gl.TexCoord2f(gl.GLfloat(u), gl.GLfloat(v2))
	gl.Vertex2i(gl.GLint(x), gl.GLint(y+h))

	gl.End()
}

func drawGlyph(x, y int, g *FontGlyph) {
	drawQuad(x + int(g.OffsetX), y + int(g.OffsetY), int(g.Width), int(g.Height),
		 float(g.TX), float(g.TY), float(g.TX2), float(g.TY2))
}

func (self *Font) Draw(x, y int, text string) {
	gl.BindTexture(gl.TEXTURE_2D, gl.GLuint(self.Texture))
	for _, rune := range text {
		index, ok := self.EncodingMap[rune]
		if !ok {
			continue
		}

		g := &self.Glyphs[index - 1]
		drawGlyph(x, y, g)
		x += int(g.XAdvance)
	}
	gl.BindTexture(gl.TEXTURE_2D, 0)
}

func (self *Font) Width(text string) int {
	x := 0
	for _, rune := range text {
		index, ok := self.EncodingMap[rune]
		if !ok {
			continue
		}

		x += int(self.Glyphs[index - 1].XAdvance)
	}
	return x
}
<|endoftext|>


<commit_before>package immortal

import (
	"os"
	"os/exec"
	"syscall"
)

// Fork crete a new process
func Fork() (int, error) {
	args := os.Args[1:]
	cmd := exec.Command(os.Args[0], args...)
	cmd.Env = append(cmd.Env, os.Environ()...)
	cmd.Stdin = nil
	cmd.Stdout = nil
	cmd.Stderr = nil
	cmd.ExtraFiles = nil
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Setsid: true,
	}
	if err := cmd.Start(); err != nil {
		return 0, err
	}
	return cmd.Process.Pid, nil
}
<commit_msg>	modified:   fork.go<commit_after>package immortal

import (
	"os"
	"os/exec"
	"syscall"
)

// Fork crete a new process
func Fork() (int, error) {
	args := os.Args[1:]
	cmd := exec.Command(os.Args[0], args...)
	cmd.Env = append(cmd.Env, os.Environ()...)
	cmd.Stdin = nil
	cmd.Stdout = nil
	cmd.Stderr = nil
	cmd.ExtraFiles = nil
	// setsid is used to detach the process from the parent (normally a shell)
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Setsid: true,
	}
	if err := cmd.Start(); err != nil {
		return 0, err
	}
	return cmd.Process.Pid, nil
}
<|endoftext|>


<commit_before>package send

import (
	"log"

	"github.com/mongodb/grip/message"
)

// ErrorHandler is a function that you can use define how a sender
// handles errors sending messages. Implementations of this type
// should perform a noop if the err object is nil.
type ErrorHandler func(error, message.Composer)

func ErrorHandlerFromLogger(l *log.Logger) ErrorHandler {
	return func(err error, m message.Composer) {
		if err == nil {
			return
		}

		l.Println("logging error:", err.Error())
		l.Println(m.String())
	}
}

// ErrorHandler wraps an existing Sender for sending error messages.
func ErrorHandlerFromSender(s Sender) ErrorHandler {
	return func(err error, m message.Composer) {
		if err == nil {
			return
		}

		s.Send(message.WrapError(err, m))
	}
}
<commit_msg>MAKE-1150: fix lint-send<commit_after>package send

import (
	"log"

	"github.com/mongodb/grip/message"
)

// ErrorHandler is a function that you can use define how a sender
// handles errors sending messages. Implementations of this type
// should perform a noop if the err object is nil.
type ErrorHandler func(error, message.Composer)

func ErrorHandlerFromLogger(l *log.Logger) ErrorHandler {
	return func(err error, m message.Composer) {
		if err == nil {
			return
		}

		l.Println("logging error:", err.Error())
		l.Println(m.String())
	}
}

// ErrorHandlerFromSender wraps an existing Sender for sending error messages.
func ErrorHandlerFromSender(s Sender) ErrorHandler {
	return func(err error, m message.Composer) {
		if err == nil {
			return
		}

		s.Send(message.WrapError(err, m))
	}
}
<|endoftext|>


<commit_before>package metrics

import (
	"strings"
	"sync"
	"time"

	"github.com/prometheus/client_golang/prometheus"
)

type PrometheusSink struct {
	mu        sync.Mutex
	gauges    map[string]prometheus.Gauge
	summaries map[string]prometheus.Summary
	counters  map[string]prometheus.Counter
}

func NewPrometheusSink() (*PrometheusSink, error) {
	return &PrometheusSink{
		gauges:    make(map[string]prometheus.Gauge),
		summaries: make(map[string]prometheus.Summary),
		counters:  make(map[string]prometheus.Counter),
	}, nil
}

func (p *PrometheusSink) flattenKey(parts []string) string {
	joined := strings.Join(parts, "_")
	return strings.Replace(strings.Replace(joined, " ", "_", -1), ".", "_", -1)
}

func (p *PrometheusSink) SetGauge(parts []string, val float32) {
	p.mu.Lock()
	defer p.mu.Unlock()
	key := p.flattenKey(parts)
	g, ok := p.gauges[key]
	if !ok {
		g = prometheus.NewGauge(prometheus.GaugeOpts{
			Name: key,
			Help: key,
		})
		prometheus.MustRegister(g)
		p.gauges[key] = g
	}
	g.Set(float64(val))
}

func (p *PrometheusSink) AddSample(parts []string, val float32) {
	p.mu.Lock()
	defer p.mu.Unlock()
	key := p.flattenKey(parts)
	g, ok := p.summaries[key]
	if !ok {
		g = prometheus.NewSummary(prometheus.SummaryOpts{
			Name:   key,
			Help:   key,
			MaxAge: 10 * time.Second,
		})
		prometheus.MustRegister(g)
		p.summaries[key] = g
	}
	g.Observe(float64(val))
}

// EmitKey is not implemented. Prometheus doesn’t offer a type for which an
// arbitrary number of values is retained, as Prometheus works with a pull
// model, rather than a push model.
func (p *PrometheusSink) EmitKey(key []string, val float32) {
}

func (p *PrometheusSink) IncrCounter(parts []string, val float32) {
	p.mu.Lock()
	defer p.mu.Unlock()
	key := p.flattenKey(parts)
	g, ok := p.counters[key]
	if !ok {
		g = prometheus.NewCounter(prometheus.CounterOpts{
			Name: key,
			Help: key,
		})
		prometheus.MustRegister(g)
		p.counters[key] = g
	}
	g.Add(float64(val))
}
<commit_msg>Only build PrometheusSink on Go >=1.3.<commit_after>// +build go1.3

package metrics

import (
	"strings"
	"sync"
	"time"

	"github.com/prometheus/client_golang/prometheus"
)

type PrometheusSink struct {
	mu        sync.Mutex
	gauges    map[string]prometheus.Gauge
	summaries map[string]prometheus.Summary
	counters  map[string]prometheus.Counter
}

func NewPrometheusSink() (*PrometheusSink, error) {
	return &PrometheusSink{
		gauges:    make(map[string]prometheus.Gauge),
		summaries: make(map[string]prometheus.Summary),
		counters:  make(map[string]prometheus.Counter),
	}, nil
}

func (p *PrometheusSink) flattenKey(parts []string) string {
	joined := strings.Join(parts, "_")
	return strings.Replace(strings.Replace(joined, " ", "_", -1), ".", "_", -1)
}

func (p *PrometheusSink) SetGauge(parts []string, val float32) {
	p.mu.Lock()
	defer p.mu.Unlock()
	key := p.flattenKey(parts)
	g, ok := p.gauges[key]
	if !ok {
		g = prometheus.NewGauge(prometheus.GaugeOpts{
			Name: key,
			Help: key,
		})
		prometheus.MustRegister(g)
		p.gauges[key] = g
	}
	g.Set(float64(val))
}

func (p *PrometheusSink) AddSample(parts []string, val float32) {
	p.mu.Lock()
	defer p.mu.Unlock()
	key := p.flattenKey(parts)
	g, ok := p.summaries[key]
	if !ok {
		g = prometheus.NewSummary(prometheus.SummaryOpts{
			Name:   key,
			Help:   key,
			MaxAge: 10 * time.Second,
		})
		prometheus.MustRegister(g)
		p.summaries[key] = g
	}
	g.Observe(float64(val))
}

// EmitKey is not implemented. Prometheus doesn’t offer a type for which an
// arbitrary number of values is retained, as Prometheus works with a pull
// model, rather than a push model.
func (p *PrometheusSink) EmitKey(key []string, val float32) {
}

func (p *PrometheusSink) IncrCounter(parts []string, val float32) {
	p.mu.Lock()
	defer p.mu.Unlock()
	key := p.flattenKey(parts)
	g, ok := p.counters[key]
	if !ok {
		g = prometheus.NewCounter(prometheus.CounterOpts{
			Name: key,
			Help: key,
		})
		prometheus.MustRegister(g)
		p.counters[key] = g
	}
	g.Add(float64(val))
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	debug                        bool
	verbose                      bool
	info                         bool
	quiet                        bool
	force                        bool
	usemove                      bool
	usecacheFallback             bool
	retryGitCommands             bool
	pfMode                       bool
	pfLocation                   string
	dryRun                       bool
	validate                     bool
	check4update                 bool
	checkSum                     bool
	gitObjectSyntaxNotSupported  bool
	moduleDirParam               string
	cacheDirParam                string
	branchParam                  string
	tags                         bool
	outputNameParam              string
	moduleParam                  string
	configFile                   string
	config                       ConfigSettings
	mutex                        sync.Mutex
	empty                        struct{}
	syncGitCount                 int
	syncForgeCount               int
	needSyncGitCount             int
	needSyncForgeCount           int
	needSyncDirs                 []string
	needSyncEnvs                 map[string]struct{}
	syncGitTime                  float64
	syncForgeTime                float64
	ioGitTime                    float64
	ioForgeTime                  float64
	forgeJSONParseTime           float64
	metadataJSONParseTime        float64
	gmetadataJSONParseTime       float64
	buildtime                    string
	uniqueForgeModules           map[string]ForgeModule
	latestForgeModules           LatestForgeModules
	maxworker                    int
	maxExtractworker             int
	forgeModuleDeprecationNotice string
)

// LatestForgeModules contains a map of unique Forge modules
// that should be the latest versions of them
type LatestForgeModules struct {
	sync.RWMutex
	m map[string]string
}

// ConfigSettings contains the key value pairs from the g10k config file
type ConfigSettings struct {
	CacheDir                    string `yaml:"cachedir"`
	ForgeCacheDir               string
	ModulesCacheDir             string
	EnvCacheDir                 string
	Git                         Git
	Forge                       Forge
	Sources                     map[string]Source
	Timeout                     int      `yaml:"timeout"`
	IgnoreUnreachableModules    bool     `yaml:"ignore_unreachable_modules"`
	Maxworker                   int      `yaml:"maxworker"`
	MaxExtractworker            int      `yaml:"maxextractworker"`
	UseCacheFallback            bool     `yaml:"use_cache_fallback"`
	RetryGitCommands            bool     `yaml:"retry_git_commands"`
	GitObjectSyntaxNotSupported bool     `yaml:"git_object_syntax_not_supported"`
	PostRunCommand              []string `yaml:"postrun"`
}

// Forge is a simple struct that contains the base URL of
// the Forge that g10k should use. Defaults to: https://forgeapi.puppetlabs.com
type Forge struct {
	Baseurl string `yaml:"baseurl"`
}

// Git is a simple struct that contains the optional SSH private key to
// use for authentication
type Git struct {
	privateKey string `yaml:"private_key"`
}

// Source contains basic information about a Puppet environment repository
type Source struct {
	Remote                      string
	Basedir                     string
	Prefix                      string
	PrivateKey                  string `yaml:"private_key"`
	ForceForgeVersions          bool   `yaml:"force_forge_versions"`
	WarnMissingBranch           bool   `yaml:"warn_if_branch_is_missing"`
	ExitIfUnreachable           bool   `yaml:"exit_if_unreachable"`
	AutoCorrectEnvironmentNames string `yaml:"invalid_branches"`
}

// Puppetfile contains the key value pairs from the Puppetfile
type Puppetfile struct {
	forgeBaseURL      string
	forgeCacheTTL     time.Duration
	forgeModules      map[string]ForgeModule
	gitModules        map[string]GitModule
	privateKey        string
	source            string
	workDir           string
	moduleDirs        []string
	controlRepoBranch string
}

// ForgeModule contains information (Version, Name, Author, md5 checksum, file size of the tar.gz archive, Forge BaseURL if custom) about a Puppetlabs Forge module
type ForgeModule struct {
	version   string
	name      string
	author    string
	md5sum    string
	fileSize  int64
	baseURL   string
	cacheTTL  time.Duration
	sha256sum string
	moduleDir string
}

// GitModule contains information about a Git Puppet module
type GitModule struct {
	privateKey        string
	git               string
	branch            string
	tag               string
	commit            string
	ref               string
	link              bool
	ignoreUnreachable bool
	fallback          []string
	installPath       string
	local             bool
	moduleDir         string
}

// ForgeResult is returned by queryForgeAPI and contains if and which version of the Puppetlabs Forge module needs to be downloaded
type ForgeResult struct {
	needToGet     bool
	versionNumber string
	md5sum        string
	fileSize      int64
}

// ExecResult contains the exit code and output of an external command (e.g. git)
type ExecResult struct {
	returnCode int
	output     string
}

func init() {
	// initialize global maps
	needSyncEnvs = make(map[string]struct{})
	uniqueForgeModules = make(map[string]ForgeModule)
}

func main() {

	var (
		configFileFlag = flag.String("config", "", "which config file to use")
		versionFlag    = flag.Bool("version", false, "show build time and version number")
	)
	flag.StringVar(&branchParam, "branch", "", "which git branch of the Puppet environment to update, e.g. core_foobar")
	flag.BoolVar(&tags, "tags", false, "to pull tags as well as branches")
	flag.StringVar(&outputNameParam, "outputname", "", "overwrite the environment name if -branch is specified")
	flag.StringVar(&moduleParam, "module", "", "which module of the Puppet environment to update, e.g. stdlib")
	flag.StringVar(&moduleDirParam, "moduledir", "", "allows overriding of Puppetfile specific moduledir setting, the folder in which Puppet modules will be extracted")
	flag.StringVar(&cacheDirParam, "cachedir", "", "allows overriding of the g10k config file cachedir setting, the folder in which g10k will download git repositories and Forge modules")
	flag.IntVar(&maxworker, "maxworker", 50, "how many Goroutines are allowed to run in parallel for Git and Forge module resolving")
	flag.IntVar(&maxExtractworker, "maxextractworker", 20, "how many Goroutines are allowed to run in parallel for local Git and Forge module extracting processes (git clone, untar and gunzip)")
	flag.BoolVar(&pfMode, "puppetfile", false, "install all modules from Puppetfile in cwd")
	flag.StringVar(&pfLocation, "puppetfilelocation", "./Puppetfile", "which Puppetfile to use in -puppetfile mode")
	flag.BoolVar(&force, "force", false, "purge the Puppet environment directory and do a full sync")
	flag.BoolVar(&dryRun, "dryrun", false, "do not modify anything, just print what would be changed")
	flag.BoolVar(&validate, "validate", false, "only validate given configuration and exit")
	flag.BoolVar(&usemove, "usemove", false, "do not use hardlinks to populate your Puppet environments with Puppetlabs Forge modules. Instead uses simple move commands and purges the Forge cache directory after each run! (Useful for g10k runs inside a Docker container)")
	flag.BoolVar(&check4update, "check4update", false, "only check if the is newer version of the Puppet module avaialable. Does implicitly set dryrun to true")
	flag.BoolVar(&checkSum, "checksum", false, "get the md5 check sum for each Puppetlabs Forge module and verify the integrity of the downloaded archive. Increases g10k run time!")
	flag.BoolVar(&debug, "debug", false, "log debug output, defaults to false")
	flag.BoolVar(&verbose, "verbose", false, "log verbose output, defaults to false")
	flag.BoolVar(&info, "info", false, "log info output, defaults to false")
	flag.BoolVar(&quiet, "quiet", false, "no output, defaults to false")
	flag.BoolVar(&usecacheFallback, "usecachefallback", false, "if g10k should try to use its cache for sources and modules instead of failing")
	flag.BoolVar(&retryGitCommands, "retrygitcommands", false, "if g10k should purge the local repository and retry a failed git command (clone or remote update) instead of failing")
	flag.BoolVar(&gitObjectSyntaxNotSupported, "gitobjectsyntaxnotsupported", false, "if your git version is too old to support reference syntax like master^{object} use this setting to revert to the older syntax")
	flag.Parse()

	configFile = *configFileFlag
	version := *versionFlag

	if version {
		fmt.Println("g10k version 0.5.9 Build time:", buildtime, "UTC")
		os.Exit(0)
	}

	if check4update {
		dryRun = true
	}

	// check for git executable dependency
	if _, err := exec.LookPath("git"); err != nil {
		Fatalf("Error: could not find 'git' executable in PATH")
	}

	target := ""
	before := time.Now()
	if len(configFile) > 0 {
		if usemove {
			Fatalf("Error: -usemove parameter is only allowed in -puppetfile mode!")
		}
		if pfMode {
			Fatalf("Error: -puppetfile parameter is not allowed with -config parameter!")
		}
		if (len(outputNameParam) > 0) && (len(branchParam) == 0) {
			Fatalf("Error: -outputname specified without -branch!")
		}
		if usecacheFallback {
			config.UseCacheFallback = true
		}
		Debugf("Using as config file: " + configFile)
		config = readConfigfile(configFile)
		target = configFile
		if len(branchParam) > 0 {
			resolvePuppetEnvironment(branchParam, tags, outputNameParam)
			target += " with branch " + branchParam
		} else {
			resolvePuppetEnvironment("", tags, "")
		}
	} else {
		if pfMode {
			Debugf("Trying to use as Puppetfile: " + pfLocation)
			sm := make(map[string]Source)
			sm["cmdlineparam"] = Source{Basedir: "./"}
			cachedir := "/tmp/g10k"
			if len(os.Getenv("g10k_cachedir")) > 0 {
				cachedir = os.Getenv("g10k_cachedir")
				cachedir = checkDirAndCreate(cachedir, "cachedir environment variable g10k_cachedir")
				Debugf("Found environment variable g10k_cachedir set to: " + cachedir)
			} else if len(cacheDirParam) > 0 {
				Debugf("Using -cachedir parameter set to : " + cacheDirParam)
				cachedir = checkDirAndCreate(cacheDirParam, "cachedir CLI param")
			} else {
				cachedir = checkDirAndCreate(cachedir, "cachedir default value")
			}
			forgeDefaultSettings := Forge{Baseurl: "https://forgeapi.puppetlabs.com"}
			config = ConfigSettings{CacheDir: cachedir, ForgeCacheDir: cachedir, ModulesCacheDir: cachedir, EnvCacheDir: cachedir, Sources: sm, Forge: forgeDefaultSettings, Maxworker: maxworker, UseCacheFallback: usecacheFallback, MaxExtractworker: maxExtractworker, RetryGitCommands: retryGitCommands, GitObjectSyntaxNotSupported: gitObjectSyntaxNotSupported}
			target = pfLocation
			puppetfile := readPuppetfile(target, "", "cmdlineparam", false, false)
			puppetfile.workDir = "./"
			pfm := make(map[string]Puppetfile)
			pfm["cmdlineparam"] = puppetfile
			resolvePuppetfile(pfm)
		} else {
			Fatalf("Error: you need to specify at least a config file or use the Puppetfile mode\nExample call: " + os.Args[0] + " -config test.yaml or " + os.Args[0] + " -puppetfile\n")
		}
	}

	if usemove {
		// we can not reuse the Forge cache at all when -usemove gets used, because we can not delete the -latest link for some reason
		defer purgeDir(config.ForgeCacheDir, "main() -puppetfile mode with -usemove parameter")
	}

	Debugf("Forge response JSON parsing took " + strconv.FormatFloat(forgeJSONParseTime, 'f', 4, 64) + " seconds")
	Debugf("Forge modules metadata.json parsing took " + strconv.FormatFloat(metadataJSONParseTime, 'f', 4, 64) + " seconds")

	if !check4update && !quiet {
		if len(forgeModuleDeprecationNotice) > 0 {
			Warnf(strings.TrimSuffix(forgeModuleDeprecationNotice, "\n"))
		}
		fmt.Println("Synced", target, "with", syncGitCount, "git repositories and", syncForgeCount, "Forge modules in "+strconv.FormatFloat(time.Since(before).Seconds(), 'f', 1, 64)+"s with git ("+strconv.FormatFloat(syncGitTime, 'f', 1, 64)+"s sync, I/O", strconv.FormatFloat(ioGitTime, 'f', 1, 64)+"s) and Forge ("+strconv.FormatFloat(syncForgeTime, 'f', 1, 64)+"s query+download, I/O", strconv.FormatFloat(ioForgeTime, 'f', 1, 64)+"s) using", strconv.Itoa(config.Maxworker), "resolv and", strconv.Itoa(config.MaxExtractworker), "extract workers")
	}
	if dryRun && (needSyncForgeCount > 0 || needSyncGitCount > 0) {
		os.Exit(1)
	}

	checkForAndExecutePostrunCommand()
}
<commit_msg>bump version to v0.6<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	debug                        bool
	verbose                      bool
	info                         bool
	quiet                        bool
	force                        bool
	usemove                      bool
	usecacheFallback             bool
	retryGitCommands             bool
	pfMode                       bool
	pfLocation                   string
	dryRun                       bool
	validate                     bool
	check4update                 bool
	checkSum                     bool
	gitObjectSyntaxNotSupported  bool
	moduleDirParam               string
	cacheDirParam                string
	branchParam                  string
	tags                         bool
	outputNameParam              string
	moduleParam                  string
	configFile                   string
	config                       ConfigSettings
	mutex                        sync.Mutex
	empty                        struct{}
	syncGitCount                 int
	syncForgeCount               int
	needSyncGitCount             int
	needSyncForgeCount           int
	needSyncDirs                 []string
	needSyncEnvs                 map[string]struct{}
	syncGitTime                  float64
	syncForgeTime                float64
	ioGitTime                    float64
	ioForgeTime                  float64
	forgeJSONParseTime           float64
	metadataJSONParseTime        float64
	gmetadataJSONParseTime       float64
	buildtime                    string
	uniqueForgeModules           map[string]ForgeModule
	latestForgeModules           LatestForgeModules
	maxworker                    int
	maxExtractworker             int
	forgeModuleDeprecationNotice string
)

// LatestForgeModules contains a map of unique Forge modules
// that should be the latest versions of them
type LatestForgeModules struct {
	sync.RWMutex
	m map[string]string
}

// ConfigSettings contains the key value pairs from the g10k config file
type ConfigSettings struct {
	CacheDir                    string `yaml:"cachedir"`
	ForgeCacheDir               string
	ModulesCacheDir             string
	EnvCacheDir                 string
	Git                         Git
	Forge                       Forge
	Sources                     map[string]Source
	Timeout                     int      `yaml:"timeout"`
	IgnoreUnreachableModules    bool     `yaml:"ignore_unreachable_modules"`
	Maxworker                   int      `yaml:"maxworker"`
	MaxExtractworker            int      `yaml:"maxextractworker"`
	UseCacheFallback            bool     `yaml:"use_cache_fallback"`
	RetryGitCommands            bool     `yaml:"retry_git_commands"`
	GitObjectSyntaxNotSupported bool     `yaml:"git_object_syntax_not_supported"`
	PostRunCommand              []string `yaml:"postrun"`
}

// Forge is a simple struct that contains the base URL of
// the Forge that g10k should use. Defaults to: https://forgeapi.puppetlabs.com
type Forge struct {
	Baseurl string `yaml:"baseurl"`
}

// Git is a simple struct that contains the optional SSH private key to
// use for authentication
type Git struct {
	privateKey string `yaml:"private_key"`
}

// Source contains basic information about a Puppet environment repository
type Source struct {
	Remote                      string
	Basedir                     string
	Prefix                      string
	PrivateKey                  string `yaml:"private_key"`
	ForceForgeVersions          bool   `yaml:"force_forge_versions"`
	WarnMissingBranch           bool   `yaml:"warn_if_branch_is_missing"`
	ExitIfUnreachable           bool   `yaml:"exit_if_unreachable"`
	AutoCorrectEnvironmentNames string `yaml:"invalid_branches"`
}

// Puppetfile contains the key value pairs from the Puppetfile
type Puppetfile struct {
	forgeBaseURL      string
	forgeCacheTTL     time.Duration
	forgeModules      map[string]ForgeModule
	gitModules        map[string]GitModule
	privateKey        string
	source            string
	workDir           string
	moduleDirs        []string
	controlRepoBranch string
}

// ForgeModule contains information (Version, Name, Author, md5 checksum, file size of the tar.gz archive, Forge BaseURL if custom) about a Puppetlabs Forge module
type ForgeModule struct {
	version   string
	name      string
	author    string
	md5sum    string
	fileSize  int64
	baseURL   string
	cacheTTL  time.Duration
	sha256sum string
	moduleDir string
}

// GitModule contains information about a Git Puppet module
type GitModule struct {
	privateKey        string
	git               string
	branch            string
	tag               string
	commit            string
	ref               string
	link              bool
	ignoreUnreachable bool
	fallback          []string
	installPath       string
	local             bool
	moduleDir         string
}

// ForgeResult is returned by queryForgeAPI and contains if and which version of the Puppetlabs Forge module needs to be downloaded
type ForgeResult struct {
	needToGet     bool
	versionNumber string
	md5sum        string
	fileSize      int64
}

// ExecResult contains the exit code and output of an external command (e.g. git)
type ExecResult struct {
	returnCode int
	output     string
}

func init() {
	// initialize global maps
	needSyncEnvs = make(map[string]struct{})
	uniqueForgeModules = make(map[string]ForgeModule)
}

func main() {

	var (
		configFileFlag = flag.String("config", "", "which config file to use")
		versionFlag    = flag.Bool("version", false, "show build time and version number")
	)
	flag.StringVar(&branchParam, "branch", "", "which git branch of the Puppet environment to update, e.g. core_foobar")
	flag.BoolVar(&tags, "tags", false, "to pull tags as well as branches")
	flag.StringVar(&outputNameParam, "outputname", "", "overwrite the environment name if -branch is specified")
	flag.StringVar(&moduleParam, "module", "", "which module of the Puppet environment to update, e.g. stdlib")
	flag.StringVar(&moduleDirParam, "moduledir", "", "allows overriding of Puppetfile specific moduledir setting, the folder in which Puppet modules will be extracted")
	flag.StringVar(&cacheDirParam, "cachedir", "", "allows overriding of the g10k config file cachedir setting, the folder in which g10k will download git repositories and Forge modules")
	flag.IntVar(&maxworker, "maxworker", 50, "how many Goroutines are allowed to run in parallel for Git and Forge module resolving")
	flag.IntVar(&maxExtractworker, "maxextractworker", 20, "how many Goroutines are allowed to run in parallel for local Git and Forge module extracting processes (git clone, untar and gunzip)")
	flag.BoolVar(&pfMode, "puppetfile", false, "install all modules from Puppetfile in cwd")
	flag.StringVar(&pfLocation, "puppetfilelocation", "./Puppetfile", "which Puppetfile to use in -puppetfile mode")
	flag.BoolVar(&force, "force", false, "purge the Puppet environment directory and do a full sync")
	flag.BoolVar(&dryRun, "dryrun", false, "do not modify anything, just print what would be changed")
	flag.BoolVar(&validate, "validate", false, "only validate given configuration and exit")
	flag.BoolVar(&usemove, "usemove", false, "do not use hardlinks to populate your Puppet environments with Puppetlabs Forge modules. Instead uses simple move commands and purges the Forge cache directory after each run! (Useful for g10k runs inside a Docker container)")
	flag.BoolVar(&check4update, "check4update", false, "only check if the is newer version of the Puppet module avaialable. Does implicitly set dryrun to true")
	flag.BoolVar(&checkSum, "checksum", false, "get the md5 check sum for each Puppetlabs Forge module and verify the integrity of the downloaded archive. Increases g10k run time!")
	flag.BoolVar(&debug, "debug", false, "log debug output, defaults to false")
	flag.BoolVar(&verbose, "verbose", false, "log verbose output, defaults to false")
	flag.BoolVar(&info, "info", false, "log info output, defaults to false")
	flag.BoolVar(&quiet, "quiet", false, "no output, defaults to false")
	flag.BoolVar(&usecacheFallback, "usecachefallback", false, "if g10k should try to use its cache for sources and modules instead of failing")
	flag.BoolVar(&retryGitCommands, "retrygitcommands", false, "if g10k should purge the local repository and retry a failed git command (clone or remote update) instead of failing")
	flag.BoolVar(&gitObjectSyntaxNotSupported, "gitobjectsyntaxnotsupported", false, "if your git version is too old to support reference syntax like master^{object} use this setting to revert to the older syntax")
	flag.Parse()

	configFile = *configFileFlag
	version := *versionFlag

	if version {
		fmt.Println("g10k version 0.6 Build time:", buildtime, "UTC")
		os.Exit(0)
	}

	if check4update {
		dryRun = true
	}

	// check for git executable dependency
	if _, err := exec.LookPath("git"); err != nil {
		Fatalf("Error: could not find 'git' executable in PATH")
	}

	target := ""
	before := time.Now()
	if len(configFile) > 0 {
		if usemove {
			Fatalf("Error: -usemove parameter is only allowed in -puppetfile mode!")
		}
		if pfMode {
			Fatalf("Error: -puppetfile parameter is not allowed with -config parameter!")
		}
		if (len(outputNameParam) > 0) && (len(branchParam) == 0) {
			Fatalf("Error: -outputname specified without -branch!")
		}
		if usecacheFallback {
			config.UseCacheFallback = true
		}
		Debugf("Using as config file: " + configFile)
		config = readConfigfile(configFile)
		target = configFile
		if len(branchParam) > 0 {
			resolvePuppetEnvironment(branchParam, tags, outputNameParam)
			target += " with branch " + branchParam
		} else {
			resolvePuppetEnvironment("", tags, "")
		}
	} else {
		if pfMode {
			Debugf("Trying to use as Puppetfile: " + pfLocation)
			sm := make(map[string]Source)
			sm["cmdlineparam"] = Source{Basedir: "./"}
			cachedir := "/tmp/g10k"
			if len(os.Getenv("g10k_cachedir")) > 0 {
				cachedir = os.Getenv("g10k_cachedir")
				cachedir = checkDirAndCreate(cachedir, "cachedir environment variable g10k_cachedir")
				Debugf("Found environment variable g10k_cachedir set to: " + cachedir)
			} else if len(cacheDirParam) > 0 {
				Debugf("Using -cachedir parameter set to : " + cacheDirParam)
				cachedir = checkDirAndCreate(cacheDirParam, "cachedir CLI param")
			} else {
				cachedir = checkDirAndCreate(cachedir, "cachedir default value")
			}
			forgeDefaultSettings := Forge{Baseurl: "https://forgeapi.puppetlabs.com"}
			config = ConfigSettings{CacheDir: cachedir, ForgeCacheDir: cachedir, ModulesCacheDir: cachedir, EnvCacheDir: cachedir, Sources: sm, Forge: forgeDefaultSettings, Maxworker: maxworker, UseCacheFallback: usecacheFallback, MaxExtractworker: maxExtractworker, RetryGitCommands: retryGitCommands, GitObjectSyntaxNotSupported: gitObjectSyntaxNotSupported}
			target = pfLocation
			puppetfile := readPuppetfile(target, "", "cmdlineparam", false, false)
			puppetfile.workDir = "./"
			pfm := make(map[string]Puppetfile)
			pfm["cmdlineparam"] = puppetfile
			resolvePuppetfile(pfm)
		} else {
			Fatalf("Error: you need to specify at least a config file or use the Puppetfile mode\nExample call: " + os.Args[0] + " -config test.yaml or " + os.Args[0] + " -puppetfile\n")
		}
	}

	if usemove {
		// we can not reuse the Forge cache at all when -usemove gets used, because we can not delete the -latest link for some reason
		defer purgeDir(config.ForgeCacheDir, "main() -puppetfile mode with -usemove parameter")
	}

	Debugf("Forge response JSON parsing took " + strconv.FormatFloat(forgeJSONParseTime, 'f', 4, 64) + " seconds")
	Debugf("Forge modules metadata.json parsing took " + strconv.FormatFloat(metadataJSONParseTime, 'f', 4, 64) + " seconds")

	if !check4update && !quiet {
		if len(forgeModuleDeprecationNotice) > 0 {
			Warnf(strings.TrimSuffix(forgeModuleDeprecationNotice, "\n"))
		}
		fmt.Println("Synced", target, "with", syncGitCount, "git repositories and", syncForgeCount, "Forge modules in "+strconv.FormatFloat(time.Since(before).Seconds(), 'f', 1, 64)+"s with git ("+strconv.FormatFloat(syncGitTime, 'f', 1, 64)+"s sync, I/O", strconv.FormatFloat(ioGitTime, 'f', 1, 64)+"s) and Forge ("+strconv.FormatFloat(syncForgeTime, 'f', 1, 64)+"s query+download, I/O", strconv.FormatFloat(ioForgeTime, 'f', 1, 64)+"s) using", strconv.Itoa(config.Maxworker), "resolv and", strconv.Itoa(config.MaxExtractworker), "extract workers")
	}
	if dryRun && (needSyncForgeCount > 0 || needSyncGitCount > 0) {
		os.Exit(1)
	}

	checkForAndExecutePostrunCommand()
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

import (
	`fmt`
	`strings`
	`time`
)

type History [14][64]int
type Killers [MaxPly][2]Move
type RootPv  []Move
type Pv      [MaxPly]RootPv

type Game struct {
	nodes       int 	// Number of regular nodes searched.
	qnodes      int 	// Number of quiescence nodes searched.
	token       uint8 	// Cache's expiration token.
	deepening   bool 	// True when searching first root move.
	improving   bool 	// True when root search score is not falling.
	volatility  float32 	// Root search stability count.
	initial     string   	// Initial position (FEN or algebraic).
	history     History  	// Good moves history.
	killers     Killers  	// Killer moves.
	rootpv      RootPv 	// Principal variation for root moves.
	pv          Pv 		// Principal variations for each ply.
	cache       Cache 	// Transposition table.
	pawnCache   PawnCache 	// Cache of pawn structures.
}

// Use single statically allocated variable.
var game Game

// We have two ways to initialize the game: 1) pass FEN string, and 2) specify
// white and black pieces using regular chess notation.
//
// In latter case we need to tell who gets to move first when starting the game.
// The second option is a bit less pricise (ex. no en-passant square) but it is
// much more useful when writing tests from memory.
func NewGame(args ...string) *Game {
	game = Game{}
	game.cache = NewCache(engine.cacheSize)
	game.pawnCache = PawnCache{}

	game.rootpv = make([]Move, 0, MaxPly)
	for ply := 0;  ply < MaxPly; ply++ {
		game.pv[ply] = make([]Move, 0, MaxPly)
	}

	switch len(args) {
	case 0: // Initial position.
		game.initial = `rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1`
	case 1: // Genuine FEN.
		game.initial = args[0]
	case 2: // Standard algebraic notation (white and black).
		game.initial = args[0] + `:` + args[1]
	}

	return &game
}

// The color parameter is optional.
func (game *Game) Start(args ...int) *Position {
	engine.clock.halt = false
	tree, node, rootNode = [1024]Position{}, 0, 0

	// Was the game started with FEN or algebraic notation?
	sides := strings.Split(game.initial, `:`)
	if len(sides) == 2 {
		return NewPosition(game, sides[White], sides[Black], args[0])
	}
	return NewPositionFromFEN(game, game.initial)
}

func (game *Game) Position() *Position {
	return &tree[node]
}

// Resets principal variation as well as killer moves and move history. Cache
// entries get expired by incrementing cache token. Root node gets set to the
// current tree node to match the position.
func (game *Game) getReady() *Game {
	game.rootpv = game.rootpv[:0]
	for ply := 0;  ply < MaxPly; ply++ {
		game.pv[ply] = game.pv[ply][:0]
	}

	game.killers = Killers{}
	game.history = History{}
	game.deepening = false
	game.improving = true
	game.volatility = 0.0
	game.token++ // <-- Wraps around: ...254, 255, 0, 1...

	rootNode = node
	return game
}

func (game *Game) Think() Move {
	start := time.Now()
	position := game.Position()
	game.nodes, game.qnodes = 0, 0

	if book, err := NewBook(); err == nil {
		if move := book.pickMove(position); move != 0 {
			game.printBestMove(move, time.Since(start).Seconds())
			return move
		}
	}

	game.getReady()
	score, move, status, alpha, beta := 0, Move(0), InProgress, -Checkmate, Checkmate

	if engine.uci {
		engine.debug(position.String())
	} else {
		fmt.Println(`Depth/Time     Nodes      QNodes     Nodes/s   Score   Best`)
	}

	if !engine.fixedDepth() {
		engine.startClock(); defer engine.stopClock();
	}

	for depth := 1; status == InProgress && game.keepThinking(depth, move); depth++ {
		// Save previous best score in case search gets interrupted.
		bestScore := score

		// Assume volatility decreases with each new iteration.
		game.volatility /= 2.0

		// At low depths do the search with full alpha/beta spread.
		// Aspiration window searches kick in at depth 5 and up.
		if depth < 5 {
			score = position.search(alpha, beta, depth)
			if score > alpha {
				bestScore = score
				game.rootpv = append(game.rootpv[:0], game.pv[0]...)
			}
		} else {
			aspiration := onePawn / 3
			alpha = Max(score - aspiration, -Checkmate)
			beta = Min(score + aspiration, Checkmate)

			// Do the search with smaller alpha/beta spread based on
			// previous iteration score, and re-search with the bigger
			// window as necessary.
			for {
				//Log("\tscore -> %d, searchRoot(%d, %d, %d)\n", score, alpha, beta, depth)
				score = position.search(alpha, beta, depth)
				if score > alpha {
					bestScore = score
					game.rootpv = append(game.rootpv[:0], game.pv[0]...)
				}

				if !engine.fixedDepth() && engine.clock.halt {
					break
				}

				if score <= alpha {
					game.improving = false
					alpha = Max(score - aspiration, -Checkmate)
				} else if score >= beta {
					beta = Min(score + aspiration, Checkmate)
				} else {
					break;
				}

				aspiration *= 2
			}
			// TBD: position.cache(game.rootpv[0], score, 0, 0)
		}
		if engine.clock.halt {
			//Log("\ttimed out pv => %v\n\ttimed out rv => %v\n", game.pv[0], game.rootpv)
			score = bestScore
		}

		move = game.rootpv[0]
		status = position.status(move, score)
		game.printPrincipal(depth, score, status, time.Since(start).Seconds())
	}

	game.printBestMove(move, time.Since(start).Seconds())

	return move
}

func (game *Game) keepThinking(depth int, move Move) bool {
	if depth == 1 {
		return true
	}

	if engine.fixedDepth() {
		return depth <= engine.options.maxDepth
	} else if engine.clock.halt {
		engine.debug(fmt.Sprintf("# Depth %02d Early out with %s\n", depth, move))
		return false
	}

	// Stop deepening if it's the only move.
	gen := NewRootGen(nil, depth)
	if depth == 2 && gen.onlyMove() {
		engine.debug(fmt.Sprintf("# Depth %02d Only move %s\n", depth, move))
		return false
	}

	// Stop if the move seems to be obvious and we've searched deep enough.
	if gen.obvious != Move(0) {
		if move == gen.obvious {
			engine.debug(fmt.Sprintf("# Depth %02d Kinda obvious %s\n", depth, move))
			return depth < 9
		} else { // The move is no longer obvious.
			engine.debug(fmt.Sprintf("# Depth %02d No longer obvious %s\n", depth, move))
			gen.obvious = Move(0)
		}
	}

	// Stop if the time left is not enough to gets through the next iteration.
	if engine.varyingTime() {
		elapsed := engine.elapsed(time.Now())
		remaining := engine.factor(depth, game.volatility).remaining()

		engine.debug(fmt.Sprintf("# Depth %02d Volatility %.2f Elapsed %s Remaining %s\n", depth, game.volatility, ms(elapsed), ms(remaining)))
		if elapsed > engine.factor(depth, game.volatility).remaining() {
			engine.debug(fmt.Sprintf("# Depth %02d Bailing out with %s\n", depth, move))
			return false
		}
	}

	return true
}

func (game *Game) printBestMove(move Move, duration float64) {
	if engine.uci {
		engine.uciBestMove(move, duration)
	} else {
		engine.replBestMove(move)
	}
}

// Prints principal variation. Note that in REPL advantage white is always +score
// and advantage black is -score whereas in UCI +score is advantage current side
// and -score is advantage opponent.
func (game *Game) printPrincipal(depth, score, status int, duration float64) {
	if engine.uci {
		engine.uciPrincipal(depth, score, duration)
	} else {
		if game.Position().color == Black {
			score = -score
		}
		engine.replPrincipal(depth, score, status, duration)
	}
}

func (game *Game) saveBest(ply int, move Move) *Game {
	game.pv[ply] = append(game.pv[ply][0:ply], move)

	next := ply + 1
	if length := len(game.pv[next]); length > 0 {
		game.pv[ply] = append(game.pv[ply], game.pv[next][next : length]...)
	}

	return game
}

func (game *Game) saveGood(depth int, move Move) *Game {
	if ply := Ply(); move.isQuiet() && move != game.killers[ply][0] {
		game.killers[ply][1] = game.killers[ply][0]
		game.killers[ply][0] = move
		game.history[move.piece()][move.to()] += depth * depth
	}

	return game
}

// Checks whether the move is among good moves captured so far and returns its
// history value.
func (game *Game) good(move Move) int {
	return game.history[move.piece()][move.to()]
}

func (game *Game) String() string {
	return game.Position().String()
}
<commit_msg>Stop iterative deepening if we have the only move<commit_after>// Copyright (c) 2013-2014 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

import (
	`fmt`
	`strings`
	`time`
)

type History [14][64]int
type Killers [MaxPly][2]Move
type RootPv  []Move
type Pv      [MaxPly]RootPv

type Game struct {
	nodes       int 	// Number of regular nodes searched.
	qnodes      int 	// Number of quiescence nodes searched.
	token       uint8 	// Cache's expiration token.
	deepening   bool 	// True when searching first root move.
	improving   bool 	// True when root search score is not falling.
	volatility  float32 	// Root search stability count.
	initial     string   	// Initial position (FEN or algebraic).
	history     History  	// Good moves history.
	killers     Killers  	// Killer moves.
	rootpv      RootPv 	// Principal variation for root moves.
	pv          Pv 		// Principal variations for each ply.
	cache       Cache 	// Transposition table.
	pawnCache   PawnCache 	// Cache of pawn structures.
}

// Use single statically allocated variable.
var game Game

// We have two ways to initialize the game: 1) pass FEN string, and 2) specify
// white and black pieces using regular chess notation.
//
// In latter case we need to tell who gets to move first when starting the game.
// The second option is a bit less pricise (ex. no en-passant square) but it is
// much more useful when writing tests from memory.
func NewGame(args ...string) *Game {
	game = Game{}
	game.cache = NewCache(engine.cacheSize)
	game.pawnCache = PawnCache{}

	game.rootpv = make([]Move, 0, MaxPly)
	for ply := 0;  ply < MaxPly; ply++ {
		game.pv[ply] = make([]Move, 0, MaxPly)
	}

	switch len(args) {
	case 0: // Initial position.
		game.initial = `rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1`
	case 1: // Genuine FEN.
		game.initial = args[0]
	case 2: // Standard algebraic notation (white and black).
		game.initial = args[0] + `:` + args[1]
	}

	return &game
}

// The color parameter is optional.
func (game *Game) Start(args ...int) *Position {
	engine.clock.halt = false
	tree, node, rootNode = [1024]Position{}, 0, 0

	// Was the game started with FEN or algebraic notation?
	sides := strings.Split(game.initial, `:`)
	if len(sides) == 2 {
		return NewPosition(game, sides[White], sides[Black], args[0])
	}
	return NewPositionFromFEN(game, game.initial)
}

func (game *Game) Position() *Position {
	return &tree[node]
}

// Resets principal variation as well as killer moves and move history. Cache
// entries get expired by incrementing cache token. Root node gets set to the
// current tree node to match the position.
func (game *Game) getReady() *Game {
	game.rootpv = game.rootpv[:0]
	for ply := 0;  ply < MaxPly; ply++ {
		game.pv[ply] = game.pv[ply][:0]
	}

	game.killers = Killers{}
	game.history = History{}
	game.deepening = false
	game.improving = true
	game.volatility = 0.0
	game.token++ // <-- Wraps around: ...254, 255, 0, 1...

	rootNode = node
	return game
}

func (game *Game) Think() Move {
	start := time.Now()
	position := game.Position()
	game.nodes, game.qnodes = 0, 0

	if book, err := NewBook(); err == nil {
		if move := book.pickMove(position); move != 0 {
			game.printBestMove(move, time.Since(start).Seconds())
			return move
		}
	}

	game.getReady()
	score, move, status, alpha, beta := 0, Move(0), InProgress, -Checkmate, Checkmate

	if engine.uci {
		engine.debug(position.String())
	} else {
		fmt.Println(`Depth/Time     Nodes      QNodes     Nodes/s   Score   Best`)
	}

	if !engine.fixedDepth() {
		engine.startClock(); defer engine.stopClock();
	}

	for depth := 1; status == InProgress && game.keepThinking(depth, move); depth++ {
		// Save previous best score in case search gets interrupted.
		bestScore := score

		// Assume volatility decreases with each new iteration.
		game.volatility /= 2.0

		// At low depths do the search with full alpha/beta spread.
		// Aspiration window searches kick in at depth 5 and up.
		if depth < 5 {
			score = position.search(alpha, beta, depth)
			if score > alpha {
				bestScore = score
				game.rootpv = append(game.rootpv[:0], game.pv[0]...)
			}
		} else {
			aspiration := onePawn / 3
			alpha = Max(score - aspiration, -Checkmate)
			beta = Min(score + aspiration, Checkmate)

			// Do the search with smaller alpha/beta spread based on
			// previous iteration score, and re-search with the bigger
			// window as necessary.
			for {
				//Log("\tscore -> %d, searchRoot(%d, %d, %d)\n", score, alpha, beta, depth)
				score = position.search(alpha, beta, depth)
				if score > alpha {
					bestScore = score
					game.rootpv = append(game.rootpv[:0], game.pv[0]...)
				}

				if !engine.fixedDepth() && engine.clock.halt {
					break
				}

				if score <= alpha {
					game.improving = false
					alpha = Max(score - aspiration, -Checkmate)
				} else if score >= beta {
					beta = Min(score + aspiration, Checkmate)
				} else {
					break;
				}

				aspiration *= 2
			}
			// TBD: position.cache(game.rootpv[0], score, 0, 0)
		}
		if engine.clock.halt {
			//Log("\ttimed out pv => %v\n\ttimed out rv => %v\n", game.pv[0], game.rootpv)
			score = bestScore
		}

		move = game.rootpv[0]
		status = position.status(move, score)
		game.printPrincipal(depth, score, status, time.Since(start).Seconds())
	}

	game.printBestMove(move, time.Since(start).Seconds())

	return move
}

func (game *Game) keepThinking(depth int, move Move) bool {
	if depth == 1 {
		return true
	}

	if engine.fixedDepth() {
		return depth <= engine.options.maxDepth
	} else if engine.clock.halt {
		engine.debug(fmt.Sprintf("# Depth %02d Early out with %s\n", depth, move))
		return false
	}

	// Stop deepening if it's the only move.
	gen := NewRootGen(nil, depth)
	if gen.onlyMove() {
		engine.debug(fmt.Sprintf("# Depth %02d Only move %s\n", depth, move))
		return false
	}

	// Stop if the move seems to be obvious and we've searched deep enough.
	if gen.obvious != Move(0) {
		if move == gen.obvious {
			engine.debug(fmt.Sprintf("# Depth %02d Kinda obvious %s\n", depth, move))
			return depth < 9
		} else { // The move is no longer obvious.
			engine.debug(fmt.Sprintf("# Depth %02d No longer obvious %s\n", depth, move))
			gen.obvious = Move(0)
		}
	}

	// Stop if the time left is not enough to gets through the next iteration.
	if engine.varyingTime() {
		elapsed := engine.elapsed(time.Now())
		remaining := engine.factor(depth, game.volatility).remaining()

		engine.debug(fmt.Sprintf("# Depth %02d Volatility %.2f Elapsed %s Remaining %s\n", depth, game.volatility, ms(elapsed), ms(remaining)))
		if elapsed > engine.factor(depth, game.volatility).remaining() {
			engine.debug(fmt.Sprintf("# Depth %02d Bailing out with %s\n", depth, move))
			return false
		}
	}

	return true
}

func (game *Game) printBestMove(move Move, duration float64) {
	if engine.uci {
		engine.uciBestMove(move, duration)
	} else {
		engine.replBestMove(move)
	}
}

// Prints principal variation. Note that in REPL advantage white is always +score
// and advantage black is -score whereas in UCI +score is advantage current side
// and -score is advantage opponent.
func (game *Game) printPrincipal(depth, score, status int, duration float64) {
	if engine.uci {
		engine.uciPrincipal(depth, score, duration)
	} else {
		if game.Position().color == Black {
			score = -score
		}
		engine.replPrincipal(depth, score, status, duration)
	}
}

func (game *Game) saveBest(ply int, move Move) *Game {
	game.pv[ply] = append(game.pv[ply][0:ply], move)

	next := ply + 1
	if length := len(game.pv[next]); length > 0 {
		game.pv[ply] = append(game.pv[ply], game.pv[next][next : length]...)
	}

	return game
}

func (game *Game) saveGood(depth int, move Move) *Game {
	if ply := Ply(); move.isQuiet() && move != game.killers[ply][0] {
		game.killers[ply][1] = game.killers[ply][0]
		game.killers[ply][0] = move
		game.history[move.piece()][move.to()] += depth * depth
	}

	return game
}

// Checks whether the move is among good moves captured so far and returns its
// history value.
func (game *Game) good(move Move) int {
	return game.history[move.piece()][move.to()]
}

func (game *Game) String() string {
	return game.Position().String()
}
<|endoftext|>


<commit_before>package main

import "sync"

type MoveType int

const (
	Hint MoveType = iota
	Play
	Discard
)

type Color int

const (
	Red Color = iota
	Yellow
	Green
	Blue
	Black
	White
)

type GameState int

const (
	NotStarted GameState = iota
	WaitingForTurn
	YourTurn
	Finished
)

var Colors = [...]Color{Red, Yellow, Green, Blue, White}
var Numbers = [...]int{1, 2, 3, 4, 5}

type Move struct {
	Type MoveType `json:"type"`
	// for Hint:
	ToPlayer string `json:"to_player"`
	Color    Color  `json:"color"`
	Number   int    `json:"number"`
	CardIds  []int  `json:"card_ids"`
	// for Play/Discard:
	CardId int `json:"card_id"`
}

type Card struct {
	Id     int   `json:"id"`
	Color  Color `json:"color"`
	Number int   `json:"number"`
}

// Card implements Cardy
var _ Cardy = (*Card)(nil)

func (f *Card) GetId() int {
	return f.Id
}

func (f *Card) Hide() HiddenCard {
	return HiddenCard{
		Id: f.Id,
	}
}

type HiddenCard struct {
	Id int `json:"id"`
}

// HiddenCard implements Cardy
var _ Cardy = (*HiddenCard)(nil)

func (h *HiddenCard) GetId() int {
	return h.Id
}

type Cardy interface {
	GetId() int
}
type Deck []Card
type Turn struct {
	Id     int    `json:"id"`
	Player string `json:"player"`
	Move   Move   `json:"move"`
	// no NewCard for Hint move
	NewCard Card `json:"new_card"`
}

type InfoResponse struct {
	State      GameState         `json:"state"`
	Players    []string          `json:"players"`
	Hand       []HiddenCard      `json:"hand"`        // the focused player's hand
	OtherHands map[string][]Card `json:"other_hands"` // the other player's hands
	Board      map[Color]int     `json:"board"`
	Discard    []Card            `json:"discard"`
	Turns      []Turn            `json:"turns"`
	TurnCursor int               `json:"turn_cursor"`
}

type InfoRequest struct {
	Player     string
	TurnCursor int
	Resp       chan InfoResponse
}

type DoTurnRequest struct {
	Turn Turn
	Resp chan DoTurnResponse
}

type DoTurnResponse struct {
	Response DoTurnResponseType
	Info     InfoResponse
	// Maybe we want more info, like if a bomb went off
}
type DoTurnResponseType int

const (
	Ok DoTurnResponseType = iota
	NotYourTurn
)

type Game struct {
	// Immutable Fields
	Name        string
	NumPlayers  int
	AddPlayer   chan string
	DoTurn      chan Turn
	RequestInfo chan InfoRequest

	// Mutable, private fields
	players   []string
	turns     []Turn
	deck      Deck
	hands     map[string][]Card
	board     map[Color]int
	discard   []Card
	whoseTurn int // Index into players. Use -1 when game is over
}

var Games map[string]Game
var GamesLock sync.Mutex

func (g *Game) DoGame() {
	for len(g.players) < g.NumPlayers {
		select {
		case p := <-g.AddPlayer:
			g.players = append(g.players, p)
		case r := <-g.RequestInfo:
			r.Resp <- g.InfoResponse(r.Player, r.TurnCursor)
		}
	}
	// TODO: now play the game
	for g.whoseTurn != -1 {
		select {
		case r := <-g.RequestInfo:
			r.Resp <- g.InfoResponse(r.Player, r.TurnCursor)
		case t := g.DoTurn:
			g.DoTurn(t)
		}
	}
}

func (g *Game) InfoResponse(player string, turnCursor int) InfoResponse {
	var resp InfoResponse
	// fill these no matter what
	resp.Players = g.players
	resp.Board = g.board
	resp.Discard = g.discard
	resp.Hand = g.hiddenPlayerHand(player)
	resp.OtherHands = g.otherHands(player)

	if len(g.players) < g.NumPlayers {
		// Game has not started yet
		resp.State = NotStarted
		return resp
	} else if g.whoseTurn == -1 {
		resp.State = Finished
	} else if g.players[g.whoseTurn] == player {
		resp.State = YourTurn
	} else {
		resp.State = WaitingForTurn
	}
	resp.Turns = g.turns[turnCursor:]
	resp.TurnCursor = len(g.turns)
	return resp
}

// The hand of a player, as hidden cards
func (g *Game) hiddenPlayerHand(player string) []HiddenCard {
	panic("todo")
}

// The hands of the players _except_ the specified player.
func (g *Game) otherHands(exceptPlayer string) map[string][]Card {
	panic("todo")
}

// // Take hands and hide the hand of the player.
// func hideCards(hands hands, player string) map[string][]Card {
// 	ret := make(map[string][]Card)
// 	for p, hand := range hands {
// 		if p == player {
// 			// Hide the cards for the player.
// 			ret[p] = nil
// 			for _, card := range hand {
// 				ret[p] = append(ret[p], card.Hide())
// 			}
// 		} else {
// 			// Pass through the other hands unmodified.
// 			ret[p] = hand
// 		}
// 	}
// }
<commit_msg>fix compile error<commit_after>package main

import "sync"

type MoveType int

const (
	Hint MoveType = iota
	Play
	Discard
)

type Color int

const (
	Red Color = iota
	Yellow
	Green
	Blue
	Black
	White
)

type GameState int

const (
	NotStarted GameState = iota
	WaitingForTurn
	YourTurn
	Finished
)

var Colors = [...]Color{Red, Yellow, Green, Blue, White}
var Numbers = [...]int{1, 2, 3, 4, 5}

type Move struct {
	Type MoveType `json:"type"`
	// for Hint:
	ToPlayer string `json:"to_player"`
	Color    Color  `json:"color"`
	Number   int    `json:"number"`
	CardIds  []int  `json:"card_ids"`
	// for Play/Discard:
	CardId int `json:"card_id"`
}

type Card struct {
	Id     int   `json:"id"`
	Color  Color `json:"color"`
	Number int   `json:"number"`
}

// Card implements Cardy
var _ Cardy = (*Card)(nil)

func (f *Card) GetId() int {
	return f.Id
}

func (f *Card) Hide() HiddenCard {
	return HiddenCard{
		Id: f.Id,
	}
}

type HiddenCard struct {
	Id int `json:"id"`
}

// HiddenCard implements Cardy
var _ Cardy = (*HiddenCard)(nil)

func (h *HiddenCard) GetId() int {
	return h.Id
}

type Cardy interface {
	GetId() int
}
type Deck []Card
type Turn struct {
	Id     int    `json:"id"`
	Player string `json:"player"`
	Move   Move   `json:"move"`
	// no NewCard for Hint move
	NewCard Card `json:"new_card"`
}

type InfoResponse struct {
	State      GameState         `json:"state"`
	Players    []string          `json:"players"`
	Hand       []HiddenCard      `json:"hand"`        // the focused player's hand
	OtherHands map[string][]Card `json:"other_hands"` // the other player's hands
	Board      map[Color]int     `json:"board"`
	Discard    []Card            `json:"discard"`
	Turns      []Turn            `json:"turns"`
	TurnCursor int               `json:"turn_cursor"`
}

type InfoRequest struct {
	Player     string
	TurnCursor int
	Resp       chan InfoResponse
}

type DoTurnRequest struct {
	Turn Turn
	Resp chan DoTurnResponse
}

type DoTurnResponse struct {
	Response DoTurnResponseType
	Info     InfoResponse
	// Maybe we want more info, like if a bomb went off
}
type DoTurnResponseType int

const (
	Ok DoTurnResponseType = iota
	NotYourTurn
)

type Game struct {
	// Immutable Fields
	Name        string
	NumPlayers  int
	AddPlayer   chan string
	DoTurn      chan Turn
	RequestInfo chan InfoRequest

	// Mutable, private fields
	players   []string
	turns     []Turn
	deck      Deck
	hands     map[string][]Card
	board     map[Color]int
	discard   []Card
	whoseTurn int // Index into players. Use -1 when game is over
}

var Games map[string]Game
var GamesLock sync.Mutex

func (g *Game) DoGame() {
	for len(g.players) < g.NumPlayers {
		select {
		case p := <-g.AddPlayer:
			g.players = append(g.players, p)
		case r := <-g.RequestInfo:
			r.Resp <- g.InfoResponse(r.Player, r.TurnCursor)
		}
	}
	// TODO: now play the game
	for g.whoseTurn != -1 {
		select {
		case r := <-g.RequestInfo:
			r.Resp <- g.InfoResponse(r.Player, r.TurnCursor)
		case t := <-g.DoTurn:
			g.doTurn(t)
		}
	}
}

func (g *Game) doTurn(turn Turn) {
	return
}

func (g *Game) InfoResponse(player string, turnCursor int) InfoResponse {
	var resp InfoResponse
	// fill these no matter what
	resp.Players = g.players
	resp.Board = g.board
	resp.Discard = g.discard
	resp.Hand = g.hiddenPlayerHand(player)
	resp.OtherHands = g.otherHands(player)

	if len(g.players) < g.NumPlayers {
		// Game has not started yet
		resp.State = NotStarted
		return resp
	} else if g.whoseTurn == -1 {
		resp.State = Finished
	} else if g.players[g.whoseTurn] == player {
		resp.State = YourTurn
	} else {
		resp.State = WaitingForTurn
	}
	resp.Turns = g.turns[turnCursor:]
	resp.TurnCursor = len(g.turns)
	return resp
}

// The hand of a player, as hidden cards
func (g *Game) hiddenPlayerHand(player string) []HiddenCard {
	panic("todo")
}

// The hands of the players _except_ the specified player.
func (g *Game) otherHands(exceptPlayer string) map[string][]Card {
	panic("todo")
}

// // Take hands and hide the hand of the player.
// func hideCards(hands hands, player string) map[string][]Card {
// 	ret := make(map[string][]Card)
// 	for p, hand := range hands {
// 		if p == player {
// 			// Hide the cards for the player.
// 			ret[p] = nil
// 			for _, card := range hand {
// 				ret[p] = append(ret[p], card.Hide())
// 			}
// 		} else {
// 			// Pass through the other hands unmodified.
// 			ret[p] = hand
// 		}
// 	}
// }
<|endoftext|>


<commit_before>package handler

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"

	"socialapi/config"
	"socialapi/models"
	"socialapi/workers/common/metrics"
	"strings"

	kmetrics "github.com/koding/metrics"

	tigertonic "github.com/rcrowley/go-tigertonic"

	gometrics "github.com/rcrowley/go-metrics"
)

const (
	PostRequest   = "POST"
	GetRequest    = "GET"
	DeleteRequest = "DELETE"
)

var (
	// TODO allowed origins must be configurable
	cors     = tigertonic.NewCORSBuilder().AddAllowedOrigins("*")
	conf     *config.Config
	trackers *metrics.Trackers
)

type Request struct {
	Handler        interface{}
	Endpoint       string
	Type           string
	Name           string
	CollectMetrics bool
	Metrics        *kmetrics.Metrics
	// used for external requests
	Params  map[string]string
	Cookie  string
	Cookies []*http.Cookie
	Body    interface{}
	Headers map[string]string
}

// todo add prooper logging
func getAccount(r *http.Request) *models.Account {
	cookie, err := r.Cookie("clientId")
	if err != nil {
		return models.NewAccount()
	}

	// if cookie doenst exists return empty account
	if cookie.Value == "" {
		return models.NewAccount()
	}

	session, err := models.Cache.Session.ById(cookie.Value)
	if err != nil {
		return models.NewAccount()
	}

	// if session doesnt have username, return empty account
	if session.Username == "" {
		return models.NewAccount()
	}

	acc, err := models.Cache.Account.ByNick(session.Username)
	if err != nil {
		return models.NewAccount()
	}

	return acc
}

func Wrapper(r Request) http.Handler {
	handler := r.Handler

	var hHandler http.Handler

	// count the statuses of the requests
	hHandler = buildHandlerWithStatusCount(handler, r)

	hHandler = buildHandlerWithTimeTracking(hHandler, r)

	// create the final handler
	return cors.Build(hHandler)
}

// count the statuses of the requests
func buildHandlerWithStatusCount(handler interface{}, r Request) http.Handler {
	return CountedByStatus(
		tigertonic.Marshaled(handler), r.Name, r.CollectMetrics,
	)
}

// add request time tracking
func buildHandlerWithTimeTracking(handler http.Handler, r Request) http.Handler {
	var registry gometrics.Registry

	if r.Metrics != nil {
		registry = r.Metrics.Registry
	}

	return tigertonic.Timed(
		handler,
		r.Name,
		registry,
	)
}

func BuildHandlerWithContext(handler http.Handler) http.Handler {
	// add context
	return tigertonic.If(
		func(r *http.Request) (http.Header, error) {
			// this is an example
			// set group name to context
			//
			context := &models.Context{
				GroupName: "koding",
				Client: &models.Client{
					Account: getAccount(r),
				},
			}

			*(tigertonic.Context(r).(*models.Context)) = *context
			return nil, nil
		},
		handler,
	)
}

//----------------------------------------------------------
// CounterByStatus
//----------------------------------------------------------

type CounterByStatus struct {
	name           string
	handler        http.Handler
	collectMetrics bool
}

func (c *CounterByStatus) ServeHTTP(w0 http.ResponseWriter, r *http.Request) {
	w := tigertonic.NewTeeHeaderResponseWriter(w0)
	c.handler.ServeHTTP(w, r)

	if w.StatusCode <= 300 {
		c.track()
	}
}

func (c *CounterByStatus) track() {
	// don't log if analytics are disabled for that endpoint
	if !c.collectMetrics {
		return
	}

	// set `conf` and `trackers` if either is not set
	if conf == nil || trackers == nil {
		conf = config.MustGet()

		// don't log if analytics are disabled globally
		if !conf.Mixpanel.Enabled {
			return
		}

		trackers = metrics.InitTrackers(
			metrics.NewMixpanelTracker(conf.Mixpanel.Token),
		)
	}

	trackers.Track(c.name)
}

func CountedByStatus(handler http.Handler, name string, collectMetrics bool) *CounterByStatus {
	return &CounterByStatus{
		name:           name,
		handler:        handler,
		collectMetrics: collectMetrics,
	}
}

//
func MakeRequest(request *Request) (*http.Response, error) {
	if request.Cookie != "" {
		request.Cookies = parseCookiesToArray(request.Cookie)
	}

	request.Endpoint = prepareQueryString(request.Endpoint, request.Params)

	client := new(http.Client)
	hostname := config.MustGet().CustomDomain.Local
	endpoint := fmt.Sprintf("%s/%s", hostname, request.Endpoint)

	var byteData io.Reader
	if request.Body != nil {
		body, err := json.Marshal(request.Body)
		if err != nil {
			return nil, err
		}
		byteData = bytes.NewReader(body)
	}

	req, err := http.NewRequest(request.Type, endpoint, byteData)
	req = prepareHeaders(req, request.Headers)

	if err != nil {
		return nil, err
	}

	// Add cookies
	for _, cookie := range request.Cookies {
		req.AddCookie(cookie)
	}

	return client.Do(req)
}

func prepareQueryString(endpoint string, params map[string]string) string {
	if len(params) == 0 {
		return endpoint
	}

	fullPath := fmt.Sprintf("%s?", endpoint)

	for key, value := range params {
		fullPath = fmt.Sprintf("%s%s=%s&", fullPath, key, value)
	}

	return fullPath[0 : len(fullPath)-1]
}

func prepareHeaders(req *http.Request, headers map[string]string) *http.Request {
	newReq := new(http.Request)
	*newReq = *req
	for k, v := range headers {
		newReq.Header.Set(k, v)
	}

	return newReq
}

func parseCookiesToArray(cookie string) []*http.Cookie {
	pairs := strings.Split(cookie, "; ")
	cookies := make([]*http.Cookie, 0)

	if len(pairs) == 0 {
		return cookies
	}

	for _, val := range pairs {
		cp := strings.Split(val, "=")
		if len(cp) != 2 {
			continue
		}

		c := new(http.Cookie)
		c.Name = cp[0]
		c.Value = cp[1]

		cookies = append(cookies, c)
	}

	return cookies
}

func ParseCookiesToMap(cookie string) map[string]*http.Cookie {
	pairs := strings.Split(cookie, "; ")
	cookies := make(map[string]*http.Cookie, 0)

	if len(pairs) == 0 {
		return cookies
	}

	for _, val := range pairs {
		cp := strings.Split(val, "=")
		if len(cp) != 2 {
			continue
		}

		c := new(http.Cookie)
		c.Name = cp[0]
		c.Value = cp[1]

		cookies[cp[0]] = c
	}

	return cookies
}
<commit_msg>Socialapi: implement a throttling system<commit_after>package handler

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"

	"socialapi/config"
	"socialapi/models"
	"socialapi/workers/common/metrics"
	"socialapi/workers/common/response"
	"strconv"
	"strings"

	"github.com/juju/ratelimit"
	kmetrics "github.com/koding/metrics"

	tigertonic "github.com/rcrowley/go-tigertonic"

	gometrics "github.com/rcrowley/go-metrics"
)

const (
	PostRequest   = "POST"
	GetRequest    = "GET"
	DeleteRequest = "DELETE"
)

var (
	// TODO allowed origins must be configurable
	cors     = tigertonic.NewCORSBuilder().AddAllowedOrigins("*")
	conf     *config.Config
	trackers *metrics.Trackers
)

type Request struct {
	Handler        interface{}
	Endpoint       string
	Type           string
	Name           string
	CollectMetrics bool
	Metrics        *kmetrics.Metrics
	// used for external requests
	Params    map[string]string
	Cookie    string
	Cookies   []*http.Cookie
	Body      interface{}
	Headers   map[string]string
	Ratelimit func(*http.Request) *ratelimit.Bucket
}

// todo add prooper logging
func getAccount(r *http.Request) *models.Account {
	cookie, err := r.Cookie("clientId")
	if err != nil {
		return models.NewAccount()
	}

	// if cookie doenst exists return empty account
	if cookie.Value == "" {
		return models.NewAccount()
	}

	session, err := models.Cache.Session.ById(cookie.Value)
	if err != nil {
		return models.NewAccount()
	}

	// if session doesnt have username, return empty account
	if session.Username == "" {
		return models.NewAccount()
	}

	acc, err := models.Cache.Account.ByNick(session.Username)
	if err != nil {
		return models.NewAccount()
	}

	return acc
}

func Wrapper(r Request) http.Handler {
	handler := r.Handler

	var hHandler http.Handler

	// count the statuses of the requests
	hHandler = buildHandlerWithStatusCount(handler, r)

	hHandler = buildHandlerWithTimeTracking(hHandler, r)

	if r.Ratelimit != nil {
		hHandler = BuildHandlerWithRateLimit(hHandler, r.Ratelimit)
	}
	// create the final handler
	return cors.Build(hHandler)
}

// count the statuses of the requests
func buildHandlerWithStatusCount(handler interface{}, r Request) http.Handler {
	return CountedByStatus(
		tigertonic.Marshaled(handler), r.Name, r.CollectMetrics,
	)
}

// add request time tracking
func buildHandlerWithTimeTracking(handler http.Handler, r Request) http.Handler {
	var registry gometrics.Registry

	if r.Metrics != nil {
		registry = r.Metrics.Registry
	}

	return tigertonic.Timed(
		handler,
		r.Name,
		registry,
	)
}

func BuildHandlerWithContext(handler http.Handler) http.Handler {
	// add context
	return tigertonic.If(
		func(r *http.Request) (http.Header, error) {
			// this is an example
			// set group name to context
			//
			context := &models.Context{
				GroupName: "koding",
				Client: &models.Client{
					Account: getAccount(r),
				},
			}

			*(tigertonic.Context(r).(*models.Context)) = *context
			return nil, nil
		},
		handler,
	)
}

func BuildHandlerWithRateLimit(handler http.Handler, limiter func(*http.Request) *ratelimit.Bucket) http.Handler {
	// add context
	return tigertonic.If(
		func(r *http.Request) (http.Header, error) {
			// get the limiter
			bucket := limiter(r)

			h := http.Header{}
			h.Add("X-RateLimit-Limit", strconv.FormatInt(int64(bucket.Rate()*60), 10))

			// check if any 		throttling is enabled and then check token's available.
			// Tokens are filled per frequency of the initial bucket, so every request
			// is going to take one token from the bucket. If many requests come in (in
			// span time larger than the bucket's frequency), there will be no token's
			// available more so it will return a zero.
			timeToWait := bucket.Take(1)
			if timeToWait > time.Duration(0) {
				h.Add("Retry-After", strconv.FormatInt(int64(timeToWait.Seconds()), 10))
				return h, &response.LimitRateExceededError{}
			}

			return h, nil
		},
		handler,
	)
}

//----------------------------------------------------------
// CounterByStatus
//----------------------------------------------------------

type CounterByStatus struct {
	name           string
	handler        http.Handler
	collectMetrics bool
}

func (c *CounterByStatus) ServeHTTP(w0 http.ResponseWriter, r *http.Request) {
	w := tigertonic.NewTeeHeaderResponseWriter(w0)
	c.handler.ServeHTTP(w, r)

	if w.StatusCode <= 300 {
		c.track()
	}
}

func (c *CounterByStatus) track() {
	// don't log if analytics are disabled for that endpoint
	if !c.collectMetrics {
		return
	}

	// set `conf` and `trackers` if either is not set
	if conf == nil || trackers == nil {
		conf = config.MustGet()

		// don't log if analytics are disabled globally
		if !conf.Mixpanel.Enabled {
			return
		}

		trackers = metrics.InitTrackers(
			metrics.NewMixpanelTracker(conf.Mixpanel.Token),
		)
	}

	trackers.Track(c.name)
}

func CountedByStatus(handler http.Handler, name string, collectMetrics bool) *CounterByStatus {
	return &CounterByStatus{
		name:           name,
		handler:        handler,
		collectMetrics: collectMetrics,
	}
}

//
func MakeRequest(request *Request) (*http.Response, error) {
	if request.Cookie != "" {
		request.Cookies = parseCookiesToArray(request.Cookie)
	}

	request.Endpoint = prepareQueryString(request.Endpoint, request.Params)

	client := new(http.Client)
	hostname := config.MustGet().CustomDomain.Local
	endpoint := fmt.Sprintf("%s/%s", hostname, request.Endpoint)

	var byteData io.Reader
	if request.Body != nil {
		body, err := json.Marshal(request.Body)
		if err != nil {
			return nil, err
		}
		byteData = bytes.NewReader(body)
	}

	req, err := http.NewRequest(request.Type, endpoint, byteData)
	req = prepareHeaders(req, request.Headers)

	if err != nil {
		return nil, err
	}

	// Add cookies
	for _, cookie := range request.Cookies {
		req.AddCookie(cookie)
	}

	return client.Do(req)
}

func prepareQueryString(endpoint string, params map[string]string) string {
	if len(params) == 0 {
		return endpoint
	}

	fullPath := fmt.Sprintf("%s?", endpoint)

	for key, value := range params {
		fullPath = fmt.Sprintf("%s%s=%s&", fullPath, key, value)
	}

	return fullPath[0 : len(fullPath)-1]
}

func prepareHeaders(req *http.Request, headers map[string]string) *http.Request {
	newReq := new(http.Request)
	*newReq = *req
	for k, v := range headers {
		newReq.Header.Set(k, v)
	}

	return newReq
}

func parseCookiesToArray(cookie string) []*http.Cookie {
	pairs := strings.Split(cookie, "; ")
	cookies := make([]*http.Cookie, 0)

	if len(pairs) == 0 {
		return cookies
	}

	for _, val := range pairs {
		cp := strings.Split(val, "=")
		if len(cp) != 2 {
			continue
		}

		c := new(http.Cookie)
		c.Name = cp[0]
		c.Value = cp[1]

		cookies = append(cookies, c)
	}

	return cookies
}

func ParseCookiesToMap(cookie string) map[string]*http.Cookie {
	pairs := strings.Split(cookie, "; ")
	cookies := make(map[string]*http.Cookie, 0)

	if len(pairs) == 0 {
		return cookies
	}

	for _, val := range pairs {
		cp := strings.Split(val, "=")
		if len(cp) != 2 {
			continue
		}

		c := new(http.Cookie)
		c.Name = cp[0]
		c.Value = cp[1]

		cookies[cp[0]] = c
	}

	return cookies
}
<|endoftext|>