<commit_before>package addrutil

import (
	"fmt"

	eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog"

	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
)

var log = eventlog.Logger("p2p/net/swarm/addr")

// SupportedTransportStrings is the list of supported transports for the swarm.
// These are strings of encapsulated multiaddr protocols. E.g.:
//   /ip4/tcp
var SupportedTransportStrings = []string{
	"/ip4/tcp",
	"/ip6/tcp",
	// "/ip4/udp/utp", disabled because the lib is broken
	// "/ip6/udp/utp", disabled because the lib is broken
	// "/ip4/udp/udt", disabled because the lib doesnt work on arm
	// "/ip6/udp/udt", disabled because the lib doesnt work on arm
}

// SupportedTransportProtocols is the list of supported transports for the swarm.
// These are []ma.Protocol lists. Populated at runtime from SupportedTransportStrings
var SupportedTransportProtocols = [][]ma.Protocol{}

func init() {
	// initialize SupportedTransportProtocols
	transports := make([][]ma.Protocol, len(SupportedTransportStrings))
	for _, s := range SupportedTransportStrings {
		t, err := ma.ProtocolsWithString(s)
		if err != nil {
			panic(err) // important to fix this in the codebase
		}
		transports = append(transports, t)
	}
	SupportedTransportProtocols = transports
}

// FilterAddrs is a filter that removes certain addresses, according to filter.
// if filter returns true, the address is kept.
func FilterAddrs(a []ma.Multiaddr, filter func(ma.Multiaddr) bool) []ma.Multiaddr {
	b := make([]ma.Multiaddr, 0, len(a))
	for _, addr := range a {
		if filter(addr) {
			b = append(b, addr)
		}
	}
	return b
}

// FilterUsableAddrs removes certain addresses
// from a list. the addresses removed are those known NOT
// to work with our network. Namely, addresses with UTP.
func FilterUsableAddrs(a []ma.Multiaddr) []ma.Multiaddr {
	return FilterAddrs(a, func(m ma.Multiaddr) bool {
		return AddrUsable(m, false)
	})
}

// AddrOverNonLocalIP returns whether the addr uses a non-local ip link
func AddrOverNonLocalIP(a ma.Multiaddr) bool {
	split := ma.Split(a)
	if len(split) < 1 {
		return false
	}
	if manet.IsIP6LinkLocal(split[0]) {
		return false
	}
	return true
}

// AddrUsable returns whether our network can use this addr.
// We only use the transports in SupportedTransportStrings,
// and we do not link local addresses. Loopback is ok
// as we need to be able to connect to multiple ipfs nodes
// in the same machine.
func AddrUsable(a ma.Multiaddr, partial bool) bool {

	if !AddrOverNonLocalIP(a) {
		return false
	}

	// test the address protocol list is in SupportedTransportProtocols
	matches := func(supported, test []ma.Protocol) bool {
		if len(test) > len(supported) {
			return false
		}

		// when partial, it's ok if test < supported.
		if !partial && len(supported) != len(test) {
			return false
		}

		for i := range test {
			if supported[i].Code != test[i].Code {
				return false
			}
		}
		return true
	}

	transport := a.Protocols()
	for _, supported := range SupportedTransportProtocols {
		if matches(supported, transport) {
			return true
		}
	}

	return false
}

// ResolveUnspecifiedAddress expands an unspecified ip addresses (/ip4/0.0.0.0, /ip6/::) to
// use the known local interfaces. If ifaceAddr is nil, we request interface addresses
// from the network stack. (this is so you can provide a cached value if resolving many addrs)
func ResolveUnspecifiedAddress(resolve ma.Multiaddr, ifaceAddrs []ma.Multiaddr) ([]ma.Multiaddr, error) {
	// split address into its components
	split := ma.Split(resolve)

	// if first component (ip) is not unspecified, use it as is.
	if !manet.IsIPUnspecified(split[0]) {
		return []ma.Multiaddr{resolve}, nil
	}

	out := make([]ma.Multiaddr, 0, len(ifaceAddrs))
	for _, ia := range ifaceAddrs {
		// must match the first protocol to be resolve.
		if ia.Protocols()[0].Code != resolve.Protocols()[0].Code {
			continue
		}

		split[0] = ia
		joined := ma.Join(split...)
		out = append(out, joined)
		log.Debug("adding resolved addr:", resolve, joined, out)
	}
	if len(out) < 1 {
		return nil, fmt.Errorf("failed to resolve: %s", resolve)
	}
	return out, nil
}

// ResolveUnspecifiedAddresses expands unspecified ip addresses (/ip4/0.0.0.0, /ip6/::) to
// use the known local interfaces.
func ResolveUnspecifiedAddresses(unspecAddrs, ifaceAddrs []ma.Multiaddr) ([]ma.Multiaddr, error) {

	// todo optimize: only fetch these if we have a "any" addr.
	if len(ifaceAddrs) < 1 {
		var err error
		ifaceAddrs, err = InterfaceAddresses()
		if err != nil {
			return nil, err
		}
		// log.Debug("InterfaceAddresses:", ifaceAddrs)
	}

	var outputAddrs []ma.Multiaddr
	for _, a := range unspecAddrs {
		// unspecified?
		resolved, err := ResolveUnspecifiedAddress(a, ifaceAddrs)
		if err != nil {
			continue // optimistic. if we cant resolve anything, we'll know at the bottom.
		}
		// log.Debug("resolved:", a, resolved)
		outputAddrs = append(outputAddrs, resolved...)
	}

	if len(outputAddrs) < 1 {
		return nil, fmt.Errorf("failed to specify addrs: %s", unspecAddrs)
	}

	log.Event(context.TODO(), "interfaceListenAddresses", func() eventlog.Loggable {
		var addrs []string
		for _, addr := range outputAddrs {
			addrs = append(addrs, addr.String())
		}
		return eventlog.Metadata{"addresses": addrs}
	}())

	log.Debug("ResolveUnspecifiedAddresses:", unspecAddrs, ifaceAddrs, outputAddrs)
	return outputAddrs, nil
}

// InterfaceAddresses returns a list of addresses associated with local machine
// Note: we do not return link local addresses. IP loopback is ok, because we
// may be connecting to other nodes in the same machine.
func InterfaceAddresses() ([]ma.Multiaddr, error) {
	maddrs, err := manet.InterfaceMultiaddrs()
	if err != nil {
		return nil, err
	}
	log.Debug("InterfaceAddresses: from manet:", maddrs)

	var out []ma.Multiaddr
	for _, a := range maddrs {
		if !AddrUsable(a, true) { // partial
			// log.Debug("InterfaceAddresses: skipping unusable:", a)
			continue
		}

		out = append(out, a)
	}

	log.Debug("InterfaceAddresses: usable:", out)
	return out, nil
}

// AddrInList returns whether or not an address is part of a list.
// this is useful to check if NAT is happening (or other bugs?)
func AddrInList(addr ma.Multiaddr, list []ma.Multiaddr) bool {
	for _, addr2 := range list {
		if addr.Equal(addr2) {
			return true
		}
	}
	return false
}

// AddrIsShareableOnWAN returns whether the given address should be shareable on the
// wide area network (wide internet).
func AddrIsShareableOnWAN(addr ma.Multiaddr) bool {
	s := ma.Split(addr)
	if len(s) < 1 {
		return false
	}
	a := s[0]
	if manet.IsIPLoopback(a) || manet.IsIP6LinkLocal(a) || manet.IsIPUnspecified(a) {
		return false
	}
	return manet.IsThinWaist(a)
}

// WANShareableAddrs filters addresses based on whether they're shareable on WAN
func WANShareableAddrs(inp []ma.Multiaddr) []ma.Multiaddr {
	return FilterAddrs(inp, AddrIsShareableOnWAN)
}

// Subtract filters out all addrs in b from a
func Subtract(a, b []ma.Multiaddr) []ma.Multiaddr {
	return FilterAddrs(a, func(m ma.Multiaddr) bool {
		for _, bb := range b {
			if m.Equal(bb) {
				return false
			}
		}
		return true
	})
}

// CheckNATWarning checks if our observed addresses differ. if so,
// informs the user that certain things might not work yet
func CheckNATWarning(observed, expected ma.Multiaddr, listen []ma.Multiaddr) {
	if observed.Equal(expected) {
		return
	}

	if !AddrInList(observed, listen) { // probably a nat
		log.Warningf(natWarning, observed, listen)
	}
}

const natWarning = `Remote peer observed our address to be: %s
The local addresses are: %s
Thus, connection is going through NAT, and other connections may fail.

IPFS NAT traversal is still under development. Please bug us on github or irc to fix this.
Baby steps: http://jbenet.static.s3.amazonaws.com/271dfcf/baby-steps.gif
`
<commit_msg>p2p/net/swarm/addr: check for nil addr<commit_after>package addrutil

import (
	"fmt"

	eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog"

	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
)

var log = eventlog.Logger("p2p/net/swarm/addr")

// SupportedTransportStrings is the list of supported transports for the swarm.
// These are strings of encapsulated multiaddr protocols. E.g.:
//   /ip4/tcp
var SupportedTransportStrings = []string{
	"/ip4/tcp",
	"/ip6/tcp",
	// "/ip4/udp/utp", disabled because the lib is broken
	// "/ip6/udp/utp", disabled because the lib is broken
	// "/ip4/udp/udt", disabled because the lib doesnt work on arm
	// "/ip6/udp/udt", disabled because the lib doesnt work on arm
}

// SupportedTransportProtocols is the list of supported transports for the swarm.
// These are []ma.Protocol lists. Populated at runtime from SupportedTransportStrings
var SupportedTransportProtocols = [][]ma.Protocol{}

func init() {
	// initialize SupportedTransportProtocols
	transports := make([][]ma.Protocol, len(SupportedTransportStrings))
	for _, s := range SupportedTransportStrings {
		t, err := ma.ProtocolsWithString(s)
		if err != nil {
			panic(err) // important to fix this in the codebase
		}
		transports = append(transports, t)
	}
	SupportedTransportProtocols = transports
}

// FilterAddrs is a filter that removes certain addresses, according to filter.
// if filter returns true, the address is kept.
func FilterAddrs(a []ma.Multiaddr, filter func(ma.Multiaddr) bool) []ma.Multiaddr {
	b := make([]ma.Multiaddr, 0, len(a))
	for _, addr := range a {
		if filter(addr) {
			b = append(b, addr)
		}
	}
	return b
}

// FilterUsableAddrs removes certain addresses
// from a list. the addresses removed are those known NOT
// to work with our network. Namely, addresses with UTP.
func FilterUsableAddrs(a []ma.Multiaddr) []ma.Multiaddr {
	return FilterAddrs(a, func(m ma.Multiaddr) bool {
		return AddrUsable(m, false)
	})
}

// AddrOverNonLocalIP returns whether the addr uses a non-local ip link
func AddrOverNonLocalIP(a ma.Multiaddr) bool {
	split := ma.Split(a)
	if len(split) < 1 {
		return false
	}
	if manet.IsIP6LinkLocal(split[0]) {
		return false
	}
	return true
}

// AddrUsable returns whether our network can use this addr.
// We only use the transports in SupportedTransportStrings,
// and we do not link local addresses. Loopback is ok
// as we need to be able to connect to multiple ipfs nodes
// in the same machine.
func AddrUsable(a ma.Multiaddr, partial bool) bool {
	if a == nil {
		return false
	}

	if !AddrOverNonLocalIP(a) {
		return false
	}

	// test the address protocol list is in SupportedTransportProtocols
	matches := func(supported, test []ma.Protocol) bool {
		if len(test) > len(supported) {
			return false
		}

		// when partial, it's ok if test < supported.
		if !partial && len(supported) != len(test) {
			return false
		}

		for i := range test {
			if supported[i].Code != test[i].Code {
				return false
			}
		}
		return true
	}

	transport := a.Protocols()
	for _, supported := range SupportedTransportProtocols {
		if matches(supported, transport) {
			return true
		}
	}

	return false
}

// ResolveUnspecifiedAddress expands an unspecified ip addresses (/ip4/0.0.0.0, /ip6/::) to
// use the known local interfaces. If ifaceAddr is nil, we request interface addresses
// from the network stack. (this is so you can provide a cached value if resolving many addrs)
func ResolveUnspecifiedAddress(resolve ma.Multiaddr, ifaceAddrs []ma.Multiaddr) ([]ma.Multiaddr, error) {
	// split address into its components
	split := ma.Split(resolve)

	// if first component (ip) is not unspecified, use it as is.
	if !manet.IsIPUnspecified(split[0]) {
		return []ma.Multiaddr{resolve}, nil
	}

	out := make([]ma.Multiaddr, 0, len(ifaceAddrs))
	for _, ia := range ifaceAddrs {
		// must match the first protocol to be resolve.
		if ia.Protocols()[0].Code != resolve.Protocols()[0].Code {
			continue
		}

		split[0] = ia
		joined := ma.Join(split...)
		out = append(out, joined)
		log.Debug("adding resolved addr:", resolve, joined, out)
	}
	if len(out) < 1 {
		return nil, fmt.Errorf("failed to resolve: %s", resolve)
	}
	return out, nil
}

// ResolveUnspecifiedAddresses expands unspecified ip addresses (/ip4/0.0.0.0, /ip6/::) to
// use the known local interfaces.
func ResolveUnspecifiedAddresses(unspecAddrs, ifaceAddrs []ma.Multiaddr) ([]ma.Multiaddr, error) {

	// todo optimize: only fetch these if we have a "any" addr.
	if len(ifaceAddrs) < 1 {
		var err error
		ifaceAddrs, err = InterfaceAddresses()
		if err != nil {
			return nil, err
		}
		// log.Debug("InterfaceAddresses:", ifaceAddrs)
	}

	var outputAddrs []ma.Multiaddr
	for _, a := range unspecAddrs {
		// unspecified?
		resolved, err := ResolveUnspecifiedAddress(a, ifaceAddrs)
		if err != nil {
			continue // optimistic. if we cant resolve anything, we'll know at the bottom.
		}
		// log.Debug("resolved:", a, resolved)
		outputAddrs = append(outputAddrs, resolved...)
	}

	if len(outputAddrs) < 1 {
		return nil, fmt.Errorf("failed to specify addrs: %s", unspecAddrs)
	}

	log.Event(context.TODO(), "interfaceListenAddresses", func() eventlog.Loggable {
		var addrs []string
		for _, addr := range outputAddrs {
			addrs = append(addrs, addr.String())
		}
		return eventlog.Metadata{"addresses": addrs}
	}())

	log.Debug("ResolveUnspecifiedAddresses:", unspecAddrs, ifaceAddrs, outputAddrs)
	return outputAddrs, nil
}

// InterfaceAddresses returns a list of addresses associated with local machine
// Note: we do not return link local addresses. IP loopback is ok, because we
// may be connecting to other nodes in the same machine.
func InterfaceAddresses() ([]ma.Multiaddr, error) {
	maddrs, err := manet.InterfaceMultiaddrs()
	if err != nil {
		return nil, err
	}
	log.Debug("InterfaceAddresses: from manet:", maddrs)

	var out []ma.Multiaddr
	for _, a := range maddrs {
		if !AddrUsable(a, true) { // partial
			// log.Debug("InterfaceAddresses: skipping unusable:", a)
			continue
		}

		out = append(out, a)
	}

	log.Debug("InterfaceAddresses: usable:", out)
	return out, nil
}

// AddrInList returns whether or not an address is part of a list.
// this is useful to check if NAT is happening (or other bugs?)
func AddrInList(addr ma.Multiaddr, list []ma.Multiaddr) bool {
	for _, addr2 := range list {
		if addr.Equal(addr2) {
			return true
		}
	}
	return false
}

// AddrIsShareableOnWAN returns whether the given address should be shareable on the
// wide area network (wide internet).
func AddrIsShareableOnWAN(addr ma.Multiaddr) bool {
	s := ma.Split(addr)
	if len(s) < 1 {
		return false
	}
	a := s[0]
	if manet.IsIPLoopback(a) || manet.IsIP6LinkLocal(a) || manet.IsIPUnspecified(a) {
		return false
	}
	return manet.IsThinWaist(a)
}

// WANShareableAddrs filters addresses based on whether they're shareable on WAN
func WANShareableAddrs(inp []ma.Multiaddr) []ma.Multiaddr {
	return FilterAddrs(inp, AddrIsShareableOnWAN)
}

// Subtract filters out all addrs in b from a
func Subtract(a, b []ma.Multiaddr) []ma.Multiaddr {
	return FilterAddrs(a, func(m ma.Multiaddr) bool {
		for _, bb := range b {
			if m.Equal(bb) {
				return false
			}
		}
		return true
	})
}

// CheckNATWarning checks if our observed addresses differ. if so,
// informs the user that certain things might not work yet
func CheckNATWarning(observed, expected ma.Multiaddr, listen []ma.Multiaddr) {
	if observed.Equal(expected) {
		return
	}

	if !AddrInList(observed, listen) { // probably a nat
		log.Warningf(natWarning, observed, listen)
	}
}

const natWarning = `Remote peer observed our address to be: %s
The local addresses are: %s
Thus, connection is going through NAT, and other connections may fail.

IPFS NAT traversal is still under development. Please bug us on github or irc to fix this.
Baby steps: http://jbenet.static.s3.amazonaws.com/271dfcf/baby-steps.gif
`
<|endoftext|>


<commit_before>// Package output contains reaction and visualization data related functionality
//
// Copyright 2015 Markus Dittrich
// Licensed under BSD license, see LICENSE file for details
package output

import (
	"encoding/binary"
	"fmt"
	"io"
	"math"
	"os"
	"path"

	"github.com/haskelladdict/mcellLite/mol"
)

// WriteCB writes the current molecule position to a CellBlender compatible
// input format
// NOTE: This function does a lot of type casting mostly due to the way
// CellBlender format was designed. E.g., while positions are internally stored
// as 64bit doubles, CellBlender format writes them as 32bit floats. Eventually,
// the CellBlender format should probably be overhauled or completele redesigned.
func WriteCB(molMap mol.MolMap, outPath, fileName string, iter int64) error {
	// if output dir does not exist we create it
	if err := os.MkdirAll(outPath, 0700); err != nil {
		return fmt.Errorf("in writeCB: %s", err)
	}

	p := fmt.Sprintf("%s.cellbin.%04d.dat", fileName, iter)
	p = path.Join(outPath, p)
	file, err := os.Create(p)
	if err != nil {
		return fmt.Errorf("in writeCB: %s", err)
	}
	defer file.Close()

	// write version info
	var version uint32 = 1
	if err := writeUint32(file, version); err != nil {
		return err
	}

	// write molecules
	for sp, mols := range molMap {
		// write species info
		if err := writeUint8(file, uint8(len(sp))); err != nil {
			return err
		}
		if n, err := file.Write([]byte(sp)); err != nil || len(sp) != n {
			return err
		}

		// write mol type (0 = volume molecule)
		if err := writeUint8(file, 0); err != nil {
			return err
		}

		// write number of molecules and then molecule info
		if err := writeUint32(file, 3*uint32(len(mols))); err != nil {
			return err
		}
		for _, mol := range mols {
			if err := writeUint32(file, math.Float32bits(float32(mol.R.X))); err != nil {
				return err
			}
			if err := writeUint32(file, math.Float32bits(float32(mol.R.Y))); err != nil {
				return err
			}
			if err := writeUint32(file, math.Float32bits(float32(mol.R.Z))); err != nil {
				return err
			}
		}
	}
	return nil
}

// writeUint8 write an uint8 to the provided Writer in little endian format
func writeUint8(w io.Writer, i uint8) error {
	buf := []byte{i}
	if n, err := w.Write(buf); err != nil || n != 1 {
		return fmt.Errorf("in WriteUint8: %s", err)
	}
	return nil
}

// writeUint16 write an uint16 to the provided Writer in little endian format
func writeUint16(w io.Writer, i uint16) error {
	buf := make([]byte, 2)
	binary.LittleEndian.PutUint16(buf, i)
	if n, err := w.Write(buf); err != nil || n != 2 {
		return fmt.Errorf("in WriteUint16: %s", err)
	}
	return nil
}

// writeUint32 write an uint32 to the provided Writer in little endian format
func writeUint32(w io.Writer, i uint32) error {
	buf := make([]byte, 4)
	binary.LittleEndian.PutUint32(buf, i)
	if n, err := w.Write(buf); err != nil || n != 4 {
		return fmt.Errorf("in WriteUint32: %s", err)
	}
	return nil
}

// writeUint64 write an uint64 to the provided Writer in little endian format
func writeUint64(w io.Writer, i uint64) error {
	buf := make([]byte, 8)
	binary.LittleEndian.PutUint64(buf, i)
	if n, err := w.Write(buf); err != nil || n != 8 {
		return fmt.Errorf("in WriteUint64: %s", err)
	}
	return nil
}
<commit_msg>Switch to buffered IO for CellBlender output.<commit_after>// Package output contains reaction and visualization data related functionality
//
// Copyright 2015 Markus Dittrich
// Licensed under BSD license, see LICENSE file for details
package output

import (
	"bufio"
	"encoding/binary"
	"fmt"
	"io"
	"math"
	"os"
	"path"

	"github.com/haskelladdict/mcellLite/mol"
)

// WriteCB writes the current molecule position to a CellBlender compatible
// input format
// NOTE: This function does a lot of type casting mostly due to the way
// CellBlender format was designed. E.g., while positions are internally stored
// as 64bit doubles, CellBlender format writes them as 32bit floats. Eventually,
// the CellBlender format should probably be overhauled or completele redesigned.
func WriteCB(molMap mol.MolMap, outPath, fileName string, iter int64) error {
	// if output dir does not exist we create it
	if err := os.MkdirAll(outPath, 0700); err != nil {
		return fmt.Errorf("in writeCB: %s", err)
	}

	p := fmt.Sprintf("%s.cellbin.%04d.dat", fileName, iter)
	p = path.Join(outPath, p)
	file, err := os.Create(p)
	if err != nil {
		return fmt.Errorf("in writeCB: %s", err)
	}
	defer file.Close()

	w := bufio.NewWriter(file)

	// write version info
	var version uint32 = 1
	if err := writeUint32(w, version); err != nil {
		return err
	}

	// write molecules
	for sp, mols := range molMap {
		// write species info
		if err := writeUint8(w, uint8(len(sp))); err != nil {
			return err
		}
		if n, err := w.Write([]byte(sp)); err != nil || len(sp) != n {
			return err
		}

		// write mol type (0 = volume molecule)
		if err := writeUint8(w, 0); err != nil {
			return err
		}

		// write number of molecules and then molecule info
		if err := writeUint32(w, 3*uint32(len(mols))); err != nil {
			return err
		}
		for _, mol := range mols {
			if err := writeUint32(w, math.Float32bits(float32(mol.R.X))); err != nil {
				return err
			}
			if err := writeUint32(w, math.Float32bits(float32(mol.R.Y))); err != nil {
				return err
			}
			if err := writeUint32(w, math.Float32bits(float32(mol.R.Z))); err != nil {
				return err
			}
		}
	}
	return nil
}

// writeUint8 write an uint8 to the provided Writer in little endian format
func writeUint8(w io.Writer, i uint8) error {
	buf := []byte{i}
	if n, err := w.Write(buf); err != nil || n != 1 {
		return fmt.Errorf("in WriteUint8: %s", err)
	}
	return nil
}

// writeUint16 write an uint16 to the provided Writer in little endian format
func writeUint16(w io.Writer, i uint16) error {
	buf := make([]byte, 2)
	binary.LittleEndian.PutUint16(buf, i)
	if n, err := w.Write(buf); err != nil || n != 2 {
		return fmt.Errorf("in WriteUint16: %s", err)
	}
	return nil
}

// writeUint32 write an uint32 to the provided Writer in little endian format
func writeUint32(w io.Writer, i uint32) error {
	buf := make([]byte, 4)
	binary.LittleEndian.PutUint32(buf, i)
	if n, err := w.Write(buf); err != nil || n != 4 {
		return fmt.Errorf("in WriteUint32: %s", err)
	}
	return nil
}

// writeUint64 write an uint64 to the provided Writer in little endian format
func writeUint64(w io.Writer, i uint64) error {
	buf := make([]byte, 8)
	binary.LittleEndian.PutUint64(buf, i)
	if n, err := w.Write(buf); err != nil || n != 8 {
		return fmt.Errorf("in WriteUint64: %s", err)
	}
	return nil
}
<|endoftext|>


<commit_before>package rpc

import (
	"io"
	"log"
	"math/rand"
	"net/rpc"

	"github.com/hashicorp/packer/packer"
)

// An implementation of packer.Ui where the Ui is actually executed
// over an RPC connection.
type Ui struct {
	client   *rpc.Client
	endpoint string
}

var _ packer.Ui = new(Ui)

// UiServer wraps a packer.Ui implementation and makes it exportable
// as part of a Golang RPC server.
type UiServer struct {
	ui       packer.Ui
	register func(name string, rcvr interface{}) error
}

// The arguments sent to Ui.Machine
type UiMachineArgs struct {
	Category string
	Args     []string
}

func (u *Ui) Ask(query string) (result string, err error) {
	err = u.client.Call("Ui.Ask", query, &result)
	return
}

func (u *Ui) Error(message string) {
	if err := u.client.Call("Ui.Error", message, new(interface{})); err != nil {
		log.Printf("Error in Ui RPC call: %s", err)
	}
}

func (u *Ui) Machine(t string, args ...string) {
	rpcArgs := &UiMachineArgs{
		Category: t,
		Args:     args,
	}

	if err := u.client.Call("Ui.Machine", rpcArgs, new(interface{})); err != nil {
		log.Printf("Error in Ui RPC call: %s", err)
	}
}

func (u *Ui) Message(message string) {
	if err := u.client.Call("Ui.Message", message, new(interface{})); err != nil {
		log.Printf("Error in Ui RPC call: %s", err)
	}
}

func (u *Ui) Say(message string) {
	if err := u.client.Call("Ui.Say", message, new(interface{})); err != nil {
		log.Printf("Error in Ui RPC call: %s", err)
	}
}

func (u *Ui) ProgressBar() packer.ProgressBar {
	var callMeMaybe string
	if err := u.client.Call("Ui.ProgressBar", nil, &callMeMaybe); err != nil {
		log.Printf("Error in Ui RPC call: %s", err)
		return new(packer.NoopProgressBar)
	}

	return &RemoteProgressBarClient{
		id:     callMeMaybe,
		client: u.client,
	}
}

type RemoteProgressBarClient struct {
	id     string // TODO(azr): don't need an id any more since bar is a singleton
	client *rpc.Client
}

var _ packer.ProgressBar = new(RemoteProgressBarClient)

func (pb *RemoteProgressBarClient) Start(total uint64) {
	pb.client.Call(pb.id+".Start", total, new(interface{}))
}

func (pb *RemoteProgressBarClient) Add(current uint64) {
	pb.client.Call(pb.id+".Add", current, new(interface{}))
}

func (pb *RemoteProgressBarClient) Finish() {
	pb.client.Call(pb.id+".Finish", nil, new(interface{}))
}

func (pb *RemoteProgressBarClient) NewProxyReader(r io.Reader) io.Reader {
	return &packer.ProxyReader{Reader: r, ProgressBar: pb}
}

func (u *UiServer) Ask(query string, reply *string) (err error) {
	*reply, err = u.ui.Ask(query)
	return
}

func (u *UiServer) Error(message *string, reply *interface{}) error {
	u.ui.Error(*message)

	*reply = nil
	return nil
}

func (u *UiServer) Machine(args *UiMachineArgs, reply *interface{}) error {
	u.ui.Machine(args.Category, args.Args...)

	*reply = nil
	return nil
}

func (u *UiServer) Message(message *string, reply *interface{}) error {
	u.ui.Message(*message)
	*reply = nil
	return nil
}

func (u *UiServer) Say(message *string, reply *interface{}) error {
	u.ui.Say(*message)

	*reply = nil
	return nil
}

func RandStringBytes(n int) string { // TODO(azr): remove before merging
	const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

	b := make([]byte, n)
	for i := range b {
		b[i] = letterBytes[rand.Intn(len(letterBytes))]
	}
	return string(b)
}

func (u *UiServer) ProgressBar(_ *string, reply *interface{}) error {
	bar := u.ui.ProgressBar()

	callbackName := RandStringBytes(6)

	log.Printf("registering progressbar %s", callbackName)
	err := u.register(callbackName, &RemoteProgressBarServer{bar})
	if err != nil {
		log.Printf("failed to register a new progress bar rpc server, %s", err)
		return err
	}
	*reply = callbackName
	return nil
}

type RemoteProgressBarServer struct {
	pb packer.ProgressBar
}

func (pb *RemoteProgressBarServer) Finish(_ string, _ *interface{}) error {
	pb.pb.Finish()
	return nil
}

func (pb *RemoteProgressBarServer) Start(total uint64, _ *interface{}) error {
	pb.pb.Start(total)
	return nil
}

func (pb *RemoteProgressBarServer) Add(current uint64, _ *interface{}) error {
	pb.pb.Add(current)
	return nil
}
<commit_msg>use freshly merged random.AlphaNum instead of our own random<commit_after>package rpc

import (
	"io"
	"log"
	"net/rpc"

	"github.com/hashicorp/packer/common/random"

	"github.com/hashicorp/packer/packer"
)

// An implementation of packer.Ui where the Ui is actually executed
// over an RPC connection.
type Ui struct {
	client   *rpc.Client
	endpoint string
}

var _ packer.Ui = new(Ui)

// UiServer wraps a packer.Ui implementation and makes it exportable
// as part of a Golang RPC server.
type UiServer struct {
	ui       packer.Ui
	register func(name string, rcvr interface{}) error
}

// The arguments sent to Ui.Machine
type UiMachineArgs struct {
	Category string
	Args     []string
}

func (u *Ui) Ask(query string) (result string, err error) {
	err = u.client.Call("Ui.Ask", query, &result)
	return
}

func (u *Ui) Error(message string) {
	if err := u.client.Call("Ui.Error", message, new(interface{})); err != nil {
		log.Printf("Error in Ui RPC call: %s", err)
	}
}

func (u *Ui) Machine(t string, args ...string) {
	rpcArgs := &UiMachineArgs{
		Category: t,
		Args:     args,
	}

	if err := u.client.Call("Ui.Machine", rpcArgs, new(interface{})); err != nil {
		log.Printf("Error in Ui RPC call: %s", err)
	}
}

func (u *Ui) Message(message string) {
	if err := u.client.Call("Ui.Message", message, new(interface{})); err != nil {
		log.Printf("Error in Ui RPC call: %s", err)
	}
}

func (u *Ui) Say(message string) {
	if err := u.client.Call("Ui.Say", message, new(interface{})); err != nil {
		log.Printf("Error in Ui RPC call: %s", err)
	}
}

func (u *Ui) ProgressBar() packer.ProgressBar {
	var callMeMaybe string
	if err := u.client.Call("Ui.ProgressBar", nil, &callMeMaybe); err != nil {
		log.Printf("Error in Ui RPC call: %s", err)
		return new(packer.NoopProgressBar)
	}

	return &RemoteProgressBarClient{
		id:     callMeMaybe,
		client: u.client,
	}
}

type RemoteProgressBarClient struct {
	id     string // TODO(azr): don't need an id any more since bar is a singleton
	client *rpc.Client
}

var _ packer.ProgressBar = new(RemoteProgressBarClient)

func (pb *RemoteProgressBarClient) Start(total uint64) {
	pb.client.Call(pb.id+".Start", total, new(interface{}))
}

func (pb *RemoteProgressBarClient) Add(current uint64) {
	pb.client.Call(pb.id+".Add", current, new(interface{}))
}

func (pb *RemoteProgressBarClient) Finish() {
	pb.client.Call(pb.id+".Finish", nil, new(interface{}))
}

func (pb *RemoteProgressBarClient) NewProxyReader(r io.Reader) io.Reader {
	return &packer.ProxyReader{Reader: r, ProgressBar: pb}
}

func (u *UiServer) Ask(query string, reply *string) (err error) {
	*reply, err = u.ui.Ask(query)
	return
}

func (u *UiServer) Error(message *string, reply *interface{}) error {
	u.ui.Error(*message)

	*reply = nil
	return nil
}

func (u *UiServer) Machine(args *UiMachineArgs, reply *interface{}) error {
	u.ui.Machine(args.Category, args.Args...)

	*reply = nil
	return nil
}

func (u *UiServer) Message(message *string, reply *interface{}) error {
	u.ui.Message(*message)
	*reply = nil
	return nil
}

func (u *UiServer) Say(message *string, reply *interface{}) error {
	u.ui.Say(*message)

	*reply = nil
	return nil
}

func (u *UiServer) ProgressBar(_ *string, reply *interface{}) error {
	bar := u.ui.ProgressBar()

	callbackName := random.AlphaNum(6)

	log.Printf("registering progressbar %s", callbackName)
	err := u.register(callbackName, &RemoteProgressBarServer{bar})
	if err != nil {
		log.Printf("failed to register a new progress bar rpc server, %s", err)
		return err
	}
	*reply = callbackName
	return nil
}

type RemoteProgressBarServer struct {
	pb packer.ProgressBar
}

func (pb *RemoteProgressBarServer) Finish(_ string, _ *interface{}) error {
	pb.pb.Finish()
	return nil
}

func (pb *RemoteProgressBarServer) Start(total uint64, _ *interface{}) error {
	pb.pb.Start(total)
	return nil
}

func (pb *RemoteProgressBarServer) Add(current uint64, _ *interface{}) error {
	pb.pb.Add(current)
	return nil
}
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 toolchain

import (
	"bytes"
	"io/ioutil"
	"path/filepath"
	"strings"
	"time"

	"mynewt.apache.org/newt/util"
)

type DepTracker struct {
	// Most recent .o modification time.
	MostRecent time.Time

	compiler *Compiler
}

func NewDepTracker(c *Compiler) DepTracker {
	tracker := DepTracker{
		MostRecent: time.Unix(0, 0),
		compiler:   c,
	}

	return tracker
}

// @return string               The name of the dependent file (i.e., the first
//                                  .o file encountered).
// @return []string             Populated with the dependencies' filenames.
func parseDepsLine(line string) (string, []string, error) {
	tokens := strings.Fields(line)
	if len(tokens) == 0 {
		return "", nil, nil
	}

	dFileTok := tokens[0]
	if dFileTok[len(dFileTok)-1:] != ":" {
		return "", nil, util.NewNewtError("Invalid Makefile dependency file; " +
			"line missing ':'")
	}

	dFileName := dFileTok[:len(dFileTok)-1]
	return dFileName, tokens[1:], nil

}

// Parses a dependency (.d) file generated by gcc.  On success, the returned
// string array is populated with the dependency filenames.  This function
// expects each line of a dependency file to have the following format:
//
// <file>.o: <file>.c a.h b.h c.h \
//  d.h e.h f.h
//
// Only the first dependent object(<file>.o) is considered.
//
// @return []string             Populated with the dependencies' filenames.
func ParseDepsFile(filename string) ([]string, error) {
	lines, err := util.ReadLines(filename)
	if err != nil {
		return nil, err
	}

	if len(lines) == 0 {
		return []string{}, nil
	}

	var dFile string
	allDeps := []string{}
	for _, line := range lines {
		src, deps, err := parseDepsLine(line)
		if err != nil {
			return nil, err
		}

		if dFile == "" {
			dFile = src
		}

		if src == dFile {
			allDeps = append(allDeps, deps...)
		}
	}

	return allDeps, nil
}

// Updates the dependency tracker's most recent timestamp according to the
// modification time of the specified file.  If the specified file is older
// than the tracker's currently most-recent time, this function has no effect.
func (tracker *DepTracker) ProcessFileTime(file string) error {
	modTime, err := util.FileModificationTime(file)
	if err != nil {
		return err
	}

	if modTime.After(tracker.MostRecent) {
		tracker.MostRecent = modTime
	}

	return nil
}

// Determines if a file was previously built with a command line invocation
// different from the one specified.
//
// @param dstFile               The output file whose build invocation is being
//                                  tested.
// @param cmd                   The command that would be used to generate the
//                                  specified destination file.
//
// @return                      true if the command has changed or if the
//                                  destination file was never built;
//                              false otherwise.
func commandHasChanged(dstFile string, cmd string) bool {
	cmdFile := dstFile + ".cmd"
	prevCmd, err := ioutil.ReadFile(cmdFile)
	if err != nil {
		return true
	}

	return bytes.Compare(prevCmd, []byte(cmd)) != 0
}

// Determines if the specified C or assembly file needs to be built.  A compile
// is required if any of the following is true:
//     * The destination object file does not exist.
//     * The existing object file was built with a different compiler
//       invocation.
//     * The source file has a newer modification time than the object file.
//     * One or more included header files has a newer modification time than
//       the object file.
func (tracker *DepTracker) CompileRequired(srcFile string,
	compilerType int) (bool, error) {

	objFile := tracker.compiler.DstDir() + "/" +
		strings.TrimSuffix(srcFile, filepath.Ext(srcFile)) + ".o"
	depFile := tracker.compiler.DstDir() + "/" +
		strings.TrimSuffix(srcFile, filepath.Ext(srcFile)) + ".d"

	// If the object was previously built with a different set of options, a
	// rebuild is necessary.
	cmd, err := tracker.compiler.CompileFileCmd(srcFile, compilerType)
	if err != nil {
		return false, err
	}
	if commandHasChanged(objFile, cmd) {
		util.StatusMessage(util.VERBOSITY_VERBOSE, "%s - rebuild required; "+
			"different command\n", srcFile)
		err := tracker.compiler.GenDepsForFile(srcFile)
		if err != nil {
			return false, err
		}
		return true, nil
	}

	srcModTime, err := util.FileModificationTime(srcFile)
	if err != nil {
		return false, err
	}

	objModTime, err := util.FileModificationTime(objFile)
	if err != nil {
		return false, err
	}

	// If the object doesn't exist or is older than the source file, a build is
	// required; no need to check dependencies.
	if srcModTime.After(objModTime) {
		util.StatusMessage(util.VERBOSITY_VERBOSE, "%s - rebuild required; "+
			"source newer than obj\n", srcFile)
		return true, nil
	}

	// Determine if the dependency (.d) file needs to be generated.  If it
	// doesn't exist or is older than the source file, it is out of date and
	// needs to be created.
	depModTime, err := util.FileModificationTime(depFile)
	if err != nil {
		return false, err
	}

	if srcModTime.After(depModTime) {
		err := tracker.compiler.GenDepsForFile(srcFile)
		if err != nil {
			return false, err
		}
	}

	// Extract the dependency filenames from the dependency file.
	deps, err := ParseDepsFile(depFile)
	if err != nil {
		return false, err
	}

	// Check if any dependencies are newer than the destination object file.
	for _, dep := range deps {
		if util.NodeNotExist(dep) {
			// The dependency has been deleted; the .d file is out of date.
			// Recreate the dependency file and repeat this entire function.
			err := tracker.compiler.GenDepsForFile(srcFile)
			if err != nil {
				return false, err
			}

			return tracker.CompileRequired(srcFile, compilerType)
		} else {
			depModTime, err = util.FileModificationTime(dep)
			if err != nil {
				return false, err
			}
		}

		if depModTime.After(objModTime) {
			util.StatusMessage(util.VERBOSITY_VERBOSE, "%s - rebuild required; obj older than dependency (%s)\n", srcFile, dep)
			return true, nil
		}
	}

	return false, nil
}

// Determines if the specified static library needs to be rearchived.  The
// library needs to be archived if any of the following is true:
//     * The destination library file does not exist.
//     * The existing library file was built with a different compiler
//       invocation.
//     * One or more source object files has a newer modification time than the
//       library file.
func (tracker *DepTracker) ArchiveRequired(archiveFile string,
	objFiles []string) (bool, error) {

	// If the archive was previously built with a different set of options, a
	// rebuild is required.
	cmd := tracker.compiler.CompileArchiveCmd(archiveFile, objFiles)
	if commandHasChanged(archiveFile, cmd) {
		return true, nil
	}

	// If the archive doesn't exist or is older than any object file, a rebuild
	// is required.
	aModTime, err := util.FileModificationTime(archiveFile)
	if err != nil {
		return false, err
	}
	if tracker.MostRecent.After(aModTime) {
		return true, nil
	}

	// The library is up to date.
	return false, nil
}

// Determines if the specified elf file needs to be linked.  Linking is
// necessary if the elf file does not exist or has an older modification time
// than any source object or library file.
// Determines if the specified static library needs to be rearchived.  The
// library needs to be archived if any of the following is true:
//     * The destination library file does not exist.
//     * The existing library file was built with a different compiler
//       invocation.
//     * One or more source object files has a newer modification time than the
//       library file.
func (tracker *DepTracker) LinkRequired(dstFile string,
	options map[string]bool, objFiles []string) (bool, error) {

	// If the elf file was previously built with a different set of options, a
	// rebuild is required.
	cmd := tracker.compiler.CompileBinaryCmd(dstFile, options, objFiles)
	if commandHasChanged(dstFile, cmd) {
		return true, nil
	}

	// If the elf file doesn't exist or is older than any input file, a rebuild
	// is required.
	dstModTime, err := util.FileModificationTime(dstFile)
	if err != nil {
		return false, err
	}

	// Check timestamp of each .o file in the project.
	if tracker.MostRecent.After(dstModTime) {
		return true, nil
	}

	// Check timestamp of the linker script and all input libraries.
	if tracker.compiler.LinkerScript != "" {
		objFiles = append(objFiles, tracker.compiler.LinkerScript)
	}
	for _, obj := range objFiles {
		objModTime, err := util.FileModificationTime(obj)
		if err != nil {
			return false, err
		}

		if objModTime.After(dstModTime) {
			return true, nil
		}
	}

	return false, nil
}
<commit_msg>newt tool - Fix deadlock due to deleted header<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 toolchain

import (
	"bytes"
	"io/ioutil"
	"path/filepath"
	"strings"
	"time"

	"mynewt.apache.org/newt/util"
)

type DepTracker struct {
	// Most recent .o modification time.
	MostRecent time.Time

	compiler *Compiler
}

func NewDepTracker(c *Compiler) DepTracker {
	tracker := DepTracker{
		MostRecent: time.Unix(0, 0),
		compiler:   c,
	}

	return tracker
}

// @return string               The name of the dependent file (i.e., the first
//                                  .o file encountered).
// @return []string             Populated with the dependencies' filenames.
func parseDepsLine(line string) (string, []string, error) {
	tokens := strings.Fields(line)
	if len(tokens) == 0 {
		return "", nil, nil
	}

	dFileTok := tokens[0]
	if dFileTok[len(dFileTok)-1:] != ":" {
		return "", nil, util.NewNewtError("Invalid Makefile dependency file; " +
			"line missing ':'")
	}

	dFileName := dFileTok[:len(dFileTok)-1]
	return dFileName, tokens[1:], nil

}

// Parses a dependency (.d) file generated by gcc.  On success, the returned
// string array is populated with the dependency filenames.  This function
// expects each line of a dependency file to have the following format:
//
// <file>.o: <file>.c a.h b.h c.h \
//  d.h e.h f.h
//
// Only the first dependent object(<file>.o) is considered.
//
// @return []string             Populated with the dependencies' filenames.
func ParseDepsFile(filename string) ([]string, error) {
	lines, err := util.ReadLines(filename)
	if err != nil {
		return nil, err
	}

	if len(lines) == 0 {
		return []string{}, nil
	}

	var dFile string
	allDeps := []string{}
	for _, line := range lines {
		src, deps, err := parseDepsLine(line)
		if err != nil {
			return nil, err
		}

		if dFile == "" {
			dFile = src
		}

		if src == dFile {
			allDeps = append(allDeps, deps...)
		}
	}

	return allDeps, nil
}

// Updates the dependency tracker's most recent timestamp according to the
// modification time of the specified file.  If the specified file is older
// than the tracker's currently most-recent time, this function has no effect.
func (tracker *DepTracker) ProcessFileTime(file string) error {
	modTime, err := util.FileModificationTime(file)
	if err != nil {
		return err
	}

	if modTime.After(tracker.MostRecent) {
		tracker.MostRecent = modTime
	}

	return nil
}

// Determines if a file was previously built with a command line invocation
// different from the one specified.
//
// @param dstFile               The output file whose build invocation is being
//                                  tested.
// @param cmd                   The command that would be used to generate the
//                                  specified destination file.
//
// @return                      true if the command has changed or if the
//                                  destination file was never built;
//                              false otherwise.
func commandHasChanged(dstFile string, cmd string) bool {
	cmdFile := dstFile + ".cmd"
	prevCmd, err := ioutil.ReadFile(cmdFile)
	if err != nil {
		return true
	}

	return bytes.Compare(prevCmd, []byte(cmd)) != 0
}

// Determines if the specified C or assembly file needs to be built.  A compile
// is required if any of the following is true:
//     * The destination object file does not exist.
//     * The existing object file was built with a different compiler
//       invocation.
//     * The source file has a newer modification time than the object file.
//     * One or more included header files has a newer modification time than
//       the object file.
func (tracker *DepTracker) CompileRequired(srcFile string,
	compilerType int) (bool, error) {

	objFile := tracker.compiler.DstDir() + "/" +
		strings.TrimSuffix(srcFile, filepath.Ext(srcFile)) + ".o"
	depFile := tracker.compiler.DstDir() + "/" +
		strings.TrimSuffix(srcFile, filepath.Ext(srcFile)) + ".d"

	// If the object was previously built with a different set of options, a
	// rebuild is necessary.
	cmd, err := tracker.compiler.CompileFileCmd(srcFile, compilerType)
	if err != nil {
		return false, err
	}
	if commandHasChanged(objFile, cmd) {
		util.StatusMessage(util.VERBOSITY_VERBOSE, "%s - rebuild required; "+
			"different command\n", srcFile)
		err := tracker.compiler.GenDepsForFile(srcFile)
		if err != nil {
			return false, err
		}
		return true, nil
	}

	srcModTime, err := util.FileModificationTime(srcFile)
	if err != nil {
		return false, err
	}

	objModTime, err := util.FileModificationTime(objFile)
	if err != nil {
		return false, err
	}

	// If the object doesn't exist or is older than the source file, a build is
	// required; no need to check dependencies.
	if srcModTime.After(objModTime) {
		util.StatusMessage(util.VERBOSITY_VERBOSE, "%s - rebuild required; "+
			"source newer than obj\n", srcFile)
		return true, nil
	}

	// Determine if the dependency (.d) file needs to be generated.  If it
	// doesn't exist or is older than the source file, it is out of date and
	// needs to be created.
	depModTime, err := util.FileModificationTime(depFile)
	if err != nil {
		return false, err
	}

	if srcModTime.After(depModTime) {
		err := tracker.compiler.GenDepsForFile(srcFile)
		if err != nil {
			return false, err
		}
	}

	// Extract the dependency filenames from the dependency file.
	deps, err := ParseDepsFile(depFile)
	if err != nil {
		return false, err
	}

	// Check if any dependencies are newer than the destination object file.
	for _, dep := range deps {
		if util.NodeNotExist(dep) {
			// The dependency has been deleted; a rebuild is required.
			return true, nil
		} else {
			depModTime, err = util.FileModificationTime(dep)
			if err != nil {
				return false, err
			}
		}

		if depModTime.After(objModTime) {
			util.StatusMessage(util.VERBOSITY_VERBOSE, "%s - rebuild required; obj older than dependency (%s)\n", srcFile, dep)
			return true, nil
		}
	}

	return false, nil
}

// Determines if the specified static library needs to be rearchived.  The
// library needs to be archived if any of the following is true:
//     * The destination library file does not exist.
//     * The existing library file was built with a different compiler
//       invocation.
//     * One or more source object files has a newer modification time than the
//       library file.
func (tracker *DepTracker) ArchiveRequired(archiveFile string,
	objFiles []string) (bool, error) {

	// If the archive was previously built with a different set of options, a
	// rebuild is required.
	cmd := tracker.compiler.CompileArchiveCmd(archiveFile, objFiles)
	if commandHasChanged(archiveFile, cmd) {
		return true, nil
	}

	// If the archive doesn't exist or is older than any object file, a rebuild
	// is required.
	aModTime, err := util.FileModificationTime(archiveFile)
	if err != nil {
		return false, err
	}
	if tracker.MostRecent.After(aModTime) {
		return true, nil
	}

	// The library is up to date.
	return false, nil
}

// Determines if the specified elf file needs to be linked.  Linking is
// necessary if the elf file does not exist or has an older modification time
// than any source object or library file.
// Determines if the specified static library needs to be rearchived.  The
// library needs to be archived if any of the following is true:
//     * The destination library file does not exist.
//     * The existing library file was built with a different compiler
//       invocation.
//     * One or more source object files has a newer modification time than the
//       library file.
func (tracker *DepTracker) LinkRequired(dstFile string,
	options map[string]bool, objFiles []string) (bool, error) {

	// If the elf file was previously built with a different set of options, a
	// rebuild is required.
	cmd := tracker.compiler.CompileBinaryCmd(dstFile, options, objFiles)
	if commandHasChanged(dstFile, cmd) {
		return true, nil
	}

	// If the elf file doesn't exist or is older than any input file, a rebuild
	// is required.
	dstModTime, err := util.FileModificationTime(dstFile)
	if err != nil {
		return false, err
	}

	// Check timestamp of each .o file in the project.
	if tracker.MostRecent.After(dstModTime) {
		return true, nil
	}

	// Check timestamp of the linker script and all input libraries.
	if tracker.compiler.LinkerScript != "" {
		objFiles = append(objFiles, tracker.compiler.LinkerScript)
	}
	for _, obj := range objFiles {
		objModTime, err := util.FileModificationTime(obj)
		if err != nil {
			return false, err
		}

		if objModTime.After(dstModTime) {
			return true, nil
		}
	}

	return false, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package monitoring

import (
	"fmt"
	"io/ioutil"

	"code.google.com/p/goauth2/oauth/jwt"
	"google.golang.org/api/cloudmonitoring/v2beta2"
)

const (
	customMetricPrefix = "custom.cloudmonitoring.googleapis.com"
)

// CustomMetricDescriptors is a map from metric's short names to their
// MetricDescriptor definitions.
var CustomMetricDescriptors = map[string]*cloudmonitoring.MetricDescriptor{
	// Custom metric for recording check latency of vanadium production services.
	"service-latency": createMetric("service/latency", "The check latency (ms) of vanadium production services.", "double"),

	// Custom metric for recording various counters of vanadium production services.
	"service-counters": createMetric("service/counters", "Various counters of vanadium production services.", "double"),

	// Custom metric for recording gce instance stats.
	"gce-instance": createMetric("gce-instance/stats", "Various stats for GCE instances.", "double"),

	// Custom metric for recording nginx stats.
	"nginx": createMetric("nginx/stats", "Various stats for Nginx server.", "double"),
}

func createMetric(metricType, description, valueType string) *cloudmonitoring.MetricDescriptor {
	return &cloudmonitoring.MetricDescriptor{
		Name:        fmt.Sprintf("%s/v/%s", customMetricPrefix, metricType),
		Description: description,
		TypeDescriptor: &cloudmonitoring.MetricDescriptorTypeDescriptor{
			MetricType: "gauge",
			ValueType:  valueType,
		},
		Labels: []*cloudmonitoring.MetricDescriptorLabelDescriptor{
			&cloudmonitoring.MetricDescriptorLabelDescriptor{
				Key:         fmt.Sprintf("%s/gce-instance", customMetricPrefix),
				Description: "The name of the GCE instance associated with this metric.",
			},
			&cloudmonitoring.MetricDescriptorLabelDescriptor{
				Key:         fmt.Sprintf("%s/gce-zone", customMetricPrefix),
				Description: "The zone of the GCE instance associated with this metric.",
			},
			&cloudmonitoring.MetricDescriptorLabelDescriptor{
				Key:         fmt.Sprintf("%s/metric-name", customMetricPrefix),
				Description: "The name of the metric.",
			},
		},
	}
}

// Authenticate authenticates the given service account's email with the given
// key. If successful, it returns a service object that can be used in GCM API
// calls.
func Authenticate(serviceAccountEmail, keyFilePath string) (*cloudmonitoring.Service, error) {
	bytes, err := ioutil.ReadFile(keyFilePath)
	if err != nil {
		return nil, fmt.Errorf("ReadFile(%s) failed: %v", keyFilePath, err)
	}

	token := jwt.NewToken(serviceAccountEmail, cloudmonitoring.MonitoringScope, bytes)
	transport, err := jwt.NewTransport(token)
	if err != nil {
		return nil, fmt.Errorf("NewTransport() failed: %v", err)
	}
	c := transport.Client()
	s, err := cloudmonitoring.New(c)
	if err != nil {
		return nil, fmt.Errorf("New() failed: %v", err)
	}
	return s, nil
}
<commit_msg>devtools/vmon: send rpc-load results to GCM.<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package monitoring

import (
	"fmt"
	"io/ioutil"

	"code.google.com/p/goauth2/oauth/jwt"
	"google.golang.org/api/cloudmonitoring/v2beta2"
)

const (
	customMetricPrefix = "custom.cloudmonitoring.googleapis.com"
)

// CustomMetricDescriptors is a map from metric's short names to their
// MetricDescriptor definitions.
var CustomMetricDescriptors = map[string]*cloudmonitoring.MetricDescriptor{
	// Custom metric for recording check latency of vanadium production services.
	"service-latency": createMetric("service/latency", "The check latency (ms) of vanadium production services.", "double", true),

	// Custom metric for recording various counters of vanadium production services.
	"service-counters": createMetric("service/counters", "Various counters of vanadium production services.", "double", true),

	// Custom metric for recording gce instance stats.
	"gce-instance": createMetric("gce-instance/stats", "Various stats for GCE instances.", "double", true),

	// Custom metric for recording nginx stats.
	"nginx": createMetric("nginx/stats", "Various stats for Nginx server.", "double", true),

	// Custom metric for rpc load tests.
	"rpc-load-test": createMetric("rpc-load-test", "Results of rpc load test", "double", false),
}

func createMetric(metricType, description, valueType string, includeGCELabels bool) *cloudmonitoring.MetricDescriptor {
	labels := []*cloudmonitoring.MetricDescriptorLabelDescriptor{
		&cloudmonitoring.MetricDescriptorLabelDescriptor{
			Key:         fmt.Sprintf("%s/metric-name", customMetricPrefix),
			Description: "The name of the metric.",
		},
	}
	if includeGCELabels {
		labels = append(labels, &cloudmonitoring.MetricDescriptorLabelDescriptor{
			Key:         fmt.Sprintf("%s/gce-instance", customMetricPrefix),
			Description: "The name of the GCE instance associated with this metric.",
		}, &cloudmonitoring.MetricDescriptorLabelDescriptor{
			Key:         fmt.Sprintf("%s/gce-zone", customMetricPrefix),
			Description: "The zone of the GCE instance associated with this metric.",
		})
	}

	return &cloudmonitoring.MetricDescriptor{
		Name:        fmt.Sprintf("%s/v/%s", customMetricPrefix, metricType),
		Description: description,
		TypeDescriptor: &cloudmonitoring.MetricDescriptorTypeDescriptor{
			MetricType: "gauge",
			ValueType:  valueType,
		},
		Labels: labels,
	}
}

// Authenticate authenticates the given service account's email with the given
// key. If successful, it returns a service object that can be used in GCM API
// calls.
func Authenticate(serviceAccountEmail, keyFilePath string) (*cloudmonitoring.Service, error) {
	bytes, err := ioutil.ReadFile(keyFilePath)
	if err != nil {
		return nil, fmt.Errorf("ReadFile(%s) failed: %v", keyFilePath, err)
	}

	token := jwt.NewToken(serviceAccountEmail, cloudmonitoring.MonitoringScope, bytes)
	transport, err := jwt.NewTransport(token)
	if err != nil {
		return nil, fmt.Errorf("NewTransport() failed: %v", err)
	}
	c := transport.Client()
	s, err := cloudmonitoring.New(c)
	if err != nil {
		return nil, fmt.Errorf("New() failed: %v", err)
	}
	return s, nil
}
<|endoftext|>


<commit_before>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package event provides support for event based telemetry.
package event

import (
	"fmt"
	"time"
)

type eventType uint8

const (
	LogType = eventType(iota)
	StartSpanType
	EndSpanType
	LabelType
	DetachType
	RecordType
)

type Event struct {
	typ     eventType
	At      time.Time
	Message string
	Error   error

	tags []Tag
}

func (e Event) IsLog() bool       { return e.typ == LogType }
func (e Event) IsEndSpan() bool   { return e.typ == EndSpanType }
func (e Event) IsStartSpan() bool { return e.typ == StartSpanType }
func (e Event) IsLabel() bool     { return e.typ == LabelType }
func (e Event) IsDetach() bool    { return e.typ == DetachType }
func (e Event) IsRecord() bool    { return e.typ == RecordType }

func (e Event) Format(f fmt.State, r rune) {
	if !e.At.IsZero() {
		fmt.Fprint(f, e.At.Format("2006/01/02 15:04:05 "))
	}
	fmt.Fprint(f, e.Message)
	if e.Error != nil {
		if f.Flag('+') {
			fmt.Fprintf(f, ": %+v", e.Error)
		} else {
			fmt.Fprintf(f, ": %v", e.Error)
		}
	}
	for it := e.Tags(); it.Valid(); it.Advance() {
		tag := it.Tag()
		fmt.Fprintf(f, "\n\t%s = %v", tag.Key.Name(), tag.Value)
	}
}

func (ev Event) Tags() TagIterator {
	if len(ev.tags) == 0 {
		return TagIterator{}
	}
	return NewTagIterator(ev.tags...)
}

func (ev Event) Map() TagMap {
	return NewTagMap(ev.tags...)
}
<commit_msg>internal/telemetry: normalize the event reciever names to all use ev<commit_after>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package event provides support for event based telemetry.
package event

import (
	"fmt"
	"time"
)

type eventType uint8

const (
	LogType = eventType(iota)
	StartSpanType
	EndSpanType
	LabelType
	DetachType
	RecordType
)

type Event struct {
	typ     eventType
	At      time.Time
	Message string
	Error   error

	tags []Tag
}

func (ev Event) IsLog() bool       { return ev.typ == LogType }
func (ev Event) IsEndSpan() bool   { return ev.typ == EndSpanType }
func (ev Event) IsStartSpan() bool { return ev.typ == StartSpanType }
func (ev Event) IsLabel() bool     { return ev.typ == LabelType }
func (ev Event) IsDetach() bool    { return ev.typ == DetachType }
func (ev Event) IsRecord() bool    { return ev.typ == RecordType }

func (ev Event) Format(f fmt.State, r rune) {
	if !ev.At.IsZero() {
		fmt.Fprint(f, ev.At.Format("2006/01/02 15:04:05 "))
	}
	fmt.Fprint(f, ev.Message)
	if ev.Error != nil {
		if f.Flag('+') {
			fmt.Fprintf(f, ": %+v", ev.Error)
		} else {
			fmt.Fprintf(f, ": %v", ev.Error)
		}
	}
	for it := ev.Tags(); it.Valid(); it.Advance() {
		tag := it.Tag()
		fmt.Fprintf(f, "\n\t%s = %v", tag.Key.Name(), tag.Value)
	}
}

func (ev Event) Tags() TagIterator {
	if len(ev.tags) == 0 {
		return TagIterator{}
	}
	return NewTagIterator(ev.tags...)
}

func (ev Event) Map() TagMap {
	return NewTagMap(ev.tags...)
}
<|endoftext|>


<commit_before>package connections

import (
	"errors"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	"github.com/pquerna/ffjson/ffjson"
	"github.com/sirupsen/logrus"

	"github.com/ivan1993spb/snake-server/broadcast"
	"github.com/ivan1993spb/snake-server/game"
)

const (
	chanBroadcastBuffer  = 128
	chanGameEventsBuffer = 8192
	chanBytesProxyBuffer = 8192
	chanBytesOutBuffer   = 8192

	chanEncodeGroupMessageBuffer = 8192

	sendPreparedMessageTimeout = time.Millisecond * 50

	broadcastOutputMessageBufferMonitoringDelay = time.Second * 10
	gameOutputMessageBufferMonitoringDelay      = time.Second * 10
	encodeGroupMessageBufferMonitoringDelay     = time.Second * 10
	preparedMessageBufferMonitoringDelay        = time.Second * 10
)

type ConnectionGroup struct {
	limit      int
	counter    int
	counterMux *sync.RWMutex

	logger logrus.FieldLogger

	game      *game.Game
	broadcast *broadcast.GroupBroadcast

	chs    []chan *websocket.PreparedMessage
	chsMux *sync.RWMutex

	stop    chan struct{}
	stopper *sync.Once
}

type errCreateConnectionGroup string

func (e errCreateConnectionGroup) Error() string {
	return "cannot create connection group: " + string(e)
}

func NewConnectionGroup(logger logrus.FieldLogger, connectionLimit int, width, height uint8) (*ConnectionGroup, error) {
	g, err := game.NewGame(logger, width, height)
	if err != nil {
		return nil, errCreateConnectionGroup(err.Error())
	}

	if connectionLimit > 0 {
		return &ConnectionGroup{
			limit:      connectionLimit,
			counterMux: &sync.RWMutex{},
			game:       g,
			broadcast:  broadcast.NewGroupBroadcast(),
			logger:     logger,
			chs:        make([]chan *websocket.PreparedMessage, 0),
			chsMux:     &sync.RWMutex{},
			stop:       make(chan struct{}),
			stopper:    &sync.Once{},
		}, nil
	}

	return nil, errCreateConnectionGroup("invalid connection limit")
}

func (cg *ConnectionGroup) GetLimit() int {
	cg.counterMux.RLock()
	defer cg.counterMux.RUnlock()
	return cg.limit
}

func (cg *ConnectionGroup) SetLimit(limit int) {
	cg.counterMux.Lock()
	cg.limit = limit
	cg.counterMux.Unlock()
}

func (cg *ConnectionGroup) GetCount() int {
	cg.counterMux.RLock()
	defer cg.counterMux.RUnlock()
	return cg.counter
}

// unsafeIsFull returns true if group is full
func (cg *ConnectionGroup) unsafeIsFull() bool {
	return cg.counter == cg.limit
}

func (cg *ConnectionGroup) IsFull() bool {
	cg.counterMux.RLock()
	defer cg.counterMux.RUnlock()
	return cg.unsafeIsFull()
}

// unsafeIsEmpty returns true if group is empty
func (cg *ConnectionGroup) unsafeIsEmpty() bool {
	return cg.counter == 0
}

func (cg *ConnectionGroup) IsEmpty() bool {
	cg.counterMux.RLock()
	defer cg.counterMux.RUnlock()
	return cg.unsafeIsEmpty()
}

type ErrHandleConnection struct {
	Err error
}

func (e *ErrHandleConnection) Error() string {
	return "handle connection error: " + e.Err.Error()
}

var ErrGroupIsFull = errors.New("group is full")

func (cg *ConnectionGroup) Handle(connectionWorker *ConnectionWorker) error {
	cg.counterMux.Lock()
	if cg.unsafeIsFull() {
		cg.counterMux.Unlock()
		return &ErrHandleConnection{
			Err: ErrGroupIsFull,
		}
	}
	cg.counter += 1
	cg.counterMux.Unlock()

	defer func() {
		cg.counterMux.Lock()
		cg.counter -= 1
		cg.counterMux.Unlock()
	}()

	chStopHandle := make(chan struct{})
	defer close(chStopHandle)

	if err := connectionWorker.Start(cg.stop, cg.game, cg.broadcast, cg.proxyCh(chStopHandle, chanBytesOutBuffer)); err != nil {
		return &ErrHandleConnection{
			Err: err,
		}
	}

	return nil
}

func (cg *ConnectionGroup) Start() {
	cg.broadcast.Start(cg.stop)
	cg.game.Start(cg.stop)

	chMessagesGame := cg.listenGame(cg.stop, cg.game.ListenEvents(cg.stop, chanGameEventsBuffer))
	chMessagesBroadcast := cg.listenBroadcast(cg.stop, cg.broadcast.ListenMessages(cg.stop, chanBroadcastBuffer))
	chBytes := cg.encode(cg.stop, chMessagesGame, chMessagesBroadcast)
	chPreparedMessages := cg.prepare(cg.stop, chBytes)
	cg.broadcastPreparedMessages(chPreparedMessages)
}

func (cg *ConnectionGroup) broadcastPreparedMessages(chin <-chan *websocket.PreparedMessage) {
	go func() {
		for {
			select {
			case pm, ok := <-chin:
				if !ok {
					return
				}

				cg.doBroadcast(pm)
			case <-cg.stop:
				return
			}
		}
	}()
}

func (cg *ConnectionGroup) doBroadcast(pm *websocket.PreparedMessage) {
	cg.chsMux.RLock()
	defer cg.chsMux.RUnlock()

	for _, ch := range cg.chs {
		select {
		case ch <- pm:
		case <-cg.stop:
			return
		}
	}
}

func (cg *ConnectionGroup) Stop() {
	cg.stopper.Do(func() {
		close(cg.stop)
	})
}

func (cg *ConnectionGroup) GetWorldWidth() uint8 {
	return cg.game.World().Width()
}

func (cg *ConnectionGroup) GetWorldHeight() uint8 {
	return cg.game.World().Height()
}

func (cg *ConnectionGroup) GetObjects() []interface{} {
	return cg.game.World().GetObjects()
}

func (cg *ConnectionGroup) createChan() chan *websocket.PreparedMessage {
	ch := make(chan *websocket.PreparedMessage, chanBytesProxyBuffer)

	cg.chsMux.Lock()
	cg.chs = append(cg.chs, ch)
	cg.chsMux.Unlock()

	return ch
}

func (cg *ConnectionGroup) deleteChan(ch chan *websocket.PreparedMessage) {
	go func() {
		for range ch {
		}
	}()

	cg.chsMux.Lock()
	for i := range cg.chs {
		if cg.chs[i] == ch {
			cg.chs = append(cg.chs[:i], cg.chs[i+1:]...)
			close(ch)
			break
		}
	}
	cg.chsMux.Unlock()
}

func (cg *ConnectionGroup) proxyCh(stop <-chan struct{}, buffer uint) <-chan *websocket.PreparedMessage {
	ch := cg.createChan()
	chOut := make(chan *websocket.PreparedMessage, buffer)

	go func() {
		defer close(chOut)
		defer cg.deleteChan(ch)

		for {
			select {
			case <-stop:
				return
			case <-cg.stop:
				return
			case message, ok := <-ch:
				if !ok {
					return
				}
				cg.sendTimeout(chOut, message, stop, sendPreparedMessageTimeout)
			}
		}
	}()

	return chOut
}

func (cg *ConnectionGroup) sendTimeout(ch chan *websocket.PreparedMessage, pm *websocket.PreparedMessage, stop <-chan struct{}, timeout time.Duration) {
	const warnFormat = "game group message was not send to connection: %s"
	var timer = time.NewTimer(timeout)
	defer timer.Stop()
	select {
	case ch <- pm:
	case <-cg.stop:
		cg.logger.Warnf(warnFormat, "game group stopped")
	case <-stop:
		cg.logger.Warnf(warnFormat, "connection handler stopped")
	case <-timer.C:
		cg.logger.Warnf(warnFormat, "time is out")
		if len(ch) == cap(ch) {
			cg.logger.Warn("connection group output channel buffer is overflow for connection")
		}
	}
}

func (cg *ConnectionGroup) listenGame(stop <-chan struct{}, chin <-chan game.Event) <-chan OutputMessage {
	chout := make(chan OutputMessage, cap(chin))

	go func() {
		defer close(chout)

		ticker := time.NewTicker(gameOutputMessageBufferMonitoringDelay)
		defer ticker.Stop()

		var count = 0

		for {
			select {
			case event, ok := <-chin:
				if !ok {
					return
				}

				// Do not send internal game errors to clients
				if event.Type == game.EventTypeError {
					continue
				}

				// Do not send checked events to clients
				if event.Type == game.EventTypeObjectChecked {
					continue
				}

				outputMessage := OutputMessage{
					Type:    OutputMessageTypeGame,
					Payload: event,
				}

				select {
				case chout <- outputMessage:
					count++
				case <-stop:
					return
				}
			case <-stop:
				return
			case <-ticker.C:
				cg.logger.WithFields(logrus.Fields{
					"buffered_messages": len(chout),
					"buffer_size":       cap(chout),
					"time_frame":        gameOutputMessageBufferMonitoringDelay,
					"count":             count,
				}).Debug("game output messages buffer monitoring")

				count = 0
			}
		}
	}()

	return chout
}

func (cg *ConnectionGroup) listenBroadcast(stop <-chan struct{}, chin <-chan broadcast.Message) <-chan OutputMessage {
	chout := make(chan OutputMessage, cap(chin))

	go func() {
		defer close(chout)

		ticker := time.NewTicker(broadcastOutputMessageBufferMonitoringDelay)
		defer ticker.Stop()

		var count = 0

		for {
			select {
			case message, ok := <-chin:
				if !ok {
					return
				}

				outputMessage := OutputMessage{
					Type:    OutputMessageTypeBroadcast,
					Payload: message,
				}

				select {
				case chout <- outputMessage:
					count++
				case <-stop:
					return
				}
			case <-stop:
				return
			case <-ticker.C:
				cg.logger.WithFields(logrus.Fields{
					"buffered_messages": len(chout),
					"buffer_size":       cap(chout),
					"time_frame":        broadcastOutputMessageBufferMonitoringDelay,
					"count":             count,
				}).Debug("broadcast output messages buffer monitoring")

				count = 0
			}
		}
	}()

	return chout
}

func (cg *ConnectionGroup) encode(stop <-chan struct{}, chins ...<-chan OutputMessage) <-chan []byte {
	chout := make(chan []byte, chanEncodeGroupMessageBuffer)

	wg := sync.WaitGroup{}
	wg.Add(len(chins))

	for i, chin := range chins {
		go func(i int, chin <-chan OutputMessage) {
			defer wg.Done()

			ticker := time.NewTicker(encodeGroupMessageBufferMonitoringDelay)
			defer ticker.Stop()

			var count = 0

			for {
				select {
				case <-stop:
					return
				case message, ok := <-chin:
					if !ok {
						return
					}

					if data, err := ffjson.Marshal(message); err != nil {
						cg.logger.Errorln("encode output message error:", err)
					} else {
						select {
						case chout <- data:
							count++
						case <-stop:
							return
						}
					}
				case <-ticker.C:
					cg.logger.WithFields(logrus.Fields{
						"buffered_messages": len(chout),
						"buffer_size":       chanEncodeGroupMessageBuffer,
						"time_frame":        encodeGroupMessageBufferMonitoringDelay,
						"count":             count,
						"channel":           i,
					}).Debug("encoded group messages buffer monitoring")

					count = 0
				}
			}
		}(i, chin)
	}

	go func() {
		wg.Wait()
		close(chout)
	}()

	return chout
}

func (cg *ConnectionGroup) prepare(stop <-chan struct{}, chin <-chan []byte) <-chan *websocket.PreparedMessage {
	chout := make(chan *websocket.PreparedMessage, cap(chin))

	go func() {
		defer close(chout)

		ticker := time.NewTicker(preparedMessageBufferMonitoringDelay)
		defer ticker.Stop()

		var count = 0

		for {
			select {
			case data, ok := <-chin:
				if !ok {
					return
				}

				if pm, err := websocket.NewPreparedMessage(websocket.TextMessage, data); err != nil {
					cg.logger.Errorln("prepare group output message error:", err)
				} else {
					select {
					case chout <- pm:
						count++
					case <-stop:
						return
					}
				}
			case <-stop:
				return
			case <-ticker.C:
				cg.logger.WithFields(logrus.Fields{
					"buffered_messages": len(chout),
					"buffer_size":       cap(chout),
					"time_frame":        preparedMessageBufferMonitoringDelay,
					"count":             count,
				}).Debug("prepared messages buffer monitoring")

				count = 0
			}
		}
	}()

	return chout
}

func (cg *ConnectionGroup) BroadcastMessageTimeout(message string, timeout time.Duration) bool {
	return cg.broadcast.BroadcastMessageTimeout(broadcast.Message(message), timeout)
}
<commit_msg>Fix const names in ConnectionGroup<commit_after>package connections

import (
	"errors"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	"github.com/pquerna/ffjson/ffjson"
	"github.com/sirupsen/logrus"

	"github.com/ivan1993spb/snake-server/broadcast"
	"github.com/ivan1993spb/snake-server/game"
)

const (
	chanBroadcastBuffer  = 128
	chanGameEventsBuffer = 8192

	chanPreparedMessageProxyBuffer = 8192
	chanPreparedMessageOutBuffer   = 8192

	chanEncodedOutputMessageBuffer = 8192

	sendPreparedMessageTimeout = time.Millisecond * 50

	broadcastOutputMessageBufferMonitoringDelay = time.Second * 30
	gameOutputMessageBufferMonitoringDelay      = time.Second * 30
	encodedOutputMessageBufferMonitoringDelay   = time.Second * 30
	preparedMessageBufferMonitoringDelay        = time.Second * 30

	minimalConnectionLimit = 1
)

type ConnectionGroup struct {
	limit      int
	counter    int
	counterMux *sync.RWMutex

	logger logrus.FieldLogger

	game      *game.Game
	broadcast *broadcast.GroupBroadcast

	chs    []chan *websocket.PreparedMessage
	chsMux *sync.RWMutex

	stop    chan struct{}
	stopper *sync.Once
}

type errCreateConnectionGroup string

func (e errCreateConnectionGroup) Error() string {
	return "cannot create connection group: " + string(e)
}

func NewConnectionGroup(logger logrus.FieldLogger, connectionLimit int, width, height uint8) (*ConnectionGroup, error) {
	g, err := game.NewGame(logger, width, height)
	if err != nil {
		return nil, errCreateConnectionGroup(err.Error())
	}

	if connectionLimit < minimalConnectionLimit {
		return nil, errCreateConnectionGroup("invalid connection limit")
	}

	return &ConnectionGroup{
		limit:      connectionLimit,
		counterMux: &sync.RWMutex{},
		game:       g,
		broadcast:  broadcast.NewGroupBroadcast(),
		logger:     logger,
		chs:        make([]chan *websocket.PreparedMessage, 0),
		chsMux:     &sync.RWMutex{},
		stop:       make(chan struct{}),
		stopper:    &sync.Once{},
	}, nil
}

func (cg *ConnectionGroup) GetLimit() int {
	cg.counterMux.RLock()
	defer cg.counterMux.RUnlock()
	return cg.limit
}

func (cg *ConnectionGroup) SetLimit(limit int) {
	cg.counterMux.Lock()
	cg.limit = limit
	cg.counterMux.Unlock()
}

func (cg *ConnectionGroup) GetCount() int {
	cg.counterMux.RLock()
	defer cg.counterMux.RUnlock()
	return cg.counter
}

// unsafeIsFull returns true if group is full
func (cg *ConnectionGroup) unsafeIsFull() bool {
	return cg.counter == cg.limit
}

func (cg *ConnectionGroup) IsFull() bool {
	cg.counterMux.RLock()
	defer cg.counterMux.RUnlock()
	return cg.unsafeIsFull()
}

// unsafeIsEmpty returns true if group is empty
func (cg *ConnectionGroup) unsafeIsEmpty() bool {
	return cg.counter == 0
}

func (cg *ConnectionGroup) IsEmpty() bool {
	cg.counterMux.RLock()
	defer cg.counterMux.RUnlock()
	return cg.unsafeIsEmpty()
}

type ErrHandleConnection struct {
	Err error
}

func (e *ErrHandleConnection) Error() string {
	return "handle connection error: " + e.Err.Error()
}

var ErrGroupIsFull = errors.New("group is full")

func (cg *ConnectionGroup) Handle(connectionWorker *ConnectionWorker) error {
	cg.counterMux.Lock()
	if cg.unsafeIsFull() {
		cg.counterMux.Unlock()
		return &ErrHandleConnection{
			Err: ErrGroupIsFull,
		}
	}
	cg.counter += 1
	cg.counterMux.Unlock()

	defer func() {
		cg.counterMux.Lock()
		cg.counter -= 1
		cg.counterMux.Unlock()
	}()

	chStopHandle := make(chan struct{})
	defer close(chStopHandle)

	chout := cg.proxyCh(chStopHandle, chanPreparedMessageOutBuffer)

	if err := connectionWorker.Start(cg.stop, cg.game, cg.broadcast, chout); err != nil {
		return &ErrHandleConnection{
			Err: err,
		}
	}

	return nil
}

func (cg *ConnectionGroup) Start() {
	cg.broadcast.Start(cg.stop)
	cg.game.Start(cg.stop)

	chMessagesGame := cg.listenGame(cg.stop, cg.game.ListenEvents(cg.stop, chanGameEventsBuffer))
	chMessagesBroadcast := cg.listenBroadcast(cg.stop, cg.broadcast.ListenMessages(cg.stop, chanBroadcastBuffer))
	chBytes := cg.encode(cg.stop, chMessagesGame, chMessagesBroadcast)
	chPreparedMessages := cg.prepare(cg.stop, chBytes)
	cg.broadcastPreparedMessages(chPreparedMessages)
}

func (cg *ConnectionGroup) broadcastPreparedMessages(chin <-chan *websocket.PreparedMessage) {
	go func() {
		for {
			select {
			case pm, ok := <-chin:
				if !ok {
					return
				}

				cg.doBroadcast(pm)
			case <-cg.stop:
				return
			}
		}
	}()
}

func (cg *ConnectionGroup) doBroadcast(pm *websocket.PreparedMessage) {
	cg.chsMux.RLock()
	defer cg.chsMux.RUnlock()

	for _, ch := range cg.chs {
		select {
		case ch <- pm:
		case <-cg.stop:
			return
		}
	}
}

func (cg *ConnectionGroup) Stop() {
	cg.stopper.Do(func() {
		close(cg.stop)
	})
}

func (cg *ConnectionGroup) GetWorldWidth() uint8 {
	return cg.game.World().Width()
}

func (cg *ConnectionGroup) GetWorldHeight() uint8 {
	return cg.game.World().Height()
}

func (cg *ConnectionGroup) GetObjects() []interface{} {
	return cg.game.World().GetObjects()
}

func (cg *ConnectionGroup) createChan() chan *websocket.PreparedMessage {
	ch := make(chan *websocket.PreparedMessage, chanPreparedMessageProxyBuffer)

	cg.chsMux.Lock()
	cg.chs = append(cg.chs, ch)
	cg.chsMux.Unlock()

	return ch
}

func (cg *ConnectionGroup) deleteChan(ch chan *websocket.PreparedMessage) {
	go func() {
		for range ch {
		}
	}()

	cg.chsMux.Lock()
	for i := range cg.chs {
		if cg.chs[i] == ch {
			cg.chs = append(cg.chs[:i], cg.chs[i+1:]...)
			close(ch)
			break
		}
	}
	cg.chsMux.Unlock()
}

func (cg *ConnectionGroup) proxyCh(stop <-chan struct{}, buffer uint) <-chan *websocket.PreparedMessage {
	ch := cg.createChan()
	chOut := make(chan *websocket.PreparedMessage, buffer)

	go func() {
		defer close(chOut)
		defer cg.deleteChan(ch)

		for {
			select {
			case <-stop:
				return
			case <-cg.stop:
				return
			case message, ok := <-ch:
				if !ok {
					return
				}
				cg.sendTimeout(chOut, message, stop, sendPreparedMessageTimeout)
			}
		}
	}()

	return chOut
}

func (cg *ConnectionGroup) sendTimeout(ch chan *websocket.PreparedMessage, pm *websocket.PreparedMessage, stop <-chan struct{}, timeout time.Duration) {
	const warnFormat = "game group message was not send to connection: %s"
	var timer = time.NewTimer(timeout)
	defer timer.Stop()
	select {
	case ch <- pm:
	case <-cg.stop:
		cg.logger.Warnf(warnFormat, "game group stopped")
	case <-stop:
		cg.logger.Warnf(warnFormat, "connection handler stopped")
	case <-timer.C:
		cg.logger.Warnf(warnFormat, "time is out")
		if len(ch) == cap(ch) {
			cg.logger.Warn("connection group output channel buffer is overflow for connection")
		}
	}
}

func (cg *ConnectionGroup) listenGame(stop <-chan struct{}, chin <-chan game.Event) <-chan OutputMessage {
	chout := make(chan OutputMessage, cap(chin))

	go func() {
		defer close(chout)

		ticker := time.NewTicker(gameOutputMessageBufferMonitoringDelay)
		defer ticker.Stop()

		var count = 0

		for {
			select {
			case event, ok := <-chin:
				if !ok {
					return
				}

				// Do not send internal game errors to clients
				if event.Type == game.EventTypeError {
					continue
				}

				// Do not send checked events to clients
				if event.Type == game.EventTypeObjectChecked {
					continue
				}

				outputMessage := OutputMessage{
					Type:    OutputMessageTypeGame,
					Payload: event,
				}

				select {
				case chout <- outputMessage:
					count++
				case <-stop:
					return
				}
			case <-stop:
				return
			case <-ticker.C:
				cg.logger.WithFields(logrus.Fields{
					"buffered_messages": len(chout),
					"buffer_size":       cap(chout),
					"time_frame":        gameOutputMessageBufferMonitoringDelay,
					"count":             count,
				}).Debug("game output messages buffer monitoring")

				count = 0
			}
		}
	}()

	return chout
}

func (cg *ConnectionGroup) listenBroadcast(stop <-chan struct{}, chin <-chan broadcast.Message) <-chan OutputMessage {
	chout := make(chan OutputMessage, cap(chin))

	go func() {
		defer close(chout)

		ticker := time.NewTicker(broadcastOutputMessageBufferMonitoringDelay)
		defer ticker.Stop()

		var count = 0

		for {
			select {
			case message, ok := <-chin:
				if !ok {
					return
				}

				outputMessage := OutputMessage{
					Type:    OutputMessageTypeBroadcast,
					Payload: message,
				}

				select {
				case chout <- outputMessage:
					count++
				case <-stop:
					return
				}
			case <-stop:
				return
			case <-ticker.C:
				cg.logger.WithFields(logrus.Fields{
					"buffered_messages": len(chout),
					"buffer_size":       cap(chout),
					"time_frame":        broadcastOutputMessageBufferMonitoringDelay,
					"count":             count,
				}).Debug("broadcast output messages buffer monitoring")

				count = 0
			}
		}
	}()

	return chout
}

func (cg *ConnectionGroup) encode(stop <-chan struct{}, chins ...<-chan OutputMessage) <-chan []byte {
	chout := make(chan []byte, chanEncodedOutputMessageBuffer)

	wg := sync.WaitGroup{}
	wg.Add(len(chins))

	for i, chin := range chins {
		go func(i int, chin <-chan OutputMessage) {
			defer wg.Done()

			ticker := time.NewTicker(encodedOutputMessageBufferMonitoringDelay)
			defer ticker.Stop()

			var count = 0

			for {
				select {
				case <-stop:
					return
				case message, ok := <-chin:
					if !ok {
						return
					}

					if data, err := ffjson.Marshal(message); err != nil {
						cg.logger.Errorln("encode output message error:", err)
					} else {
						select {
						case chout <- data:
							count++
						case <-stop:
							return
						}
					}
				case <-ticker.C:
					cg.logger.WithFields(logrus.Fields{
						"buffered_messages": len(chout),
						"buffer_size":       chanEncodedOutputMessageBuffer,
						"time_frame":        encodedOutputMessageBufferMonitoringDelay,
						"count":             count,
						"channel":           i,
					}).Debug("encoded group messages buffer monitoring")

					count = 0
				}
			}
		}(i, chin)
	}

	go func() {
		wg.Wait()
		close(chout)
	}()

	return chout
}

func (cg *ConnectionGroup) prepare(stop <-chan struct{}, chin <-chan []byte) <-chan *websocket.PreparedMessage {
	chout := make(chan *websocket.PreparedMessage, cap(chin))

	go func() {
		defer close(chout)

		ticker := time.NewTicker(preparedMessageBufferMonitoringDelay)
		defer ticker.Stop()

		var count = 0

		for {
			select {
			case data, ok := <-chin:
				if !ok {
					return
				}

				if pm, err := websocket.NewPreparedMessage(websocket.TextMessage, data); err != nil {
					cg.logger.Errorln("prepare group output message error:", err)
				} else {
					select {
					case chout <- pm:
						count++
					case <-stop:
						return
					}
				}
			case <-stop:
				return
			case <-ticker.C:
				cg.logger.WithFields(logrus.Fields{
					"buffered_messages": len(chout),
					"buffer_size":       cap(chout),
					"time_frame":        preparedMessageBufferMonitoringDelay,
					"count":             count,
				}).Debug("prepared messages buffer monitoring")

				count = 0
			}
		}
	}()

	return chout
}

func (cg *ConnectionGroup) BroadcastMessageTimeout(message string, timeout time.Duration) bool {
	return cg.broadcast.BroadcastMessageTimeout(broadcast.Message(message), timeout)
}
<|endoftext|>


<commit_before>// This program solves the (English) peg
// solitaire board game.
// http://en.wikipedia.org/wiki/Peg_solitaire

package main

import "fmt"

const N = 11 + 1 // length of a row (+1 for \n)

// The board must be surrounded by 2 illegal
// fields in each direction so that move()
// doesn't need to check the board boundaries.
// Periods represent illegal fields,
// ● are pegs, and ○ are holes.

var board = []int(
	`...........
...........
....●●●....
....●●●....
..●●●●●●●..
..●●●○●●●..
..●●●●●●●..
....●●●....
....●●●....
...........
...........
`)

// center is the position of the center hole if
// there is a single one; otherwise it is -1.
var center int

func init() {
	n := 0
	for pos, field := range board {
		if field == '○' {
			center = pos
			n++
		}
	}
	if n != 1 {
		center = -1 // no single hole
	}
}

var moves int // number of times move is called

// move tests if there is a peg at position pos that
// can jump over another peg in direction dir. If the
// move is valid, it is executed and move returns true.
// Otherwise, move returns false.
func move(pos, dir int) bool {
	moves++
	if board[pos] == '●' && board[pos+dir] == '●' && board[pos+2*dir] == '○' {
		board[pos] = '○'
		board[pos+dir] = '○'
		board[pos+2*dir] = '●'
		return true
	}
	return false
}

// unmove reverts a previously executed valid move.
func unmove(pos, dir int) {
	board[pos] = '●'
	board[pos+dir] = '●'
	board[pos+2*dir] = '○'
}

// solve tries to find a sequence of moves such that
// there is only one peg left at the end; if center is
// >= 0, that last peg must be in the center position.
// If a solution is found, solve prints the board after
// each move in a backward fashion (i.e., the last
// board position is printed first, all the way back to
// the starting board position).
func solve() bool {
	var last, n int
	for pos, field := range board {
		// try each board position
		if field == '●' {
			// found a peg
			for _, dir := range [...]int{-1, -N, +1, +N} {
				// try each direction
				if move(pos, dir) {
					// a valid move was found and executed,
					// see if this new board has a solution
					if solve() {
						unmove(pos, dir)
						println(string(board))
						return true
					}
					unmove(pos, dir)
				}
			}
			last = pos
			n++
		}
	}
	// tried each possible move
	if n == 1 && (center < 0 || last == center) {
		// there's only one peg left
		println(string(board))
		return true
	}
	// no solution found for this board
	return false
}

func main() {
	if !solve() {
		fmt.Println("no solution found")
	}
	fmt.Println(moves, "moves tried")
}
<commit_msg>doc/play: use []rune insetead of []int.<commit_after>// This program solves the (English) peg
// solitaire board game.
// http://en.wikipedia.org/wiki/Peg_solitaire

package main

import "fmt"

const N = 11 + 1 // length of a row (+1 for \n)

// The board must be surrounded by 2 illegal
// fields in each direction so that move()
// doesn't need to check the board boundaries.
// Periods represent illegal fields,
// ● are pegs, and ○ are holes.

var board = []rune(
	`...........
...........
....●●●....
....●●●....
..●●●●●●●..
..●●●○●●●..
..●●●●●●●..
....●●●....
....●●●....
...........
...........
`)

// center is the position of the center hole if
// there is a single one; otherwise it is -1.
var center int

func init() {
	n := 0
	for pos, field := range board {
		if field == '○' {
			center = pos
			n++
		}
	}
	if n != 1 {
		center = -1 // no single hole
	}
}

var moves int // number of times move is called

// move tests if there is a peg at position pos that
// can jump over another peg in direction dir. If the
// move is valid, it is executed and move returns true.
// Otherwise, move returns false.
func move(pos, dir int) bool {
	moves++
	if board[pos] == '●' && board[pos+dir] == '●' && board[pos+2*dir] == '○' {
		board[pos] = '○'
		board[pos+dir] = '○'
		board[pos+2*dir] = '●'
		return true
	}
	return false
}

// unmove reverts a previously executed valid move.
func unmove(pos, dir int) {
	board[pos] = '●'
	board[pos+dir] = '●'
	board[pos+2*dir] = '○'
}

// solve tries to find a sequence of moves such that
// there is only one peg left at the end; if center is
// >= 0, that last peg must be in the center position.
// If a solution is found, solve prints the board after
// each move in a backward fashion (i.e., the last
// board position is printed first, all the way back to
// the starting board position).
func solve() bool {
	var last, n int
	for pos, field := range board {
		// try each board position
		if field == '●' {
			// found a peg
			for _, dir := range [...]int{-1, -N, +1, +N} {
				// try each direction
				if move(pos, dir) {
					// a valid move was found and executed,
					// see if this new board has a solution
					if solve() {
						unmove(pos, dir)
						println(string(board))
						return true
					}
					unmove(pos, dir)
				}
			}
			last = pos
			n++
		}
	}
	// tried each possible move
	if n == 1 && (center < 0 || last == center) {
		// there's only one peg left
		println(string(board))
		return true
	}
	// no solution found for this board
	return false
}

func main() {
	if !solve() {
		fmt.Println("no solution found")
	}
	fmt.Println(moves, "moves tried")
}
<|endoftext|>


<commit_before>package params

import (
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
)

// Type for http post body
type PostBody []byte // Byte array with request body

// Get params stands for "query params"
type GetParams map[string]string

type Params struct {
	Query      GetParams
	Body       PostBody
	PathParams PathParams
}

func NewParams(request *http.Request, pattern *regexp.Regexp, path string) (*Params, error) {
	body, err := ioutil.ReadAll(request.Body)
	if err != nil {
		return nil, err
	}

	return &Params{
		Query:      ValuesToGetParams(request.URL.Query()),
		Body:       body,
		PathParams: ExtractPathParams(pattern, path),
	}, nil
}

func ExtractParams(req *http.Request) *Params {
	return req.Context().Value("params").(*Params)
}

// Converts url.Url.Query() from "Values" (map[string][]string)
// to "getParams" (map[string]string)
func ValuesToGetParams(values url.Values) GetParams {
	var params map[string]string
	for key := range values {
		params[key] = values.Get(key)
	}
	return params
}

// Example: url "/api/v1/users/599a49bacdf43b817eeea57b" and pattern `/api/v1/users/:id`
// path params = {"id": "599a49bacdf43b817eeea57b"}
type PathParams map[string]string

// Extract path params from path
func ExtractPathParams(pattern *regexp.Regexp, path string) PathParams {
	match := pattern.FindStringSubmatch(path)
	result := make(PathParams)

	for i, name := range pattern.SubexpNames() {
		if i != 0 {
			result[name] = match[i]
		}
	}

	return result
}
<commit_msg>fix get params from get query<commit_after>package params

import (
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
)

// Type for http post body
type PostBody []byte // Byte array with request body

// Get params stands for "query params"
type GetParams map[string]string

type Params struct {
	Query      GetParams
	Body       PostBody
	PathParams PathParams
}

func NewParams(request *http.Request, pattern *regexp.Regexp, path string) (*Params, error) {
	body, err := ioutil.ReadAll(request.Body)
	if err != nil {
		return nil, err
	}

	return &Params{
		Query:      ValuesToGetParams(request.URL.Query()),
		Body:       body,
		PathParams: ExtractPathParams(pattern, path),
	}, nil
}

func ExtractParams(req *http.Request) *Params {
	return req.Context().Value("params").(*Params)
}

// Converts url.Url.Query() from "Values" (map[string][]string)
// to "getParams" (map[string]string)
func ValuesToGetParams(values url.Values) GetParams {
	params := make(map[string]string)
	for key := range values {
		params[key] = values.Get(key)
	}
	return params
}

// Example: url "/api/v1/users/599a49bacdf43b817eeea57b" and pattern `/api/v1/users/:id`
// path params = {"id": "599a49bacdf43b817eeea57b"}
type PathParams map[string]string

// Extract path params from path
func ExtractPathParams(pattern *regexp.Regexp, path string) PathParams {
	match := pattern.FindStringSubmatch(path)
	result := make(PathParams)

	for i, name := range pattern.SubexpNames() {
		if i != 0 {
			result[name] = match[i]
		}
	}

	return result
}
<|endoftext|>


<commit_before>package parser

import (
	"fmt"

	"github.com/Tamrin007/monkey/ast"
	"github.com/Tamrin007/monkey/lexer"
	"github.com/Tamrin007/monkey/token"
)

type Parser struct {
	l         *lexer.Lexer
	errors    []string
	curToken  token.Token
	peekToken token.Token
}

func New(l *lexer.Lexer) *Parser {
	p := &Parser{
		l:      l,
		errors: []string{},
	}

	p.nextToken()
	p.nextToken()

	return p
}

func (p *Parser) Errors() []string {
	return p.errors
}

func (p *Parser) peekError(t token.TokenType) {
	msg := fmt.Sprintf("expected next token to be %s, got %s instead", t, p.peekToken.Type)
	p.errors = append(p.errors, msg)
}

func (p *Parser) nextToken() {
	p.curToken = p.peekToken
	p.peekToken = p.l.NextToken()
}

func (p *Parser) ParseProgram() *ast.Program {
	program := &ast.Program{}
	program.Statements = []ast.Statement{}

	for p.curToken.Type != token.EOF {
		stmt := p.parseStatement()
		if stmt != nil {
			program.Statements = append(program.Statements, stmt)
		}
		p.nextToken()
	}
	return program
}

func (p *Parser) parseStatement() ast.Statement {
	switch p.curToken.Type {
	case token.LET:
		return p.parseLetStatement()
	default:
		return nil
	}
}

func (p *Parser) parseLetStatement() *ast.LetStatement {
	stmt := &ast.LetStatement{Token: p.curToken}

	if !p.expectPeek(token.IDENT) {
		return nil
	}

	stmt.Name = &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}

	if !p.expectPeek(token.ASSIGN) {
		return nil
	}

	// TODO: We're skiping the expressions until we encounter a semicolon
	for !p.curTokenIs(token.SEMICOLON) {
		p.nextToken()
	}

	return stmt
}

func (p *Parser) curTokenIs(t token.TokenType) bool {
	return p.curToken.Type == t
}

func (p *Parser) peekTokenIs(t token.TokenType) bool {
	return p.peekToken.Type == t
}

func (p *Parser) expectPeek(t token.TokenType) bool {
	if p.peekTokenIs(t) {
		p.nextToken()
		return true
	}
	p.peekError(t)
	return false
}
<commit_msg>add function to parse return statement<commit_after>package parser

import (
	"fmt"

	"github.com/Tamrin007/monkey/ast"
	"github.com/Tamrin007/monkey/lexer"
	"github.com/Tamrin007/monkey/token"
)

type Parser struct {
	l         *lexer.Lexer
	errors    []string
	curToken  token.Token
	peekToken token.Token
}

func New(l *lexer.Lexer) *Parser {
	p := &Parser{
		l:      l,
		errors: []string{},
	}

	p.nextToken()
	p.nextToken()

	return p
}

func (p *Parser) Errors() []string {
	return p.errors
}

func (p *Parser) peekError(t token.TokenType) {
	msg := fmt.Sprintf("expected next token to be %s, got %s instead", t, p.peekToken.Type)
	p.errors = append(p.errors, msg)
}

func (p *Parser) nextToken() {
	p.curToken = p.peekToken
	p.peekToken = p.l.NextToken()
}

func (p *Parser) ParseProgram() *ast.Program {
	program := &ast.Program{}
	program.Statements = []ast.Statement{}

	for p.curToken.Type != token.EOF {
		stmt := p.parseStatement()
		if stmt != nil {
			program.Statements = append(program.Statements, stmt)
		}
		p.nextToken()
	}
	return program
}

func (p *Parser) parseStatement() ast.Statement {
	switch p.curToken.Type {
	case token.LET:
		return p.parseLetStatement()
	case token.RETURN:
		return p.parseReturnStatement()
	default:
		return nil
	}
}

func (p *Parser) parseLetStatement() *ast.LetStatement {
	stmt := &ast.LetStatement{Token: p.curToken}

	if !p.expectPeek(token.IDENT) {
		return nil
	}

	stmt.Name = &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}

	if !p.expectPeek(token.ASSIGN) {
		return nil
	}

	// TODO: We're skiping the expressions until we encounter a semicolon
	for !p.curTokenIs(token.SEMICOLON) {
		p.nextToken()
	}

	return stmt
}

func (p *Parser) parseReturnStatement() *ast.ReturnStatement {
	stmt := &ast.ReturnStatement{Token: p.curToken}

	p.nextToken()

	// TODO: We're skipping the expressions until we encounter a semicolon
	for !p.curTokenIs(token.SEMICOLON) {
		p.nextToken()
	}

	return stmt
}

func (p *Parser) curTokenIs(t token.TokenType) bool {
	return p.curToken.Type == t
}

func (p *Parser) peekTokenIs(t token.TokenType) bool {
	return p.peekToken.Type == t
}

func (p *Parser) expectPeek(t token.TokenType) bool {
	if p.peekTokenIs(t) {
		p.nextToken()
		return true
	}
	p.peekError(t)
	return false
}
<|endoftext|>