<commit_before>//
// DISCLAIMER
//
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package service

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"reflect"
	"sync"
	"time"

	"github.com/arangodb-helper/arangodb/service/arangod"
	logging "github.com/op/go-logging"
)

const (
	masterURLTTL = time.Second * 30
)

var (
	masterURLKey = []string{"arangodb-helper", "arangodb", "master"}
)

// runtimeClusterManager keeps the cluster configuration up to date during a running state.
type runtimeClusterManager struct {
	mutex            sync.Mutex
	runtimeContext   runtimeClusterManagerContext
	lastMasterURL    string
	avoidBeingMaster bool // If set, this peer will not try to become master
	interruptChan    chan struct{}
}

// runtimeClusterManagerContext provides a context for the runtimeClusterManager.
type runtimeClusterManagerContext interface {
	// ClusterConfig returns the current cluster configuration and the current peer
	ClusterConfig() (ClusterConfig, *Peer, ServiceMode)

	// ChangeState alters the current state of the service
	ChangeState(newState State)

	// PrepareDatabaseServerRequestFunc returns a function that is used to
	// prepare a request to a database server (including authentication).
	PrepareDatabaseServerRequestFunc() func(*http.Request) error

	// UpdateClusterConfig updates the current cluster configuration.
	UpdateClusterConfig(ClusterConfig)
}

// Create a client for the agency
func (s *runtimeClusterManager) createAgencyAPI() (arangod.AgencyAPI, error) {
	prepareReq := s.runtimeContext.PrepareDatabaseServerRequestFunc()
	// Get cluster config
	clusterConfig, _, _ := s.runtimeContext.ClusterConfig()
	// Create client
	return clusterConfig.CreateAgencyAPI(prepareReq)
}

// getMasterURL tries to get the URL of the current master from
// a well known location in the agency.
func (s *runtimeClusterManager) getMasterURL(ctx context.Context) (string, error) {
	// Get api client
	api, err := s.createAgencyAPI()
	if err != nil {
		return "", maskAny(err)
	}
	// Try to read master URL
	ctx, cancel := context.WithTimeout(ctx, time.Second*10)
	defer cancel()
	if result, err := api.ReadKey(ctx, masterURLKey); err != nil {
		if arangod.IsKeyNotFound(err) {
			return "", nil
		}
		return "", maskAny(err)
	} else if strResult, ok := result.(string); ok {
		return strResult, nil
	} else if mapResult, ok := result.(map[string]interface{}); ok && len(mapResult) == 0 {
		return "", nil
	} else {
		return "", maskAny(fmt.Errorf("Invalid value type at key: %v", reflect.TypeOf(result)))
	}
}

// tryBecomeMaster tries to write our URL into a well known location in the agency,
// assuming there is no master.
func (s *runtimeClusterManager) tryBecomeMaster(ctx context.Context, ownURL string) error {
	// Get api client
	api, err := s.createAgencyAPI()
	if err != nil {
		return maskAny(err)
	}
	// Try to write our master URL
	ctx, cancel := context.WithTimeout(ctx, time.Second*10)
	defer cancel()
	if err := api.WriteKeyIfEmpty(ctx, masterURLKey, ownURL, masterURLTTL); err != nil {
		return maskAny(err)
	}
	return nil
}

// tryRemainMaster tries to write our URL into a well known location in the agency,
// assuming we're already the master.
func (s *runtimeClusterManager) tryRemainMaster(ctx context.Context, ownURL string) error {
	// Get api client
	api, err := s.createAgencyAPI()
	if err != nil {
		return maskAny(err)
	}
	// Try to update our master URL
	ctx, cancel := context.WithTimeout(ctx, time.Second*10)
	defer cancel()
	if err := api.WriteKeyIfEqualTo(ctx, masterURLKey, ownURL, ownURL, masterURLTTL); err != nil {
		return maskAny(err)
	}
	return nil
}

// tryStopBeingMaster tries to write an empty URL into a well known location in the agency,
// assuming we're already the master.
// This will enable other peers to become master
func (s *runtimeClusterManager) tryStopBeingMaster(ctx context.Context, ownURL string) error {
	// Get api client
	api, err := s.createAgencyAPI()
	if err != nil {
		return maskAny(err)
	}
	// Try to update our master URL
	ctx, cancel := context.WithTimeout(ctx, time.Second*10)
	defer cancel()
	if err := api.RemoveKeyIfEqualTo(ctx, masterURLKey, ownURL); err != nil {
		return maskAny(err)
	}
	return nil
}

// updateClusterConfiguration asks the master at given URL for the latest cluster configuration.
func (s *runtimeClusterManager) updateClusterConfiguration(ctx context.Context, masterURL string) error {
	helloURL, err := getURLWithPath(masterURL, "/hello?update=1")
	if err != nil {
		return maskAny(err)
	}
	// Perform request
	r, err := httpClient.Get(helloURL)
	if err != nil {
		return maskAny(err)
	}
	// Check status
	if r.StatusCode != 200 {
		return maskAny(fmt.Errorf("Invalid status %d from master", r.StatusCode))
	}
	// Parse result
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return maskAny(err)
	}
	var clusterConfig ClusterConfig
	if err := json.Unmarshal(body, &clusterConfig); err != nil {
		return maskAny(err)
	}
	// We've received a cluster config
	s.runtimeContext.UpdateClusterConfig(clusterConfig)

	return nil
}

// registerMasterChangedCallback registers our callback URL with the agency
func (s *runtimeClusterManager) registerMasterChangedCallback(ctx context.Context, ownURL string) error {
	// Get api client
	api, err := s.createAgencyAPI()
	if err != nil {
		return maskAny(err)
	}
	// Register callback
	cbURL, err := getURLWithPath(ownURL, "/cb/masterChanged")
	if err != nil {
		return maskAny(err)
	}
	ctx, cancel := context.WithTimeout(ctx, time.Second*10)
	defer cancel()
	if err := api.RegisterChangeCallback(ctx, masterURLKey, cbURL); err != nil {
		return maskAny(err)
	}
	return nil
}

// unregisterMasterChangedCallback removes our callback URL from the agency
func (s *runtimeClusterManager) unregisterMasterChangedCallback(ctx context.Context, ownURL string) error {
	// Get api client
	api, err := s.createAgencyAPI()
	if err != nil {
		return maskAny(err)
	}
	// Register callback
	cbURL, err := getURLWithPath(ownURL, "/cb/masterChanged")
	if err != nil {
		return maskAny(err)
	}
	ctx, cancel := context.WithTimeout(ctx, time.Second*10)
	defer cancel()
	if err := api.UnregisterChangeCallback(ctx, masterURLKey, cbURL); err != nil {
		return maskAny(err)
	}
	return nil
}

// Run keeps the cluster configuration up to date, either as master or as slave
// during a running state.
func (s *runtimeClusterManager) Run(ctx context.Context, log *logging.Logger, runtimeContext runtimeClusterManagerContext) {
	s.runtimeContext = runtimeContext
	s.interruptChan = make(chan struct{}, 32)
	_, myPeer, mode := runtimeContext.ClusterConfig()
	if !mode.IsClusterMode() {
		// Cluster manager is only relevant in cluster mode
		return
	}
	if myPeer == nil {
		// We need to know our own peer
		log.Error("Cannot run runtime cluster manager without own peer")
		return
	}
	ownURL := myPeer.CreateStarterURL("/")

	callbackRegistered := false
	for {
		var delay time.Duration
		// Loop until stopping
		if ctx.Err() != nil {
			// Stop requested
			return
		}

		// Try to get master URL
		masterURL, err := s.getMasterURL(ctx)
		if err != nil {
			// Cannot obtain master url, wait a while and try again
			log.Infof("Failed to get master URL, retrying in 5sec (%#v)", err)
			delay = time.Second * 5
		} else {
			// Store current master
			s.mutex.Lock()
			s.lastMasterURL = masterURL
			s.mutex.Unlock()

			// Register master changed callback (if needed)
			if !callbackRegistered && masterURL != "" {
				log.Info("Register master callback...")
				if err := s.registerMasterChangedCallback(ctx, ownURL); err != nil {
					log.Debugf("Failed to register master callback: %#v", err)
				} else {
					log.Info("Registered master callback")
					callbackRegistered = true
					defer s.unregisterMasterChangedCallback(context.Background(), ownURL)
				}
			}

			if masterURL == "" {
				// There is currently no master, try to become master (if allowed)
				if !s.avoidBeingMaster {
					log.Debug("There is no current master, try to become master")
					if err := s.tryBecomeMaster(ctx, ownURL); err != nil {
						log.Infof("tried to become master but failed: %#v", err)
						runtimeContext.ChangeState(stateRunningSlave)
					} else {
						log.Info("Just became master")
						runtimeContext.ChangeState(stateRunningMaster)
					}
					// Wait a bit, after which we'll register callback (if needed)
					delay = time.Second
				} else {
					// We're not allowed to become master but there is no master,
					// just wait a bit
					delay = time.Second
				}
			} else if masterURL == ownURL {
				// We are the master, update our entry in the agency
				log.Debug("We're master, try to remain it")
				runtimeContext.ChangeState(stateRunningMaster)

				if !s.avoidBeingMaster {
					// Update agency
					if err := s.tryRemainMaster(ctx, ownURL); err != nil {
						log.Info("Failed to remain master: %#v", err)
						runtimeContext.ChangeState(stateRunningSlave)

						// Retry soon
						delay = time.Second
					} else {
						// I'm still the master
						// wait a bit before updating master URL
						delay = masterURLTTL / 3
					}
				} else {
					// We're master, but we want to avoid that, try giving up being master
					log.Info("Trying to stop being master...")
					if err := s.tryStopBeingMaster(ctx, ownURL); err != nil {
						log.Warningf("Failed to stop being master: %#v", err)
						// Retry soon
						delay = time.Second
					} else {
						// I'm no longer master
						log.Info("Stopped being master")
						runtimeContext.ChangeState(stateRunningSlave)
						// Come back soon to see who took over
						delay = time.Second
					}
				}
			} else {
				// We are slave, try to update cluster configuration from master
				log.Debugf("We're slave, try to update cluster config from %s", masterURL)
				runtimeContext.ChangeState(stateRunningSlave)

				// Ask current master for cluster configuration
				if err := s.updateClusterConfiguration(ctx, masterURL); err != nil {
					log.Warningf("Failed to load cluster configuration from %s: %#v", masterURL, err)
				}

				// Wait a bit until re-updating the configuration
				delay = time.Second * 15
			}
		}

		// Wait a bit
		select {
		case <-time.After(delay):
		// Delay over, just continue
		case <-ctx.Done():
			// We're asked to stop
			return
		case <-s.interruptChan:
			// We're being interrupted
			log.Debug("Being interrupted")
			// continue now
		}
	}
}

// Get the last known URL of the master (can be empty)
func (s *runtimeClusterManager) GetMasterURL() string {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	return s.lastMasterURL
}

// AvoidBeingMaster instructs the runtime cluster manager to avoid
// becoming master and when it is master, to give that up.
func (s *runtimeClusterManager) AvoidBeingMaster() {
	s.avoidBeingMaster = true
	s.Interrupt()
}

// Interrupt the runtime cluster manager loop after some event has happened.
func (s *runtimeClusterManager) Interrupt() {
	// Interrupt loop so we act on this right away
	if ch := s.interruptChan; ch != nil {
		ch <- struct{}{}
	}
}
<commit_msg>Minor logging changes<commit_after>//
// DISCLAIMER
//
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package service

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"reflect"
	"sync"
	"time"

	"github.com/arangodb-helper/arangodb/service/arangod"
	logging "github.com/op/go-logging"
)

const (
	masterURLTTL = time.Second * 30
)

var (
	masterURLKey = []string{"arangodb-helper", "arangodb", "master"}
)

// runtimeClusterManager keeps the cluster configuration up to date during a running state.
type runtimeClusterManager struct {
	mutex            sync.Mutex
	runtimeContext   runtimeClusterManagerContext
	lastMasterURL    string
	avoidBeingMaster bool // If set, this peer will not try to become master
	interruptChan    chan struct{}
}

// runtimeClusterManagerContext provides a context for the runtimeClusterManager.
type runtimeClusterManagerContext interface {
	// ClusterConfig returns the current cluster configuration and the current peer
	ClusterConfig() (ClusterConfig, *Peer, ServiceMode)

	// ChangeState alters the current state of the service
	ChangeState(newState State)

	// PrepareDatabaseServerRequestFunc returns a function that is used to
	// prepare a request to a database server (including authentication).
	PrepareDatabaseServerRequestFunc() func(*http.Request) error

	// UpdateClusterConfig updates the current cluster configuration.
	UpdateClusterConfig(ClusterConfig)
}

// Create a client for the agency
func (s *runtimeClusterManager) createAgencyAPI() (arangod.AgencyAPI, error) {
	prepareReq := s.runtimeContext.PrepareDatabaseServerRequestFunc()
	// Get cluster config
	clusterConfig, _, _ := s.runtimeContext.ClusterConfig()
	// Create client
	return clusterConfig.CreateAgencyAPI(prepareReq)
}

// getMasterURL tries to get the URL of the current master from
// a well known location in the agency.
func (s *runtimeClusterManager) getMasterURL(ctx context.Context) (string, error) {
	// Get api client
	api, err := s.createAgencyAPI()
	if err != nil {
		return "", maskAny(err)
	}
	// Try to read master URL
	ctx, cancel := context.WithTimeout(ctx, time.Second*10)
	defer cancel()
	if result, err := api.ReadKey(ctx, masterURLKey); err != nil {
		if arangod.IsKeyNotFound(err) {
			return "", nil
		}
		return "", maskAny(err)
	} else if strResult, ok := result.(string); ok {
		return strResult, nil
	} else if mapResult, ok := result.(map[string]interface{}); ok && len(mapResult) == 0 {
		return "", nil
	} else {
		return "", maskAny(fmt.Errorf("Invalid value type at key: %v", reflect.TypeOf(result)))
	}
}

// tryBecomeMaster tries to write our URL into a well known location in the agency,
// assuming there is no master.
func (s *runtimeClusterManager) tryBecomeMaster(ctx context.Context, ownURL string) error {
	// Get api client
	api, err := s.createAgencyAPI()
	if err != nil {
		return maskAny(err)
	}
	// Try to write our master URL
	ctx, cancel := context.WithTimeout(ctx, time.Second*10)
	defer cancel()
	if err := api.WriteKeyIfEmpty(ctx, masterURLKey, ownURL, masterURLTTL); err != nil {
		return maskAny(err)
	}
	return nil
}

// tryRemainMaster tries to write our URL into a well known location in the agency,
// assuming we're already the master.
func (s *runtimeClusterManager) tryRemainMaster(ctx context.Context, ownURL string) error {
	// Get api client
	api, err := s.createAgencyAPI()
	if err != nil {
		return maskAny(err)
	}
	// Try to update our master URL
	ctx, cancel := context.WithTimeout(ctx, time.Second*10)
	defer cancel()
	if err := api.WriteKeyIfEqualTo(ctx, masterURLKey, ownURL, ownURL, masterURLTTL); err != nil {
		return maskAny(err)
	}
	return nil
}

// tryStopBeingMaster tries to write an empty URL into a well known location in the agency,
// assuming we're already the master.
// This will enable other peers to become master
func (s *runtimeClusterManager) tryStopBeingMaster(ctx context.Context, ownURL string) error {
	// Get api client
	api, err := s.createAgencyAPI()
	if err != nil {
		return maskAny(err)
	}
	// Try to update our master URL
	ctx, cancel := context.WithTimeout(ctx, time.Second*10)
	defer cancel()
	if err := api.RemoveKeyIfEqualTo(ctx, masterURLKey, ownURL); err != nil {
		return maskAny(err)
	}
	return nil
}

// updateClusterConfiguration asks the master at given URL for the latest cluster configuration.
func (s *runtimeClusterManager) updateClusterConfiguration(ctx context.Context, masterURL string) error {
	helloURL, err := getURLWithPath(masterURL, "/hello?update=1")
	if err != nil {
		return maskAny(err)
	}
	// Perform request
	r, err := httpClient.Get(helloURL)
	if err != nil {
		return maskAny(err)
	}
	// Check status
	if r.StatusCode != 200 {
		return maskAny(fmt.Errorf("Invalid status %d from master", r.StatusCode))
	}
	// Parse result
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return maskAny(err)
	}
	var clusterConfig ClusterConfig
	if err := json.Unmarshal(body, &clusterConfig); err != nil {
		return maskAny(err)
	}
	// We've received a cluster config
	s.runtimeContext.UpdateClusterConfig(clusterConfig)

	return nil
}

// registerMasterChangedCallback registers our callback URL with the agency
func (s *runtimeClusterManager) registerMasterChangedCallback(ctx context.Context, ownURL string) error {
	// Get api client
	api, err := s.createAgencyAPI()
	if err != nil {
		return maskAny(err)
	}
	// Register callback
	cbURL, err := getURLWithPath(ownURL, "/cb/masterChanged")
	if err != nil {
		return maskAny(err)
	}
	ctx, cancel := context.WithTimeout(ctx, time.Second*10)
	defer cancel()
	if err := api.RegisterChangeCallback(ctx, masterURLKey, cbURL); err != nil {
		return maskAny(err)
	}
	return nil
}

// unregisterMasterChangedCallback removes our callback URL from the agency
func (s *runtimeClusterManager) unregisterMasterChangedCallback(ctx context.Context, ownURL string) error {
	// Get api client
	api, err := s.createAgencyAPI()
	if err != nil {
		return maskAny(err)
	}
	// Register callback
	cbURL, err := getURLWithPath(ownURL, "/cb/masterChanged")
	if err != nil {
		return maskAny(err)
	}
	ctx, cancel := context.WithTimeout(ctx, time.Second*10)
	defer cancel()
	if err := api.UnregisterChangeCallback(ctx, masterURLKey, cbURL); err != nil {
		return maskAny(err)
	}
	return nil
}

// Run keeps the cluster configuration up to date, either as master or as slave
// during a running state.
func (s *runtimeClusterManager) Run(ctx context.Context, log *logging.Logger, runtimeContext runtimeClusterManagerContext) {
	s.runtimeContext = runtimeContext
	s.interruptChan = make(chan struct{}, 32)
	_, myPeer, mode := runtimeContext.ClusterConfig()
	if !mode.IsClusterMode() {
		// Cluster manager is only relevant in cluster mode
		return
	}
	if myPeer == nil {
		// We need to know our own peer
		log.Error("Cannot run runtime cluster manager without own peer")
		return
	}
	ownURL := myPeer.CreateStarterURL("/")

	callbackRegistered := false
	gotMasterURLOnce := false
	startTime := time.Now()
	for {
		var delay time.Duration
		// Loop until stopping
		if ctx.Err() != nil {
			// Stop requested
			return
		}

		// Try to get master URL
		masterURL, err := s.getMasterURL(ctx)
		if err != nil {
			// Cannot obtain master url, wait a while and try again
			if gotMasterURLOnce || time.Since(startTime) >= time.Minute {
				log.Infof("Failed to get master URL, retrying in 5sec (%#v)", err)
			} else {
				log.Debugf("Failed to get master URL, retrying in 5sec (%#v)", err)
			}
			delay = time.Second * 5
		} else {
			// Store current master
			gotMasterURLOnce = true
			s.mutex.Lock()
			s.lastMasterURL = masterURL
			s.mutex.Unlock()

			// Register master changed callback (if needed)
			if !callbackRegistered && masterURL != "" {
				log.Debug("Register master callback...")
				if err := s.registerMasterChangedCallback(ctx, ownURL); err != nil {
					log.Debugf("Failed to register master callback: %#v", err)
				} else {
					log.Debug("Registered master callback")
					callbackRegistered = true
					defer s.unregisterMasterChangedCallback(context.Background(), ownURL)
				}
			}

			if masterURL == "" {
				// There is currently no master, try to become master (if allowed)
				if !s.avoidBeingMaster {
					log.Debug("There is no current master, try to become master")
					if err := s.tryBecomeMaster(ctx, ownURL); err != nil {
						log.Infof("tried to become master but failed: %#v", err)
						runtimeContext.ChangeState(stateRunningSlave)
					} else {
						log.Info("Just became master")
						runtimeContext.ChangeState(stateRunningMaster)
					}
					// Wait a bit, after which we'll register callback (if needed)
					delay = time.Second
				} else {
					// We're not allowed to become master but there is no master,
					// just wait a bit
					delay = time.Second
				}
			} else if masterURL == ownURL {
				// We are the master, update our entry in the agency
				log.Debug("We're master, try to remain it")
				runtimeContext.ChangeState(stateRunningMaster)

				if !s.avoidBeingMaster {
					// Update agency
					if err := s.tryRemainMaster(ctx, ownURL); err != nil {
						log.Warningf("Failed to remain master: %#v", err)
						runtimeContext.ChangeState(stateRunningSlave)

						// Retry soon
						delay = time.Second
					} else {
						// I'm still the master
						// wait a bit before updating master URL
						delay = masterURLTTL / 3
					}
				} else {
					// We're master, but we want to avoid that, try giving up being master
					log.Info("Trying to stop being master...")
					if err := s.tryStopBeingMaster(ctx, ownURL); err != nil {
						log.Warningf("Failed to stop being master: %#v", err)
						// Retry soon
						delay = time.Second
					} else {
						// I'm no longer master
						log.Info("Stopped being master")
						runtimeContext.ChangeState(stateRunningSlave)
						// Come back soon to see who took over
						delay = time.Second
					}
				}
			} else {
				// We are slave, try to update cluster configuration from master
				log.Debugf("We're slave, try to update cluster config from %s", masterURL)
				runtimeContext.ChangeState(stateRunningSlave)

				// Ask current master for cluster configuration
				if err := s.updateClusterConfiguration(ctx, masterURL); err != nil {
					log.Warningf("Failed to load cluster configuration from %s: %#v", masterURL, err)
				}

				// Wait a bit until re-updating the configuration
				delay = time.Second * 15
			}
		}

		// Wait a bit
		select {
		case <-time.After(delay):
		// Delay over, just continue
		case <-ctx.Done():
			// We're asked to stop
			return
		case <-s.interruptChan:
			// We're being interrupted
			log.Debug("Being interrupted")
			// continue now
		}
	}
}

// Get the last known URL of the master (can be empty)
func (s *runtimeClusterManager) GetMasterURL() string {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	return s.lastMasterURL
}

// AvoidBeingMaster instructs the runtime cluster manager to avoid
// becoming master and when it is master, to give that up.
func (s *runtimeClusterManager) AvoidBeingMaster() {
	s.avoidBeingMaster = true
	s.Interrupt()
}

// Interrupt the runtime cluster manager loop after some event has happened.
func (s *runtimeClusterManager) Interrupt() {
	// Interrupt loop so we act on this right away
	if ch := s.interruptChan; ch != nil {
		ch <- struct{}{}
	}
}
<|endoftext|>


<commit_before>package matches

type Player struct {
	CurrentPlatformID string `json:"currentPlatformId"`
	SummonerName      string `json:"summonerName"`
	MatchHistoryURI   string `json:"matchHistoryUri"`
	PlatformID        string `json:"platformId"`
	CurrentAccountID  string `json:"currentAccountId"`
	ProfileIcon       int    `json:"profileIcon"`
	SummonerID        string `json:"summonerId"`
	AccountID         string `json:"accountId"`
}

type ParticipantID struct {
	Player        Player `json:"player"`
	ParticipantID int    `json:"participantId"`
}

type Team struct {
	FirstDragon          bool          `json:"firstDragon"`
	Bans                 []interface{} `json:"bans"`
	FirstInhibitor       bool          `json:"firstInhibitor"`
	Win                  string        `json:"win"`
	FirstRiftHerald      bool          `json:"firstRiftHerald"`
	FirstBaron           bool          `json:"firstBaron"`
	BaronKills           int           `json:"baronKills"`
	RiftHeraldKills      int           `json:"riftHeraldKills"`
	FirstBlood           bool          `json:"firstBlood"`
	TeamID               int           `json:"teamId"`
	FirstTower           bool          `json:"firstTower"`
	VilemawKills         int           `json:"vilemawKills"`
	InhibitorKills       int           `json:"inhibitorKills"`
	TowerKills           int           `json:"towerKills"`
	DominionVictoryScore int           `json:"dominionVictoryScore"`
	DragonKills          int           `json:"dragonKills"`
}

type Match struct {
	SeasonID              int             `json:"seasonId"`
	QueueID               int             `json:"queueId"`
	GameID                int64           `json:"gameId"`
	ParticipantIdentities []ParticipantID `json:"participantIdentities"`
	GameVersion           string          `json:"gameVersion"`
	PlatformID            string          `json:"platformId"`
	GameMode              string          `json:"gameMode"`
	MapID                 int             `json:"mapId"`
	GameType              string          `json:"gameType"`
	Teams                 []Team          `json:"teams"`
	Participants          []Participant   `json:"participants"`
	GameDuration          int             `json:"gameDuration"`
	GameCreation          int64           `json:"gameCreation"`
}
<commit_msg>get pid from match (#30)<commit_after>package matches

import (
	"fmt"
)

type Player struct {
	CurrentPlatformID string `json:"currentPlatformId"`
	SummonerName      string `json:"summonerName"`
	MatchHistoryURI   string `json:"matchHistoryUri"`
	PlatformID        string `json:"platformId"`
	CurrentAccountID  string `json:"currentAccountId"`
	ProfileIcon       int    `json:"profileIcon"`
	SummonerID        string `json:"summonerId"`
	AccountID         string `json:"accountId"`
}

type ParticipantID struct {
	Player        Player `json:"player"`
	ParticipantID int    `json:"participantId"`
}

type Team struct {
	FirstDragon          bool          `json:"firstDragon"`
	Bans                 []interface{} `json:"bans"`
	FirstInhibitor       bool          `json:"firstInhibitor"`
	Win                  string        `json:"win"`
	FirstRiftHerald      bool          `json:"firstRiftHerald"`
	FirstBaron           bool          `json:"firstBaron"`
	BaronKills           int           `json:"baronKills"`
	RiftHeraldKills      int           `json:"riftHeraldKills"`
	FirstBlood           bool          `json:"firstBlood"`
	TeamID               int           `json:"teamId"`
	FirstTower           bool          `json:"firstTower"`
	VilemawKills         int           `json:"vilemawKills"`
	InhibitorKills       int           `json:"inhibitorKills"`
	TowerKills           int           `json:"towerKills"`
	DominionVictoryScore int           `json:"dominionVictoryScore"`
	DragonKills          int           `json:"dragonKills"`
}

type Match struct {
	SeasonID              int             `json:"seasonId"`
	QueueID               int             `json:"queueId"`
	GameID                int64           `json:"gameId"`
	ParticipantIdentities []ParticipantID `json:"participantIdentities"`
	GameVersion           string          `json:"gameVersion"`
	PlatformID            string          `json:"platformId"`
	GameMode              string          `json:"gameMode"`
	MapID                 int             `json:"mapId"`
	GameType              string          `json:"gameType"`
	Teams                 []Team          `json:"teams"`
	Participants          []Participant   `json:"participants"`
	GameDuration          int             `json:"gameDuration"`
	GameCreation          int64           `json:"gameCreation"`
}

func (m Match) GetParticipantId(summonerName string) (pid ParticipantID, err error) {
	for _, player := range m.ParticipantIdentities {
		if player.Player.SummonerName == summonerName {
			pid = player
			return pid, err
		}
	}

	err = fmt.Errorf("player not found: %s", summonerName)
	return pid, err
}
<|endoftext|>


<commit_before>/*
   Copyright (C) 2014  Salsita s.r.o.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program. If not, see {http://www.gnu.org/licenses/}.
*/

package pivotal

import (
	"fmt"
	"net/http"
	"net/url"
	"time"
)

const (
	StoryTypeFeature = "feature"
	StoryTypeBug     = "bug"
	StoryTypeChore   = "chore"
	StoryTypeRelease = "release"
)

const (
	StoryStateUnscheduled = "unscheduled"
	StoryStatePlanned     = "planned"
	StoryStateUnstarted   = "unstarted"
	StoryStateStarted     = "started"
	StoryStateFinished    = "finished"
	StoryStateDelivered   = "delivered"
	StoryStateAccepted    = "accepted"
	StoryStateRejected    = "rejected"
)

type Story struct {
	Id            int        `json:"id,omitempty"`
	ProjectId     int        `json:"project_id,omitempty"`
	Name          string     `json:"name,omitempty"`
	Description   string     `json:"description,omitempty"`
	Type          string     `json:"story_type,omitempty"`
	State         string     `json:"current_state,omitempty"`
	Estimate      float64    `json:"estimate,omitempty"`
	AcceptedAt    *time.Time `json:"accepted_at,omitempty"`
	Deadline      *time.Time `json:"deadline,omitempty"`
	RequestedById int        `json:"requested_by_id,omitempty"`
	OwnerIds      []int      `json:"owner_ids,omitempty"`
	LabelIds      []int      `json:"label_ids,omitempty"`
	Labels        []*Label   `json:"labels,omitempty"`
	TaskIds       []int      `json:"task_ids,omitempty"`
	Tasks         []int      `json:"tasks,omitempty"`
	FollowerIds   []int      `json:"follower_ids,omitempty"`
	CommentIds    []int      `json:"comment_ids,omitempty"`
	CreatedAt     *time.Time `json:"created_at,omitempty"`
	UpdatedAt     *time.Time `json:"updated_at,omitempty"`
	IntegrationId int        `json:"integration_id,omitempty"`
	ExternalId    string     `json:"external_id,omitempty"`
	URL           string     `json:"url,omitempty"`
	Kind          string     `json:"kind,omitempty"`
}

type Label struct {
	Id        int        `json:"id,omitempty"`
	ProjectId int        `json:"project_id,omitempty"`
	Name      string     `json:"name,omitempty"`
	CreatedAt *time.Time `json:"created_at,omitempty"`
	UpdatedAt *time.Time `json:"updated_at,omitempty"`
	Kind      string     `json:"kind,omitempty"`
}

type Task struct {
	Id          int        `json:"id,omitempty"`
	StoryId     int        `json:"story_id,omitempty"`
	Description string     `json:"description,omitempty"`
	Position    int        `json:"position,omitempty"`
	Complete    bool       `json:"complete,omitempty"`
	CreatedAt   *time.Time `json:"created_at,omitempty"`
	UpdatedAt   *time.Time `json:"updated_at,omitempty"`
}

type Person struct {
	Id       int    `json:"id,omitempty"`
	Name     string `json:"name,omitempty"`
	Email    string `json:"email,omitempty"`
	Initials string `json:"initials,omitempty"`
	Username string `json:"username,omitempty"`
	Kind     string `json:"kind,omitempty"`
}

type StoryService struct {
	client *Client
}

func newStoryService(client *Client) *StoryService {
	return &StoryService{client}
}

func (service *StoryService) List(projectId int, filter string) ([]*Story, *http.Response, error) {
	u := fmt.Sprintf("projects/%v/stories", projectId)
	if filter != "" {
		u += "?filter=" + url.QueryEscape(filter)
	}

	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var stories []*Story
	resp, err := service.client.Do(req, &stories)
	if err != nil {
		return nil, resp, err
	}

	return stories, resp, err
}

func (service *StoryService) Get(projectId, storyId int) (*Story, *http.Response, error) {
	u := fmt.Sprintf("projects/%v/stories/%v", projectId, storyId)
	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var story Story
	resp, err := service.client.Do(req, &story)
	if err != nil {
		return nil, resp, err
	}

	return &story, resp, err
}

func (service *StoryService) Update(projectId, storyId int, story *Story) (*Story, *http.Response, error) {
	u := fmt.Sprintf("projects/%v/stories/%v", projectId, storyId)
	req, err := service.client.NewRequest("PUT", u, story)
	if err != nil {
		return nil, nil, err
	}

	var bodyStory Story
	resp, err := service.client.Do(req, &bodyStory)
	if err != nil {
		return nil, resp, err
	}

	return &bodyStory, resp, err

}

func (service *StoryService) ListTasks(projectId, storyId int) ([]*Task, *http.Response, error) {
	u := fmt.Sprintf("projects/%v/stories/%v/tasks", projectId, storyId)
	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var tasks []*Task
	resp, err := service.client.Do(req, &tasks)
	if err != nil {
		return nil, resp, err
	}

	return tasks, resp, err
}

func (service *StoryService) AddTask(projectId, storyId int, task *Task) (*http.Response, error) {
	if task.Description == "" {
		return nil, &ErrFieldNotSet{"description"}
	}

	u := fmt.Sprintf("projects/%v/stories/%v/tasks", projectId, storyId)
	req, err := service.client.NewRequest("POST", u, task)
	if err != nil {
		return nil, err
	}

	return service.client.Do(req, nil)
}

func (service *StoryService) ListOwners(projectId, storyId int) ([]*Person, *http.Response, error) {
	u := fmt.Sprintf("projects/%d/stories/%d/owners", projectId, storyId)
	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var owners []*Person
	resp, err := service.client.Do(req, &owners)
	if err != nil {
		return nil, resp, err
	}

	return owners, resp, err
}
<commit_msg>Story: Make Estimate into a pointer<commit_after>/*
   Copyright (C) 2014  Salsita s.r.o.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program. If not, see {http://www.gnu.org/licenses/}.
*/

package pivotal

import (
	"fmt"
	"net/http"
	"net/url"
	"time"
)

const (
	StoryTypeFeature = "feature"
	StoryTypeBug     = "bug"
	StoryTypeChore   = "chore"
	StoryTypeRelease = "release"
)

const (
	StoryStateUnscheduled = "unscheduled"
	StoryStatePlanned     = "planned"
	StoryStateUnstarted   = "unstarted"
	StoryStateStarted     = "started"
	StoryStateFinished    = "finished"
	StoryStateDelivered   = "delivered"
	StoryStateAccepted    = "accepted"
	StoryStateRejected    = "rejected"
)

type Story struct {
	Id            int        `json:"id,omitempty"`
	ProjectId     int        `json:"project_id,omitempty"`
	Name          string     `json:"name,omitempty"`
	Description   string     `json:"description,omitempty"`
	Type          string     `json:"story_type,omitempty"`
	State         string     `json:"current_state,omitempty"`
	Estimate      *float64   `json:"estimate,omitempty"`
	AcceptedAt    *time.Time `json:"accepted_at,omitempty"`
	Deadline      *time.Time `json:"deadline,omitempty"`
	RequestedById int        `json:"requested_by_id,omitempty"`
	OwnerIds      []int      `json:"owner_ids,omitempty"`
	LabelIds      []int      `json:"label_ids,omitempty"`
	Labels        []*Label   `json:"labels,omitempty"`
	TaskIds       []int      `json:"task_ids,omitempty"`
	Tasks         []int      `json:"tasks,omitempty"`
	FollowerIds   []int      `json:"follower_ids,omitempty"`
	CommentIds    []int      `json:"comment_ids,omitempty"`
	CreatedAt     *time.Time `json:"created_at,omitempty"`
	UpdatedAt     *time.Time `json:"updated_at,omitempty"`
	IntegrationId int        `json:"integration_id,omitempty"`
	ExternalId    string     `json:"external_id,omitempty"`
	URL           string     `json:"url,omitempty"`
	Kind          string     `json:"kind,omitempty"`
}

type Label struct {
	Id        int        `json:"id,omitempty"`
	ProjectId int        `json:"project_id,omitempty"`
	Name      string     `json:"name,omitempty"`
	CreatedAt *time.Time `json:"created_at,omitempty"`
	UpdatedAt *time.Time `json:"updated_at,omitempty"`
	Kind      string     `json:"kind,omitempty"`
}

type Task struct {
	Id          int        `json:"id,omitempty"`
	StoryId     int        `json:"story_id,omitempty"`
	Description string     `json:"description,omitempty"`
	Position    int        `json:"position,omitempty"`
	Complete    bool       `json:"complete,omitempty"`
	CreatedAt   *time.Time `json:"created_at,omitempty"`
	UpdatedAt   *time.Time `json:"updated_at,omitempty"`
}

type Person struct {
	Id       int    `json:"id,omitempty"`
	Name     string `json:"name,omitempty"`
	Email    string `json:"email,omitempty"`
	Initials string `json:"initials,omitempty"`
	Username string `json:"username,omitempty"`
	Kind     string `json:"kind,omitempty"`
}

type StoryService struct {
	client *Client
}

func newStoryService(client *Client) *StoryService {
	return &StoryService{client}
}

func (service *StoryService) List(projectId int, filter string) ([]*Story, *http.Response, error) {
	u := fmt.Sprintf("projects/%v/stories", projectId)
	if filter != "" {
		u += "?filter=" + url.QueryEscape(filter)
	}

	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var stories []*Story
	resp, err := service.client.Do(req, &stories)
	if err != nil {
		return nil, resp, err
	}

	return stories, resp, err
}

func (service *StoryService) Get(projectId, storyId int) (*Story, *http.Response, error) {
	u := fmt.Sprintf("projects/%v/stories/%v", projectId, storyId)
	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var story Story
	resp, err := service.client.Do(req, &story)
	if err != nil {
		return nil, resp, err
	}

	return &story, resp, err
}

func (service *StoryService) Update(projectId, storyId int, story *Story) (*Story, *http.Response, error) {
	u := fmt.Sprintf("projects/%v/stories/%v", projectId, storyId)
	req, err := service.client.NewRequest("PUT", u, story)
	if err != nil {
		return nil, nil, err
	}

	var bodyStory Story
	resp, err := service.client.Do(req, &bodyStory)
	if err != nil {
		return nil, resp, err
	}

	return &bodyStory, resp, err

}

func (service *StoryService) ListTasks(projectId, storyId int) ([]*Task, *http.Response, error) {
	u := fmt.Sprintf("projects/%v/stories/%v/tasks", projectId, storyId)
	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var tasks []*Task
	resp, err := service.client.Do(req, &tasks)
	if err != nil {
		return nil, resp, err
	}

	return tasks, resp, err
}

func (service *StoryService) AddTask(projectId, storyId int, task *Task) (*http.Response, error) {
	if task.Description == "" {
		return nil, &ErrFieldNotSet{"description"}
	}

	u := fmt.Sprintf("projects/%v/stories/%v/tasks", projectId, storyId)
	req, err := service.client.NewRequest("POST", u, task)
	if err != nil {
		return nil, err
	}

	return service.client.Do(req, nil)
}

func (service *StoryService) ListOwners(projectId, storyId int) ([]*Person, *http.Response, error) {
	u := fmt.Sprintf("projects/%d/stories/%d/owners", projectId, storyId)
	req, err := service.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	var owners []*Person
	resp, err := service.client.Do(req, &owners)
	if err != nil {
		return nil, resp, err
	}

	return owners, resp, err
}
<|endoftext|>


<commit_before>package warehouse

import (
	"reflect"
	"testing"
	"time"

	"../config"
)

var _ Warehouse = &Redshift{}
var _ Field = &dataField{}

func TestToColumnDef(t *testing.T) {
	var testCases = []struct {
		input    Field
		expected string
	}{
		{&dataField{name: "StringField", dataType: reflect.TypeOf(""), isTime: false}, "StringField VARCHAR(max)"},
		{&dataField{name: "BigintField", dataType: reflect.TypeOf(int64(0)), isTime: false}, "BigintField BIGINT"},
		{&dataField{name: "TimestampField", dataType: reflect.TypeOf(time.Now()), isTime: true}, "TimestampField TIMESTAMP"},
	}

	for _, testCase := range testCases {
		if got := toColumnDef(testCase.input); got != testCase.expected {
			t.Errorf("Expected column definition %q from field %v, got %q", testCase.expected, testCase.input, got)
		}
	}
}

func TestRedshiftValueToString(t *testing.T) {
	wh := &Redshift{
		conf: &config.Config{
			Redshift: config.RedshiftConfig{
				VarCharMax: 20,
			},
		},
	}
	ts := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)

	var testCases = []struct {
		input    interface{}
		field    Field
		expected string
	}{
		{"short string", &dataField{dataType: reflect.TypeOf("")}, "short string"},
		{"I'm too long, truncate me", &dataField{dataType: reflect.TypeOf("")}, "I'm too long, trunc"},
		{"no\nnew\nlines", &dataField{dataType: reflect.TypeOf("")}, "no new lines"},
		{"no\x00null\x00chars", &dataField{dataType: reflect.TypeOf("")}, "nonullchars"},
		{5, &dataField{dataType: reflect.TypeOf(5)}, "5"},
		{"2009-11-10T23:00:00.000Z", &dataField{dataType: reflect.TypeOf(ts), isTime: true}, "2009-11-10 23:00:00 +0000 UTC"},
	}

	for _, testCase := range testCases {
		if got := wh.ValueToString(testCase.input, testCase.field); got != testCase.expected {
			t.Errorf("Expected value %q, got %q", testCase.expected, got)
		}
	}
}
<commit_msg>Remove unnecessary variable in unit test<commit_after>package warehouse

import (
	"reflect"
	"testing"
	"time"

	"../config"
)

var _ Warehouse = &Redshift{}
var _ Field = &dataField{}

func TestToColumnDef(t *testing.T) {
	var testCases = []struct {
		input    Field
		expected string
	}{
		{&dataField{name: "StringField", dataType: reflect.TypeOf(""), isTime: false}, "StringField VARCHAR(max)"},
		{&dataField{name: "BigintField", dataType: reflect.TypeOf(int64(0)), isTime: false}, "BigintField BIGINT"},
		{&dataField{name: "TimestampField", dataType: reflect.TypeOf(time.Now()), isTime: true}, "TimestampField TIMESTAMP"},
	}

	for _, testCase := range testCases {
		if got := toColumnDef(testCase.input); got != testCase.expected {
			t.Errorf("Expected column definition %q from field %v, got %q", testCase.expected, testCase.input, got)
		}
	}
}

func TestRedshiftValueToString(t *testing.T) {
	wh := &Redshift{
		conf: &config.Config{
			Redshift: config.RedshiftConfig{
				VarCharMax: 20,
			},
		},
	}

	var testCases = []struct {
		input    interface{}
		field    Field
		expected string
	}{
		{"short string", &dataField{dataType: reflect.TypeOf("")}, "short string"},
		{"I'm too long, truncate me", &dataField{dataType: reflect.TypeOf("")}, "I'm too long, trunc"},
		{"no\nnew\nlines", &dataField{dataType: reflect.TypeOf("")}, "no new lines"},
		{"no\x00null\x00chars", &dataField{dataType: reflect.TypeOf("")}, "nonullchars"},
		{5, &dataField{dataType: reflect.TypeOf(5)}, "5"},
		{"2009-11-10T23:00:00.000Z", &dataField{dataType: reflect.TypeOf(time.Now()), isTime: true}, "2009-11-10 23:00:00 +0000 UTC"},
	}

	for _, testCase := range testCases {
		if got := wh.ValueToString(testCase.input, testCase.field); got != testCase.expected {
			t.Errorf("Expected value %q, got %q", testCase.expected, got)
		}
	}
}
<|endoftext|>


<commit_before>/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.

    * Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.

    * Neither the name of "The Computer Language Benchmarks Game" nor the
    name of "The Computer Language Shootout Benchmarks" nor the names of
    its contributors may be used to endorse or promote products derived
    from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/

/* The Computer Language Benchmarks Game
 * http://shootout.alioth.debian.org/
 *
 * contributed by The Go Authors.
 * based on pidigits.c (by Paolo Bonzini & Sean Bartlett,
 *                      modified by Michael Mellor)
 */

package main

import (
	"big"
	"flag"
	"fmt"
)

var n = flag.Int("n", 27, "number of digits")
var silent = flag.Bool("s", false, "don't print result")

var (
	tmp1  = big.NewInt(0)
	tmp2  = big.NewInt(0)
	y2    = big.NewInt(0)
	bigk  = big.NewInt(0)
	numer = big.NewInt(1)
	accum = big.NewInt(0)
	denom = big.NewInt(1)
	ten   = big.NewInt(10)
)

func extract_digit() int64 {
	if numer.Cmp(accum) > 0 {
		return -1
	}

	// Compute (numer * 3 + accum) / denom
	tmp1.Lsh(numer, 1)
	tmp1.Add(tmp1, numer)
	tmp1.Add(tmp1, accum)
	tmp1.DivMod(tmp1, denom, tmp2)

	// Now, if (numer * 4 + accum) % denom...
	tmp2.Add(tmp2, numer)

	// ... is normalized, then the two divisions have the same result.
	if tmp2.Cmp(denom) >= 0 {
		return -1
	}

	return tmp1.Int64()
}

func next_term(k int64) {
	// TODO(eds) If big.Int ever gets a Scale method, y2 and bigk could be int64
	y2.New(k*2 + 1)
	bigk.New(k)

	tmp1.Lsh(numer, 1)
	accum.Add(accum, tmp1)
	accum.Mul(accum, y2)
	numer.Mul(numer, bigk)
	denom.Mul(denom, y2)
}

func eliminate_digit(d int64) {
	tmp := big.NewInt(0).Set(denom)
	accum.Sub(accum, tmp.Mul(tmp, big.NewInt(d)))
	accum.Mul(accum, ten)
	numer.Mul(numer, ten)
}

func printf(s string, arg ...interface{}) {
	if !*silent {
		fmt.Printf(s, arg)
	}
}

func main() {
	flag.Parse()

	var m int // 0 <= m < 10
	for i, k := 0, int64(0); ; {
		d := int64(-1)
		for d < 0 {
			k++
			next_term(k)
			d = extract_digit()
		}

		printf("%c", d+'0')

		i++
		m = i % 10
		if m == 0 {
			printf("\t:%d\n", i)
		}
		if i >= *n {
			break
		}
		eliminate_digit(d)
	}

	if m > 0 {
		printf("%s\t:%d\n", "          "[m:10], *n)
	}
}
<commit_msg>pidigits: ~10% performance win by using adds instead of shifts<commit_after>/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.

    * Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.

    * Neither the name of "The Computer Language Benchmarks Game" nor the
    name of "The Computer Language Shootout Benchmarks" nor the names of
    its contributors may be used to endorse or promote products derived
    from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/

/* The Computer Language Benchmarks Game
 * http://shootout.alioth.debian.org/
 *
 * contributed by The Go Authors.
 * based on pidigits.c (by Paolo Bonzini & Sean Bartlett,
 *                      modified by Michael Mellor)
 */

package main

import (
	"big"
	"flag"
	"fmt"
)

var n = flag.Int("n", 27, "number of digits")
var silent = flag.Bool("s", false, "don't print result")

var (
	tmp1  = big.NewInt(0)
	tmp2  = big.NewInt(0)
	y2    = big.NewInt(0)
	bigk  = big.NewInt(0)
	numer = big.NewInt(1)
	accum = big.NewInt(0)
	denom = big.NewInt(1)
	ten   = big.NewInt(10)
)

func extract_digit() int64 {
	if numer.Cmp(accum) > 0 {
		return -1
	}

	// Compute (numer * 3 + accum) / denom
	tmp1.Add(numer, numer) // tmp1.Lsh(numer, 1)
	tmp1.Add(tmp1, numer)
	tmp1.Add(tmp1, accum)
	tmp1.DivMod(tmp1, denom, tmp2)

	// Now, if (numer * 4 + accum) % denom...
	tmp2.Add(tmp2, numer)

	// ... is normalized, then the two divisions have the same result.
	if tmp2.Cmp(denom) >= 0 {
		return -1
	}

	return tmp1.Int64()
}

func next_term(k int64) {
	// TODO(eds) If big.Int ever gets a Scale method, y2 and bigk could be int64
	y2.New(k*2 + 1)
	bigk.New(k)

	tmp1.Add(numer, numer) // tmp1.Lsh(numer, 1)
	accum.Add(accum, tmp1)
	accum.Mul(accum, y2)
	numer.Mul(numer, bigk)
	denom.Mul(denom, y2)
}

func eliminate_digit(d int64) {
	tmp := big.NewInt(0).Set(denom)
	accum.Sub(accum, tmp.Mul(tmp, big.NewInt(d)))
	accum.Mul(accum, ten)
	numer.Mul(numer, ten)
}

func printf(s string, arg ...interface{}) {
	if !*silent {
		fmt.Printf(s, arg)
	}
}

func main() {
	flag.Parse()

	var m int // 0 <= m < 10
	for i, k := 0, int64(0); ; {
		d := int64(-1)
		for d < 0 {
			k++
			next_term(k)
			d = extract_digit()
		}

		printf("%c", d+'0')

		i++
		m = i % 10
		if m == 0 {
			printf("\t:%d\n", i)
		}
		if i >= *n {
			break
		}
		eliminate_digit(d)
	}

	if m > 0 {
		printf("%s\t:%d\n", "          "[m:10], *n)
	}
}
<|endoftext|>


<commit_before>package video

import "image"

func (self *VideoSequence) picture_data() (frame *image.YCbCr, err error) {

	w := int(self.SequenceHeader.horizontal_size_value)
	h := int(self.SequenceHeader.vertical_size_value)

	r := image.Rect(0, 0, w, h)

	var subsampleRatio image.YCbCrSubsampleRatio
	switch self.SequenceExtension.chroma_format {
	case ChromaFormat_420:
		subsampleRatio = image.YCbCrSubsampleRatio420
	case ChromaFormat_422:
		subsampleRatio = image.YCbCrSubsampleRatio422
	case ChromaFormat_444:
		subsampleRatio = image.YCbCrSubsampleRatio444
	}

	frame = image.NewYCbCr(r, subsampleRatio)

	for {
		if err := self.slice(frame); err != nil {
			return nil, err
		}

		if nextbits, err := self.Peek32(32); err != nil {
			return nil, err
		} else if StartCode(nextbits).isSlice() == false {
			break
		}
	}

	return frame, self.next_start_code()
}
<commit_msg>make sure to create the frame buffer an even number of macroblocks<commit_after>package video

import "image"

func createFrameBuffer(w, h uint32, cf chromaFormat) *image.YCbCr {

	horizontalMacroblocks := w >> 4
	verticalMacroblocks := h >> 4

	if w&15 != 0 {
		horizontalMacroblocks++
	}

	if h&15 != 0 {
		verticalMacroblocks++
	}

	r := image.Rect(0, 0, int(horizontalMacroblocks<<4), int(verticalMacroblocks<<4))

	var subsampleRatio image.YCbCrSubsampleRatio
	switch cf {
	case ChromaFormat_420:
		subsampleRatio = image.YCbCrSubsampleRatio420
	case ChromaFormat_422:
		subsampleRatio = image.YCbCrSubsampleRatio422
	case ChromaFormat_444:
		subsampleRatio = image.YCbCrSubsampleRatio444
	}

	return image.NewYCbCr(r, subsampleRatio)

}

func (self *VideoSequence) picture_data() (frame *image.YCbCr, err error) {

	frame = createFrameBuffer(self.SequenceHeader.horizontal_size_value, self.SequenceHeader.vertical_size_value, self.SequenceExtension.chroma_format)

	for {
		if err := self.slice(frame); err != nil {
			return nil, err
		}

		if nextbits, err := self.Peek32(32); err != nil {
			return nil, err
		} else if StartCode(nextbits).isSlice() == false {
			break
		}
	}

	return frame, self.next_start_code()
}
<|endoftext|>


<commit_before>/*
 * Copyright 2017 Dgraph Labs, Inc. and Contributors
 *
 * 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 badger

import (
	"context"
	"encoding/binary"
	"fmt"
	"path/filepath"
	"sync"
	"time"

	"github.com/dgraph-io/badger/mem"
	"github.com/dgraph-io/badger/table"
	"github.com/dgraph-io/badger/value"
	"github.com/dgraph-io/badger/y"
)

var (
	Head = []byte("/head/") // For storing value offset for replay.
)

// Options are params for creating DB object.
type Options struct {
	Dir                     string
	NumLevelZeroTables      int   // Maximum number of Level 0 tables before we start compacting.
	NumLevelZeroTablesStall int   // If we hit this number of Level 0 tables, we will stall until level 0 is compacted away.
	LevelOneSize            int64 // Maximum total size for Level 1.
	MaxLevels               int   // Maximum number of levels of compaction. May be made variable later.
	MaxTableSize            int64 // Each table (or file) is at most this size.
	LevelSizeMultiplier     int
	ValueThreshold          int // If value size >= this threshold, we store value offsets in tables.
	Verbose                 bool
	DoNotCompact            bool
	MapTablesTo             int
	NumMemtables            int
}

var DefaultOptions = Options{
	Dir:                     "/tmp",
	NumLevelZeroTables:      5,
	NumLevelZeroTablesStall: 10,
	LevelOneSize:            256 << 20,
	MaxLevels:               7,
	MaxTableSize:            64 << 20,
	LevelSizeMultiplier:     10,
	ValueThreshold:          20,
	Verbose:                 true,
	DoNotCompact:            false, // Only for testing.
	MapTablesTo:             table.MemoryMap,
	NumMemtables:            5,
}

type KV struct {
	sync.RWMutex // Guards imm, mem.

	mt        *mem.Table
	imm       []*mem.Table   // Add here only AFTER pushing to flushChan.
	flushChan chan flushTask // For flushing memtables.
	flushDone chan struct{}
	opt       Options
	lc        *levelsController
	vlog      value.Log
	voffset   uint64
}

type flushTask struct {
	mt      *mem.Table
	voffset uint64
}

// Summary is produced when DB is closed. Currently it is used only for testing.
type Summary struct {
	fileIDs map[uint64]bool
}

func (s *KV) flushMemtable() {
	var done bool
	for !done {
		select {
		case ft := <-s.flushChan:
			if ft.mt == nil {
				if s.opt.Verbose {
					y.Printf("Closing flushChan\n")
				}
				close(s.flushChan) // This is our close signal.
				done = true
				break
			}
			if ft.voffset > 0 {
				if s.opt.Verbose {
					fmt.Printf("Storing offset: %v\n", ft.voffset)
				}
				offset := make([]byte, 8)
				binary.BigEndian.PutUint64(offset, ft.voffset)
				ft.mt.Put(Head, offset, 0)
			}
			fileID, _ := s.lc.reserveFileIDs(1)
			fd, err := y.OpenSyncedFile(table.NewFilename(fileID, s.opt.Dir))
			y.Check(err)
			y.Check(ft.mt.WriteLevel0Table(fd))
			tbl, err := table.OpenTable(fd, s.opt.MapTablesTo)
			defer tbl.DecrRef()
			y.Check(err)
			s.lc.addLevel0Table(tbl) // This will incrRef again.

			// Update s.imm. Need a lock.
			s.Lock()
			y.AssertTrue(ft.mt == s.imm[0]) //For now, single threaded.
			s.imm = s.imm[1:]
			s.Unlock()
		}
	}
	s.flushDone <- struct{}{}
}

// NewKV returns a new KV object. Compact levels are created as well.
func NewKV(opt *Options) *KV {
	y.AssertTrue(len(opt.Dir) > 0)
	out := &KV{
		mt:        mem.NewTable(),
		imm:       make([]*mem.Table, 0, opt.NumMemtables),
		flushChan: make(chan flushTask, opt.NumMemtables),
		flushDone: make(chan struct{}),
		opt:       *opt, // Make a copy.
	}

	// newLevelsController potentially loads files in directory.
	out.lc = newLevelsController(out)
	out.lc.startCompact()
	go out.flushMemtable() // Need levels controller to be up.

	vlogPath := filepath.Join(opt.Dir, "vlog")
	out.vlog.Open(vlogPath)

	val := out.Get(context.Background(), Head)
	var voffset uint64
	if len(val) == 0 {
		voffset = 0
	} else {
		voffset = binary.BigEndian.Uint64(val)
	}

	first := true
	fn := func(k, v []byte, meta byte) {
		if first {
			fmt.Printf("key=%s\n", k)
		}
		first = false
		out.mt.Put(k, v, meta)
	}
	out.vlog.Replay(voffset, fn)
	return out
}

// Close closes a KV.
func (s *KV) Close() {
	if s.opt.Verbose {
		y.Printf("Closing database\n")
	}

	// TODO(ganesh): Block writes to mem.
	if s.mt.Size() > 0 {
		s.Lock()
		if s.opt.Verbose {
			y.Printf("Flushing memtable\n")
		}
		y.AssertTrue(s.mt != nil)
		s.flushChan <- flushTask{s.mt, s.voffset}
		s.imm = append(s.imm, s.mt) // Flusher will attempt to remove this from s.imm.
		s.mt = nil                  // Will segfault if we try writing!
		s.Unlock()
	}
	s.flushChan <- flushTask{nil, 0} // Tell flusher to quit.
	<-s.flushDone                    // Wait for flusher to be done.
	if s.opt.Verbose {
		y.Printf("Memtable flushed\n")
	}
	s.lc.close()
}

func (s *KV) getMemTables() (*mem.Table, []*mem.Table) {
	s.RLock()
	defer s.RUnlock()
	return s.mt, s.imm
}

func (s *KV) decodeValue(ctx context.Context, val []byte, meta byte) []byte {
	if (meta & value.BitDelete) != 0 {
		// Tombstone encountered.
		return nil
	}
	if (meta & value.BitValuePointer) == 0 {
		y.Trace(ctx, "Value stored with key")
		return val
	}

	var vp value.Pointer
	vp.Decode(val)
	entry, err := s.vlog.Read(ctx, vp)
	y.Checkf(err, "Unable to read from value log: %+v", vp)

	if (entry.Meta & value.BitDelete) == 0 { // Not tombstone.
		return entry.Value
	}
	return []byte{}
}

// getValueHelper returns the value in memtable or disk for given key.
// Note that value will include meta byte.
func (s *KV) get(ctx context.Context, key []byte) ([]byte, byte) {
	y.Trace(ctx, "Retrieving key from memtables")
	mem, imm := s.getMemTables() // Lock should be released.
	if mem != nil {
		v, meta := mem.Get(key)
		if meta != 0 || v != nil {
			return v, meta
		}
	}
	for i := len(imm) - 1; i >= 0; i-- {
		v, meta := imm[i].Get(key)
		if meta != 0 || v != nil {
			return v, meta
		}
	}
	y.Trace(ctx, "Not found in memtables. Getting from levels")
	return s.lc.get(ctx, key)
}

// Get looks for key and returns value. If not found, return nil.
func (s *KV) Get(ctx context.Context, key []byte) []byte {
	val, meta := s.get(ctx, key)
	return s.decodeValue(ctx, val, meta)
}

func (s *KV) updateOffset(ptrs []value.Pointer) {
	ptr := ptrs[len(ptrs)-1]

	s.Lock()
	defer s.Unlock()
	if s.voffset < ptr.Offset {
		s.voffset = ptr.Offset + uint64(ptr.Len)
	}
}

// Write applies a list of value.Entry to our memtable.
func (s *KV) Write(ctx context.Context, entries []value.Entry) error {
	y.Trace(ctx, "Making room for writes")
	for !s.hasRoomForWrite() {
		// We need to poll a bit because if both hasRoomForWrite and the flusher need
		// access to s.imm. When flushChan is full and you are blocked there, and the flusher
		// is trying to update s.imm, you will get a deadlock.
		time.Sleep(10 * time.Millisecond)
	}

	y.Trace(ctx, "Writing to value log.")
	ptrs, err := s.vlog.Write(entries)
	y.Check(err)
	y.AssertTrue(len(ptrs) == len(entries))
	s.updateOffset(ptrs)

	y.Trace(ctx, "Writing to memtable")
	var offsetBuf [20]byte
	for i, entry := range entries {
		if len(entry.Value) < s.opt.ValueThreshold { // Will include deletion / tombstone case.
			s.mt.Put(entry.Key, entry.Value, entry.Meta)
		} else {
			s.mt.Put(entry.Key, ptrs[i].Encode(offsetBuf[:]), entry.Meta|value.BitValuePointer)
		}
	}
	y.Trace(ctx, "Wrote %d entries.", len(entries))
	return nil
}

// Put puts a key-val pair.
func (s *KV) Put(ctx context.Context, key []byte, val []byte) error {
	return s.Write(ctx, []value.Entry{
		{
			Key:   key,
			Value: val,
		},
	})
}

// Delete deletes a key.
func (s *KV) Delete(ctx context.Context, key []byte) error {
	return s.Write(ctx, []value.Entry{
		{
			Key:  key,
			Meta: value.BitDelete,
		},
	})
}

func (s *KV) hasRoomForWrite() bool {
	s.Lock()
	defer s.Unlock()
	if s.mt.Size() < s.opt.MaxTableSize {
		return true
	}

	y.AssertTrue(s.mt != nil) // A nil mt indicates that KV is being closed.
	select {
	case s.flushChan <- flushTask{s.mt, s.voffset}:
		if s.opt.Verbose {
			y.Printf("Flushing memtable, size of flushChan: %d\n", len(s.flushChan))
		}
		// We manage to push this task. Let's modify imm.
		s.imm = append(s.imm, s.mt)
		s.mt = mem.NewTable()
		// New memtable is empty. We certainly have room.
		return true
	default:
		// We need to do this to unlock and allow the flusher to modify imm.
		return false
	}
}

func (s *KV) Validate() { s.lc.validate() }

func (s *KV) DebugPrintMore() { s.lc.debugPrintMore() }
<commit_msg>Fix comments<commit_after>/*
 * Copyright 2017 Dgraph Labs, Inc. and Contributors
 *
 * 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 badger

import (
	"context"
	"encoding/binary"
	"fmt"
	"path/filepath"
	"sync"
	"time"

	"github.com/dgraph-io/badger/mem"
	"github.com/dgraph-io/badger/table"
	"github.com/dgraph-io/badger/value"
	"github.com/dgraph-io/badger/y"
)

var (
	Head = []byte("/head/") // For storing value offset for replay.
)

// Options are params for creating DB object.
type Options struct {
	Dir                     string
	NumLevelZeroTables      int   // Maximum number of Level 0 tables before we start compacting.
	NumLevelZeroTablesStall int   // If we hit this number of Level 0 tables, we will stall until level 0 is compacted away.
	LevelOneSize            int64 // Maximum total size for Level 1.
	MaxLevels               int   // Maximum number of levels of compaction. May be made variable later.
	MaxTableSize            int64 // Each table (or file) is at most this size.
	LevelSizeMultiplier     int
	ValueThreshold          int // If value size >= this threshold, we store value offsets in tables.
	Verbose                 bool
	DoNotCompact            bool
	MapTablesTo             int
	NumMemtables            int
}

var DefaultOptions = Options{
	Dir:                     "/tmp",
	NumLevelZeroTables:      5,
	NumLevelZeroTablesStall: 10,
	LevelOneSize:            256 << 20,
	MaxLevels:               7,
	MaxTableSize:            64 << 20,
	LevelSizeMultiplier:     10,
	ValueThreshold:          20,
	Verbose:                 true,
	DoNotCompact:            false, // Only for testing.
	MapTablesTo:             table.MemoryMap,
	NumMemtables:            5,
}

type KV struct {
	sync.RWMutex // Guards imm, mem.

	mt        *mem.Table
	imm       []*mem.Table   // Add here only AFTER pushing to flushChan.
	flushChan chan flushTask // For flushing memtables.
	flushDone chan struct{}
	opt       Options
	lc        *levelsController
	vlog      value.Log
	voffset   uint64
}

type flushTask struct {
	mt      *mem.Table
	voffset uint64
}

// Summary is produced when DB is closed. Currently it is used only for testing.
type Summary struct {
	fileIDs map[uint64]bool
}

func (s *KV) flushMemtable() {
	var done bool
	for !done {
		select {
		case ft := <-s.flushChan:
			if ft.mt == nil {
				if s.opt.Verbose {
					y.Printf("Closing flushChan\n")
				}
				close(s.flushChan) // This is our close signal.
				done = true
				break
			}
			if ft.voffset > 0 {
				if s.opt.Verbose {
					fmt.Printf("Storing offset: %v\n", ft.voffset)
				}
				offset := make([]byte, 8)
				binary.BigEndian.PutUint64(offset, ft.voffset)
				ft.mt.Put(Head, offset, 0)
			}
			fileID, _ := s.lc.reserveFileIDs(1)
			fd, err := y.OpenSyncedFile(table.NewFilename(fileID, s.opt.Dir))
			y.Check(err)
			y.Check(ft.mt.WriteLevel0Table(fd))
			tbl, err := table.OpenTable(fd, s.opt.MapTablesTo)
			defer tbl.DecrRef()
			y.Check(err)
			s.lc.addLevel0Table(tbl) // This will incrRef again.

			// Update s.imm. Need a lock.
			s.Lock()
			y.AssertTrue(ft.mt == s.imm[0]) //For now, single threaded.
			s.imm = s.imm[1:]
			s.Unlock()
		}
	}
	s.flushDone <- struct{}{}
}

// NewKV returns a new KV object. Compact levels are created as well.
func NewKV(opt *Options) *KV {
	y.AssertTrue(len(opt.Dir) > 0)
	out := &KV{
		mt:        mem.NewTable(),
		imm:       make([]*mem.Table, 0, opt.NumMemtables),
		flushChan: make(chan flushTask, opt.NumMemtables),
		flushDone: make(chan struct{}),
		opt:       *opt, // Make a copy.
	}

	// newLevelsController potentially loads files in directory.
	out.lc = newLevelsController(out)
	out.lc.startCompact()
	go out.flushMemtable() // Need levels controller to be up.

	vlogPath := filepath.Join(opt.Dir, "vlog")
	out.vlog.Open(vlogPath)

	val := out.Get(context.Background(), Head)
	var voffset uint64
	if len(val) == 0 {
		voffset = 0
	} else {
		voffset = binary.BigEndian.Uint64(val)
	}

	first := true
	fn := func(k, v []byte, meta byte) {
		if first {
			fmt.Printf("key=%s\n", k)
		}
		first = false
		out.mt.Put(k, v, meta)
	}
	out.vlog.Replay(voffset, fn)
	return out
}

// Close closes a KV.
func (s *KV) Close() {
	if s.opt.Verbose {
		y.Printf("Closing database\n")
	}

	// TODO(ganesh): Block writes to mem.
	if s.mt.Size() > 0 {
		s.Lock()
		if s.opt.Verbose {
			y.Printf("Flushing memtable\n")
		}
		y.AssertTrue(s.mt != nil)
		s.flushChan <- flushTask{s.mt, s.voffset}
		s.imm = append(s.imm, s.mt) // Flusher will attempt to remove this from s.imm.
		s.mt = nil                  // Will segfault if we try writing!
		s.Unlock()
	}
	s.flushChan <- flushTask{nil, 0} // Tell flusher to quit.
	<-s.flushDone                    // Wait for flusher to be done.
	if s.opt.Verbose {
		y.Printf("Memtable flushed\n")
	}
	s.lc.close()
}

func (s *KV) getMemTables() (*mem.Table, []*mem.Table) {
	s.RLock()
	defer s.RUnlock()
	return s.mt, s.imm
}

func (s *KV) decodeValue(ctx context.Context, val []byte, meta byte) []byte {
	if (meta & value.BitDelete) != 0 {
		// Tombstone encountered.
		return nil
	}
	if (meta & value.BitValuePointer) == 0 {
		y.Trace(ctx, "Value stored with key")
		return val
	}

	var vp value.Pointer
	vp.Decode(val)
	entry, err := s.vlog.Read(ctx, vp)
	y.Checkf(err, "Unable to read from value log: %+v", vp)

	if (entry.Meta & value.BitDelete) == 0 { // Not tombstone.
		return entry.Value
	}
	return []byte{}
}

// getValueHelper returns the value in memtable or disk for given key.
// Note that value will include meta byte.
func (s *KV) get(ctx context.Context, key []byte) ([]byte, byte) {
	y.Trace(ctx, "Retrieving key from memtables")
	mem, imm := s.getMemTables() // Lock should be released.
	if mem != nil {
		v, meta := mem.Get(key)
		if meta != 0 || v != nil {
			return v, meta
		}
	}
	for i := len(imm) - 1; i >= 0; i-- {
		v, meta := imm[i].Get(key)
		if meta != 0 || v != nil {
			return v, meta
		}
	}
	y.Trace(ctx, "Not found in memtables. Getting from levels")
	return s.lc.get(ctx, key)
}

// Get looks for key and returns value. If not found, return nil.
func (s *KV) Get(ctx context.Context, key []byte) []byte {
	val, meta := s.get(ctx, key)
	return s.decodeValue(ctx, val, meta)
}

func (s *KV) updateOffset(ptrs []value.Pointer) {
	ptr := ptrs[len(ptrs)-1]

	s.Lock()
	defer s.Unlock()
	if s.voffset < ptr.Offset {
		s.voffset = ptr.Offset + uint64(ptr.Len)
	}
}

// Write applies a list of value.Entry to our memtable.
func (s *KV) Write(ctx context.Context, entries []value.Entry) error {
	y.Trace(ctx, "Making room for writes")
	for !s.hasRoomForWrite() {
		// We need to poll a bit because both hasRoomForWrite and the flusher need access to s.imm.
		// When flushChan is full and you are blocked there, and the flusher is trying to update s.imm,
		// you will get a deadlock.
		time.Sleep(10 * time.Millisecond)
	}

	y.Trace(ctx, "Writing to value log.")
	ptrs, err := s.vlog.Write(entries)
	y.Check(err)
	y.AssertTrue(len(ptrs) == len(entries))
	s.updateOffset(ptrs)

	y.Trace(ctx, "Writing to memtable")
	var offsetBuf [20]byte
	for i, entry := range entries {
		if len(entry.Value) < s.opt.ValueThreshold { // Will include deletion / tombstone case.
			s.mt.Put(entry.Key, entry.Value, entry.Meta)
		} else {
			s.mt.Put(entry.Key, ptrs[i].Encode(offsetBuf[:]), entry.Meta|value.BitValuePointer)
		}
	}
	y.Trace(ctx, "Wrote %d entries.", len(entries))
	return nil
}

// Put puts a key-val pair.
func (s *KV) Put(ctx context.Context, key []byte, val []byte) error {
	return s.Write(ctx, []value.Entry{
		{
			Key:   key,
			Value: val,
		},
	})
}

// Delete deletes a key.
func (s *KV) Delete(ctx context.Context, key []byte) error {
	return s.Write(ctx, []value.Entry{
		{
			Key:  key,
			Meta: value.BitDelete,
		},
	})
}

func (s *KV) hasRoomForWrite() bool {
	s.Lock()
	defer s.Unlock()
	if s.mt.Size() < s.opt.MaxTableSize {
		return true
	}

	y.AssertTrue(s.mt != nil) // A nil mt indicates that KV is being closed.
	select {
	case s.flushChan <- flushTask{s.mt, s.voffset}:
		if s.opt.Verbose {
			y.Printf("Flushing memtable, size of flushChan: %d\n", len(s.flushChan))
		}
		// We manage to push this task. Let's modify imm.
		s.imm = append(s.imm, s.mt)
		s.mt = mem.NewTable()
		// New memtable is empty. We certainly have room.
		return true
	default:
		// We need to do this to unlock and allow the flusher to modify imm.
		return false
	}
}

func (s *KV) Validate() { s.lc.validate() }

func (s *KV) DebugPrintMore() { s.lc.debugPrintMore() }
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"
	"path/filepath"

	"github.com/hashicorp/otto/directory"
	"github.com/hashicorp/otto/foundation"
)

// Foundation is a helper for various operations a foundation must
// perform with Terraform.
type Foundation struct {
	// Dir is the directory where Terraform is run. If this isn't set, it'll
	// default to "#{ctx.Dir}/deploy".
	Dir string
}

// Infra manages a foundation using Terraform.
//
// This will verify the infrastruction is created and use that information
// to execute Terraform with the options given.
//
// This function implements foundation.Foundation.Infra.
func (f *Foundation) Infra(ctx *foundation.Context) error {
	switch ctx.Action {
	case "":
		if err := f.execute(ctx, "get", "."); err != nil {
			return err
		}

		return f.execute(ctx, "apply")
	case "destroy":
		return f.execute(ctx, "destroy", "-force")
	default:
		return fmt.Errorf("unknown action: %s", ctx.Action)
	}
}

func (f *Foundation) execute(ctx *foundation.Context, args ...string) error {
	project, err := Project(&ctx.Shared)
	if err != nil {
		return err
	}

	appInfra := ctx.Appfile.ActiveInfrastructure()

	// Foundations themselves are represented as infrastructure in the
	// backend. Let's look that up. If it doesn't exist, we have to create
	// it in order to get our UUID for storing state.
	lookup := directory.Lookup{Infra: appInfra.Name, Foundation: ctx.Tuple.Type}
	foundationInfra, err := ctx.Directory.GetInfra(&directory.Infra{Lookup: lookup})
	if err != nil {
		return fmt.Errorf(
			"Error looking up existing infrastructure data: %s\n\n"+
				"These errors are usually transient and can be fixed by retrying\n"+
				"the command. Additional causes of errors are networking or disk\n"+
				"issues that can be resolved external to Otto.",
			err)
	}
	if foundationInfra == nil {
		// If we don't have an infra, create one
		foundationInfra = &directory.Infra{Lookup: lookup}
		foundationInfra.State = directory.InfraStatePartial

		// Put the infrastructure so we can get the UUID to use for our state
		if err := ctx.Directory.PutInfra(foundationInfra); err != nil {
			return fmt.Errorf(
				"Error preparing infrastructure: %s\n\n"+
					"These errors are usually transient and can be fixed by retrying\n"+
					"the command. Additional causes of errors are networking or disk\n"+
					"issues that can be resolved external to Otto.",
				err)
		}
	}

	// Get the infrastructure state. The infrastructure must be
	// created for us to deploy to it.
	infra, err := ctx.Directory.GetInfra(&directory.Infra{
		Lookup: directory.Lookup{Infra: appInfra.Name}})
	if err != nil {
		return err
	}
	if infra == nil || infra.State != directory.InfraStateReady {
		return fmt.Errorf(
			"Infrastructure for this application hasn't been built yet.\n" +
				"Building a foundation requires a target infrastruction to\n" +
				"be built. Please run `otta infra` to build the underlying\n" +
				"infrastructure.")
	}

	// Construct the variables for Terraform from our queried infra
	vars := make(map[string]string)
	for k, v := range infra.Outputs {
		vars[k] = v
	}
	for k, v := range ctx.InfraCreds {
		vars[k] = v
	}

	// Get the directory
	tfDir := f.Dir
	if tfDir == "" {
		tfDir = filepath.Join(ctx.Dir, "deploy")
	}

	// Run Terraform!
	tf := &Terraform{
		Path:      project.Path(),
		Dir:       tfDir,
		Ui:        ctx.Ui,
		Variables: vars,
		Directory: ctx.Directory,
		StateId:   foundationInfra.ID,
	}
	if err := tf.Execute(args...); err != nil {
		return fmt.Errorf(
			"Error running Terraform: %s\n\n"+
				"Terraform usually has helpful error messages. Please read the error\n"+
				"messages above and resolve them. Sometimes simply re-running the\n"+
				"command again will work.",
			err)
	}

	ctx.Ui.Header("Terraform execution complete. Saving results...")
	if err == nil {
		if ctx.Action == "destroy" {
			// If we just destroyed successfully, the infra is now empty.
			foundationInfra.State = directory.InfraStateInvalid
			foundationInfra.Outputs = map[string]string{}
		} else {
			// If an apply was successful, populate the state and outputs.
			foundationInfra.State = directory.InfraStateReady
			foundationInfra.Outputs, err = tf.Outputs()
			if err != nil {
				err = fmt.Errorf("Error reading Terraform outputs: %s", err)
				foundationInfra.State = directory.InfraStatePartial
			}
		}
	}

	// Save the infrastructure information
	if err := ctx.Directory.PutInfra(foundationInfra); err != nil {
		return fmt.Errorf(
			"Error storing infrastructure data: %s\n\n"+
				"This means that Otto won't be able to know that your infrastructure\n"+
				"was successfully created. Otto tries a few times to save the\n"+
				"infrastructure. At this point in time, Otto doesn't support gracefully\n"+
				"recovering from this error. Your infrastructure is now orphaned from\n"+
				"Otto's management. Please reference the community for help.\n\n"+
				"A future version of Otto will resolve this.",
			err)
	}

	return nil
}
<commit_msg>helper/terraform: use proper infra lookup<commit_after>package terraform

import (
	"fmt"
	"path/filepath"

	"github.com/hashicorp/otto/directory"
	"github.com/hashicorp/otto/foundation"
)

// Foundation is a helper for various operations a foundation must
// perform with Terraform.
type Foundation struct {
	// Dir is the directory where Terraform is run. If this isn't set, it'll
	// default to "#{ctx.Dir}/deploy".
	Dir string
}

// Infra manages a foundation using Terraform.
//
// This will verify the infrastruction is created and use that information
// to execute Terraform with the options given.
//
// This function implements foundation.Foundation.Infra.
func (f *Foundation) Infra(ctx *foundation.Context) error {
	switch ctx.Action {
	case "":
		if err := f.execute(ctx, "get", "."); err != nil {
			return err
		}

		return f.execute(ctx, "apply")
	case "destroy":
		return f.execute(ctx, "destroy", "-force")
	default:
		return fmt.Errorf("unknown action: %s", ctx.Action)
	}
}

func (f *Foundation) execute(ctx *foundation.Context, args ...string) error {
	project, err := Project(&ctx.Shared)
	if err != nil {
		return err
	}

	appInfra := ctx.Appfile.ActiveInfrastructure()

	// Foundations themselves are represented as infrastructure in the
	// backend. Let's look that up. If it doesn't exist, we have to create
	// it in order to get our UUID for storing state.
	lookup := directory.Lookup{Infra: appInfra.Type, Foundation: ctx.Tuple.Type}
	foundationInfra, err := ctx.Directory.GetInfra(&directory.Infra{Lookup: lookup})
	if err != nil {
		return fmt.Errorf(
			"Error looking up existing infrastructure data: %s\n\n"+
				"These errors are usually transient and can be fixed by retrying\n"+
				"the command. Additional causes of errors are networking or disk\n"+
				"issues that can be resolved external to Otto.",
			err)
	}
	if foundationInfra == nil {
		// If we don't have an infra, create one
		foundationInfra = &directory.Infra{Lookup: lookup}
		foundationInfra.State = directory.InfraStatePartial

		// Put the infrastructure so we can get the UUID to use for our state
		if err := ctx.Directory.PutInfra(foundationInfra); err != nil {
			return fmt.Errorf(
				"Error preparing infrastructure: %s\n\n"+
					"These errors are usually transient and can be fixed by retrying\n"+
					"the command. Additional causes of errors are networking or disk\n"+
					"issues that can be resolved external to Otto.",
				err)
		}
	}

	// Get the infrastructure state. The infrastructure must be
	// created for us to deploy to it.
	infra, err := ctx.Directory.GetInfra(&directory.Infra{
		Lookup: directory.Lookup{Infra: appInfra.Name}})
	if err != nil {
		return err
	}
	if infra == nil || infra.State != directory.InfraStateReady {
		return fmt.Errorf(
			"Infrastructure for this application hasn't been built yet.\n" +
				"Building a foundation requires a target infrastruction to\n" +
				"be built. Please run `otta infra` to build the underlying\n" +
				"infrastructure.")
	}

	// Construct the variables for Terraform from our queried infra
	vars := make(map[string]string)
	for k, v := range infra.Outputs {
		vars[k] = v
	}
	for k, v := range ctx.InfraCreds {
		vars[k] = v
	}

	// Get the directory
	tfDir := f.Dir
	if tfDir == "" {
		tfDir = filepath.Join(ctx.Dir, "deploy")
	}

	// Run Terraform!
	tf := &Terraform{
		Path:      project.Path(),
		Dir:       tfDir,
		Ui:        ctx.Ui,
		Variables: vars,
		Directory: ctx.Directory,
		StateId:   foundationInfra.ID,
	}
	err = tf.Execute(args...)
	if err != nil {
		return fmt.Errorf(
			"Error running Terraform: %s\n\n"+
				"Terraform usually has helpful error messages. Please read the error\n"+
				"messages above and resolve them. Sometimes simply re-running the\n"+
				"command again will work.",
			err)
	}

	ctx.Ui.Header("Terraform execution complete. Saving results...")
	if err == nil {
		if ctx.Action == "destroy" {
			// If we just destroyed successfully, the infra is now empty.
			foundationInfra.State = directory.InfraStateInvalid
			foundationInfra.Outputs = map[string]string{}
		} else {
			// If an apply was successful, populate the state and outputs.
			foundationInfra.State = directory.InfraStateReady
			foundationInfra.Outputs, err = tf.Outputs()
			if err != nil {
				err = fmt.Errorf("Error reading Terraform outputs: %s", err)
				foundationInfra.State = directory.InfraStatePartial
			}
		}
	}

	// Save the infrastructure information
	if err := ctx.Directory.PutInfra(foundationInfra); err != nil {
		return fmt.Errorf(
			"Error storing infrastructure data: %s\n\n"+
				"This means that Otto won't be able to know that your infrastructure\n"+
				"was successfully created. Otto tries a few times to save the\n"+
				"infrastructure. At this point in time, Otto doesn't support gracefully\n"+
				"recovering from this error. Your infrastructure is now orphaned from\n"+
				"Otto's management. Please reference the community for help.\n\n"+
				"A future version of Otto will resolve this.",
			err)
	}

	return err
}
<|endoftext|>


<commit_before>//go:generate bash collect.sh modules.go

package lib

/*
func init() {
	Modules = joinModules(
		decorator.Module,
		doc.Module,
		lark.Module,
	)
}

// Modules contains all the modules defined in the library.
var Modules []gluamodule.Module

func joinModules(m ...gluamodule.Module) []gluamodule.Module {
	var mods []gluamodule.Module
	for _, m := range m {
		mods = append(mods, gluamodule.Collect(m)...)
	}
	return mods
}
*/
<commit_msg>lib: remove commented code that was replaced by code generation<commit_after>//go:generate bash collect.sh modules.go

package lib
<|endoftext|>


<commit_before>// Package lib contains functions useful for most programs.
package lib

import (
	"fmt"
	"os"
	"path/filepath"
	"time"

	"golang.org/x/sys/unix"
)

var progname = filepath.Base(os.Args[0])

// ProgName returns what lib thinks the program name is, namely the
// basename of of argv0.
//
// It is similar to the Linux __progname.
func ProgName() string {
	return progname
}

// Warnx displays a formatted error message to standard error, à la
// warnx(3).
func Warnx(format string, a ...interface{}) (int, error) {
	format = fmt.Sprintf("[%s] %s", progname, format)
	format += "\n"
	return fmt.Fprintf(os.Stderr, format, a...)
}

// Warn displays a formatted error message to standard output,
// appending the error string, à la warn(3).
func Warn(err error, format string, a ...interface{}) (int, error) {
	format = fmt.Sprintf("[%s] %s", progname, format)
	format += ": %v\n"
	a = append(a, err)
	return fmt.Fprintf(os.Stderr, format, a...)
}

// Errx displays a formatted error message to standard error and exits
// with the status code from `exit`, à la errx(3).
func Errx(exit int, format string, a ...interface{}) {
	format = fmt.Sprintf("[%s] %s", progname, format)
	format += "\n"
	fmt.Fprintf(os.Stderr, format, a...)
	os.Exit(exit)
}

// Err displays a formatting error message to standard error,
// appending the error string, and exits with the status code from
// `exit`, à la err(3).
func Err(exit int, err error, format string, a ...interface{}) {
	format = fmt.Sprintf("[%s] %s", progname, format)
	format += ": %v\n"
	a = append(a, err)
	fmt.Fprintf(os.Stderr, format, a...)
	os.Exit(exit)
}

// CheckFatal calls Err if err isn't nil.
func CheckFatal(err error, format string, a ...interface{}) {
	if err == nil {
		return
	}

	Err(ExitFailure, err, format, a...)
}

// Itoa provides cheap integer to fixed-width decimal ASCII.  Give a
// negative width to avoid zero-padding. Adapted from the 'itoa'
// function in the log/log.go file in the standard library.
func Itoa(i int, wid int) string {
	// Assemble decimal in reverse order.
	var b [20]byte
	bp := len(b) - 1
	for i >= 10 || wid > 1 {
		wid--
		q := i / 10
		b[bp] = byte('0' + i - q*10)
		bp--
		i = q
	}
	// i < 10
	b[bp] = byte('0' + i)
	return string(b[bp:])
}

var (
	dayDuration  = 24 * time.Hour
	yearDuration = (365 * dayDuration) + (6 * time.Hour)
)

func Duration(d time.Duration) string {
	var s string
	if d >= yearDuration {
		years := d / yearDuration
		s += fmt.Sprintf("%dy", years)
		d -= (years * yearDuration)
	}

	if d >= dayDuration {
		days := d / dayDuration
		s += fmt.Sprintf("%dd", days)
	}

	if s != "" {
		return s
	}

	d %= 1 * time.Second
	hours := d / time.Hour
	d -= (hours * time.Hour)
	s += fmt.Sprintf("%dh%s", hours, d)
	return s
}

// FileTime contains the created, modified, and accessed timestamps
// for a file.
type FileTime struct {
	Created  time.Time
	Modified time.Time
	Accessed time.Time
}

func timeSpecToTime(ts unix.TimeSpec) time.Time {
	return time.Unix(ts.Sec, ts.Nsec)
}

// LoadFileTime returns a FileTime associated with the file.
func LoadFileTime(path string) (FileTime, error) {
	var ft = FileTime{}
	var st = unix.Stat_t{}

	err := unix.Stat(path, &st)
	if err != nil {
		return ft, err
	}

	ft.Created = timeSpecToTime(st.Ctim)
	ft.Modified = timeSpecToTime(st.Mtim)
	ft.Accessed = timeSpecToTime(st.Atim)
	return ft, nil
}
<commit_msg>Fix type: TimeSpec -> Timespec.<commit_after>// Package lib contains functions useful for most programs.
package lib

import (
	"fmt"
	"os"
	"path/filepath"
	"time"

	"golang.org/x/sys/unix"
)

var progname = filepath.Base(os.Args[0])

// ProgName returns what lib thinks the program name is, namely the
// basename of of argv0.
//
// It is similar to the Linux __progname.
func ProgName() string {
	return progname
}

// Warnx displays a formatted error message to standard error, à la
// warnx(3).
func Warnx(format string, a ...interface{}) (int, error) {
	format = fmt.Sprintf("[%s] %s", progname, format)
	format += "\n"
	return fmt.Fprintf(os.Stderr, format, a...)
}

// Warn displays a formatted error message to standard output,
// appending the error string, à la warn(3).
func Warn(err error, format string, a ...interface{}) (int, error) {
	format = fmt.Sprintf("[%s] %s", progname, format)
	format += ": %v\n"
	a = append(a, err)
	return fmt.Fprintf(os.Stderr, format, a...)
}

// Errx displays a formatted error message to standard error and exits
// with the status code from `exit`, à la errx(3).
func Errx(exit int, format string, a ...interface{}) {
	format = fmt.Sprintf("[%s] %s", progname, format)
	format += "\n"
	fmt.Fprintf(os.Stderr, format, a...)
	os.Exit(exit)
}

// Err displays a formatting error message to standard error,
// appending the error string, and exits with the status code from
// `exit`, à la err(3).
func Err(exit int, err error, format string, a ...interface{}) {
	format = fmt.Sprintf("[%s] %s", progname, format)
	format += ": %v\n"
	a = append(a, err)
	fmt.Fprintf(os.Stderr, format, a...)
	os.Exit(exit)
}

// CheckFatal calls Err if err isn't nil.
func CheckFatal(err error, format string, a ...interface{}) {
	if err == nil {
		return
	}

	Err(ExitFailure, err, format, a...)
}

// Itoa provides cheap integer to fixed-width decimal ASCII.  Give a
// negative width to avoid zero-padding. Adapted from the 'itoa'
// function in the log/log.go file in the standard library.
func Itoa(i int, wid int) string {
	// Assemble decimal in reverse order.
	var b [20]byte
	bp := len(b) - 1
	for i >= 10 || wid > 1 {
		wid--
		q := i / 10
		b[bp] = byte('0' + i - q*10)
		bp--
		i = q
	}
	// i < 10
	b[bp] = byte('0' + i)
	return string(b[bp:])
}

var (
	dayDuration  = 24 * time.Hour
	yearDuration = (365 * dayDuration) + (6 * time.Hour)
)

func Duration(d time.Duration) string {
	var s string
	if d >= yearDuration {
		years := d / yearDuration
		s += fmt.Sprintf("%dy", years)
		d -= (years * yearDuration)
	}

	if d >= dayDuration {
		days := d / dayDuration
		s += fmt.Sprintf("%dd", days)
	}

	if s != "" {
		return s
	}

	d %= 1 * time.Second
	hours := d / time.Hour
	d -= (hours * time.Hour)
	s += fmt.Sprintf("%dh%s", hours, d)
	return s
}

// FileTime contains the created, modified, and accessed timestamps
// for a file.
type FileTime struct {
	Created  time.Time
	Modified time.Time
	Accessed time.Time
}

func timeSpecToTime(ts unix.Timespec) time.Time {
	return time.Unix(ts.Sec, ts.Nsec)
}

// LoadFileTime returns a FileTime associated with the file.
func LoadFileTime(path string) (FileTime, error) {
	var ft = FileTime{}
	var st = unix.Stat_t{}

	err := unix.Stat(path, &st)
	if err != nil {
		return ft, err
	}

	ft.Created = timeSpecToTime(st.Ctim)
	ft.Modified = timeSpecToTime(st.Mtim)
	ft.Accessed = timeSpecToTime(st.Atim)
	return ft, nil
}
<|endoftext|>


<commit_before>package twilio

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

	"github.com/google/go-querystring/query"
)

const errorStatusCode = 400

// Credentials store user authentication credentials.
type Credentials struct {
	AccountSid string
	AuthToken  string
}

// Request provides a standard HTTP backend.
type Request struct {
	Credentials
	Client  *http.Client
	BaseURL string
}

// Error provides information about an unsuccessful request.
type Error struct {
	Code     int    `json:"code"`
	Detail   string `json:"detail"`
	Message  string `json:"message"`
	MoreInfo string `json:"more_info"`
	Status   int    `json:"status"`
}

func (err *Error) Error() string {
	return fmt.Sprintf("Status: %d - Error %d: %s (%s) More info: %s",
		err.Status, err.Code, err.Message, err.Detail, err.MoreInfo)
}

func (request *Request) basicAuth() (string, string) {
	return request.Credentials.AccountSid, request.Credentials.AuthToken
}

func doWithErr(req *http.Request, client *http.Client) (*http.Response, error) {
	res, httpErr := client.Do(req)
	if httpErr != nil {
		return nil, httpErr
	}

	if res.StatusCode >= errorStatusCode {
		apiErr := &Error{}
		if decodeErr := json.NewDecoder(res.Body).Decode(apiErr); decodeErr != nil {
			return nil, decodeErr
		}

		return nil, apiErr
	}

	return res, nil
}

//MakeRequest make an HTTP request
func (request Request) MakeRequest(method string, fullyQualifiedURI string, data interface{}) (*http.Response, error) {
	var valueReader *strings.Reader

	if data != nil {
		v, _ := query.Values(data)
		valueReader = strings.NewReader(v.Encode())
	}

	req, err := http.NewRequest(method, fullyQualifiedURI, valueReader)
	if err != nil {
		return nil, err
	}

	req.SetBasicAuth(request.basicAuth())

	if method == http.MethodPost {
		req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	}

	return doWithErr(req, request.Client)
}

// Post performs a POST request on the object at the provided URI in the context of the Request's BaseURL
// with the provided data as parameters.
func (request Request) Post(uri string, data interface{}) (*http.Response, error) {
	fullyQualifiedURI := request.BaseURL + uri
	return request.MakeRequest(http.MethodPost, fullyQualifiedURI, data)
}

// Get performs a GET request on the object at the provided URI in the context of the Request's BaseURL
// with the provided data as parameters.
func (request Request) Get(uri string) (*http.Response, error) {
	fullyQualifiedURI := request.BaseURL + uri
	return request.MakeRequest(http.MethodGet, fullyQualifiedURI, nil)
}

// Delete performs a DELETE request on the object at the provided URI in the context of the Request's BaseURL
// with the provided data as parameters.
func (request Request) Delete(uri string) (*http.Response, error) {
	fullyQualifiedURI := request.BaseURL + uri
	return request.MakeRequest(http.MethodDelete, fullyQualifiedURI, nil)
}
<commit_msg>Feature/taskrouter (#6)<commit_after>package twilio

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

	"github.com/google/go-querystring/query"
)

const errorStatusCode = 400

// Credentials store user authentication credentials.
type Credentials struct {
	AccountSid string
	AuthToken  string
}

// Request provides a standard HTTP backend.
type Request struct {
	Credentials
	Client  *http.Client
	BaseURL string
}

// Error provides information about an unsuccessful request.
type Error struct {
	Code     int    `json:"code"`
	Detail   string `json:"detail"`
	Message  string `json:"message"`
	MoreInfo string `json:"more_info"`
	Status   int    `json:"status"`
}

func (err *Error) Error() string {
	return fmt.Sprintf("Status: %d - Error %d: %s (%s) More info: %s",
		err.Status, err.Code, err.Message, err.Detail, err.MoreInfo)
}

func (request *Request) basicAuth() (string, string) {
	return request.Credentials.AccountSid, request.Credentials.AuthToken
}

func doWithErr(req *http.Request, client *http.Client) (*http.Response, error) {
	res, httpErr := client.Do(req)
	if httpErr != nil {
		return nil, httpErr
	}

	if res.StatusCode >= errorStatusCode {
		apiErr := &Error{}
		if decodeErr := json.NewDecoder(res.Body).Decode(apiErr); decodeErr != nil {
			return nil, decodeErr
		}

		return nil, apiErr
	}

	return res, nil
}

// MakeRequest makes HTTP request.
func (request Request) MakeRequest(method string, fullyQualifiedURI string, data interface{}) (*http.Response, error) {
	valueReader := &strings.Reader{}

	if data != nil {
		v, _ := query.Values(data)
		valueReader = strings.NewReader(v.Encode())
	}

	req, err := http.NewRequest(method, fullyQualifiedURI, valueReader)
	if err != nil {
		return nil, err
	}

	req.SetBasicAuth(request.basicAuth())

	if method == http.MethodPost {
		req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	}

	return doWithErr(req, request.Client)
}

// Post performs a POST request on the object at the provided URI in the context of the Request's BaseURL
// with the provided data as parameters.
func (request Request) Post(uri string, data interface{}) (*http.Response, error) {
	fullyQualifiedURI := request.BaseURL + uri
	return request.MakeRequest(http.MethodPost, fullyQualifiedURI, data)
}

// Get performs a GET request on the object at the provided URI in the context of the Request's BaseURL
// with the provided data as parameters.
func (request Request) Get(uri string) (*http.Response, error) {
	fullyQualifiedURI := request.BaseURL + uri
	return request.MakeRequest(http.MethodGet, fullyQualifiedURI, nil)
}

// Delete performs a DELETE request on the object at the provided URI in the context of the Request's BaseURL
// with the provided data as parameters.
func (request Request) Delete(uri string) (*http.Response, error) {
	fullyQualifiedURI := request.BaseURL + uri
	return request.MakeRequest(http.MethodDelete, fullyQualifiedURI, nil)
}
<|endoftext|>