<commit_before>package netsync

import (
	"strconv"
	"sync"

	log "github.com/sirupsen/logrus"
	"gopkg.in/fatih/set.v0"

	"github.com/bytom/consensus"
	"github.com/bytom/errors"
	"github.com/bytom/p2p"
	"github.com/bytom/p2p/trust"
	"github.com/bytom/protocol/bc"
	"github.com/bytom/protocol/bc/types"
)

var (
	errClosed            = errors.New("peer set is closed")
	errAlreadyRegistered = errors.New("peer is already registered")
	errNotRegistered     = errors.New("peer is not registered")
)

const (
	defaultVersion      = 1
	defaultBanThreshold = uint64(100)
)

type peer struct {
	mtx      sync.RWMutex
	version  int // Protocol version negotiated
	services consensus.ServiceFlag
	id       string
	height   uint64
	hash     *bc.Hash
	banScore trust.DynamicBanScore

	swPeer *p2p.Peer

	knownTxs    *set.Set // Set of transaction hashes known to be known by this peer
	knownBlocks *set.Set // Set of block hashes known to be known by this peer
}

// PeerInfo indicate peer information
type PeerInfo struct {
	Id         string
	RemoteAddr string
	Height     uint64
	delay      uint32
}

func newPeer(height uint64, hash *bc.Hash, Peer *p2p.Peer) *peer {
	services := consensus.SFFullNode
	if len(Peer.Other) != 0 {
		if serviceFlag, err := strconv.ParseUint(Peer.Other[0], 10, 64); err != nil {
			services = consensus.ServiceFlag(serviceFlag)
		}
	}

	return &peer{
		version:     defaultVersion,
		services:    services,
		id:          Peer.Key,
		height:      height,
		hash:        hash,
		swPeer:      Peer,
		knownTxs:    set.New(),
		knownBlocks: set.New(),
	}
}

func (p *peer) GetStatus() (height uint64, hash *bc.Hash) {
	p.mtx.RLock()
	defer p.mtx.RUnlock()
	return p.height, p.hash
}

func (p *peer) SetStatus(height uint64, hash *bc.Hash) {
	p.mtx.Lock()
	defer p.mtx.Unlock()

	p.height = height
	p.hash = hash
}

func (p *peer) requestBlockByHash(hash *bc.Hash) error {
	msg := &BlockRequestMessage{RawHash: hash.Byte32()}
	p.swPeer.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg})
	return nil
}

func (p *peer) requestBlockByHeight(height uint64) error {
	msg := &BlockRequestMessage{Height: height}
	p.swPeer.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg})
	return nil
}

func (p *peer) SendTransactions(txs []*types.Tx) error {
	for _, tx := range txs {
		msg, err := NewTransactionNotifyMessage(tx)
		if err != nil {
			return errors.New("Failed construction tx msg")
		}
		hash := &tx.ID
		p.knownTxs.Add(hash.String())
		if p.swPeer == nil {
			return errPeerDropped
		}
		p.swPeer.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg})
	}
	return nil
}

func (p *peer) GetPeer() *p2p.Peer {
	p.mtx.RLock()
	defer p.mtx.RUnlock()

	return p.swPeer
}

func (p *peer) GetPeerInfo() *PeerInfo {
	p.mtx.RLock()
	defer p.mtx.RUnlock()
	return &PeerInfo{
		Id:         p.id,
		RemoteAddr: p.swPeer.RemoteAddr,
		Height:     p.height,
		delay:      0, // TODO
	}
}

// MarkTransaction marks a transaction as known for the peer, ensuring that it
// will never be propagated to this particular peer.
func (p *peer) MarkTransaction(hash *bc.Hash) {
	p.mtx.Lock()
	defer p.mtx.Unlock()

	// If we reached the memory allowance, drop a previously known transaction hash
	for p.knownTxs.Size() >= maxKnownTxs {
		p.knownTxs.Pop()
	}
	p.knownTxs.Add(hash.String())
}

// MarkBlock marks a block as known for the peer, ensuring that the block will
// never be propagated to this particular peer.
func (p *peer) MarkBlock(hash *bc.Hash) {
	p.mtx.Lock()
	defer p.mtx.Unlock()

	// If we reached the memory allowance, drop a previously known block hash
	for p.knownBlocks.Size() >= maxKnownBlocks {
		p.knownBlocks.Pop()
	}
	p.knownBlocks.Add(hash.String())
}

// addBanScore increases the persistent and decaying ban score fields by the
// values passed as parameters. If the resulting score exceeds half of the ban
// threshold, a warning is logged including the reason provided. Further, if
// the score is above the ban threshold, the peer will be banned and
// disconnected.
func (p *peer) addBanScore(persistent, transient uint64, reason string) bool {
	warnThreshold := defaultBanThreshold >> 1
	if transient == 0 && persistent == 0 {
		// The score is not being increased, but a warning message is still
		// logged if the score is above the warn threshold.
		score := p.banScore.Int()
		if score > warnThreshold {
			log.Infof("Misbehaving peer %s: %s -- ban score is %d, "+"it was not increased this time", p.id, reason, score)
		}
		return false
	}
	score := p.banScore.Increase(persistent, transient)
	if score > warnThreshold {
		log.Infof("Misbehaving peer %s: %s -- ban score increased to %d", p.id, reason, score)
		if score > defaultBanThreshold {
			log.Errorf("Misbehaving peer %s -- banning and disconnecting", p.id)
			return true
		}
	}
	return false
}

type peerSet struct {
	peers  map[string]*peer
	lock   sync.RWMutex
	closed bool
}

// newPeerSet creates a new peer set to track the active participants.
func newPeerSet() *peerSet {
	return &peerSet{
		peers: make(map[string]*peer),
	}
}

// Register injects a new peer into the working set, or returns an error if the
// peer is already known.
func (ps *peerSet) Register(p *peer) error {
	ps.lock.Lock()
	defer ps.lock.Unlock()

	if ps.closed {
		return errClosed
	}
	if _, ok := ps.peers[p.id]; ok {
		return errAlreadyRegistered
	}
	ps.peers[p.id] = p
	return nil
}

// Unregister removes a remote peer from the active set, disabling any further
// actions to/from that particular entity.
func (ps *peerSet) Unregister(id string) error {
	ps.lock.Lock()
	defer ps.lock.Unlock()

	if _, ok := ps.peers[id]; !ok {
		return errNotRegistered
	}
	delete(ps.peers, id)
	return nil
}

// Peer retrieves the registered peer with the given id.
func (ps *peerSet) Peer(id string) (*peer, bool) {
	ps.lock.RLock()
	defer ps.lock.RUnlock()
	p, ok := ps.peers[id]
	return p, ok
}

// getPeerInfos return all peer information of current node
func (ps *peerSet) GetPeerInfos() []*PeerInfo {
	var peerInfos []*PeerInfo
	for _, peer := range ps.peers {
		peerInfos = append(peerInfos, peer.GetPeerInfo())
	}
	return peerInfos
}

// Len returns if the current number of peers in the set.
func (ps *peerSet) Len() int {
	ps.lock.RLock()
	defer ps.lock.RUnlock()

	return len(ps.peers)
}

// MarkTransaction marks a transaction as known for the peer, ensuring that it
// will never be propagated to this particular peer.
func (ps *peerSet) MarkTransaction(peerID string, hash *bc.Hash) {
	ps.lock.RLock()
	defer ps.lock.RUnlock()

	if peer, ok := ps.peers[peerID]; ok {
		peer.MarkTransaction(hash)
	}
}

// MarkBlock marks a block as known for the peer, ensuring that the block will
// never be propagated to this particular peer.
func (ps *peerSet) MarkBlock(peerID string, hash *bc.Hash) {
	ps.lock.RLock()
	defer ps.lock.RUnlock()

	if peer, ok := ps.peers[peerID]; ok {
		peer.MarkBlock(hash)
	}
}

// PeersWithoutBlock retrieves a list of peers that do not have a given block in
// their set of known hashes.
func (ps *peerSet) PeersWithoutBlock(hash *bc.Hash) []*peer {
	ps.lock.RLock()
	defer ps.lock.RUnlock()

	list := make([]*peer, 0, len(ps.peers))
	for _, p := range ps.peers {
		if !p.knownBlocks.Has(hash.String()) {
			list = append(list, p)
		}
	}
	return list
}

// PeersWithoutTx retrieves a list of peers that do not have a given transaction
// in their set of known hashes.
func (ps *peerSet) PeersWithoutTx(hash *bc.Hash) []*peer {
	ps.lock.RLock()
	defer ps.lock.RUnlock()

	list := make([]*peer, 0, len(ps.peers))
	for _, p := range ps.peers {
		if !p.knownTxs.Has(hash.String()) {
			list = append(list, p)
		}
	}
	return list
}

// BestPeer retrieves the known peer with the currently highest total difficulty.
func (ps *peerSet) BestPeer() (*p2p.Peer, uint64) {
	ps.lock.RLock()
	defer ps.lock.RUnlock()

	var bestPeer *p2p.Peer
	var bestHeight uint64

	for _, p := range ps.peers {
		if bestPeer == nil || p.height > bestHeight {
			bestPeer, bestHeight = p.swPeer, p.height
		}
	}

	return bestPeer, bestHeight
}

// Close disconnects all peers.
// No new peers can be registered after Close has returned.
func (ps *peerSet) Close() {
	ps.lock.Lock()
	defer ps.lock.Unlock()

	for _, p := range ps.peers {
		p.swPeer.CloseConn()
	}
	ps.closed = true
}

func (ps *peerSet) AddPeer(peer *p2p.Peer) {
	ps.lock.Lock()
	defer ps.lock.Unlock()

	if _, ok := ps.peers[peer.Key]; !ok {
		keeperPeer := newPeer(0, nil, peer)
		ps.peers[peer.Key] = keeperPeer
		log.WithFields(log.Fields{"ID": peer.Key}).Info("Add new peer to blockKeeper")
		return
	}
	log.WithField("ID", peer.Key).Warning("Add existing peer to blockKeeper")
}

func (ps *peerSet) RemovePeer(peerID string) {
	ps.lock.Lock()
	defer ps.lock.Unlock()

	delete(ps.peers, peerID)
	log.WithField("ID", peerID).Info("Delete peer from peerset")
}

func (ps *peerSet) SetPeerStatus(peerID string, height uint64, hash *bc.Hash) {
	ps.lock.Lock()
	defer ps.lock.Unlock()

	if peer, ok := ps.peers[peerID]; ok {
		peer.SetStatus(height, hash)
	}
}

func (ps *peerSet) requestBlockByHash(peerID string, hash *bc.Hash) error {
	peer, ok := ps.Peer(peerID)
	if !ok {
		return errors.New("Can't find peer. ")
	}
	return peer.requestBlockByHash(hash)
}

func (ps *peerSet) requestBlockByHeight(peerID string, height uint64) error {
	peer, ok := ps.Peer(peerID)
	if !ok {
		return errors.New("Can't find peer. ")
	}
	return peer.requestBlockByHeight(height)
}

func (ps *peerSet) BroadcastMinedBlock(block *types.Block) ([]*peer, error) {
	msg, err := NewMinedBlockMessage(block)
	if err != nil {
		return nil, errors.New("Failed construction block msg")
	}
	hash := block.Hash()
	peers := ps.PeersWithoutBlock(&hash)
	abnormalPeers := make([]*peer, 0)
	for _, peer := range peers {
		if ok := peer.swPeer.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg}); !ok {
			abnormalPeers = append(abnormalPeers, peer)
			continue
		}
		if p, ok := ps.Peer(peer.id); ok {
			p.MarkBlock(&hash)
		}
	}
	return abnormalPeers, nil
}

func (ps *peerSet) BroadcastNewStatus(block *types.Block) ([]*peer, error) {
	return ps.BroadcastMinedBlock(block)
}

func (ps *peerSet) BroadcastTx(tx *types.Tx) ([]*peer, error) {
	msg, err := NewTransactionNotifyMessage(tx)
	if err != nil {
		return nil, errors.New("Failed construction tx msg")
	}
	peers := ps.PeersWithoutTx(&tx.ID)
	abnormalPeers := make([]*peer, 0)
	for _, peer := range peers {
		if ok := peer.swPeer.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg}); !ok {
			abnormalPeers = append(abnormalPeers, peer)
			continue
		}
		if p, ok := ps.Peer(peer.id); ok {
			p.MarkTransaction(&tx.ID)
		}
	}
	return abnormalPeers, nil
}
<commit_msg>the struct of node_info add json field (#1120)<commit_after>package netsync

import (
	"strconv"
	"sync"

	log "github.com/sirupsen/logrus"
	"gopkg.in/fatih/set.v0"

	"github.com/bytom/consensus"
	"github.com/bytom/errors"
	"github.com/bytom/p2p"
	"github.com/bytom/p2p/trust"
	"github.com/bytom/protocol/bc"
	"github.com/bytom/protocol/bc/types"
)

var (
	errClosed            = errors.New("peer set is closed")
	errAlreadyRegistered = errors.New("peer is already registered")
	errNotRegistered     = errors.New("peer is not registered")
)

const (
	defaultVersion      = 1
	defaultBanThreshold = uint64(100)
)

type peer struct {
	mtx      sync.RWMutex
	version  int // Protocol version negotiated
	services consensus.ServiceFlag
	id       string
	height   uint64
	hash     *bc.Hash
	banScore trust.DynamicBanScore

	swPeer *p2p.Peer

	knownTxs    *set.Set // Set of transaction hashes known to be known by this peer
	knownBlocks *set.Set // Set of block hashes known to be known by this peer
}

// PeerInfo indicate peer information
type PeerInfo struct {
	Id         string `json:"id"`
	RemoteAddr string `json:"remote_addr"`
	Height     uint64 `json:"height"`
	Delay      uint32 `json:"delay"`
}

func newPeer(height uint64, hash *bc.Hash, Peer *p2p.Peer) *peer {
	services := consensus.SFFullNode
	if len(Peer.Other) != 0 {
		if serviceFlag, err := strconv.ParseUint(Peer.Other[0], 10, 64); err != nil {
			services = consensus.ServiceFlag(serviceFlag)
		}
	}

	return &peer{
		version:     defaultVersion,
		services:    services,
		id:          Peer.Key,
		height:      height,
		hash:        hash,
		swPeer:      Peer,
		knownTxs:    set.New(),
		knownBlocks: set.New(),
	}
}

func (p *peer) GetStatus() (height uint64, hash *bc.Hash) {
	p.mtx.RLock()
	defer p.mtx.RUnlock()
	return p.height, p.hash
}

func (p *peer) SetStatus(height uint64, hash *bc.Hash) {
	p.mtx.Lock()
	defer p.mtx.Unlock()

	p.height = height
	p.hash = hash
}

func (p *peer) requestBlockByHash(hash *bc.Hash) error {
	msg := &BlockRequestMessage{RawHash: hash.Byte32()}
	p.swPeer.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg})
	return nil
}

func (p *peer) requestBlockByHeight(height uint64) error {
	msg := &BlockRequestMessage{Height: height}
	p.swPeer.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg})
	return nil
}

func (p *peer) SendTransactions(txs []*types.Tx) error {
	for _, tx := range txs {
		msg, err := NewTransactionNotifyMessage(tx)
		if err != nil {
			return errors.New("Failed construction tx msg")
		}
		hash := &tx.ID
		p.knownTxs.Add(hash.String())
		if p.swPeer == nil {
			return errPeerDropped
		}
		p.swPeer.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg})
	}
	return nil
}

func (p *peer) GetPeer() *p2p.Peer {
	p.mtx.RLock()
	defer p.mtx.RUnlock()

	return p.swPeer
}

func (p *peer) GetPeerInfo() *PeerInfo {
	p.mtx.RLock()
	defer p.mtx.RUnlock()
	return &PeerInfo{
		Id:         p.id,
		RemoteAddr: p.swPeer.RemoteAddr,
		Height:     p.height,
		Delay:      0, // TODO
	}
}

// MarkTransaction marks a transaction as known for the peer, ensuring that it
// will never be propagated to this particular peer.
func (p *peer) MarkTransaction(hash *bc.Hash) {
	p.mtx.Lock()
	defer p.mtx.Unlock()

	// If we reached the memory allowance, drop a previously known transaction hash
	for p.knownTxs.Size() >= maxKnownTxs {
		p.knownTxs.Pop()
	}
	p.knownTxs.Add(hash.String())
}

// MarkBlock marks a block as known for the peer, ensuring that the block will
// never be propagated to this particular peer.
func (p *peer) MarkBlock(hash *bc.Hash) {
	p.mtx.Lock()
	defer p.mtx.Unlock()

	// If we reached the memory allowance, drop a previously known block hash
	for p.knownBlocks.Size() >= maxKnownBlocks {
		p.knownBlocks.Pop()
	}
	p.knownBlocks.Add(hash.String())
}

// addBanScore increases the persistent and decaying ban score fields by the
// values passed as parameters. If the resulting score exceeds half of the ban
// threshold, a warning is logged including the reason provided. Further, if
// the score is above the ban threshold, the peer will be banned and
// disconnected.
func (p *peer) addBanScore(persistent, transient uint64, reason string) bool {
	warnThreshold := defaultBanThreshold >> 1
	if transient == 0 && persistent == 0 {
		// The score is not being increased, but a warning message is still
		// logged if the score is above the warn threshold.
		score := p.banScore.Int()
		if score > warnThreshold {
			log.Infof("Misbehaving peer %s: %s -- ban score is %d, "+"it was not increased this time", p.id, reason, score)
		}
		return false
	}
	score := p.banScore.Increase(persistent, transient)
	if score > warnThreshold {
		log.Infof("Misbehaving peer %s: %s -- ban score increased to %d", p.id, reason, score)
		if score > defaultBanThreshold {
			log.Errorf("Misbehaving peer %s -- banning and disconnecting", p.id)
			return true
		}
	}
	return false
}

type peerSet struct {
	peers  map[string]*peer
	lock   sync.RWMutex
	closed bool
}

// newPeerSet creates a new peer set to track the active participants.
func newPeerSet() *peerSet {
	return &peerSet{
		peers: make(map[string]*peer),
	}
}

// Register injects a new peer into the working set, or returns an error if the
// peer is already known.
func (ps *peerSet) Register(p *peer) error {
	ps.lock.Lock()
	defer ps.lock.Unlock()

	if ps.closed {
		return errClosed
	}
	if _, ok := ps.peers[p.id]; ok {
		return errAlreadyRegistered
	}
	ps.peers[p.id] = p
	return nil
}

// Unregister removes a remote peer from the active set, disabling any further
// actions to/from that particular entity.
func (ps *peerSet) Unregister(id string) error {
	ps.lock.Lock()
	defer ps.lock.Unlock()

	if _, ok := ps.peers[id]; !ok {
		return errNotRegistered
	}
	delete(ps.peers, id)
	return nil
}

// Peer retrieves the registered peer with the given id.
func (ps *peerSet) Peer(id string) (*peer, bool) {
	ps.lock.RLock()
	defer ps.lock.RUnlock()
	p, ok := ps.peers[id]
	return p, ok
}

// getPeerInfos return all peer information of current node
func (ps *peerSet) GetPeerInfos() []*PeerInfo {
	var peerInfos []*PeerInfo
	for _, peer := range ps.peers {
		peerInfos = append(peerInfos, peer.GetPeerInfo())
	}
	return peerInfos
}

// Len returns if the current number of peers in the set.
func (ps *peerSet) Len() int {
	ps.lock.RLock()
	defer ps.lock.RUnlock()

	return len(ps.peers)
}

// MarkTransaction marks a transaction as known for the peer, ensuring that it
// will never be propagated to this particular peer.
func (ps *peerSet) MarkTransaction(peerID string, hash *bc.Hash) {
	ps.lock.RLock()
	defer ps.lock.RUnlock()

	if peer, ok := ps.peers[peerID]; ok {
		peer.MarkTransaction(hash)
	}
}

// MarkBlock marks a block as known for the peer, ensuring that the block will
// never be propagated to this particular peer.
func (ps *peerSet) MarkBlock(peerID string, hash *bc.Hash) {
	ps.lock.RLock()
	defer ps.lock.RUnlock()

	if peer, ok := ps.peers[peerID]; ok {
		peer.MarkBlock(hash)
	}
}

// PeersWithoutBlock retrieves a list of peers that do not have a given block in
// their set of known hashes.
func (ps *peerSet) PeersWithoutBlock(hash *bc.Hash) []*peer {
	ps.lock.RLock()
	defer ps.lock.RUnlock()

	list := make([]*peer, 0, len(ps.peers))
	for _, p := range ps.peers {
		if !p.knownBlocks.Has(hash.String()) {
			list = append(list, p)
		}
	}
	return list
}

// PeersWithoutTx retrieves a list of peers that do not have a given transaction
// in their set of known hashes.
func (ps *peerSet) PeersWithoutTx(hash *bc.Hash) []*peer {
	ps.lock.RLock()
	defer ps.lock.RUnlock()

	list := make([]*peer, 0, len(ps.peers))
	for _, p := range ps.peers {
		if !p.knownTxs.Has(hash.String()) {
			list = append(list, p)
		}
	}
	return list
}

// BestPeer retrieves the known peer with the currently highest total difficulty.
func (ps *peerSet) BestPeer() (*p2p.Peer, uint64) {
	ps.lock.RLock()
	defer ps.lock.RUnlock()

	var bestPeer *p2p.Peer
	var bestHeight uint64

	for _, p := range ps.peers {
		if bestPeer == nil || p.height > bestHeight {
			bestPeer, bestHeight = p.swPeer, p.height
		}
	}

	return bestPeer, bestHeight
}

// Close disconnects all peers.
// No new peers can be registered after Close has returned.
func (ps *peerSet) Close() {
	ps.lock.Lock()
	defer ps.lock.Unlock()

	for _, p := range ps.peers {
		p.swPeer.CloseConn()
	}
	ps.closed = true
}

func (ps *peerSet) AddPeer(peer *p2p.Peer) {
	ps.lock.Lock()
	defer ps.lock.Unlock()

	if _, ok := ps.peers[peer.Key]; !ok {
		keeperPeer := newPeer(0, nil, peer)
		ps.peers[peer.Key] = keeperPeer
		log.WithFields(log.Fields{"ID": peer.Key}).Info("Add new peer to blockKeeper")
		return
	}
	log.WithField("ID", peer.Key).Warning("Add existing peer to blockKeeper")
}

func (ps *peerSet) RemovePeer(peerID string) {
	ps.lock.Lock()
	defer ps.lock.Unlock()

	delete(ps.peers, peerID)
	log.WithField("ID", peerID).Info("Delete peer from peerset")
}

func (ps *peerSet) SetPeerStatus(peerID string, height uint64, hash *bc.Hash) {
	ps.lock.Lock()
	defer ps.lock.Unlock()

	if peer, ok := ps.peers[peerID]; ok {
		peer.SetStatus(height, hash)
	}
}

func (ps *peerSet) requestBlockByHash(peerID string, hash *bc.Hash) error {
	peer, ok := ps.Peer(peerID)
	if !ok {
		return errors.New("Can't find peer. ")
	}
	return peer.requestBlockByHash(hash)
}

func (ps *peerSet) requestBlockByHeight(peerID string, height uint64) error {
	peer, ok := ps.Peer(peerID)
	if !ok {
		return errors.New("Can't find peer. ")
	}
	return peer.requestBlockByHeight(height)
}

func (ps *peerSet) BroadcastMinedBlock(block *types.Block) ([]*peer, error) {
	msg, err := NewMinedBlockMessage(block)
	if err != nil {
		return nil, errors.New("Failed construction block msg")
	}
	hash := block.Hash()
	peers := ps.PeersWithoutBlock(&hash)
	abnormalPeers := make([]*peer, 0)
	for _, peer := range peers {
		if ok := peer.swPeer.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg}); !ok {
			abnormalPeers = append(abnormalPeers, peer)
			continue
		}
		if p, ok := ps.Peer(peer.id); ok {
			p.MarkBlock(&hash)
		}
	}
	return abnormalPeers, nil
}

func (ps *peerSet) BroadcastNewStatus(block *types.Block) ([]*peer, error) {
	return ps.BroadcastMinedBlock(block)
}

func (ps *peerSet) BroadcastTx(tx *types.Tx) ([]*peer, error) {
	msg, err := NewTransactionNotifyMessage(tx)
	if err != nil {
		return nil, errors.New("Failed construction tx msg")
	}
	peers := ps.PeersWithoutTx(&tx.ID)
	abnormalPeers := make([]*peer, 0)
	for _, peer := range peers {
		if ok := peer.swPeer.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg}); !ok {
			abnormalPeers = append(abnormalPeers, peer)
			continue
		}
		if p, ok := ps.Peer(peer.id); ok {
			p.MarkTransaction(&tx.ID)
		}
	}
	return abnormalPeers, nil
}
<|endoftext|>


<commit_before>package handlers

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/consbio/mbtileserver/mbtiles"
)

// RootURL returns the root URL of the HTTP request. Optionally, a domain and a
// base path may be provided which will be used to construct the root URL if
// they are not empty. Otherwise the hostname will be determined from the
// request and the path will be empty.
func RootURL(r *http.Request, domain, path string) string {
	host := r.Host
	if len(domain) > 0 {
		host = domain
	}

	root := fmt.Sprintf("%s://%s", Scheme(r), host)
	if len(path) > 0 {
		root = fmt.Sprintf("%s/%s", root, path)
	}

	return root
}

// Scheme returns the underlying URL scheme of the original request.
func Scheme(r *http.Request) string {
	if r.TLS != nil {
		return "https"
	}
	if scheme := r.Header.Get("X-Forwarded-Proto"); scheme != "" {
		return scheme
	}
	if scheme := r.Header.Get("X-Forwarded-Protocol"); scheme != "" {
		return scheme
	}
	if ssl := r.Header.Get("X-Forwarded-Ssl"); ssl == "on" {
		return "https"
	}
	if scheme := r.Header.Get("X-Url-Scheme"); scheme != "" {
		return scheme
	}
	return "http"
}

type handlerFunc func(http.ResponseWriter, *http.Request) (int, error)

func wrapGetWithErrors(ef func(error), hf handlerFunc) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" {
			status := http.StatusMethodNotAllowed
			http.Error(w, http.StatusText(status), status)
			return
		}
		status, err := hf(w, r) // run the handlerFunc and obtain the return codes
		if err != nil && ef != nil {
			ef(err) // handle the error with the supplied function
		}
		// in case it's an error, write the status code for the requester
		if status >= 400 {
			http.Error(w, http.StatusText(status), status)
		}
	})
}

// ServiceInfo consists of two strings that contain the image type and a URL.
type ServiceInfo struct {
	ImageType string `json:"imageType"`
	URL       string `json:"url"`
}

// ServiceSet is the base type for the HTTP handlers which combines multiple
// mbtiles.DB tilesets.
type ServiceSet struct {
	tilesets map[string]*mbtiles.DB
	Domain   string
	Path     string
}

// New returns a new ServiceSet. Use AddDBOnPath to add a mbtiles file.
func New() *ServiceSet {
	return &ServiceSet{
		tilesets: make(map[string]*mbtiles.DB),
	}
}

// AddDBOnPath interprets filename as mbtiles file which is opened and which will be
// served under "/services/<urlPath>" by Handler(). The parameter urlPath may not be
// nil, otherwise an error is returned. In case the DB cannot be opened the returned
// error is non-nil.
func (s *ServiceSet) AddDBOnPath(filename string, urlPath string) error {
	var err error
	if urlPath == "" {
		return fmt.Errorf("path parameter may not be empty")
	}
	ts, err := mbtiles.NewDB(filename)
	if err != nil {
		return fmt.Errorf("could not open mbtiles file %q: %v", filename, err)
	}
	s.tilesets[urlPath] = ts
	return nil
}

// NewFromBaseDir returns a ServiceSet that combines all .mbtiles files under
// the directory at baseDir. The DBs will all be served under their relative paths
// to baseDir.
func NewFromBaseDir(baseDir string) (*ServiceSet, error) {
	var filenames []string
	err := filepath.Walk(baseDir, func(p string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if ext := filepath.Ext(p); ext == ".mbtiles" {
			filenames = append(filenames, p)
		}
		return nil
	})
	if err != nil {
		return nil, fmt.Errorf("unable to scan tilesets: %v", err)
	}

	if len(filenames) == 0 {
		return nil, fmt.Errorf("no tilesets found in %q", baseDir)
	}

	s := New()

	for _, filename := range filenames {
		subpath, err := filepath.Rel(baseDir, filename)
		if err != nil {
			return nil, fmt.Errorf("unable to extract URL path for %q: %v", filename, err)
		}
		e := filepath.Ext(filename)
		p := filepath.ToSlash(subpath)
		id := strings.ToLower(p[:len(p)-len(e)])
		err = s.AddDBOnPath(filename, id)
		if err != nil {
			return nil, err
		}
	}
	return s, nil
}

// Len returns the number of tilesets in this ServiceSet
func (s *ServiceSet) Size() int {
	return len(s.tilesets)
}

// RootURL returns the root URL of the service. If s.Domain is non-empty, it
// will be used as the hostname. If s.Path is non-empty, it will be used as a
// prefix.
func (s *ServiceSet) RootURL(r *http.Request) string {
	return RootURL(r, s.Domain, s.Path)
}

func (s *ServiceSet) listServices(w http.ResponseWriter, r *http.Request) (int, error) {
	rootURL := fmt.Sprintf("%s%s", s.RootURL(r), r.URL)
	services := []ServiceInfo{}
	for id, tileset := range s.tilesets {
		services = append(services, ServiceInfo{
			ImageType: tileset.TileFormatString(),
			URL:       fmt.Sprintf("%s/%s", rootURL, id),
		})
	}
	bytes, err := json.Marshal(services)
	if err != nil {
		return 500, fmt.Errorf("cannot marshal services JSON: %v", err)
	}
	w.Header().Set("Content-Type", "application/json")
	_, err = w.Write(bytes)
	if err != nil {
		return 0, err
	}
	return 0, nil
}

func (s *ServiceSet) serviceInfo(db *mbtiles.DB) handlerFunc {
	return func(w http.ResponseWriter, r *http.Request) (int, error) {
		// TODO implement this
		return http.StatusNotImplemented, nil
	}
}

func (s *ServiceSet) serviceHTML(db *mbtiles.DB) handlerFunc {
	return func(w http.ResponseWriter, r *http.Request) (int, error) {
		// TODO implement this
		return http.StatusNotImplemented, nil
	}
}

type Tile struct {
	z    uint8
	x, y uint64
}

// parseTileCoord parses and returns tile coordinates and an optional extension
// from a the three parameters. The parameter z is interpreted as the web
// mercator zoom level, it's supposed to be an unsigned integer that will fit
// into 8 bit. The parameters x and y are interpreted as longitudinal and
// lateral tile indices for that zoom level, both are supposed be in the range
// [0,2^z[. Additionally, y may also have an optional filename extension (e.g.
// "42.png") which is removed before parsing the number, and returned, too. In
// case an error occured during parsing or if the values are not in the
// expected range, the returned error is non-nil
func TileFromStrings(z, x, y string) (tc Tile, ext string, err error) {
	var z64 uint64
	if z64, err = strconv.ParseUint(z, 10, 8); err != nil {
		err = fmt.Errorf("cannot parse zoom level: %v", err)
		return
	}
	tc.z = uint8(z64)
	const (
		errMsgParse = "cannot parse %s coordinate axis: %v"
		errMsgOOB   = "%s coordinate (%d) is out of bounds for zoom level %d"
	)
	if tc.x, err = strconv.ParseUint(x, 10, 64); err != nil {
		err = fmt.Errorf(errMsgParse, "first", err)
		return
	}
	if tc.x >= (1 << z64) {
		err = fmt.Errorf(errMsgOOB, "x", tc.x, tc.z)
		return
	}
	s := y
	if l := strings.LastIndex(s, "."); l >= 0 {
		s, ext = s[:l], s[l:]
	}
	if tc.y, err = strconv.ParseUint(s, 10, 64); err != nil {
		err = fmt.Errorf(errMsgParse, "y", err)
		return
	}
	if tc.y >= (1 << z64) {
		err = fmt.Errorf(errMsgOOB, "y", tc.y, tc.z)
		return
	}
	return
}

// tileNotFoundHandler writes the default response for a non-existing tile of type f to w
func tileNotFoundHandler(w http.ResponseWriter, f mbtiles.TileFormat) (int, error) {
	var err error
	switch f {
	case mbtiles.PNG, mbtiles.JPG, mbtiles.WEBP:
		// Return blank PNG for all image types
		w.Header().Set("Content-Type", "image/png")
		w.WriteHeader(http.StatusOK)
		_, err = w.Write(BlankPNG())
	case mbtiles.PBF:
		// Return 204
		w.WriteHeader(http.StatusNoContent)
	default:
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprint(w, `{"message": "Tile does not exist"}`)
	}
	return 0, err
}

func (s *ServiceSet) tiles(db *mbtiles.DB) handlerFunc {
	return func(w http.ResponseWriter, r *http.Request) (int, error) {
		// split path components to extract tile coordinates x, y and z
		pcs := strings.Split(r.URL.Path[1:], "/")
		// we are expecting at least "services", <id> , "tiles", <z>, <x>, <y plus .ext>
		l := len(pcs)
		if l < 6 || pcs[5] == "" {
			return http.StatusBadRequest, fmt.Errorf("requested path is too short")
		}
		z, x, y := pcs[l-3], pcs[l-2], pcs[l-1]
		tc, ext, err := TileFromStrings(z, x, y)
		if err != nil {
			return http.StatusBadRequest, err
		}
		var data []byte
		// flip y to match the spec
		tc.y = (1 << uint64(tc.z)) - 1 - tc.y
		isGrid := ext == ".json"
		switch {
		case !isGrid:
			err = db.ReadTile(tc.z, tc.x, tc.y, &data)
		case isGrid && db.HasUTFGrid():
			err = db.ReadGrid(tc.z, tc.x, tc.y, &data)
		default:
			err = fmt.Errorf("no grid supplied by tile database")
		}
		if err != nil {
			// augment error info
			t := "tile"
			if isGrid {
				t = "grid"
			}
			err = fmt.Errorf("cannot fetch %s from DB for z=%d, x=%d, y=%d: %v", t, tc.z, tc.x, tc.y, err)
			return http.StatusInternalServerError, err
		}
		if data == nil || len(data) <= 1 {
			return tileNotFoundHandler(w, db.TileFormat())
		}

		if isGrid {
			w.Header().Set("Content-Type", "application/json")
			if db.UTFGridCompression() == mbtiles.ZLIB {
				w.Header().Set("Content-Encoding", "deflate")
			} else {
				w.Header().Set("Content-Encoding", "gzip")
			}
		} else {
			w.Header().Set("Content-Type", db.ContentType())
			if db.TileFormat() == mbtiles.PBF {
				w.Header().Set("Content-Encoding", "gzip")
			}
		}
		_, err = w.Write(data)
		return 0, err
	}
}

// Handler returns a http.Handler that serves all endpoints of the ServiceSet.
// The function ef is called with any occuring error if it is non-nil, so it
// can be used for e.g. logging with logging facitilies of the caller.
func (s *ServiceSet) Handler(ef func(error)) http.Handler {
	m := http.NewServeMux()
	m.Handle("/services", wrapGetWithErrors(ef, s.listServices))
	for id, db := range s.tilesets {
		p := "/services/" + id
		m.Handle(p, wrapGetWithErrors(ef, s.serviceInfo(db)))
		m.Handle(p+"/map", wrapGetWithErrors(ef, s.serviceHTML(db)))
		m.Handle(p+"/tiles/", wrapGetWithErrors(ef, s.tiles(db)))
		// TODO arcgis handlers
		// p = "//arcgis/rest/services/" + id + "/MapServer"
		// m.Handle(p, wrapGetWithErrors(s.getArcGISService))
		// m.Handle(p + "/layers", wrapGetWithErrors(s.getArcGISLayers))
		// m.Handle(p + "/legend", wrapGetWithErrors(s.getArcGISLegend))
		// m.Handle(p + "/tile/", wrapGetWithErrors(s.getArcGISTile))
	}
	return m
}
<commit_msg>Unexport tileCoord again<commit_after>package handlers

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/consbio/mbtileserver/mbtiles"
)

// RootURL returns the root URL of the HTTP request. Optionally, a domain and a
// base path may be provided which will be used to construct the root URL if
// they are not empty. Otherwise the hostname will be determined from the
// request and the path will be empty.
func RootURL(r *http.Request, domain, path string) string {
	host := r.Host
	if len(domain) > 0 {
		host = domain
	}

	root := fmt.Sprintf("%s://%s", Scheme(r), host)
	if len(path) > 0 {
		root = fmt.Sprintf("%s/%s", root, path)
	}

	return root
}

// Scheme returns the underlying URL scheme of the original request.
func Scheme(r *http.Request) string {
	if r.TLS != nil {
		return "https"
	}
	if scheme := r.Header.Get("X-Forwarded-Proto"); scheme != "" {
		return scheme
	}
	if scheme := r.Header.Get("X-Forwarded-Protocol"); scheme != "" {
		return scheme
	}
	if ssl := r.Header.Get("X-Forwarded-Ssl"); ssl == "on" {
		return "https"
	}
	if scheme := r.Header.Get("X-Url-Scheme"); scheme != "" {
		return scheme
	}
	return "http"
}

type handlerFunc func(http.ResponseWriter, *http.Request) (int, error)

func wrapGetWithErrors(ef func(error), hf handlerFunc) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" {
			status := http.StatusMethodNotAllowed
			http.Error(w, http.StatusText(status), status)
			return
		}
		status, err := hf(w, r) // run the handlerFunc and obtain the return codes
		if err != nil && ef != nil {
			ef(err) // handle the error with the supplied function
		}
		// in case it's an error, write the status code for the requester
		if status >= 400 {
			http.Error(w, http.StatusText(status), status)
		}
	})
}

// ServiceInfo consists of two strings that contain the image type and a URL.
type ServiceInfo struct {
	ImageType string `json:"imageType"`
	URL       string `json:"url"`
}

// ServiceSet is the base type for the HTTP handlers which combines multiple
// mbtiles.DB tilesets.
type ServiceSet struct {
	tilesets map[string]*mbtiles.DB
	Domain   string
	Path     string
}

// New returns a new ServiceSet. Use AddDBOnPath to add a mbtiles file.
func New() *ServiceSet {
	return &ServiceSet{
		tilesets: make(map[string]*mbtiles.DB),
	}
}

// AddDBOnPath interprets filename as mbtiles file which is opened and which will be
// served under "/services/<urlPath>" by Handler(). The parameter urlPath may not be
// nil, otherwise an error is returned. In case the DB cannot be opened the returned
// error is non-nil.
func (s *ServiceSet) AddDBOnPath(filename string, urlPath string) error {
	var err error
	if urlPath == "" {
		return fmt.Errorf("path parameter may not be empty")
	}
	ts, err := mbtiles.NewDB(filename)
	if err != nil {
		return fmt.Errorf("could not open mbtiles file %q: %v", filename, err)
	}
	s.tilesets[urlPath] = ts
	return nil
}

// NewFromBaseDir returns a ServiceSet that combines all .mbtiles files under
// the directory at baseDir. The DBs will all be served under their relative paths
// to baseDir.
func NewFromBaseDir(baseDir string) (*ServiceSet, error) {
	var filenames []string
	err := filepath.Walk(baseDir, func(p string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if ext := filepath.Ext(p); ext == ".mbtiles" {
			filenames = append(filenames, p)
		}
		return nil
	})
	if err != nil {
		return nil, fmt.Errorf("unable to scan tilesets: %v", err)
	}

	if len(filenames) == 0 {
		return nil, fmt.Errorf("no tilesets found in %q", baseDir)
	}

	s := New()

	for _, filename := range filenames {
		subpath, err := filepath.Rel(baseDir, filename)
		if err != nil {
			return nil, fmt.Errorf("unable to extract URL path for %q: %v", filename, err)
		}
		e := filepath.Ext(filename)
		p := filepath.ToSlash(subpath)
		id := strings.ToLower(p[:len(p)-len(e)])
		err = s.AddDBOnPath(filename, id)
		if err != nil {
			return nil, err
		}
	}
	return s, nil
}

// Len returns the number of tilesets in this ServiceSet
func (s *ServiceSet) Size() int {
	return len(s.tilesets)
}

// RootURL returns the root URL of the service. If s.Domain is non-empty, it
// will be used as the hostname. If s.Path is non-empty, it will be used as a
// prefix.
func (s *ServiceSet) RootURL(r *http.Request) string {
	return RootURL(r, s.Domain, s.Path)
}

func (s *ServiceSet) listServices(w http.ResponseWriter, r *http.Request) (int, error) {
	rootURL := fmt.Sprintf("%s%s", s.RootURL(r), r.URL)
	services := []ServiceInfo{}
	for id, tileset := range s.tilesets {
		services = append(services, ServiceInfo{
			ImageType: tileset.TileFormatString(),
			URL:       fmt.Sprintf("%s/%s", rootURL, id),
		})
	}
	bytes, err := json.Marshal(services)
	if err != nil {
		return 500, fmt.Errorf("cannot marshal services JSON: %v", err)
	}
	w.Header().Set("Content-Type", "application/json")
	_, err = w.Write(bytes)
	if err != nil {
		return 0, err
	}
	return 0, nil
}

func (s *ServiceSet) serviceInfo(db *mbtiles.DB) handlerFunc {
	return func(w http.ResponseWriter, r *http.Request) (int, error) {
		// TODO implement this
		return http.StatusNotImplemented, nil
	}
}

func (s *ServiceSet) serviceHTML(db *mbtiles.DB) handlerFunc {
	return func(w http.ResponseWriter, r *http.Request) (int, error) {
		// TODO implement this
		return http.StatusNotImplemented, nil
	}
}

type tileCoord struct {
	z    uint8
	x, y uint64
}

// tileCoordFromString parses and returns tileCoord coordinates and an optional
// extension from a the three parameters. The parameter z is interpreted as the
// web mercator zoom level, it's supposed to be an unsigned integer that will
// fit into 8 bit. The parameters x and y are interpreted as longitudinal and
// lateral tileCoord indices for that zoom level, both are supposed be in the
// range [0,2^z[. Additionally, y may also have an optional filename extension
// (e.g.  "42.png") which is removed before parsing the number, and returned,
// too. In case an error occured during parsing or if the values are not in the
// expected range, the returned error is non-nil.
func tileCoordFromString(z, x, y string) (tc tileCoord, ext string, err error) {
	var z64 uint64
	if z64, err = strconv.ParseUint(z, 10, 8); err != nil {
		err = fmt.Errorf("cannot parse zoom level: %v", err)
		return
	}
	tc.z = uint8(z64)
	const (
		errMsgParse = "cannot parse %s coordinate axis: %v"
		errMsgOOB   = "%s coordinate (%d) is out of bounds for zoom level %d"
	)
	if tc.x, err = strconv.ParseUint(x, 10, 64); err != nil {
		err = fmt.Errorf(errMsgParse, "first", err)
		return
	}
	if tc.x >= (1 << z64) {
		err = fmt.Errorf(errMsgOOB, "x", tc.x, tc.z)
		return
	}
	s := y
	if l := strings.LastIndex(s, "."); l >= 0 {
		s, ext = s[:l], s[l:]
	}
	if tc.y, err = strconv.ParseUint(s, 10, 64); err != nil {
		err = fmt.Errorf(errMsgParse, "y", err)
		return
	}
	if tc.y >= (1 << z64) {
		err = fmt.Errorf(errMsgOOB, "y", tc.y, tc.z)
		return
	}
	return
}

// tileNotFoundHandler writes the default response for a non-existing tile of type f to w
func tileNotFoundHandler(w http.ResponseWriter, f mbtiles.TileFormat) (int, error) {
	var err error
	switch f {
	case mbtiles.PNG, mbtiles.JPG, mbtiles.WEBP:
		// Return blank PNG for all image types
		w.Header().Set("Content-Type", "image/png")
		w.WriteHeader(http.StatusOK)
		_, err = w.Write(BlankPNG())
	case mbtiles.PBF:
		// Return 204
		w.WriteHeader(http.StatusNoContent)
	default:
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprint(w, `{"message": "Tile does not exist"}`)
	}
	return 0, err
}

func (s *ServiceSet) tiles(db *mbtiles.DB) handlerFunc {
	return func(w http.ResponseWriter, r *http.Request) (int, error) {
		// split path components to extract tile coordinates x, y and z
		pcs := strings.Split(r.URL.Path[1:], "/")
		// we are expecting at least "services", <id> , "tiles", <z>, <x>, <y plus .ext>
		l := len(pcs)
		if l < 6 || pcs[5] == "" {
			return http.StatusBadRequest, fmt.Errorf("requested path is too short")
		}
		z, x, y := pcs[l-3], pcs[l-2], pcs[l-1]
		tc, ext, err := tileCoordFromString(z, x, y)
		if err != nil {
			return http.StatusBadRequest, err
		}
		var data []byte
		// flip y to match the spec
		tc.y = (1 << uint64(tc.z)) - 1 - tc.y
		isGrid := ext == ".json"
		switch {
		case !isGrid:
			err = db.ReadTile(tc.z, tc.x, tc.y, &data)
		case isGrid && db.HasUTFGrid():
			err = db.ReadGrid(tc.z, tc.x, tc.y, &data)
		default:
			err = fmt.Errorf("no grid supplied by tile database")
		}
		if err != nil {
			// augment error info
			t := "tile"
			if isGrid {
				t = "grid"
			}
			err = fmt.Errorf("cannot fetch %s from DB for z=%d, x=%d, y=%d: %v", t, tc.z, tc.x, tc.y, err)
			return http.StatusInternalServerError, err
		}
		if data == nil || len(data) <= 1 {
			return tileNotFoundHandler(w, db.TileFormat())
		}

		if isGrid {
			w.Header().Set("Content-Type", "application/json")
			if db.UTFGridCompression() == mbtiles.ZLIB {
				w.Header().Set("Content-Encoding", "deflate")
			} else {
				w.Header().Set("Content-Encoding", "gzip")
			}
		} else {
			w.Header().Set("Content-Type", db.ContentType())
			if db.TileFormat() == mbtiles.PBF {
				w.Header().Set("Content-Encoding", "gzip")
			}
		}
		_, err = w.Write(data)
		return 0, err
	}
}

// Handler returns a http.Handler that serves all endpoints of the ServiceSet.
// The function ef is called with any occuring error if it is non-nil, so it
// can be used for e.g. logging with logging facitilies of the caller.
func (s *ServiceSet) Handler(ef func(error)) http.Handler {
	m := http.NewServeMux()
	m.Handle("/services", wrapGetWithErrors(ef, s.listServices))
	for id, db := range s.tilesets {
		p := "/services/" + id
		m.Handle(p, wrapGetWithErrors(ef, s.serviceInfo(db)))
		m.Handle(p+"/map", wrapGetWithErrors(ef, s.serviceHTML(db)))
		m.Handle(p+"/tiles/", wrapGetWithErrors(ef, s.tiles(db)))
		// TODO arcgis handlers
		// p = "//arcgis/rest/services/" + id + "/MapServer"
		// m.Handle(p, wrapGetWithErrors(s.getArcGISService))
		// m.Handle(p + "/layers", wrapGetWithErrors(s.getArcGISLayers))
		// m.Handle(p + "/legend", wrapGetWithErrors(s.getArcGISLegend))
		// m.Handle(p + "/tile/", wrapGetWithErrors(s.getArcGISTile))
	}
	return m
}
<|endoftext|>


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

package main

import (
	"image"
	"image/color"
	"log"
	"math"

	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/ebitenutil"
	"github.com/hajimehoshi/ebiten/v2/inpututil"
	"github.com/hajimehoshi/ebiten/v2/vector"
)

func min(x, y int) int {
	if x < y {
		return x
	}
	return y
}

var (
	emptyImage = ebiten.NewImage(3, 3)

	// emptySubImage is an internal sub image of emptyImage.
	// Use emptySubImage at DrawTriangles instead of emptyImage in order to avoid bleeding edges.
	emptySubImage = emptyImage.SubImage(image.Rect(1, 1, 2, 2)).(*ebiten.Image)
)

func init() {
	emptyImage.Fill(color.White)
}

const (
	screenWidth  = 640
	screenHeight = 480
)

type Game struct {
	counter int

	vertices []ebiten.Vertex
	indices  []uint16

	offscreen *ebiten.Image

	aa         bool
	showCenter bool
}

func (g *Game) Update() error {
	g.counter++
	if inpututil.IsKeyJustPressed(ebiten.KeyA) {
		g.aa = !g.aa
	}
	if inpututil.IsKeyJustPressed(ebiten.KeyC) {
		g.showCenter = !g.showCenter
	}
	return nil
}

func (g *Game) Draw(screen *ebiten.Image) {
	target := screen
	if g.aa {
		// Prepare the double-sized offscreen.
		// This is for anti-aliasing by a pseudo MSAA (multisample anti-aliasing).
		if g.offscreen != nil {
			sw, sh := screen.Size()
			ow, oh := g.offscreen.Size()
			if ow != sw*2 || oh != sh*2 {
				g.offscreen.Dispose()
				g.offscreen = nil
			}
		}
		if g.offscreen == nil {
			sw, sh := screen.Size()
			g.offscreen = ebiten.NewImage(sw*2, sh*2)
		}
		g.offscreen.Clear()
		target = g.offscreen
	}

	ow, oh := target.Size()
	size := min(ow/5, oh/4)
	offsetX, offsetY := (ow-size*4)/2, (oh-size*3)/2

	// Render the lines on the target.
	for j := 0; j < 3; j++ {
		for i, join := range []vector.LineJoin{
			vector.LineJoinMiter,
			vector.LineJoinMiter,
			vector.LineJoinBevel,
			vector.LineJoinRound} {
			r := image.Rect(i*size+offsetX, j*size+offsetY, (i+1)*size+offsetX, (j+1)*size+offsetY)
			miterLimit := float32(5)
			if i == 1 {
				miterLimit = 10
			}
			g.drawLine(target, r, join, miterLimit)
		}
	}

	if g.aa {
		// Render the offscreen to the screen.
		op := &ebiten.DrawImageOptions{}
		op.GeoM.Scale(0.5, 0.5)
		op.Filter = ebiten.FilterLinear
		screen.DrawImage(g.offscreen, op)
	}

	msg := `Press A to switch anti-aliasing.
Press C to switch to draw the center lines.`
	ebitenutil.DebugPrint(screen, msg)
}

func (g *Game) drawLine(screen *ebiten.Image, region image.Rectangle, join vector.LineJoin, miterLimit float32) {
	c0x := float64(region.Min.X + region.Dx()/4)
	c0y := float64(region.Min.Y + region.Dy()/4)
	c1x := float64(region.Max.X - region.Dx()/4)
	c1y := float64(region.Max.Y - region.Dy()/4)
	r := float64(min(region.Dx(), region.Dy()) / 4)
	a := 2 * math.Pi * float64(g.counter) / (10 * ebiten.DefaultTPS)

	var path vector.Path
	sin, cos := math.Sincos(a)
	path.MoveTo(float32(r*cos+c0x), float32(r*sin+c0y))
	path.LineTo(float32(-r*cos+c0x), float32(-r*sin+c0y))
	path.LineTo(float32(r*cos+c1x), float32(r*sin+c1y))
	path.LineTo(float32(-r*cos+c1x), float32(-r*sin+c1y))

	// Draw the main line in white.
	op := &vector.StrokeOptions{}
	op.LineJoin = join
	op.MiterLimit = miterLimit
	op.Width = float32(r / 2)
	vs, is := path.AppendVerticesAndIndicesForStroke(g.vertices[:0], g.indices[:0], op)
	for i := range vs {
		vs[i].SrcX = 1
		vs[i].SrcY = 1
	}
	screen.DrawTriangles(vs, is, emptySubImage, nil)

	// Draw the center line in red.
	if g.showCenter {
		op.Width = 1
		vs, is := path.AppendVerticesAndIndicesForStroke(g.vertices[:0], g.indices[:0], op)
		for i := range vs {
			vs[i].SrcX = 1
			vs[i].SrcY = 1
			vs[i].SrcX = 1
			vs[i].SrcY = 1
			vs[i].ColorR = 1
			vs[i].ColorG = 0
			vs[i].ColorB = 0
		}
		screen.DrawTriangles(vs, is, emptySubImage, nil)
	}
}

func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
	return screenWidth, screenHeight
}

func main() {
	var g Game
	ebiten.SetWindowSize(screenWidth, screenHeight)
	ebiten.SetWindowTitle("Lines (Ebitengine Demo)")
	if err := ebiten.RunGame(&g); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>examples/lines: more dynaic effect<commit_after>// Copyright 2022 The Ebitengine Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"image"
	"image/color"
	"log"
	"math"

	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/ebitenutil"
	"github.com/hajimehoshi/ebiten/v2/inpututil"
	"github.com/hajimehoshi/ebiten/v2/vector"
)

func min(x, y int) int {
	if x < y {
		return x
	}
	return y
}

var (
	emptyImage = ebiten.NewImage(3, 3)

	// emptySubImage is an internal sub image of emptyImage.
	// Use emptySubImage at DrawTriangles instead of emptyImage in order to avoid bleeding edges.
	emptySubImage = emptyImage.SubImage(image.Rect(1, 1, 2, 2)).(*ebiten.Image)
)

func init() {
	emptyImage.Fill(color.White)
}

const (
	screenWidth  = 640
	screenHeight = 480
)

type Game struct {
	counter int

	vertices []ebiten.Vertex
	indices  []uint16

	offscreen *ebiten.Image

	aa         bool
	showCenter bool
}

func (g *Game) Update() error {
	g.counter++
	if inpututil.IsKeyJustPressed(ebiten.KeyA) {
		g.aa = !g.aa
	}
	if inpututil.IsKeyJustPressed(ebiten.KeyC) {
		g.showCenter = !g.showCenter
	}
	return nil
}

func (g *Game) Draw(screen *ebiten.Image) {
	target := screen
	if g.aa {
		// Prepare the double-sized offscreen.
		// This is for anti-aliasing by a pseudo MSAA (multisample anti-aliasing).
		if g.offscreen != nil {
			sw, sh := screen.Size()
			ow, oh := g.offscreen.Size()
			if ow != sw*2 || oh != sh*2 {
				g.offscreen.Dispose()
				g.offscreen = nil
			}
		}
		if g.offscreen == nil {
			sw, sh := screen.Size()
			g.offscreen = ebiten.NewImage(sw*2, sh*2)
		}
		g.offscreen.Clear()
		target = g.offscreen
	}

	ow, oh := target.Size()
	size := min(ow/5, oh/4)
	offsetX, offsetY := (ow-size*4)/2, (oh-size*3)/2

	// Render the lines on the target.
	for j := 0; j < 3; j++ {
		for i, join := range []vector.LineJoin{
			vector.LineJoinMiter,
			vector.LineJoinMiter,
			vector.LineJoinBevel,
			vector.LineJoinRound} {
			r := image.Rect(i*size+offsetX, j*size+offsetY, (i+1)*size+offsetX, (j+1)*size+offsetY)
			miterLimit := float32(5)
			if i == 1 {
				miterLimit = 10
			}
			g.drawLine(target, r, join, miterLimit)
		}
	}

	if g.aa {
		// Render the offscreen to the screen.
		op := &ebiten.DrawImageOptions{}
		op.GeoM.Scale(0.5, 0.5)
		op.Filter = ebiten.FilterLinear
		screen.DrawImage(g.offscreen, op)
	}

	msg := `Press A to switch anti-aliasing.
Press C to switch to draw the center lines.`
	ebitenutil.DebugPrint(screen, msg)
}

func (g *Game) drawLine(screen *ebiten.Image, region image.Rectangle, join vector.LineJoin, miterLimit float32) {
	c0x := float64(region.Min.X + region.Dx()/4)
	c0y := float64(region.Min.Y + region.Dy()/4)
	c1x := float64(region.Max.X - region.Dx()/4)
	c1y := float64(region.Max.Y - region.Dy()/4)
	r := float64(min(region.Dx(), region.Dy()) / 4)
	a0 := 2 * math.Pi * float64(g.counter) / (16 * ebiten.DefaultTPS)
	a1 := 2 * math.Pi * float64(g.counter) / (9 * ebiten.DefaultTPS)

	var path vector.Path
	sin0, cos0 := math.Sincos(a0)
	sin1, cos1 := math.Sincos(a1)
	path.MoveTo(float32(r*cos0+c0x), float32(r*sin0+c0y))
	path.LineTo(float32(-r*cos0+c0x), float32(-r*sin0+c0y))
	path.LineTo(float32(r*cos1+c1x), float32(r*sin1+c1y))
	path.LineTo(float32(-r*cos1+c1x), float32(-r*sin1+c1y))

	// Draw the main line in white.
	op := &vector.StrokeOptions{}
	op.LineJoin = join
	op.MiterLimit = miterLimit
	op.Width = float32(r / 2)
	vs, is := path.AppendVerticesAndIndicesForStroke(g.vertices[:0], g.indices[:0], op)
	for i := range vs {
		vs[i].SrcX = 1
		vs[i].SrcY = 1
	}
	screen.DrawTriangles(vs, is, emptySubImage, nil)

	// Draw the center line in red.
	if g.showCenter {
		op.Width = 1
		vs, is := path.AppendVerticesAndIndicesForStroke(g.vertices[:0], g.indices[:0], op)
		for i := range vs {
			vs[i].SrcX = 1
			vs[i].SrcY = 1
			vs[i].SrcX = 1
			vs[i].SrcY = 1
			vs[i].ColorR = 1
			vs[i].ColorG = 0
			vs[i].ColorB = 0
		}
		screen.DrawTriangles(vs, is, emptySubImage, nil)
	}
}

func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
	return screenWidth, screenHeight
}

func main() {
	var g Game
	ebiten.SetWindowSize(screenWidth, screenHeight)
	ebiten.SetWindowTitle("Lines (Ebitengine Demo)")
	if err := ebiten.RunGame(&g); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"time"
	"fmt"

	"github.com/hybridgroup/gobot"
	"github.com/hybridgroup/gobot/platforms/ble"
)

func main() {
	gbot := gobot.NewGobot()

	bleAdaptor := ble.NewBLEClientAdaptor("ble", os.Args[1])
	ollie := ble.NewSpheroOllieDriver(bleAdaptor, "ollie")

	work := func() {
		ollie.SetRGB(255, 0, 255)
		gobot.After(1*time.Second, func() {
			fmt.Println("fwd")
			ollie.Roll(60, 0)
		})
		gobot.After(3*time.Second, func() {
			fmt.Println("back")
			ollie.Roll(60, 180)
		})
		gobot.After(5*time.Second, func() {
			fmt.Println("stop")
			ollie.Stop()
		})
	}

	robot := gobot.NewRobot("ollieBot",
		[]gobot.Connection{bleAdaptor},
		[]gobot.Device{ollie},
		work,
	)

	gbot.AddRobot(robot)

	gbot.Start()
}
<commit_msg>[ble] Implement Ollie Roll example<commit_after>package main

import (
	"os"
	"time"

	"github.com/hybridgroup/gobot"
	"github.com/hybridgroup/gobot/platforms/ble"
)

func main() {
	gbot := gobot.NewGobot()

	bleAdaptor := ble.NewBLEClientAdaptor("ble", os.Args[1])
	ollie := ble.NewSpheroOllieDriver(bleAdaptor, "ollie")

	work := func() {
		ollie.SetRGB(255, 0, 255)
		gobot.Every(3*time.Second, func() {
			ollie.Roll(30, uint16(gobot.Rand(360)))
		})
	}

	robot := gobot.NewRobot("ollieBot",
		[]gobot.Connection{bleAdaptor},
		[]gobot.Device{ollie},
		work,
	)

	gbot.AddRobot(robot)

	gbot.Start()
}
<|endoftext|>


<commit_before>package bitmapfont

import (
	"image"
	"image/color"
	"os"

	log "github.com/Sirupsen/logrus"
)

type FontMgr struct {
	img   image.Image
	width int
	high  int
	col   int
	row   int
}

type FontInterface interface {
	Render(fontPath string, fontName string, text string) (image.Image, error)
}

var cTable = map[string][]int{
	// Font     name,   high width rows, columns
	"font5x7": {7, 5, 4, 24},
	//"font4x7": {4, 7},
	//"font6x6": {6, 6},
	//"font6x8": {6, 8},
	//"font6x7": {6, 7},
}

func (f *FontMgr) Render(fontPath string, fontName string, text string, char_space int, top_off int) (image.Image, error) {

	if fontName == "" {
		fontName = "font5x7"
	}

	reader, err := os.Open(fontPath + string(os.PathSeparator) + fontName + ".png")
	if err != nil {
		log.Fatal(err)
		return nil, err
	}
	defer reader.Close()

	// Decode fonts table.
	cImg, _, err := image.Decode(reader)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}

	f.img = cImg
	f.high = cTable[fontName][0]
	f.width = cTable[fontName][1]
	f.row = cTable[fontName][2]
	f.col = cTable[fontName][3]

	frame_width := len(text)*f.width + (len(text)-1)*(char_space)
	log.Debug("len ", frame_width)

	// Allocate frame
	img := image.NewRGBA(image.Rect(0, 0, frame_width, 9))
	col := color.RGBA{0, 0, 0, 255}
	nm := img.Bounds()
	for y := 0; y < nm.Dy(); y++ {
		for x := 0; x < nm.Dx(); x++ {
			img.Set(x, y, col)
		}
	}
	log.Debug(text)

	// Fill frame
	for n, key := range text {
		col := int(key-' ') % f.col
		row := int(key-' ') / f.col

		log.Debug("offset ", int(key-' '))
		log.Debug("Col ", col)
		log.Debug("Row ", row)

		for y := 0; y < f.high+top_off; y++ {
			for x := 0; x < f.width+char_space; x++ {
				if x >= f.width {
					img.Set(x+n*(f.width+char_space), y+top_off, color.RGBA{0, 0, 0, 255})
				} else {
					img.Set(x+n*(f.width+char_space), y+top_off, f.img.At(x+f.width*col, y+f.high*row))
				}
			}
		}
	}

	return img, nil
}
<commit_msg>bitmapfont: Use configuration module for all fonts. Clean up code. Open font file in init.<commit_after>package bitmapfont

import (
	"errors"
	"image"
	"image/color"
	"os"

	log "github.com/Sirupsen/logrus"
	conf "github.com/develed/develed/config"
)

var fontImageTable image.Image
var Config conf.BitmapFont

func Render(text string, char_space int, top_off int) (image.Image, error) {

	img_rect := fontImageTable.Bounds()
	fontcolums := img_rect.Dx() / Config.Width

	frame_width := len(text)*Config.Width + (len(text)-1)*(char_space)
	log.Debug("len ", frame_width)

	// Allocate frame
	img := image.NewRGBA(image.Rect(0, 0, frame_width, 9))
	col := color.RGBA{0, 0, 0, 255}
	nm := img.Bounds()
	for y := 0; y < nm.Dy(); y++ {
		for x := 0; x < nm.Dx(); x++ {
			img.Set(x, y, col)
		}
	}
	log.Debug(text)

	// Fill frame
	for n, key := range text {
		col := int(key-' ') % fontcolums
		row := int(key-' ') / fontcolums

		log.Debug("offset ", int(key-' '))
		log.Debug("Col ", col)
		log.Debug("Row ", row)

		for y := 0; y < Config.High+top_off; y++ {
			for x := 0; x < Config.Width+char_space; x++ {
				if x >= Config.Width {
					img.Set(x+n*(Config.Width+char_space), y+top_off, color.RGBA{0, 0, 0, 255})
				} else {
					img.Set(x+n*(Config.Width+char_space), y+top_off, fontImageTable.At(x+Config.Width*col, y+Config.High*row))
				}
			}
		}
	}

	return img, nil
}

func Init(path string, name string, cfg []conf.BitmapFont) error {
	if name == "" {
		name = "font5x7"
	}

	for _, s := range cfg {
		log.Debug("Cerco font ", s.Name, " ", name)
		if name == s.Name {
			Config = s
			log.Debug(path + string(os.PathSeparator) + Config.FileName)
			reader, err := os.Open(path + string(os.PathSeparator) + Config.FileName)
			if err != nil {
				return err
			}
			defer reader.Close()

			// Decode fonts table.
			fontImageTable, _, err = image.Decode(reader)
			if err != nil {
				return err
			}
			return nil
		}
	}

	return errors.New("Wrong BitmatFont name.\n")
}
<|endoftext|>


<commit_before>/*
	rulehuntersrv - A server to find rules in data based on user specified goals
	Copyright (C) 2016 vLife Systems Ltd <http://vlifesystems.com>

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero 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 Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program; see the file COPYING.  If not, see
	<http://www.gnu.org/licenses/>.
*/

// Package quitter handles stopping go routines cleanly
package quitter

import (
	"fmt"
	"os"
	"sync"
)

type Quitter struct {
	shouldQuit bool
	waitGroup  *sync.WaitGroup
}

func New() *Quitter {
	return &Quitter{
		shouldQuit: false,
		waitGroup:  &sync.WaitGroup{},
	}
}

// Add adds a go routine to wait for
func (q *Quitter) Add() {
	q.waitGroup.Add(1)
}

// Done indicates that a go routine has finished
func (q *Quitter) Done() {
	q.waitGroup.Done()
}

// Quit indicates to all the go routines that they should quit, it then waits
// for them to finish. Once they have all finished if killProcess is true
// then the os.Interrupt signal is sent to stop the process.
func (q *Quitter) Quit(killProcess bool) {
	q.shouldQuit = true
	q.waitGroup.Wait()
	if killProcess {
		p, err := os.FindProcess(os.Getpid())
		if err != nil {
			panic("Can't find process to Quit")
		}
		if err := p.Signal(os.Interrupt); err != nil {
			panic(fmt.Sprintf("Can't send signal: %s", err))
		}
	}
}

// ShouldQuit returns if a go routine should quit
func (q *Quitter) ShouldQuit() bool {
	return q.shouldQuit
}
<commit_msg>Try killing if can't interrupt process in Quit<commit_after>/*
	rulehuntersrv - A server to find rules in data based on user specified goals
	Copyright (C) 2016 vLife Systems Ltd <http://vlifesystems.com>

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero 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 Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program; see the file COPYING.  If not, see
	<http://www.gnu.org/licenses/>.
*/

// Package quitter handles stopping go routines cleanly
package quitter

import (
	"fmt"
	"os"
	"sync"
)

type Quitter struct {
	shouldQuit bool
	waitGroup  *sync.WaitGroup
}

func New() *Quitter {
	return &Quitter{
		shouldQuit: false,
		waitGroup:  &sync.WaitGroup{},
	}
}

// Add adds a go routine to wait for
func (q *Quitter) Add() {
	q.waitGroup.Add(1)
}

// Done indicates that a go routine has finished
func (q *Quitter) Done() {
	q.waitGroup.Done()
}

// Quit indicates to all the go routines that they should quit, it then waits
// for them to finish. Once they have all finished if killProcess is true
// then the os.Interrupt signal is sent to stop the process.
func (q *Quitter) Quit(killProcess bool) {
	q.shouldQuit = true
	q.waitGroup.Wait()
	if killProcess {
		p, err := os.FindProcess(os.Getpid())
		if err != nil {
			panic("Can't find process to Quit")
		}
		if err := p.Signal(os.Interrupt); err != nil {
			if err := p.Kill(); err != nil {
				panic(fmt.Sprintf("Can't kill process: %s", err))
			}
		}
	}
}

// ShouldQuit returns if a go routine should quit
func (q *Quitter) ShouldQuit() bool {
	return q.shouldQuit
}
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, Inc.
//
// 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 rafthttp

import (
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"path"
	"strconv"
	"sync"
	"time"

	"github.com/coreos/etcd/etcdserver/stats"
	"github.com/coreos/etcd/pkg/types"
	"github.com/coreos/etcd/raft/raftpb"
)

type streamType string

const (
	streamTypeMessage streamType = "message"
	streamTypeMsgApp  streamType = "msgapp"

	streamBufSize = 4096
)

var (
	// linkHeartbeatMessage is a special message used as heartbeat message in
	// link layer. It never conflicts with messages from raft because raft
	// doesn't send out messages without From and To fields.
	linkHeartbeatMessage = raftpb.Message{Type: raftpb.MsgHeartbeat}
)

func isLinkHeartbeatMessage(m raftpb.Message) bool {
	return m.Type == raftpb.MsgHeartbeat && m.From == 0 && m.To == 0
}

type outgoingConn struct {
	t       streamType
	termStr string
	io.Writer
	http.Flusher
	io.Closer
}

// streamWriter is a long-running worker that writes messages into the
// attached outgoingConn.
type streamWriter struct {
	fs *stats.FollowerStats

	mu      sync.Mutex // guard field working and closer
	closer  io.Closer
	working bool

	msgc  chan raftpb.Message
	connc chan *outgoingConn
	stopc chan struct{}
	done  chan struct{}
}

func startStreamWriter(fs *stats.FollowerStats) *streamWriter {
	w := &streamWriter{
		fs:    fs,
		msgc:  make(chan raftpb.Message, streamBufSize),
		connc: make(chan *outgoingConn),
		stopc: make(chan struct{}),
		done:  make(chan struct{}),
	}
	go w.run()
	return w
}

func (cw *streamWriter) run() {
	var msgc chan raftpb.Message
	var heartbeatc <-chan time.Time
	var t streamType
	var msgAppTerm uint64
	var enc encoder
	var flusher http.Flusher
	tickc := time.Tick(ConnReadTimeout / 3)

	for {
		select {
		case <-heartbeatc:
			if err := enc.encode(linkHeartbeatMessage); err != nil {
				log.Printf("rafthttp: failed to heartbeat on stream %s due to %v. waiting for a new stream to be established.", t, err)
				cw.resetCloser()
				heartbeatc, msgc = nil, nil
				continue
			}
			flusher.Flush()
		case m := <-msgc:
			if t == streamTypeMsgApp && m.Term != msgAppTerm {
				// TODO: reasonable retry logic
				if m.Term > msgAppTerm {
					cw.resetCloser()
					heartbeatc, msgc = nil, nil
				}
				continue
			}
			if err := enc.encode(m); err != nil {
				log.Printf("rafthttp: failed to send message on stream %s due to %v. waiting for a new stream to be established.", t, err)
				cw.resetCloser()
				heartbeatc, msgc = nil, nil
				continue
			}
			flusher.Flush()
		case conn := <-cw.connc:
			cw.resetCloser()
			t = conn.t
			switch conn.t {
			case streamTypeMsgApp:
				var err error
				msgAppTerm, err = strconv.ParseUint(conn.termStr, 10, 64)
				if err != nil {
					log.Panicf("rafthttp: unexpected parse term %s error: %v", conn.termStr, err)
				}
				enc = &msgAppEncoder{w: conn.Writer, fs: cw.fs}
			case streamTypeMessage:
				enc = &messageEncoder{w: conn.Writer}
			default:
				log.Panicf("rafthttp: unhandled stream type %s", conn.t)
			}
			flusher = conn.Flusher
			cw.mu.Lock()
			cw.closer = conn.Closer
			cw.working = true
			cw.mu.Unlock()
			heartbeatc, msgc = tickc, cw.msgc
		case <-cw.stopc:
			cw.resetCloser()
			close(cw.done)
			return
		}
	}
}

func (cw *streamWriter) isWorking() bool {
	cw.mu.Lock()
	defer cw.mu.Unlock()
	return cw.working
}

func (cw *streamWriter) resetCloser() {
	cw.mu.Lock()
	defer cw.mu.Unlock()
	if cw.working {
		cw.closer.Close()
	}
	cw.working = false
}

func (cw *streamWriter) attach(conn *outgoingConn) bool {
	select {
	case cw.connc <- conn:
		return true
	case <-cw.done:
		return false
	}
}

func (cw *streamWriter) stop() {
	close(cw.stopc)
	<-cw.done
}

// streamReader is a long-running go-routine that dials to the remote stream
// endponit and reads messages from the response body returned.
type streamReader struct {
	tr       http.RoundTripper
	u        string
	t        streamType
	from, to types.ID
	cid      types.ID
	recvc    chan<- raftpb.Message

	mu         sync.Mutex
	msgAppTerm uint64
	req        *http.Request
	closer     io.Closer
	stopc      chan struct{}
	done       chan struct{}
}

func startStreamReader(tr http.RoundTripper, u string, t streamType, from, to, cid types.ID, recvc chan<- raftpb.Message) *streamReader {
	r := &streamReader{
		tr:    tr,
		u:     u,
		t:     t,
		from:  from,
		to:    to,
		cid:   cid,
		recvc: recvc,
		stopc: make(chan struct{}),
		done:  make(chan struct{}),
	}
	go r.run()
	return r
}

func (cr *streamReader) run() {
	for {
		rc, err := cr.roundtrip()
		if err != nil {
			log.Printf("rafthttp: roundtripping error: %v", err)
		} else {
			err := cr.decodeLoop(rc)
			if err != io.EOF && !isClosedConnectionError(err) {
				log.Printf("rafthttp: failed to read message on stream %s due to %v", cr.t, err)
			}
		}
		select {
		// Wait 100ms to create a new stream, so it doesn't bring too much
		// overhead when retry.
		case <-time.After(100 * time.Millisecond):
		case <-cr.stopc:
			close(cr.done)
			return
		}
	}
}

func (cr *streamReader) decodeLoop(rc io.ReadCloser) error {
	var dec decoder
	cr.mu.Lock()
	switch cr.t {
	case streamTypeMsgApp:
		dec = &msgAppDecoder{r: rc, local: cr.from, remote: cr.to, term: cr.msgAppTerm}
	case streamTypeMessage:
		dec = &messageDecoder{r: rc}
	default:
		log.Panicf("rafthttp: unhandled stream type %s", cr.t)
	}
	cr.closer = rc
	cr.mu.Unlock()

	for {
		m, err := dec.decode()
		switch {
		case err != nil:
			cr.mu.Lock()
			cr.resetCloser()
			cr.mu.Unlock()
			return err
		case isLinkHeartbeatMessage(m):
			// do nothing for linkHeartbeatMessage
		default:
			select {
			case cr.recvc <- m:
			default:
				log.Printf("rafthttp: dropping %s from %x because receive buffer is blocked",
					m.Type, m.From)
			}
		}
	}
}

func (cr *streamReader) update(u string) {
	cr.mu.Lock()
	defer cr.mu.Unlock()
	cr.u = u
	cr.resetCloser()
}

func (cr *streamReader) updateMsgAppTerm(term uint64) {
	cr.mu.Lock()
	defer cr.mu.Unlock()
	if cr.msgAppTerm == term {
		return
	}
	cr.msgAppTerm = term
	cr.resetCloser()
}

// TODO: always cancel in-flight dial and decode
func (cr *streamReader) stop() {
	close(cr.stopc)
	cr.mu.Lock()
	cr.cancelRequest()
	cr.resetCloser()
	cr.mu.Unlock()
	<-cr.done
}

func (cr *streamReader) isWorking() bool {
	cr.mu.Lock()
	defer cr.mu.Unlock()
	return cr.closer != nil
}

func (cr *streamReader) roundtrip() (io.ReadCloser, error) {
	cr.mu.Lock()
	u := cr.u
	term := cr.msgAppTerm
	cr.mu.Unlock()

	uu, err := url.Parse(u)
	if err != nil {
		return nil, fmt.Errorf("parse url %s error: %v", u, err)
	}
	uu.Path = path.Join(RaftStreamPrefix, string(cr.t), cr.from.String())
	req, err := http.NewRequest("GET", uu.String(), nil)
	if err != nil {
		return nil, fmt.Errorf("new request to %s error: %v", u, err)
	}
	req.Header.Set("X-Etcd-Cluster-ID", cr.cid.String())
	req.Header.Set("X-Raft-To", cr.to.String())
	if cr.t == streamTypeMsgApp {
		req.Header.Set("X-Raft-Term", strconv.FormatUint(term, 10))
	}
	cr.mu.Lock()
	cr.req = req
	cr.mu.Unlock()
	resp, err := cr.tr.RoundTrip(req)
	if err != nil {
		return nil, fmt.Errorf("error roundtripping to %s: %v", req.URL, err)
	}
	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return nil, fmt.Errorf("unhandled http status %d", resp.StatusCode)
	}
	return resp.Body, nil
}

func (cr *streamReader) cancelRequest() {
	if canceller, ok := cr.tr.(*http.Transport); ok {
		canceller.CancelRequest(cr.req)
	}
}

func (cr *streamReader) resetCloser() {
	if cr.closer != nil {
		cr.closer.Close()
	}
	cr.closer = nil
}

func canUseMsgAppStream(m raftpb.Message) bool {
	return m.Type == raftpb.MsgApp && m.Term == m.LogTerm
}

func isClosedConnectionError(err error) bool {
	operr, ok := err.(*net.OpError)
	return ok && operr.Err.Error() == "use of closed network connection"
}
<commit_msg>rafthttp: use /raft/stream for MsgApp stream<commit_after>// Copyright 2015 CoreOS, Inc.
//
// 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 rafthttp

import (
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"path"
	"strconv"
	"sync"
	"time"

	"github.com/coreos/etcd/etcdserver/stats"
	"github.com/coreos/etcd/pkg/types"
	"github.com/coreos/etcd/raft/raftpb"
)

type streamType string

const (
	streamTypeMessage streamType = "message"
	streamTypeMsgApp  streamType = "msgapp"

	streamBufSize = 4096
)

var (
	// linkHeartbeatMessage is a special message used as heartbeat message in
	// link layer. It never conflicts with messages from raft because raft
	// doesn't send out messages without From and To fields.
	linkHeartbeatMessage = raftpb.Message{Type: raftpb.MsgHeartbeat}
)

func isLinkHeartbeatMessage(m raftpb.Message) bool {
	return m.Type == raftpb.MsgHeartbeat && m.From == 0 && m.To == 0
}

type outgoingConn struct {
	t       streamType
	termStr string
	io.Writer
	http.Flusher
	io.Closer
}

// streamWriter is a long-running worker that writes messages into the
// attached outgoingConn.
type streamWriter struct {
	fs *stats.FollowerStats

	mu      sync.Mutex // guard field working and closer
	closer  io.Closer
	working bool

	msgc  chan raftpb.Message
	connc chan *outgoingConn
	stopc chan struct{}
	done  chan struct{}
}

func startStreamWriter(fs *stats.FollowerStats) *streamWriter {
	w := &streamWriter{
		fs:    fs,
		msgc:  make(chan raftpb.Message, streamBufSize),
		connc: make(chan *outgoingConn),
		stopc: make(chan struct{}),
		done:  make(chan struct{}),
	}
	go w.run()
	return w
}

func (cw *streamWriter) run() {
	var msgc chan raftpb.Message
	var heartbeatc <-chan time.Time
	var t streamType
	var msgAppTerm uint64
	var enc encoder
	var flusher http.Flusher
	tickc := time.Tick(ConnReadTimeout / 3)

	for {
		select {
		case <-heartbeatc:
			if err := enc.encode(linkHeartbeatMessage); err != nil {
				log.Printf("rafthttp: failed to heartbeat on stream %s due to %v. waiting for a new stream to be established.", t, err)
				cw.resetCloser()
				heartbeatc, msgc = nil, nil
				continue
			}
			flusher.Flush()
		case m := <-msgc:
			if t == streamTypeMsgApp && m.Term != msgAppTerm {
				// TODO: reasonable retry logic
				if m.Term > msgAppTerm {
					cw.resetCloser()
					heartbeatc, msgc = nil, nil
				}
				continue
			}
			if err := enc.encode(m); err != nil {
				log.Printf("rafthttp: failed to send message on stream %s due to %v. waiting for a new stream to be established.", t, err)
				cw.resetCloser()
				heartbeatc, msgc = nil, nil
				continue
			}
			flusher.Flush()
		case conn := <-cw.connc:
			cw.resetCloser()
			t = conn.t
			switch conn.t {
			case streamTypeMsgApp:
				var err error
				msgAppTerm, err = strconv.ParseUint(conn.termStr, 10, 64)
				if err != nil {
					log.Panicf("rafthttp: unexpected parse term %s error: %v", conn.termStr, err)
				}
				enc = &msgAppEncoder{w: conn.Writer, fs: cw.fs}
			case streamTypeMessage:
				enc = &messageEncoder{w: conn.Writer}
			default:
				log.Panicf("rafthttp: unhandled stream type %s", conn.t)
			}
			flusher = conn.Flusher
			cw.mu.Lock()
			cw.closer = conn.Closer
			cw.working = true
			cw.mu.Unlock()
			heartbeatc, msgc = tickc, cw.msgc
		case <-cw.stopc:
			cw.resetCloser()
			close(cw.done)
			return
		}
	}
}

func (cw *streamWriter) isWorking() bool {
	cw.mu.Lock()
	defer cw.mu.Unlock()
	return cw.working
}

func (cw *streamWriter) resetCloser() {
	cw.mu.Lock()
	defer cw.mu.Unlock()
	if cw.working {
		cw.closer.Close()
	}
	cw.working = false
}

func (cw *streamWriter) attach(conn *outgoingConn) bool {
	select {
	case cw.connc <- conn:
		return true
	case <-cw.done:
		return false
	}
}

func (cw *streamWriter) stop() {
	close(cw.stopc)
	<-cw.done
}

// streamReader is a long-running go-routine that dials to the remote stream
// endponit and reads messages from the response body returned.
type streamReader struct {
	tr       http.RoundTripper
	u        string
	t        streamType
	from, to types.ID
	cid      types.ID
	recvc    chan<- raftpb.Message

	mu         sync.Mutex
	msgAppTerm uint64
	req        *http.Request
	closer     io.Closer
	stopc      chan struct{}
	done       chan struct{}
}

func startStreamReader(tr http.RoundTripper, u string, t streamType, from, to, cid types.ID, recvc chan<- raftpb.Message) *streamReader {
	r := &streamReader{
		tr:    tr,
		u:     u,
		t:     t,
		from:  from,
		to:    to,
		cid:   cid,
		recvc: recvc,
		stopc: make(chan struct{}),
		done:  make(chan struct{}),
	}
	go r.run()
	return r
}

func (cr *streamReader) run() {
	for {
		rc, err := cr.roundtrip()
		if err != nil {
			log.Printf("rafthttp: roundtripping error: %v", err)
		} else {
			err := cr.decodeLoop(rc)
			if err != io.EOF && !isClosedConnectionError(err) {
				log.Printf("rafthttp: failed to read message on stream %s due to %v", cr.t, err)
			}
		}
		select {
		// Wait 100ms to create a new stream, so it doesn't bring too much
		// overhead when retry.
		case <-time.After(100 * time.Millisecond):
		case <-cr.stopc:
			close(cr.done)
			return
		}
	}
}

func (cr *streamReader) decodeLoop(rc io.ReadCloser) error {
	var dec decoder
	cr.mu.Lock()
	switch cr.t {
	case streamTypeMsgApp:
		dec = &msgAppDecoder{r: rc, local: cr.from, remote: cr.to, term: cr.msgAppTerm}
	case streamTypeMessage:
		dec = &messageDecoder{r: rc}
	default:
		log.Panicf("rafthttp: unhandled stream type %s", cr.t)
	}
	cr.closer = rc
	cr.mu.Unlock()

	for {
		m, err := dec.decode()
		switch {
		case err != nil:
			cr.mu.Lock()
			cr.resetCloser()
			cr.mu.Unlock()
			return err
		case isLinkHeartbeatMessage(m):
			// do nothing for linkHeartbeatMessage
		default:
			select {
			case cr.recvc <- m:
			default:
				log.Printf("rafthttp: dropping %s from %x because receive buffer is blocked",
					m.Type, m.From)
			}
		}
	}
}

func (cr *streamReader) update(u string) {
	cr.mu.Lock()
	defer cr.mu.Unlock()
	cr.u = u
	cr.resetCloser()
}

func (cr *streamReader) updateMsgAppTerm(term uint64) {
	cr.mu.Lock()
	defer cr.mu.Unlock()
	if cr.msgAppTerm == term {
		return
	}
	cr.msgAppTerm = term
	cr.resetCloser()
}

// TODO: always cancel in-flight dial and decode
func (cr *streamReader) stop() {
	close(cr.stopc)
	cr.mu.Lock()
	cr.cancelRequest()
	cr.resetCloser()
	cr.mu.Unlock()
	<-cr.done
}

func (cr *streamReader) isWorking() bool {
	cr.mu.Lock()
	defer cr.mu.Unlock()
	return cr.closer != nil
}

func (cr *streamReader) roundtrip() (io.ReadCloser, error) {
	cr.mu.Lock()
	u := cr.u
	term := cr.msgAppTerm
	cr.mu.Unlock()

	uu, err := url.Parse(u)
	if err != nil {
		return nil, fmt.Errorf("parse url %s error: %v", u, err)
	}
	switch cr.t {
	case streamTypeMsgApp:
		// for backward compatibility of v2.0
		uu.Path = path.Join(RaftStreamPrefix, cr.from.String())
	case streamTypeMessage:
		uu.Path = path.Join(RaftStreamPrefix, string(streamTypeMessage), cr.from.String())
	default:
		log.Panicf("rafthttp: unhandled stream type %v", cr.t)
	}
	req, err := http.NewRequest("GET", uu.String(), nil)
	if err != nil {
		return nil, fmt.Errorf("new request to %s error: %v", u, err)
	}
	req.Header.Set("X-Etcd-Cluster-ID", cr.cid.String())
	req.Header.Set("X-Raft-To", cr.to.String())
	if cr.t == streamTypeMsgApp {
		req.Header.Set("X-Raft-Term", strconv.FormatUint(term, 10))
	}
	cr.mu.Lock()
	cr.req = req
	cr.mu.Unlock()
	resp, err := cr.tr.RoundTrip(req)
	if err != nil {
		return nil, fmt.Errorf("error roundtripping to %s: %v", req.URL, err)
	}
	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return nil, fmt.Errorf("unhandled http status %d", resp.StatusCode)
	}
	return resp.Body, nil
}

func (cr *streamReader) cancelRequest() {
	if canceller, ok := cr.tr.(*http.Transport); ok {
		canceller.CancelRequest(cr.req)
	}
}

func (cr *streamReader) resetCloser() {
	if cr.closer != nil {
		cr.closer.Close()
	}
	cr.closer = nil
}

func canUseMsgAppStream(m raftpb.Message) bool {
	return m.Type == raftpb.MsgApp && m.Term == m.LogTerm
}

func isClosedConnectionError(err error) bool {
	operr, ok := err.(*net.OpError)
	return ok && operr.Err.Error() == "use of closed network connection"
}
<|endoftext|>


<commit_before>package ranking

import (
	"container/list"
	"fmt"
)

const TOPLIST_LEN = 20

type Item struct {
	Vote int
	Url string
}

func Insert(l *list.List, item Item) {
	var elm *list.Element
	for e := l.Front(); e != nil; e = e.Next() {
		if item.Url==e.Value.(Item).Url {
			elm = e
			break
		}
	}
	if (elm!=nil) {
		l.Remove(elm)
	}

	elm = nil
	for e := l.Front(); e != nil; e = e.Next() {
		if item.Vote>e.Value.(Item).Vote {
			elm = e
			break
		}
	}

	if (l.Len()<TOPLIST_LEN) {
		if (elm == nil) {
			l.PushBack(item)
		} else {
			l.InsertBefore(item, elm)
		}
	} else {
		l.Remove(l.Back())
		if (elm == nil) {
			l.PushBack(item)
		} else {
			l.InsertBefore(item, elm)
		}
	}
}

func main() {
	l := list.New()
	Insert(l, Item{Vote: 3, Url: "http://twitter.com"})
	Insert(l, Item{Vote: 8, Url: "http://google.com"})

	for e := l.Front(); e != nil; e = e.Next() {
		fmt.Println(e.Value.(Item).Url)
	}
}
<commit_msg>Correct the top list logic<commit_after>package ranking

import (
	"container/list"
	"fmt"
)

const TOPLIST_LEN = 20

type Item struct {
	Vote int
	Url string
}

func Insert(l *list.List, item Item) {
	fmt.Printf("List len=%d\n", l.Len())
	var elm *list.Element
	for e := l.Front(); e != nil; e = e.Next() {
		if item.Url==e.Value.(Item).Url {
			if item.Vote<=e.Value.(Item).Vote {
				item.Vote += e.Value.(Item).Vote
			}
			elm = e
			break
		}
	}
	if (elm!=nil) {
		l.Remove(elm)
	}

	elm = nil
	for e := l.Front(); e != nil; e = e.Next() {
		if item.Vote>=e.Value.(Item).Vote {
			elm = e
			break
		}
	}

	if (l.Len()<TOPLIST_LEN) {
		if (elm == nil) {
			l.PushBack(item)
		} else {
			l.InsertBefore(item, elm)
		}
	} else {
		if (elm != nil) {
			l.InsertBefore(item, elm)
			l.Remove(l.Back())
		}
	}
}

func main() {
	l := list.New()
	Insert(l, Item{Vote: 3, Url: "http://twitter.com"})
	Insert(l, Item{Vote: 8, Url: "http://google.com"})

	for e := l.Front(); e != nil; e = e.Next() {
		fmt.Println(e.Value.(Item).Url)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/carbontwelve/go-irc-stats"
	"time"
	"math"
	"strconv"
)

type SvgGraphItem struct {
	X      int64
	Value  int64
	Height int64
}

type SvgGraphLabel struct {
	X     int64
	Label string
}

type SvgGraphDay struct {
	X     int64
	Y     int64
	Date  string
	Class string
	Lines int64
}

type SvgGraphWeek struct {
	X      int64
	Y      int64
	Height int64
	Lines  int64
	First  string
	Last   string
}

type SvgGraphData struct {
	Days            []SvgGraphDay
	Weeks           []SvgGraphWeek
	Labels          []SvgGraphLabel
	MLables         []SvgGraphLabel
	MostActiveHours []SvgGraphItem
	MostActiveDays  [7]SvgGraphItem
	Width           int64
}

type ViewData struct {
	PageTitle       string
	PageDescription string
	HeatMapInterval uint
	HeatMapKey      [6]int
	Database        Database
	SvgGraphData    SvgGraphData
	WeeksMax        uint
	WeekDayMax      int64
}

func (d ViewData) TotalDays() int64 {
	return helpers.DaysDiffUnix(d.Database.Channel.Last, d.Database.Channel.First)
}

func (d *ViewData) buildDayHeatMapDays() () {
	timeNow := time.Now()
	totalDays := d.TotalDays()
	Days := make([]SvgGraphDay, totalDays)
	Weeks := make([]SvgGraphWeek, (totalDays / 7) + 2) // there is a n+1 error where Weeks starts at 0 by the 0 element is never filled, thus the +2
	Labels := make([]SvgGraphLabel, 1)
	MLables := make([]SvgGraphLabel, 1)

	// Create heatmap key
	for i := 1; i < 6; i ++ {
		d.HeatMapKey[i] = int(d.HeatMapInterval) * i
	}

	var (
		weekDays [7]SvgGraphItem
		firstWeek string
		lastWeek string
		x int64
		y int64
		mx int64
		weekLines int64
		lines int64
		cssClass string
		i int64
	)

	for i = 0; i < int64(len(weekDays)); i++ {
		weekDays[i].X = i
	}

	for i = 0; i < totalDays; i++ {
		elementTime := timeNow.AddDate(0, 0, int(-(totalDays - i)))

		// Work out first week
		if (i == 0) {
			firstWeek = elementTime.Format("Jan-01")
		}

		y = int64(elementTime.Weekday())

		// If the day is Sunday
		if (y == 0) {
			x += 1
			weekLines = 0
			firstWeek = elementTime.Format("Jan-01")
		}

		// If this is the first day of the month
		if (elementTime.Day() == 1) {
			mx ++
		}

		if d.Database.HasDay(elementTime.Format("2006-02-01")) {
			lines = int64(d.Database.Days[elementTime.Format("2006-02-01")])
		} else {
			lines = 0
		}

		weekLines += int64(lines)
		lastWeek = elementTime.Format("Jan-01")
		weekDays[elementTime.Weekday()].Value += lines

		Weeks[x] = SvgGraphWeek{
			X: x,
			Y: y,
			Lines: weekLines,
			First: firstWeek,
			Last: lastWeek,
		}

		// Identify class
		classSet := false
		for i := 1; i < 6; i ++ {
			if int(lines) < d.HeatMapKey[i] {
				cssClass = "scale-" + strconv.Itoa(i)
				classSet = true
				break
			}
		}
		if classSet == false {
			cssClass = "scale-6"
		}

		Days[i] = SvgGraphDay{
			X: x,
			Y: y,
			Date: elementTime.Format("2006-02-01"),
			Class: cssClass,
			Lines: lines,
		}

		// April, July, October
		if elementTime.YearDay() == 92 || elementTime.YearDay() == 193 || elementTime.YearDay() == 274 {
			Labels = append(Labels, SvgGraphLabel{
				X: x,
				Label: elementTime.Format("Jan"),
			})
			MLables = append(MLables, SvgGraphLabel{
				X: mx,
				Label: elementTime.Format("Jan"),
			})
		}

		// New Year
		if elementTime.YearDay() == 1 {
			Labels = append(Labels, SvgGraphLabel{
				X: x,
				Label: elementTime.Format("2006"),
			})
			MLables = append(MLables, SvgGraphLabel{
				X: mx,
				Label: elementTime.Format("2006"),
			})
		}

		//fmt.Printf("%d days ago [%s] is element %d\n", (totalDays - i), elementTime.Format("2006-02-01"), i)
	}
	d.SvgGraphData = SvgGraphData{
		Days: Days,
		Weeks: Weeks,
		Labels: Labels,
		MLables: MLables,
		MostActiveDays: weekDays, // This is a preliminary pass because its more efficient to do it here.
	}

	d.SvgGraphData.Width = (d.SvgGraphData.Days[len(d.SvgGraphData.Days) - 1].X * 10) + 10
	return
}

func (d *ViewData) buildWeekGraph() {
	// Get week max
	for _, w := range (d.SvgGraphData.Weeks) {
		if uint(w.Lines) > uint(d.WeeksMax) {
			d.WeeksMax = uint(w.Lines)
		}
	}

	// Get Weeks.Height
	tmpWeeks := make([]SvgGraphWeek, len(d.SvgGraphData.Weeks))
	for k, w := range (d.SvgGraphData.Weeks) {
		w.Height = int64(math.Floor(float64(w.Lines) / float64(d.WeeksMax) * 100))
		tmpWeeks[k] = w
	}
	d.SvgGraphData.Weeks = tmpWeeks

	// Get Most Active Times
	tmpMostActiveTimes := make([]SvgGraphItem, len(d.Database.Hours))
	for hour, lines := range d.Database.Hours {
		tmpMostActiveTimes[hour] = SvgGraphItem{
			X: int64(hour * 10),
			Value: lines,
			Height: int64(math.Floor(float64(lines) / float64(d.Database.Channel.MaxHour.Lines) * 100)),
		}
	}
	d.SvgGraphData.MostActiveHours = tmpMostActiveTimes

	// Get weekday max
	for _, v := range(d.SvgGraphData.MostActiveDays) {
		if v.Value > d.WeekDayMax {
			d.WeekDayMax = v.Value
		}
	}

	// Get Most Active Days of Week
	var tmpMostActiveDays [7]SvgGraphItem
	for day, obj := range d.SvgGraphData.MostActiveDays {
		tmpMostActiveDays[day] = SvgGraphItem{
			X: obj.X * 10,
			Height: int64(math.Floor(float64(obj.Value) / float64(d.WeekDayMax) * 100)),
		}
	}
	d.SvgGraphData.MostActiveDays = tmpMostActiveDays

	// Get week mean

	// Get week days max
}
<commit_msg>Removed circular dependency<commit_after>package main

import (
	"time"
	"math"
	"strconv"
)

type SvgGraphItem struct {
	X      int64
	Value  int64
	Height int64
}

type SvgGraphLabel struct {
	X     int64
	Label string
}

type SvgGraphDay struct {
	X     int64
	Y     int64
	Date  string
	Class string
	Lines int64
}

type SvgGraphWeek struct {
	X      int64
	Y      int64
	Height int64
	Lines  int64
	First  string
	Last   string
}

type SvgGraphData struct {
	Days            []SvgGraphDay
	Weeks           []SvgGraphWeek
	Labels          []SvgGraphLabel
	MLables         []SvgGraphLabel
	MostActiveHours []SvgGraphItem
	MostActiveDays  [7]SvgGraphItem
	Width           int64
}

type ViewData struct {
	PageTitle       string
	PageDescription string
	HeatMapInterval uint
	HeatMapKey      [6]int
	Database        Database
	SvgGraphData    SvgGraphData
	WeeksMax        uint
	WeekDayMax      int64
}

func (d ViewData) TotalDays() int64 {
	return helpers.DaysDiffUnix(d.Database.Channel.Last, d.Database.Channel.First)
}

func (d *ViewData) buildDayHeatMapDays() () {
	timeNow := time.Now()
	totalDays := d.TotalDays()
	Days := make([]SvgGraphDay, totalDays)
	Weeks := make([]SvgGraphWeek, (totalDays / 7) + 2) // there is a n+1 error where Weeks starts at 0 by the 0 element is never filled, thus the +2
	Labels := make([]SvgGraphLabel, 1)
	MLables := make([]SvgGraphLabel, 1)

	// Create heatmap key
	for i := 1; i < 6; i ++ {
		d.HeatMapKey[i] = int(d.HeatMapInterval) * i
	}

	var (
		weekDays [7]SvgGraphItem
		firstWeek string
		lastWeek string
		x int64
		y int64
		mx int64
		weekLines int64
		lines int64
		cssClass string
		i int64
	)

	for i = 0; i < int64(len(weekDays)); i++ {
		weekDays[i].X = i
	}

	for i = 0; i < totalDays; i++ {
		elementTime := timeNow.AddDate(0, 0, int(-(totalDays - i)))

		// Work out first week
		if (i == 0) {
			firstWeek = elementTime.Format("Jan-01")
		}

		y = int64(elementTime.Weekday())

		// If the day is Sunday
		if (y == 0) {
			x += 1
			weekLines = 0
			firstWeek = elementTime.Format("Jan-01")
		}

		// If this is the first day of the month
		if (elementTime.Day() == 1) {
			mx ++
		}

		if d.Database.HasDay(elementTime.Format("2006-02-01")) {
			lines = int64(d.Database.Days[elementTime.Format("2006-02-01")])
		} else {
			lines = 0
		}

		weekLines += int64(lines)
		lastWeek = elementTime.Format("Jan-01")
		weekDays[elementTime.Weekday()].Value += lines

		Weeks[x] = SvgGraphWeek{
			X: x,
			Y: y,
			Lines: weekLines,
			First: firstWeek,
			Last: lastWeek,
		}

		// Identify class
		classSet := false
		for i := 1; i < 6; i ++ {
			if int(lines) < d.HeatMapKey[i] {
				cssClass = "scale-" + strconv.Itoa(i)
				classSet = true
				break
			}
		}
		if classSet == false {
			cssClass = "scale-6"
		}

		Days[i] = SvgGraphDay{
			X: x,
			Y: y,
			Date: elementTime.Format("2006-02-01"),
			Class: cssClass,
			Lines: lines,
		}

		// April, July, October
		if elementTime.YearDay() == 92 || elementTime.YearDay() == 193 || elementTime.YearDay() == 274 {
			Labels = append(Labels, SvgGraphLabel{
				X: x,
				Label: elementTime.Format("Jan"),
			})
			MLables = append(MLables, SvgGraphLabel{
				X: mx,
				Label: elementTime.Format("Jan"),
			})
		}

		// New Year
		if elementTime.YearDay() == 1 {
			Labels = append(Labels, SvgGraphLabel{
				X: x,
				Label: elementTime.Format("2006"),
			})
			MLables = append(MLables, SvgGraphLabel{
				X: mx,
				Label: elementTime.Format("2006"),
			})
		}

		//fmt.Printf("%d days ago [%s] is element %d\n", (totalDays - i), elementTime.Format("2006-02-01"), i)
	}
	d.SvgGraphData = SvgGraphData{
		Days: Days,
		Weeks: Weeks,
		Labels: Labels,
		MLables: MLables,
		MostActiveDays: weekDays, // This is a preliminary pass because its more efficient to do it here.
	}

	d.SvgGraphData.Width = (d.SvgGraphData.Days[len(d.SvgGraphData.Days) - 1].X * 10) + 10
	return
}

func (d *ViewData) buildWeekGraph() {
	// Get week max
	for _, w := range (d.SvgGraphData.Weeks) {
		if uint(w.Lines) > uint(d.WeeksMax) {
			d.WeeksMax = uint(w.Lines)
		}
	}

	// Get Weeks.Height
	tmpWeeks := make([]SvgGraphWeek, len(d.SvgGraphData.Weeks))
	for k, w := range (d.SvgGraphData.Weeks) {
		w.Height = int64(math.Floor(float64(w.Lines) / float64(d.WeeksMax) * 100))
		tmpWeeks[k] = w
	}
	d.SvgGraphData.Weeks = tmpWeeks

	// Get Most Active Times
	tmpMostActiveTimes := make([]SvgGraphItem, len(d.Database.Hours))
	for hour, lines := range d.Database.Hours {
		tmpMostActiveTimes[hour] = SvgGraphItem{
			X: int64(hour * 10),
			Value: lines,
			Height: int64(math.Floor(float64(lines) / float64(d.Database.Channel.MaxHour.Lines) * 100)),
		}
	}
	d.SvgGraphData.MostActiveHours = tmpMostActiveTimes

	// Get weekday max
	for _, v := range(d.SvgGraphData.MostActiveDays) {
		if v.Value > d.WeekDayMax {
			d.WeekDayMax = v.Value
		}
	}

	// Get Most Active Days of Week
	var tmpMostActiveDays [7]SvgGraphItem
	for day, obj := range d.SvgGraphData.MostActiveDays {
		tmpMostActiveDays[day] = SvgGraphItem{
			X: obj.X * 10,
			Height: int64(math.Floor(float64(obj.Value) / float64(d.WeekDayMax) * 100)),
		}
	}
	d.SvgGraphData.MostActiveDays = tmpMostActiveDays

	// Get week mean

	// Get week days max
}
<|endoftext|>


<commit_before>// Copyright 2018 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package main

import (
	"context"
	"fmt"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/strib/gomounts"
)

type symlink struct {
	link string
}

func (s symlink) Attr(ctx context.Context, a *fuse.Attr) (err error) {
	a.Mode = os.ModeSymlink | a.Mode | 0555
	a.Valid = 0
	return nil
}

func (s symlink) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (
	link string, err error) {
	return s.link, nil
}

type root struct {
}

func (r root) Root() (fs.Node, error) {
	return r, nil
}

func (r root) Attr(ctx context.Context, attr *fuse.Attr) error {
	attr.Mode = os.ModeDir | 0555
	return nil
}

func (r root) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
	return []fuse.Dirent{
		{
			Type: fuse.DT_Link,
			Name: "private",
		},
		{
			Type: fuse.DT_Link,
			Name: "public",
		},
		fuse.Dirent{
			Type: fuse.DT_Link,
			Name: "team",
		},
	}, nil
}

func findKBFSMount(uid string) (string, error) {
	vols, err := gomounts.GetMountedVolumes()
	if err != nil {
		return "", err
	}
	fuseType := "fuse"
	if runtime.GOOS == "darwin" {
		fuseType = "kbfuse"
	}
	var fuseMountPoints []string
	for _, v := range vols {
		if v.Type != fuseType {
			continue
		}
		if v.Owner != uid {
			continue
		}
		fuseMountPoints = append(fuseMountPoints, v.Path)
	}

	if len(fuseMountPoints) == 0 {
		return "", fuse.ENOENT
	}
	if len(fuseMountPoints) == 1 {
		return fuseMountPoints[0], nil
	}

	// If there is more than one, pick the first one alphabetically
	// that has "keybase" in the path.
	sort.Strings(fuseMountPoints)
	for _, mp := range fuseMountPoints {
		// TODO: a better regexp that will rule out keybase.staging if
		// we're in prod mode, etc.
		if strings.Contains(mp, "keybase") {
			return mp, nil
		}
	}

	// Give up and return the first one.
	return fuseMountPoints[0], nil
}

func (r root) Lookup(
	ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (
	n fs.Node, err error) {
	u, err := user.LookupId(strconv.FormatUint(uint64(req.Header.Uid), 10))
	if err != nil {
		return nil, err
	}
	mountpoint, err := findKBFSMount(u.Uid)
	if err != nil {
		return nil, err
	}

	resp.EntryValid = 0
	switch req.Name {
	case "private":
		return symlink{filepath.Join(mountpoint, "private")}, nil
	case "public":
		return symlink{filepath.Join(mountpoint, "public")}, nil
	case "team":
		return symlink{filepath.Join(mountpoint, "team")}, nil
	}
	return nil, fuse.ENOENT
}

func main() {
	// This must be run as soon (or edit /etc/fuse.conf to enable
	// `user_allow_other`).
	c, err := fuse.Mount(os.Args[1], fuse.AllowOther())
	if err != nil {
		panic(err)
	}

	srv := fs.New(c, &fs.Config{
		WithContext: func(ctx context.Context, _ fuse.Request) context.Context {
			return context.Background()
		},
	})
	srv.Serve(root{})
}
<commit_msg>redirector: no fmt needed<commit_after>// Copyright 2018 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package main

import (
	"context"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/strib/gomounts"
)

type symlink struct {
	link string
}

func (s symlink) Attr(ctx context.Context, a *fuse.Attr) (err error) {
	a.Mode = os.ModeSymlink | a.Mode | 0555
	a.Valid = 0
	return nil
}

func (s symlink) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (
	link string, err error) {
	return s.link, nil
}

type root struct {
}

func (r root) Root() (fs.Node, error) {
	return r, nil
}

func (r root) Attr(ctx context.Context, attr *fuse.Attr) error {
	attr.Mode = os.ModeDir | 0555
	return nil
}

func (r root) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
	return []fuse.Dirent{
		{
			Type: fuse.DT_Link,
			Name: "private",
		},
		{
			Type: fuse.DT_Link,
			Name: "public",
		},
		fuse.Dirent{
			Type: fuse.DT_Link,
			Name: "team",
		},
	}, nil
}

func findKBFSMount(uid string) (string, error) {
	vols, err := gomounts.GetMountedVolumes()
	if err != nil {
		return "", err
	}
	fuseType := "fuse"
	if runtime.GOOS == "darwin" {
		fuseType = "kbfuse"
	}
	var fuseMountPoints []string
	for _, v := range vols {
		if v.Type != fuseType {
			continue
		}
		if v.Owner != uid {
			continue
		}
		fuseMountPoints = append(fuseMountPoints, v.Path)
	}

	if len(fuseMountPoints) == 0 {
		return "", fuse.ENOENT
	}
	if len(fuseMountPoints) == 1 {
		return fuseMountPoints[0], nil
	}

	// If there is more than one, pick the first one alphabetically
	// that has "keybase" in the path.
	sort.Strings(fuseMountPoints)
	for _, mp := range fuseMountPoints {
		// TODO: a better regexp that will rule out keybase.staging if
		// we're in prod mode, etc.
		if strings.Contains(mp, "keybase") {
			return mp, nil
		}
	}

	// Give up and return the first one.
	return fuseMountPoints[0], nil
}

func (r root) Lookup(
	ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (
	n fs.Node, err error) {
	u, err := user.LookupId(strconv.FormatUint(uint64(req.Header.Uid), 10))
	if err != nil {
		return nil, err
	}
	mountpoint, err := findKBFSMount(u.Uid)
	if err != nil {
		return nil, err
	}

	resp.EntryValid = 0
	switch req.Name {
	case "private":
		return symlink{filepath.Join(mountpoint, "private")}, nil
	case "public":
		return symlink{filepath.Join(mountpoint, "public")}, nil
	case "team":
		return symlink{filepath.Join(mountpoint, "team")}, nil
	}
	return nil, fuse.ENOENT
}

func main() {
	// This must be run as soon (or edit /etc/fuse.conf to enable
	// `user_allow_other`).
	c, err := fuse.Mount(os.Args[1], fuse.AllowOther())
	if err != nil {
		panic(err)
	}

	srv := fs.New(c, &fs.Config{
		WithContext: func(ctx context.Context, _ fuse.Request) context.Context {
			return context.Background()
		},
	})
	srv.Serve(root{})
}
<|endoftext|>


<commit_before>package opts

import (
	"bufio"
	"fmt"
	"os"
	"strings"
)

/*
Read in a line delimited file with environment variables enumerated
*/
func ParseEnvFile(filename string) ([]string, error) {
	fh, err := os.Open(filename)
	if err != nil {
		return []string{}, err
	}
	defer fh.Close()

	lines := []string{}
	scanner := bufio.NewScanner(fh)
	for scanner.Scan() {
		line := scanner.Text()
		// line is not empty, and not starting with '#'
		if len(line) > 0 && !strings.HasPrefix(line, "#") {
			if strings.Contains(line, "=") {
				data := strings.SplitN(line, "=", 2)

				// trim the front of a variable, but nothing else
				variable := strings.TrimLeft(data[0], whiteSpaces)
				if strings.ContainsAny(variable, whiteSpaces) {
					return []string{}, ErrBadEnvVariable{fmt.Sprintf("variable '%s' has white spaces", variable)}
				}

				// pass the value through, no trimming
				lines = append(lines, fmt.Sprintf("%s=%s", variable, data[1]))
			} else {
				// if only a pass-through variable is given, clean it up.
				lines = append(lines, fmt.Sprintf("%s=%s", strings.TrimSpace(line), os.Getenv(line)))
			}
		}
	}
	return lines, nil
}

var whiteSpaces = " \t"

type ErrBadEnvVariable struct {
	msg string
}

func (e ErrBadEnvVariable) Error() string {
	return fmt.Sprintf("poorly formatted environment: %s", e.msg)
}
<commit_msg>Return bufio error if set in ParseEnvFile<commit_after>package opts

import (
	"bufio"
	"fmt"
	"os"
	"strings"
)

/*
Read in a line delimited file with environment variables enumerated
*/
func ParseEnvFile(filename string) ([]string, error) {
	fh, err := os.Open(filename)
	if err != nil {
		return []string{}, err
	}
	defer fh.Close()

	lines := []string{}
	scanner := bufio.NewScanner(fh)
	for scanner.Scan() {
		line := scanner.Text()
		// line is not empty, and not starting with '#'
		if len(line) > 0 && !strings.HasPrefix(line, "#") {
			if strings.Contains(line, "=") {
				data := strings.SplitN(line, "=", 2)

				// trim the front of a variable, but nothing else
				variable := strings.TrimLeft(data[0], whiteSpaces)
				if strings.ContainsAny(variable, whiteSpaces) {
					return []string{}, ErrBadEnvVariable{fmt.Sprintf("variable '%s' has white spaces", variable)}
				}

				// pass the value through, no trimming
				lines = append(lines, fmt.Sprintf("%s=%s", variable, data[1]))
			} else {
				// if only a pass-through variable is given, clean it up.
				lines = append(lines, fmt.Sprintf("%s=%s", strings.TrimSpace(line), os.Getenv(line)))
			}
		}
	}
	return lines, scanner.Err()
}

var whiteSpaces = " \t"

type ErrBadEnvVariable struct {
	msg string
}

func (e ErrBadEnvVariable) Error() string {
	return fmt.Sprintf("poorly formatted environment: %s", e.msg)
}
<|endoftext|>