<commit_before>package main

import (
	"fmt"
	"github.com/bobziuchkovski/writ"
	"github.com/jkomoros/boardgame/boardgame-util/lib/build/api"
	"github.com/jkomoros/boardgame/boardgame-util/lib/build/static"
	"github.com/jkomoros/boardgame/boardgame-util/lib/config"
	"os"
	"os/exec"
	"strings"
	"time"
)

type Serve struct {
	baseSubCommand

	Storage string

	Port       string
	StaticPort string
	Prod       bool

	OfflineDevMode bool
}

func (s *Serve) Run(p writ.Path, positional []string) {

	c := s.Base().GetConfig(false)

	if s.OfflineDevMode {
		c.AddOverride(config.EnableOfflineDevMode())
	}

	mode := c.Dev

	dir := s.Base().NewTempDir("temp_serve_")

	storage := effectiveStorageType(s.Base(), mode, s.Storage)

	pkgs, err := mode.AllGamePackages()

	if err != nil {
		s.Base().errAndQuit("Not all game packages were valid: " + err.Error())
	}

	apiOptions := &api.Options{}

	if s.OfflineDevMode {
		apiOptions.OverrideOfflineDevMode = true
	}

	fmt.Println("Creating temporary binary")
	apiPath, err := api.Build(dir, pkgs, storage, apiOptions)

	if err != nil {
		s.Base().errAndQuit("Couldn't create api: " + err.Error())
	}

	fmt.Println("Creating temporary static assets folder")
	//TODO: should we allow you to pass CopyFiles? I don't know why you'd want
	//to given this is a temp dir.
	_, err = static.Build(dir, mode.Games, c, s.Prod, false, mode.OfflineDevMode)

	if err != nil {
		s.Base().errAndQuit("Couldn't create static directory: " + err.Error())
	}

	staticPort := mode.DefaultStaticPort

	if s.StaticPort != "" {
		staticPort = s.StaticPort
	}

	go func() {
		fmt.Println("Starting up asset server at " + staticPort)
		if err := static.Server(dir, staticPort); err != nil {
			//TODO: when this happens we should quit the whole program
			fmt.Println("ERROR: couldn't start static server: " + err.Error())
		}
	}()

	//TODO: simple serving of staticPath here. Do we need a new parameter for
	//default static serving port?

	port := mode.DefaultPort

	if s.Port != "" {
		port = s.Port
	}

	//cmd will be run as though it's in this directory, which is where
	//config.json is.
	cmd := exec.Command(apiPath)
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	cmd.Env = append(os.Environ(), "PORT="+port)

	err = cmd.Start()

	if err == nil {
		topLine := "************************************************************************"

		//Cheat and wait to print the message until later
		time.Sleep(time.Second * 2)

		fmt.Println(" ")
		fmt.Println(topLine)
		for i := 0; i < 2; i++ {
			fmt.Println("*")
		}
		fmt.Println("*     Server running. Open 'http://localhost:" + staticPort + "' in your browser")
		for i := 0; i < 2; i++ {
			fmt.Println("*")
		}
		fmt.Println(topLine)
		fmt.Println(" ")

		err = cmd.Wait()
	}

	if err != nil {
		exitErr, ok := err.(*exec.ExitError)
		if !ok {
			s.Base().errAndQuit("Couldn't cast exiterror")
		}

		//Programs that are signaled and who responded to it before us (the
		//parent) did (which is a race) will have Exited() false, whereas a
		//program that errored and quit on its own should have true. Only the
		//latter is an err; calling errAndQuit not in an error could prevent
		//our own clean shutdown from happening.
		if exitErr.ProcessState.Exited() {
			s.Base().errAndQuit("Error running command: " + err.Error())
		}
	}
}

func (s *Serve) Name() string {
	return "serve"
}

func (s *Serve) Description() string {
	return "Creates and runs a local development server based on config.json"
}

func (s *Serve) WritOptions() []*writ.Option {
	return []*writ.Option{
		{
			Names:       []string{"storage", "s"},
			Decoder:     writ.NewOptionDecoder(&s.Storage),
			Description: "Which storage subsystem to use. One of {" + strings.Join(api.ValidStorageTypeStrings(), ",") + "}. If not provided, falls back on the DefaultStorageType from config, or as a final fallback just the deafult storage type.",
		},
		{
			Names:       []string{"port", "p"},
			Decoder:     writ.NewOptionDecoder(&s.Port),
			Description: "Port to use for the api server, overriding value in config.json's DefaultPort",
		},
		{
			Names:       []string{"static-port"},
			Decoder:     writ.NewOptionDecoder(&s.StaticPort),
			Description: "Port to use for the static file server, overridig value in config.json's DefaultStaticPort",
		},
		{
			Names:       []string{"prod"},
			Description: "If provided, will created bundled build directory for static resources.",
			Decoder:     writ.NewFlagDecoder(&s.Prod),
			Flag:        true,
		},
		{
			Names:       []string{"offline-dev-mode"},
			Description: "If provided, will override OfflineDevMode to true, no matter what is in config. This is generally the best way to enable offline dev mode.",
			Decoder:     writ.NewFlagDecoder(&s.OfflineDevMode),
			Flag:        true,
		},
	}
}
<commit_msg>Updated help message for --offline-dev-mode. Part of #682.<commit_after>package main

import (
	"fmt"
	"github.com/bobziuchkovski/writ"
	"github.com/jkomoros/boardgame/boardgame-util/lib/build/api"
	"github.com/jkomoros/boardgame/boardgame-util/lib/build/static"
	"github.com/jkomoros/boardgame/boardgame-util/lib/config"
	"os"
	"os/exec"
	"strings"
	"time"
)

type Serve struct {
	baseSubCommand

	Storage string

	Port       string
	StaticPort string
	Prod       bool

	OfflineDevMode bool
}

func (s *Serve) Run(p writ.Path, positional []string) {

	c := s.Base().GetConfig(false)

	if s.OfflineDevMode {
		c.AddOverride(config.EnableOfflineDevMode())
	}

	mode := c.Dev

	dir := s.Base().NewTempDir("temp_serve_")

	storage := effectiveStorageType(s.Base(), mode, s.Storage)

	pkgs, err := mode.AllGamePackages()

	if err != nil {
		s.Base().errAndQuit("Not all game packages were valid: " + err.Error())
	}

	apiOptions := &api.Options{}

	if s.OfflineDevMode {
		apiOptions.OverrideOfflineDevMode = true
	}

	fmt.Println("Creating temporary binary")
	apiPath, err := api.Build(dir, pkgs, storage, apiOptions)

	if err != nil {
		s.Base().errAndQuit("Couldn't create api: " + err.Error())
	}

	fmt.Println("Creating temporary static assets folder")
	//TODO: should we allow you to pass CopyFiles? I don't know why you'd want
	//to given this is a temp dir.
	_, err = static.Build(dir, mode.Games, c, s.Prod, false, mode.OfflineDevMode)

	if err != nil {
		s.Base().errAndQuit("Couldn't create static directory: " + err.Error())
	}

	staticPort := mode.DefaultStaticPort

	if s.StaticPort != "" {
		staticPort = s.StaticPort
	}

	go func() {
		fmt.Println("Starting up asset server at " + staticPort)
		if err := static.Server(dir, staticPort); err != nil {
			//TODO: when this happens we should quit the whole program
			fmt.Println("ERROR: couldn't start static server: " + err.Error())
		}
	}()

	//TODO: simple serving of staticPath here. Do we need a new parameter for
	//default static serving port?

	port := mode.DefaultPort

	if s.Port != "" {
		port = s.Port
	}

	//cmd will be run as though it's in this directory, which is where
	//config.json is.
	cmd := exec.Command(apiPath)
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	cmd.Env = append(os.Environ(), "PORT="+port)

	err = cmd.Start()

	if err == nil {
		topLine := "************************************************************************"

		//Cheat and wait to print the message until later
		time.Sleep(time.Second * 2)

		fmt.Println(" ")
		fmt.Println(topLine)
		for i := 0; i < 2; i++ {
			fmt.Println("*")
		}
		fmt.Println("*     Server running. Open 'http://localhost:" + staticPort + "' in your browser")
		for i := 0; i < 2; i++ {
			fmt.Println("*")
		}
		fmt.Println(topLine)
		fmt.Println(" ")

		err = cmd.Wait()
	}

	if err != nil {
		exitErr, ok := err.(*exec.ExitError)
		if !ok {
			s.Base().errAndQuit("Couldn't cast exiterror")
		}

		//Programs that are signaled and who responded to it before us (the
		//parent) did (which is a race) will have Exited() false, whereas a
		//program that errored and quit on its own should have true. Only the
		//latter is an err; calling errAndQuit not in an error could prevent
		//our own clean shutdown from happening.
		if exitErr.ProcessState.Exited() {
			s.Base().errAndQuit("Error running command: " + err.Error())
		}
	}
}

func (s *Serve) Name() string {
	return "serve"
}

func (s *Serve) Description() string {
	return "Creates and runs a local development server based on config.json"
}

func (s *Serve) WritOptions() []*writ.Option {
	return []*writ.Option{
		{
			Names:       []string{"storage", "s"},
			Decoder:     writ.NewOptionDecoder(&s.Storage),
			Description: "Which storage subsystem to use. One of {" + strings.Join(api.ValidStorageTypeStrings(), ",") + "}. If not provided, falls back on the DefaultStorageType from config, or as a final fallback just the deafult storage type.",
		},
		{
			Names:       []string{"port", "p"},
			Decoder:     writ.NewOptionDecoder(&s.Port),
			Description: "Port to use for the api server, overriding value in config.json's DefaultPort",
		},
		{
			Names:       []string{"static-port"},
			Decoder:     writ.NewOptionDecoder(&s.StaticPort),
			Description: "Port to use for the static file server, overridig value in config.json's DefaultStaticPort",
		},
		{
			Names:       []string{"prod"},
			Description: "If provided, will created bundled build directory for static resources.",
			Decoder:     writ.NewFlagDecoder(&s.Prod),
			Flag:        true,
		},
		{
			Names:       []string{"offline-dev-mode"},
			Description: "If provided, will override OfflineDevMode to true, no matter what is in config. The effect of this is that the webapp won't make any calls to anything but localhost, allowing development on for example a plane. This is generally the best way to enable offline dev mode.",
			Decoder:     writ.NewFlagDecoder(&s.OfflineDevMode),
			Flag:        true,
		},
	}
}
<|endoftext|>


<commit_before>package bootparam_test

import (
	"bytes"
	"encoding/binary"
	"testing"

	"github.com/bobuhiro11/gokvm/bootparam"
)

func TestNew(t *testing.T) {
	t.Parallel()

	if _, err := bootparam.New("../bzImage"); err != nil {
		t.Fatal(err)
	}
}

func TestNewNotbzImage(t *testing.T) {
	t.Parallel()

	if _, err := bootparam.New("../README.md"); err == nil {
		t.Fatal(err)
	}
}

func TestBytes(t *testing.T) {
	t.Parallel()

	b, _ := bootparam.New("../bzImage")

	if _, err := b.Bytes(); err != nil {
		t.Fatal(err)
	}
}

func TestAddE820Entry(t *testing.T) {
	t.Parallel()

	b, _ := bootparam.New("../bzImage")
	b.AddE820Entry(
		0x1234567812345678,
		0xabcdefabcdefabcd,
		bootparam.E820Ram,
	)

	rawBootParam, _ := b.Bytes()
	if rawBootParam[0x1E8] != 1 {
		t.Fatalf("invalid e820_entries: %d", rawBootParam[0x1E8])
	}

	actual := bootparam.E820Entry{}
	reader := bytes.NewReader(rawBootParam[0x2D0:])

	if err := binary.Read(reader, binary.LittleEndian, &actual); err != nil {
		t.Fatal(err)
	}

	if actual.Addr != 0x1234567812345678 {
		t.Fatalf("invalid e820 addr: %v", actual.Addr)
	}

	if actual.Size != 0xabcdefabcdefabcd {
		t.Fatalf("invalid e820 size: %v", actual.Size)
	}

	if actual.Type != bootparam.E820Ram {
		t.Fatalf("invalid e820 type: %v", actual.Type)
	}
}
<commit_msg>bootparam_test: skip test if ../bzImage can not be opened<commit_after>package bootparam_test

import (
	"bytes"
	"encoding/binary"
	"os"
	"testing"

	"github.com/bobuhiro11/gokvm/bootparam"
)

func TestNew(t *testing.T) {
	t.Parallel()

	// Do a test open for the bzimage. If it fails for any reason,
	// just skip this test.
	if _, err := os.Open("../bzImage"); err != nil {
		t.Skipf("Skipping this test: %v", err)
	}

	if _, err := bootparam.New("../bzImage"); err != nil {
		t.Fatal(err)
	}
}

func TestNewNotbzImage(t *testing.T) {
	t.Parallel()

	if _, err := bootparam.New("../README.md"); err == nil {
		t.Fatal(err)
	}
}

func TestBytes(t *testing.T) {
	t.Parallel()

	b, _ := bootparam.New("../bzImage")

	if _, err := b.Bytes(); err != nil {
		t.Fatal(err)
	}
}

func TestAddE820Entry(t *testing.T) {
	t.Parallel()

	b, _ := bootparam.New("../bzImage")
	b.AddE820Entry(
		0x1234567812345678,
		0xabcdefabcdefabcd,
		bootparam.E820Ram,
	)

	rawBootParam, _ := b.Bytes()
	if rawBootParam[0x1E8] != 1 {
		t.Fatalf("invalid e820_entries: %d", rawBootParam[0x1E8])
	}

	actual := bootparam.E820Entry{}
	reader := bytes.NewReader(rawBootParam[0x2D0:])

	if err := binary.Read(reader, binary.LittleEndian, &actual); err != nil {
		t.Fatal(err)
	}

	if actual.Addr != 0x1234567812345678 {
		t.Fatalf("invalid e820 addr: %v", actual.Addr)
	}

	if actual.Size != 0xabcdefabcdefabcd {
		t.Fatalf("invalid e820 size: %v", actual.Size)
	}

	if actual.Type != bootparam.E820Ram {
		t.Fatalf("invalid e820 type: %v", actual.Type)
	}
}
<|endoftext|>


<commit_before>/*
 * portal - marshal
 *
 * a library that implements an algorithm for doing consumer coordination within Kafka, rather
 * than using Zookeeper or another external system.
 *
 */

package marshal

import (
	"crypto/md5"
	"encoding/binary"
	"fmt"
	"sync"
	"sync/atomic"
	"time"

	"github.com/optiopay/kafka"
	"github.com/optiopay/kafka/proto"
)

// Marshaler is the coordinator type. It is designed to be used once globally and
// is thread safe. Creating one of these will create connections to your Kafka
// cluster and begin actively monitoring the coordination topic.
type Marshaler struct {
	// These members are not protected by the lock and can be read at any
	// time as they're write-once or only ever atomically updated. They must
	// never be overwritten once a Marshaler is created.
	quit       *int32
	clientID   string
	groupID    string
	jitters    chan time.Duration
	kafka      *kafka.Broker
	offsets    kafka.OffsetCoordinator
	partitions int

	// Lock protects the following members; you must have this lock in order to
	// read from or write to these.
	lock     sync.RWMutex
	topics   map[string]int
	groups   map[string]map[string]*topicState
	producer kafka.Producer

	// This WaitGroup is used for signalling when all of the rationalizers have
	// finished processing.
	rationalizers sync.WaitGroup

	// rsteps is updated whenever a rationalizer processes a log entry, this is
	// used mainly by the test suite.
	rsteps *int32

	// This is for testing only. When this is non-zero, the rationalizer will answer
	// queries based on THIS time instead of the current, actual time.
	ts int64
}

// refreshMetadata is periodically used to update our internal state with topic information
// about the world.
func (m *Marshaler) refreshMetadata() error {
	md, err := m.kafka.Metadata()
	if err != nil {
		return err
	}

	newTopics := make(map[string]int)
	for _, topic := range md.Topics {
		newTopics[topic.Name] = len(topic.Partitions)
	}

	m.lock.Lock()
	defer m.lock.Unlock()
	m.topics = newTopics
	return nil
}

// waitForRsteps is used by the test suite to ask the rationalizer to wait until some number
// of events have been processed. This also returns the current rsteps when it returns.
func (m *Marshaler) waitForRsteps(steps int) int {
	for {
		cval := atomic.LoadInt32(m.rsteps)
		if cval >= int32(steps) {
			return int(cval)
		}
		time.Sleep(5 * time.Millisecond)
	}
}

// getClaimPartition calculates which partition a topic should use for coordination. This uses
// a hashing function (non-cryptographic) to predictably partition the topic space.
func (m *Marshaler) getClaimPartition(topicName string) int {
	// We use MD5 because it's a fast and good hashing algorithm and we don't need cryptographic
	// properties. We then take the first 8 bytes and treat them as a uint64 and modulo that
	// across how many partitions we have.
	hash := md5.Sum([]byte(topicName))
	uval := binary.LittleEndian.Uint64(hash[0:8])
	return int(uval % uint64(m.partitions))
}

// getTopicState returns a topicState and possibly creates it and the partition state within
// the State.
func (m *Marshaler) getTopicState(topicName string, partID int) *topicState {
	m.lock.Lock()
	defer m.lock.Unlock()

	group, ok := m.groups[m.groupID]
	if !ok {
		group = make(map[string]*topicState)
		m.groups[m.groupID] = group
	}

	topic, ok := group[topicName]
	if !ok {
		topic = &topicState{
			claimPartition: m.getClaimPartition(topicName),
			partitions:     make([]PartitionClaim, partID+1),
		}
		group[topicName] = topic
	}

	// Take the topic lock if we can
	topic.lock.Lock()
	defer topic.lock.Unlock()

	// They might be referring to a partition we don't know about, maybe extend it
	// TODO: This should have the topic lock
	if len(topic.partitions) < partID+1 {
		for i := len(topic.partitions); i <= partID; i++ {
			topic.partitions = append(topic.partitions, PartitionClaim{})
		}
	}

	return topic
}

// Topics returns the list of known topics.
func (m *Marshaler) Topics() []string {
	m.lock.RLock()
	defer m.lock.RUnlock()

	topics := make([]string, 0, len(m.topics))
	for topic := range m.topics {
		topics = append(topics, topic)
	}
	return topics
}

// Partitions returns the count of how many partitions are in a given topic. Returns 0 if a
// topic is unknown.
func (m *Marshaler) Partitions(topicName string) int {
	m.lock.RLock()
	defer m.lock.RUnlock()

	count, _ := m.topics[topicName]
	return count
}

// Terminate is called when we're done with the marshaler and want to shut down.
func (m *Marshaler) Terminate() {
	atomic.StoreInt32(m.quit, 1)
}

// IsClaimed returns the current status on whether or not a partition is claimed by any other
// consumer in our group (including ourselves). A topic/partition that does not exist is
// considered to be unclaimed.
func (m *Marshaler) IsClaimed(topicName string, partID int) bool {
	// The contract of this method is that if it returns something and the heartbeat is
	// non-zero, the partition is claimed.
	claim := m.GetPartitionClaim(topicName, partID)
	return claim.LastHeartbeat > 0
}

// GetPartitionClaim returns a PartitionClaim structure for a given partition. The structure
// describes the consumer that is currently claiming this partition. This is a copy of the
// claim structure, so changing it cannot change the world state.
func (m *Marshaler) GetPartitionClaim(topicName string, partID int) PartitionClaim {
	topic := m.getTopicState(topicName, partID)

	topic.lock.RLock()
	defer topic.lock.RUnlock()

	if topic.partitions[partID].isClaimed(m.ts) {
		return topic.partitions[partID] // copy.
	}
	return PartitionClaim{}
}

// GetLastPartitionClaim returns a PartitionClaim structure for a given partition. The structure
// describes the consumer that is currently or most recently claiming this partition. This is a
// copy of the claim structure, so changing it cannot change the world state.
func (m *Marshaler) GetLastPartitionClaim(topicName string, partID int) PartitionClaim {
	topic := m.getTopicState(topicName, partID)

	topic.lock.RLock()
	defer topic.lock.RUnlock()

	return topic.partitions[partID] // copy.
}

// GetPartitionOffsets returns the current state of a topic/partition. This has to hit Kafka
// thrice to ask about a partition, but it returns the full state of information that can be
// used to calculate consumer lag.
func (m *Marshaler) GetPartitionOffsets(topicName string, partID int) (PartitionOffsets, error) {
	var err error

	o := PartitionOffsets{}
	o.Earliest, err = m.kafka.OffsetEarliest(topicName, int32(partID))
	if err != nil {
		return PartitionOffsets{}, err
	}

	o.Latest, err = m.kafka.OffsetLatest(topicName, int32(partID))
	if err != nil {
		return PartitionOffsets{}, err
	}

	o.Committed, _, err = m.offsets.Offset(topicName, int32(partID))
	if err != nil {
		return PartitionOffsets{}, err
	}

	// Use the last claim we know about, whatever it is
	claim := m.GetLastPartitionClaim(topicName, partID)
	o.Current = claim.LastOffset
	return o, nil
}

// ClaimPartition is how you can actually claim a partition. If you call this, Marshal will
// attempt to claim the partition on your behalf. This is the low level function, you probably
// want to use a MarshaledConsumer. Returns a bool on whether or not the claim succeeded and
// whether you can continue.
func (m *Marshaler) ClaimPartition(topicName string, partID int) bool {
	topic := m.getTopicState(topicName, partID)

	// Unlock is later, since this function might take a while
	// TODO: Move this logic to a func and defer the lock (for sanity sake)
	topic.lock.Lock()

	// If the topic is already claimed, we can short circuit the decision process
	if topic.partitions[partID].isClaimed(m.ts) {
		defer topic.lock.Unlock()
		if topic.partitions[partID].GroupID == m.groupID &&
			topic.partitions[partID].ClientID == m.clientID {
			return true
		}
		log.Warningf("Attempt to claim already claimed partition.")
		return false
	}

	// Make a channel for results, append it to the list so we hear about claims
	out := make(chan bool, 1)
	topic.partitions[partID].pendingClaims = append(
		topic.partitions[partID].pendingClaims, out)
	topic.lock.Unlock()

	// Produce message to kafka
	// TODO: Make this work on more than just partition 0. Hash by the topic/partition we're
	// trying to claim, or something...
	cl := &msgClaimingPartition{
		msgBase: msgBase{
			Time:     int(time.Now().Unix()),
			ClientID: m.clientID,
			GroupID:  m.groupID,
			Topic:    topicName,
			PartID:   partID,
		},
	}
	_, err := m.producer.Produce(MarshalTopic, int32(topic.claimPartition),
		&proto.Message{Value: []byte(cl.Encode())})
	if err != nil {
		// If we failed to produce, this is probably serious so we should undo the work
		// we did and then return failure
		log.Errorf("Failed to produce to Kafka: %s", err)
		return false
	}

	// Finally wait and return the result. The rationalizer should see the above message
	// and know it was from us, and will be able to know if we won or not.
	return <-out
}

// Heartbeat will send an update for other people to know that we're still alive and
// still owning this partition. Returns an error if anything has gone wrong (at which
// point we can no longer assert we have the lock).
func (m *Marshaler) Heartbeat(topicName string, partID int, lastOffset int64) error {
	topic := m.getTopicState(topicName, partID)

	topic.lock.RLock()
	defer topic.lock.RUnlock()

	// If the topic is not claimed, we can short circuit the decision process
	if !topic.partitions[partID].isClaimed(m.ts) {
		return fmt.Errorf("Partition %s:%d is not claimed!", topicName, partID)
	}

	// And if it's not claimed by us...
	if topic.partitions[partID].GroupID != m.groupID ||
		topic.partitions[partID].ClientID != m.clientID {
		return fmt.Errorf("Partition %s:%d is not claimed by us!", topicName, partID)
	}

	// All good, let's heartbeat
	cl := &msgHeartbeat{
		msgBase: msgBase{
			Time:     int(time.Now().Unix()),
			ClientID: m.clientID,
			GroupID:  m.groupID,
			Topic:    topicName,
			PartID:   partID,
		},
		LastOffset: lastOffset,
	}
	_, err := m.producer.Produce(MarshalTopic, int32(topic.claimPartition),
		&proto.Message{Value: []byte(cl.Encode())})
	if err != nil {
		return fmt.Errorf("Failed to produce heartbeat to Kafka: %s", err)
	}

	// Finally, also commit this offset to Kafka so it's available in the long-term storage
	// of the offset coordination system
	err = m.offsets.Commit(topicName, int32(partID), lastOffset)
	if err != nil {
		// Do not count this as a returned error as that will cause us to drop consumption, but
		// do log it so people can see it
		log.Errorf("%s:%d failed to commit offsets: %s", topicName, partID, err)
	}

	return nil
}

// ReleasePartition will send an update for other people to know that we're done with
// a partition. Returns an error if anything has gone wrong (at which
// point we can no longer assert we have the lock).
func (m *Marshaler) ReleasePartition(topicName string, partID int, lastOffset int64) error {
	topic := m.getTopicState(topicName, partID)

	topic.lock.RLock()
	defer topic.lock.RUnlock()

	// If the topic is not claimed, we can short circuit the decision process
	if !topic.partitions[partID].isClaimed(m.ts) {
		return fmt.Errorf("Partition %s:%d is not claimed!", topicName, partID)
	}

	// And if it's not claimed by us...
	if topic.partitions[partID].GroupID != m.groupID ||
		topic.partitions[partID].ClientID != m.clientID {
		return fmt.Errorf("Partition %s:%d is not claimed by us!", topicName, partID)
	}

	// All good, let's release
	cl := &msgReleasingPartition{
		msgBase: msgBase{
			Time:     int(time.Now().Unix()),
			ClientID: m.clientID,
			GroupID:  m.groupID,
			Topic:    topicName,
			PartID:   partID,
		},
		LastOffset: lastOffset,
	}
	_, err := m.producer.Produce(MarshalTopic, int32(topic.claimPartition),
		&proto.Message{Value: []byte(cl.Encode())})
	if err != nil {
		return fmt.Errorf("Failed to produce release to Kafka: %s", err)
	}

	// Finally, also commit this offset to Kafka so it's available in the long-term storage
	// of the offset coordination system
	err = m.offsets.Commit(topicName, int32(partID), lastOffset)
	if err != nil {
		// Do not count this as a returned error as that will cause us to drop consumption, but
		// do log it so people can see it
		log.Errorf("%s:%d failed to commit offsets: %s", topicName, partID, err)
	}

	return nil
}

// ClientID returns the client ID we're using
func (m *Marshaler) ClientID() string {
	return m.clientID
}

// GroupID returns the group ID we're using
func (m *Marshaler) GroupID() string {
	return m.groupID
}
<commit_msg>Fix committed offsets<commit_after>/*
 * portal - marshal
 *
 * a library that implements an algorithm for doing consumer coordination within Kafka, rather
 * than using Zookeeper or another external system.
 *
 */

package marshal

import (
	"crypto/md5"
	"encoding/binary"
	"fmt"
	"sync"
	"sync/atomic"
	"time"

	"github.com/optiopay/kafka"
	"github.com/optiopay/kafka/proto"
)

// Marshaler is the coordinator type. It is designed to be used once globally and
// is thread safe. Creating one of these will create connections to your Kafka
// cluster and begin actively monitoring the coordination topic.
type Marshaler struct {
	// These members are not protected by the lock and can be read at any
	// time as they're write-once or only ever atomically updated. They must
	// never be overwritten once a Marshaler is created.
	quit       *int32
	clientID   string
	groupID    string
	jitters    chan time.Duration
	kafka      *kafka.Broker
	offsets    kafka.OffsetCoordinator
	partitions int

	// Lock protects the following members; you must have this lock in order to
	// read from or write to these.
	lock     sync.RWMutex
	topics   map[string]int
	groups   map[string]map[string]*topicState
	producer kafka.Producer

	// This WaitGroup is used for signalling when all of the rationalizers have
	// finished processing.
	rationalizers sync.WaitGroup

	// rsteps is updated whenever a rationalizer processes a log entry, this is
	// used mainly by the test suite.
	rsteps *int32

	// This is for testing only. When this is non-zero, the rationalizer will answer
	// queries based on THIS time instead of the current, actual time.
	ts int64
}

// refreshMetadata is periodically used to update our internal state with topic information
// about the world.
func (m *Marshaler) refreshMetadata() error {
	md, err := m.kafka.Metadata()
	if err != nil {
		return err
	}

	newTopics := make(map[string]int)
	for _, topic := range md.Topics {
		newTopics[topic.Name] = len(topic.Partitions)
	}

	m.lock.Lock()
	defer m.lock.Unlock()
	m.topics = newTopics
	return nil
}

// waitForRsteps is used by the test suite to ask the rationalizer to wait until some number
// of events have been processed. This also returns the current rsteps when it returns.
func (m *Marshaler) waitForRsteps(steps int) int {
	for {
		cval := atomic.LoadInt32(m.rsteps)
		if cval >= int32(steps) {
			return int(cval)
		}
		time.Sleep(5 * time.Millisecond)
	}
}

// getClaimPartition calculates which partition a topic should use for coordination. This uses
// a hashing function (non-cryptographic) to predictably partition the topic space.
func (m *Marshaler) getClaimPartition(topicName string) int {
	// We use MD5 because it's a fast and good hashing algorithm and we don't need cryptographic
	// properties. We then take the first 8 bytes and treat them as a uint64 and modulo that
	// across how many partitions we have.
	hash := md5.Sum([]byte(topicName))
	uval := binary.LittleEndian.Uint64(hash[0:8])
	return int(uval % uint64(m.partitions))
}

// getTopicState returns a topicState and possibly creates it and the partition state within
// the State.
func (m *Marshaler) getTopicState(topicName string, partID int) *topicState {
	m.lock.Lock()
	defer m.lock.Unlock()

	group, ok := m.groups[m.groupID]
	if !ok {
		group = make(map[string]*topicState)
		m.groups[m.groupID] = group
	}

	topic, ok := group[topicName]
	if !ok {
		topic = &topicState{
			claimPartition: m.getClaimPartition(topicName),
			partitions:     make([]PartitionClaim, partID+1),
		}
		group[topicName] = topic
	}

	// Take the topic lock if we can
	topic.lock.Lock()
	defer topic.lock.Unlock()

	// They might be referring to a partition we don't know about, maybe extend it
	// TODO: This should have the topic lock
	if len(topic.partitions) < partID+1 {
		for i := len(topic.partitions); i <= partID; i++ {
			topic.partitions = append(topic.partitions, PartitionClaim{})
		}
	}

	return topic
}

// Topics returns the list of known topics.
func (m *Marshaler) Topics() []string {
	m.lock.RLock()
	defer m.lock.RUnlock()

	topics := make([]string, 0, len(m.topics))
	for topic := range m.topics {
		topics = append(topics, topic)
	}
	return topics
}

// Partitions returns the count of how many partitions are in a given topic. Returns 0 if a
// topic is unknown.
func (m *Marshaler) Partitions(topicName string) int {
	m.lock.RLock()
	defer m.lock.RUnlock()

	count, _ := m.topics[topicName]
	return count
}

// Terminate is called when we're done with the marshaler and want to shut down.
func (m *Marshaler) Terminate() {
	atomic.StoreInt32(m.quit, 1)
}

// IsClaimed returns the current status on whether or not a partition is claimed by any other
// consumer in our group (including ourselves). A topic/partition that does not exist is
// considered to be unclaimed.
func (m *Marshaler) IsClaimed(topicName string, partID int) bool {
	// The contract of this method is that if it returns something and the heartbeat is
	// non-zero, the partition is claimed.
	claim := m.GetPartitionClaim(topicName, partID)
	return claim.LastHeartbeat > 0
}

// GetPartitionClaim returns a PartitionClaim structure for a given partition. The structure
// describes the consumer that is currently claiming this partition. This is a copy of the
// claim structure, so changing it cannot change the world state.
func (m *Marshaler) GetPartitionClaim(topicName string, partID int) PartitionClaim {
	topic := m.getTopicState(topicName, partID)

	topic.lock.RLock()
	defer topic.lock.RUnlock()

	if topic.partitions[partID].isClaimed(m.ts) {
		return topic.partitions[partID] // copy.
	}
	return PartitionClaim{}
}

// GetLastPartitionClaim returns a PartitionClaim structure for a given partition. The structure
// describes the consumer that is currently or most recently claiming this partition. This is a
// copy of the claim structure, so changing it cannot change the world state.
func (m *Marshaler) GetLastPartitionClaim(topicName string, partID int) PartitionClaim {
	topic := m.getTopicState(topicName, partID)

	topic.lock.RLock()
	defer topic.lock.RUnlock()

	return topic.partitions[partID] // copy.
}

// GetPartitionOffsets returns the current state of a topic/partition. This has to hit Kafka
// thrice to ask about a partition, but it returns the full state of information that can be
// used to calculate consumer lag.
func (m *Marshaler) GetPartitionOffsets(topicName string, partID int) (PartitionOffsets, error) {
	var err error

	o := PartitionOffsets{}
	o.Earliest, err = m.kafka.OffsetEarliest(topicName, int32(partID))
	if err != nil {
		return PartitionOffsets{}, err
	}

	o.Latest, err = m.kafka.OffsetLatest(topicName, int32(partID))
	if err != nil {
		return PartitionOffsets{}, err
	}

	o.Committed, _, err = m.offsets.Offset(topicName, int32(partID))
	if err != nil {
		// This error happens when Kafka does not know about the partition i.e. no
		// offset has been committed here. In that case we ignore it.
		if err != proto.ErrUnknownTopicOrPartition {
			return PartitionOffsets{}, fmt.Errorf("offset fetch fail: %s", err)
		}
	}

	// Use the last claim we know about, whatever it is
	claim := m.GetLastPartitionClaim(topicName, partID)
	o.Current = claim.LastOffset
	return o, nil
}

// ClaimPartition is how you can actually claim a partition. If you call this, Marshal will
// attempt to claim the partition on your behalf. This is the low level function, you probably
// want to use a MarshaledConsumer. Returns a bool on whether or not the claim succeeded and
// whether you can continue.
func (m *Marshaler) ClaimPartition(topicName string, partID int) bool {
	topic := m.getTopicState(topicName, partID)

	// Unlock is later, since this function might take a while
	// TODO: Move this logic to a func and defer the lock (for sanity sake)
	topic.lock.Lock()

	// If the topic is already claimed, we can short circuit the decision process
	if topic.partitions[partID].isClaimed(m.ts) {
		defer topic.lock.Unlock()
		if topic.partitions[partID].GroupID == m.groupID &&
			topic.partitions[partID].ClientID == m.clientID {
			return true
		}
		log.Warningf("Attempt to claim already claimed partition.")
		return false
	}

	// Make a channel for results, append it to the list so we hear about claims
	out := make(chan bool, 1)
	topic.partitions[partID].pendingClaims = append(
		topic.partitions[partID].pendingClaims, out)
	topic.lock.Unlock()

	// Produce message to kafka
	// TODO: Make this work on more than just partition 0. Hash by the topic/partition we're
	// trying to claim, or something...
	cl := &msgClaimingPartition{
		msgBase: msgBase{
			Time:     int(time.Now().Unix()),
			ClientID: m.clientID,
			GroupID:  m.groupID,
			Topic:    topicName,
			PartID:   partID,
		},
	}
	_, err := m.producer.Produce(MarshalTopic, int32(topic.claimPartition),
		&proto.Message{Value: []byte(cl.Encode())})
	if err != nil {
		// If we failed to produce, this is probably serious so we should undo the work
		// we did and then return failure
		log.Errorf("Failed to produce to Kafka: %s", err)
		return false
	}

	// Finally wait and return the result. The rationalizer should see the above message
	// and know it was from us, and will be able to know if we won or not.
	return <-out
}

// Heartbeat will send an update for other people to know that we're still alive and
// still owning this partition. Returns an error if anything has gone wrong (at which
// point we can no longer assert we have the lock).
func (m *Marshaler) Heartbeat(topicName string, partID int, lastOffset int64) error {
	topic := m.getTopicState(topicName, partID)

	topic.lock.RLock()
	defer topic.lock.RUnlock()

	// If the topic is not claimed, we can short circuit the decision process
	if !topic.partitions[partID].isClaimed(m.ts) {
		return fmt.Errorf("Partition %s:%d is not claimed!", topicName, partID)
	}

	// And if it's not claimed by us...
	if topic.partitions[partID].GroupID != m.groupID ||
		topic.partitions[partID].ClientID != m.clientID {
		return fmt.Errorf("Partition %s:%d is not claimed by us!", topicName, partID)
	}

	// All good, let's heartbeat
	cl := &msgHeartbeat{
		msgBase: msgBase{
			Time:     int(time.Now().Unix()),
			ClientID: m.clientID,
			GroupID:  m.groupID,
			Topic:    topicName,
			PartID:   partID,
		},
		LastOffset: lastOffset,
	}
	_, err := m.producer.Produce(MarshalTopic, int32(topic.claimPartition),
		&proto.Message{Value: []byte(cl.Encode())})
	if err != nil {
		return fmt.Errorf("Failed to produce heartbeat to Kafka: %s", err)
	}

	// Finally, also commit this offset to Kafka so it's available in the long-term storage
	// of the offset coordination system
	err = m.offsets.Commit(topicName, int32(partID), lastOffset)
	if err != nil {
		// Do not count this as a returned error as that will cause us to drop consumption, but
		// do log it so people can see it
		log.Errorf("%s:%d failed to commit offsets: %s", topicName, partID, err)
	}

	return nil
}

// ReleasePartition will send an update for other people to know that we're done with
// a partition. Returns an error if anything has gone wrong (at which
// point we can no longer assert we have the lock).
func (m *Marshaler) ReleasePartition(topicName string, partID int, lastOffset int64) error {
	topic := m.getTopicState(topicName, partID)

	topic.lock.RLock()
	defer topic.lock.RUnlock()

	// If the topic is not claimed, we can short circuit the decision process
	if !topic.partitions[partID].isClaimed(m.ts) {
		return fmt.Errorf("Partition %s:%d is not claimed!", topicName, partID)
	}

	// And if it's not claimed by us...
	if topic.partitions[partID].GroupID != m.groupID ||
		topic.partitions[partID].ClientID != m.clientID {
		return fmt.Errorf("Partition %s:%d is not claimed by us!", topicName, partID)
	}

	// All good, let's release
	cl := &msgReleasingPartition{
		msgBase: msgBase{
			Time:     int(time.Now().Unix()),
			ClientID: m.clientID,
			GroupID:  m.groupID,
			Topic:    topicName,
			PartID:   partID,
		},
		LastOffset: lastOffset,
	}
	_, err := m.producer.Produce(MarshalTopic, int32(topic.claimPartition),
		&proto.Message{Value: []byte(cl.Encode())})
	if err != nil {
		return fmt.Errorf("Failed to produce release to Kafka: %s", err)
	}

	// Finally, also commit this offset to Kafka so it's available in the long-term storage
	// of the offset coordination system
	err = m.offsets.Commit(topicName, int32(partID), lastOffset)
	if err != nil {
		// Do not count this as a returned error as that will cause us to drop consumption, but
		// do log it so people can see it
		log.Errorf("%s:%d failed to commit offsets: %s", topicName, partID, err)
	}

	return nil
}

// ClientID returns the client ID we're using
func (m *Marshaler) ClientID() string {
	return m.clientID
}

// GroupID returns the group ID we're using
func (m *Marshaler) GroupID() string {
	return m.groupID
}
<|endoftext|>


<commit_before>package matchers

var (
	TypeWoff  = newType("woff", "application/font-woff")
	TypeWoff2 = newType("woff2", "application/font-woff")
	TypeTtf   = newType("ttf", "application/font-sfnt")
	TypeOtf   = newType("otf", "application/font-sfnt")
)

var Font = Map{
	TypeWoff:  Woff,
	TypeWoff2: Woff2,
	TypeTtf:   Ttf,
	TypeOtf:   Otf,
}

func Woff(buf []byte) bool {
	return len(buf) > 7 &&
		buf[0] == 0x77 && buf[1] == 0x4F &&
		buf[2] == 0x46 && buf[3] == 0x46 &&
		buf[4] == 0x00 && buf[5] == 0x01 &&
		buf[6] == 0x00 && buf[7] == 0x00
}

func Woff2(buf []byte) bool {
	return len(buf) > 7 &&
		buf[0] == 0x77 && buf[1] == 0x4F &&
		buf[2] == 0x46 && buf[3] == 0x32 &&
		buf[4] == 0x00 && buf[5] == 0x01 &&
		buf[6] == 0x00 && buf[7] == 0x00
}

func Ttf(buf []byte) bool {
	return len(buf) > 4 &&
		buf[0] == 0x00 && buf[1] == 0x01 &&
		buf[2] == 0x00 && buf[3] == 0x00 &&
		buf[4] == 0x00
}

func Otf(buf []byte) bool {
	return len(buf) > 4 &&
		buf[0] == 0x4F && buf[1] == 0x54 &&
		buf[2] == 0x54 && buf[3] == 0x4F &&
		buf[4] == 0x00
}
<commit_msg>Add support check file type: .doc, .docx, .pptx, .ppt, .xls, .xlsx<commit_after>package matchers

var (
	TypeWoff  = newType("woff", "application/font-woff")
	TypeWoff2 = newType("woff2", "application/font-woff")
	TypeTtf   = newType("ttf", "application/font-sfnt")
	TypeOtf   = newType("otf", "application/font-sfnt")
	TypeDoc   = newType("doc", "application/msword")
	TypeDocx  = newType("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
	TypeXls   = newType("xls", "application/vnd.ms-excel")
	TypeXlsx  = newType("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
	TypePpt   = newType("ppt", "application/vnd.ms-powerpoint")
	TypePptx  = newType("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation")
)

var Font = Map{
	TypeWoff:  Woff,
	TypeWoff2: Woff2,
	TypeTtf:   Ttf,
	TypeOtf:   Otf,
	TypeDoc:   Doc,
	TypeDocx:  Docx,
	TypeXls:   Xls,
	TypeXlsx:  Xlsx,
	TypePpt:   Ppt,
	TypePptx:  Pptx,
}

func Woff(buf []byte) bool {
	return len(buf) > 7 &&
		buf[0] == 0x77 && buf[1] == 0x4F &&
		buf[2] == 0x46 && buf[3] == 0x46 &&
		buf[4] == 0x00 && buf[5] == 0x01 &&
		buf[6] == 0x00 && buf[7] == 0x00
}

func Woff2(buf []byte) bool {
	return len(buf) > 7 &&
		buf[0] == 0x77 && buf[1] == 0x4F &&
		buf[2] == 0x46 && buf[3] == 0x32 &&
		buf[4] == 0x00 && buf[5] == 0x01 &&
		buf[6] == 0x00 && buf[7] == 0x00
}

func Ttf(buf []byte) bool {
	return len(buf) > 4 &&
		buf[0] == 0x00 && buf[1] == 0x01 &&
		buf[2] == 0x00 && buf[3] == 0x00 &&
		buf[4] == 0x00
}

func Otf(buf []byte) bool {
	return len(buf) > 4 &&
		buf[0] == 0x4F && buf[1] == 0x54 &&
		buf[2] == 0x54 && buf[3] == 0x4F &&
		buf[4] == 0x00
}

func Doc(buf []byte) bool {
	return len(buf) > 7 &&
		buf[0] == 0xD0 && buf[1] == 0xCF &&
		buf[2] == 0x11 && buf[3] == 0xE0 &&
		buf[4] == 0xA1 && buf[5] == 0xB1 &&
		buf[6] == 0x1A && buf[7] == 0xE1
}

func Docx(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x50 && buf[1] == 0x4B &&
		buf[2] == 0x03 && buf[3] == 0x04
}

func Xls(buf []byte) bool {
	return len(buf) > 7 &&
		buf[0] == 0xD0 && buf[1] == 0xCF &&
		buf[2] == 0x11 && buf[3] == 0xE0 &&
		buf[4] == 0xA1 && buf[5] == 0xB1 &&
		buf[6] == 0x1A && buf[7] == 0xE1
}

func Xlsx(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x50 && buf[1] == 0x4B &&
		buf[2] == 0x03 && buf[3] == 0x04
}

func Ppt(buf []byte) bool {
	return len(buf) > 7 &&
		buf[0] == 0xD0 && buf[1] == 0xCF &&
		buf[2] == 0x11 && buf[3] == 0xE0 &&
		buf[4] == 0xA1 && buf[5] == 0xB1 &&
		buf[6] == 0x1A && buf[7] == 0xE1
}

func Pptx(buf []byte) bool {
	return len(buf) > 3 &&
		buf[0] == 0x50 && buf[1] == 0x4B &&
		buf[2] == 0x07 && buf[3] == 0x08
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net"
	"os"
	"time"

	"github.com/dt/go-metrics-graphite"
	"github.com/rcrowley/go-metrics"
)

var Stats metrics.Registry

func SetupStats(sendToConsole bool, sendToGraphite, graphitePrefix string) {
	r := metrics.NewRegistry()

	if sendToGraphite != "" {
		log.Println("Stats reporting to graphite: ", sendToGraphite)
		addr, _ := net.ResolveTCPAddr("tcp", sendToGraphite)

		cfg := graphite.GraphiteConfig{
			Addr:          addr,
			Registry:      r,
			FlushInterval: 15 * time.Second,
			DurationUnit:  time.Millisecond,
			Prefix:        graphitePrefix,
			Percentiles:   []float64{0.5, 0.75, 0.9, 0.95, 0.99, 0.999},
		}

		go graphite.GraphiteWithConfig(cfg)
	}

	if sendToConsole {
		log.Println("Stats reporting enabled...")
		go metrics.Log(r, time.Minute, log.New(os.Stderr, "metrics: ", log.Lmicroseconds))
	}

	Stats = r
}
<commit_msg>add debug info to stats<commit_after>package main

import (
	"log"
	"net"
	"os"
	"time"

	"github.com/dt/go-metrics-graphite"
	"github.com/rcrowley/go-metrics"
)

var Stats metrics.Registry

func SetupStats(sendToConsole bool, sendToGraphite, graphitePrefix string) {
	r := metrics.NewRegistry()

	if sendToGraphite != "" {
		log.Println("Stats reporting to graphite: ", sendToGraphite)
		addr, _ := net.ResolveTCPAddr("tcp", sendToGraphite)

		cfg := graphite.GraphiteConfig{
			Addr:          addr,
			Registry:      r,
			FlushInterval: 15 * time.Second,
			DurationUnit:  time.Millisecond,
			Prefix:        graphitePrefix,
			Percentiles:   []float64{0.5, 0.75, 0.9, 0.95, 0.99, 0.999},
		}

		go graphite.GraphiteWithConfig(cfg)
	}

	if sendToConsole {
		log.Println("Stats reporting enabled...")
		go metrics.Log(r, time.Minute, log.New(os.Stderr, "metrics: ", log.Lmicroseconds))
	}

	metrics.RegisterDebugGCStats(r)
	go metrics.CaptureRuntimeMemStats(r, 15*time.Second)

	metrics.RegisterRuntimeMemStats(r)
	go metrics.CaptureDebugGCStats(r, 15*time.Second)

	Stats = r
}
<|endoftext|>


<commit_before>// Package store automatically configures a database to store structured information in an sql database
package store

import (
	"database/sql"
	"errors"
	"reflect"
	"strings"
	"sync"
)

const (
	add = iota
	get
	update
	remove
	getPage
	count
)

type field struct {
	name      string
	pos       int
	isPointer bool
}

type typeInfo struct {
	primary    int
	fields     []field
	statements []*sql.Stmt
}

type Store struct {
	db    *sql.DB
	types map[string]typeInfo
	mutex sync.Mutex
}

func New(driverName, dataSourceName string) (*Store, error) {
	db, err := sql.Open(driverName, dataSourceName)
	if err != nil {
		return nil, err
	}
	return &Store{
		db:    db,
		types: make(map[string]typeInfo),
	}, nil
}

func (s *Store) Close() error {
	err := s.db.Close()
	s.db = nil
	return err
}

func isPointerStruct(i interface{}) bool {
	t := reflect.TypeOf(i)
	return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
}

func (s *Store) Register(i interface{}) error {
	if s.db == nil {
		return DBClosed
	} else if !isPointerStruct(i) {
		return NoPointerStruct
	}
	s.mutex.Lock()
	defer s.mutex.Unlock()
	return s.defineType(i)
}

func (s *Store) defineType(i interface{}) error {
	name := typeName(i)
	if _, ok := s.types[name]; ok {
		return nil
	}

	s.types[name] = typeInfo{}

	v := reflect.ValueOf(i).Elem()
	numFields := v.Type().NumField()
	fields := make([]field, 0, numFields)
	id := 0
	idType := 0

	for n := 0; n < numFields; n++ {
		f := v.Type().Field(n)
		if f.PkgPath != "" { // not exported
			continue
		}
		fieldName := f.Name
		if fn := f.Tag.Get("store"); fn != "" {
			fieldName = fn
		}
		if fieldName == "-" { // Skip field
			continue
		}
		tmp := strings.ToLower(fieldName)
		for _, tf := range fields {
			if strings.ToLower(tf.name) == tmp {
				return DuplicateColumn
			}
		}
		isPointer := f.Type.Kind() == reflect.Ptr
		var iface interface{}
		if isPointer {
			iface = v.Field(n).Interface()
		} else {
			iface = v.Field(n).Addr().Interface()
		}
		if isPointerStruct(iface) {
			s.defineType(iface)
		} else if !isValidType(iface) {
			continue
		}
		if isValidKeyType(iface) {
			if idType < 3 && f.Tag.Get("key") == "1" {
				idType = 3
				id = len(fields)
			} else if idType < 2 && strings.ToLower(fieldName) == "id" {
				idType = 2
				id = len(fields)
			} else if idType < 1 {
				idType = 1
				id = len(fields)
			}
		}
		fields = append(fields, field{
			fieldName,
			n,
			isPointer,
		})
	}
	if idType == 0 {
		return NoKey
	}
	s.types[name] = typeInfo{
		primary: id,
	}

	// create statements
	var (
		sqlVars, sqlParams, setSQLParams, tableVars string
		doneFirst, doneFirstNonKey                  bool
	)

	for pos, f := range fields {
		if doneFirst {
			tableVars += ", "
		} else {
			doneFirst = true
		}
		if pos != id {
			if doneFirstNonKey {
				sqlVars += ", "
				setSQLParams += ", "
				sqlParams += ", "
			} else {
				doneFirstNonKey = true
			}
		}
		varType := getType(i, pos)
		tableVars += "[" + f.name + "] " + varType
		if pos == id {
			tableVars += " PRIMARY KEY AUTOINCREMENT"
		} else {
			sqlVars += "[" + f.name + "]"
			setSQLParams += "[" + f.name + "] = ?"
			sqlParams += "?"
		}
	}

	statements := make([]*sql.Stmt, 6)

	sql := "CREATE TABLE IF NOT EXISTS [" + name + "](" + tableVars + ");"
	_, err := s.db.Exec(sql)
	if err != nil {
		return err
	}

	sql = "INSERT INTO [" + name + "] (" + sqlVars + ") VALUES (" + sqlParams + ");"
	stmt, err := s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[add] = stmt

	sql = "SELECT " + sqlVars + " FROM [" + name + "] WHERE [" + fields[id].name + "] = ? LIMIT 1;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[get] = stmt

	sql = "UPDATE [" + name + "] SET " + setSQLParams + " WHERE [" + fields[id].name + "] = ?;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[update] = stmt

	sql = "DELETE FROM [" + name + "] WHERE [" + fields[id].name + "] = ?;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[remove] = stmt

	sql = "SELECT " + sqlVars + ", [" + fields[id].name + "] FROM [" + name + "] ORDER BY [" + fields[id].name + "] LIMIT ? OFFSET ?;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[getPage] = stmt

	sql = "SELECT COUNT(1) FROM [" + name + "];"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[count] = stmt

	s.types[name] = typeInfo{
		primary:    id,
		fields:     fields,
		statements: statements,
	}
	return nil
}

// Errors

var (
	DBClosed        = errors.New("database already closed")
	NoPointerStruct = errors.New("given variable is not a pointer to a struct")
	NoKey           = errors.New("could not determine key")
	DuplicateColumn = errors.New("duplicate column name found")
)
<commit_msg>Added missing type check for struct fields<commit_after>// Package store automatically configures a database to store structured information in an sql database
package store

import (
	"database/sql"
	"errors"
	"reflect"
	"strings"
	"sync"
)

const (
	add = iota
	get
	update
	remove
	getPage
	count
)

type field struct {
	name      string
	pos       int
	isPointer bool
}

type typeInfo struct {
	primary    int
	fields     []field
	statements []*sql.Stmt
}

type Store struct {
	db    *sql.DB
	types map[string]typeInfo
	mutex sync.Mutex
}

func New(driverName, dataSourceName string) (*Store, error) {
	db, err := sql.Open(driverName, dataSourceName)
	if err != nil {
		return nil, err
	}
	return &Store{
		db:    db,
		types: make(map[string]typeInfo),
	}, nil
}

func (s *Store) Close() error {
	err := s.db.Close()
	s.db = nil
	return err
}

func isPointerStruct(i interface{}) bool {
	t := reflect.TypeOf(i)
	return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
}

func (s *Store) Register(i interface{}) error {
	if s.db == nil {
		return DBClosed
	} else if !isPointerStruct(i) {
		return NoPointerStruct
	}
	s.mutex.Lock()
	defer s.mutex.Unlock()
	return s.defineType(i)
}

func (s *Store) defineType(i interface{}) error {
	name := typeName(i)
	if _, ok := s.types[name]; ok {
		return nil
	}

	s.types[name] = typeInfo{}

	v := reflect.ValueOf(i).Elem()
	numFields := v.Type().NumField()
	fields := make([]field, 0, numFields)
	id := 0
	idType := 0

	for n := 0; n < numFields; n++ {
		f := v.Type().Field(n)
		if f.PkgPath != "" { // not exported
			continue
		}
		fieldName := f.Name
		if fn := f.Tag.Get("store"); fn != "" {
			fieldName = fn
		}
		if fieldName == "-" { // Skip field
			continue
		}
		tmp := strings.ToLower(fieldName)
		for _, tf := range fields {
			if strings.ToLower(tf.name) == tmp {
				return DuplicateColumn
			}
		}
		isPointer := f.Type.Kind() == reflect.Ptr
		var iface interface{}
		if isPointer {
			iface = v.Field(n).Interface()
		} else {
			iface = v.Field(n).Addr().Interface()
		}
		if isPointerStruct(iface) {
			s.defineType(iface)
		} else if !isValidType(iface) {
			continue
		}
		if isValidKeyType(iface) {
			if idType < 3 && f.Tag.Get("key") == "1" {
				idType = 3
				id = len(fields)
			} else if idType < 2 && strings.ToLower(fieldName) == "id" {
				idType = 2
				id = len(fields)
			} else if idType < 1 {
				idType = 1
				id = len(fields)
			}
		}
		fields = append(fields, field{
			fieldName,
			n,
			isPointer,
		})
	}
	if idType == 0 {
		return NoKey
	}
	s.types[name] = typeInfo{
		primary: id,
	}

	// create statements
	var (
		sqlVars, sqlParams, setSQLParams, tableVars string
		doneFirst, doneFirstNonKey                  bool
	)

	for pos, f := range fields {
		if doneFirst {
			tableVars += ", "
		} else {
			doneFirst = true
		}
		if pos != id {
			if doneFirstNonKey {
				sqlVars += ", "
				setSQLParams += ", "
				sqlParams += ", "
			} else {
				doneFirstNonKey = true
			}
		}
		var varType string
		if isPointerStruct(getFieldPointer(i, f.pos)) {
			varType = "INTEGER"
		} else {
			varType = getType(i, f.pos)
		}
		tableVars += "[" + f.name + "] " + varType
		if pos == id {
			tableVars += " PRIMARY KEY AUTOINCREMENT"
		} else {
			sqlVars += "[" + f.name + "]"
			setSQLParams += "[" + f.name + "] = ?"
			sqlParams += "?"
		}
	}

	statements := make([]*sql.Stmt, 6)

	sql := "CREATE TABLE IF NOT EXISTS [" + name + "](" + tableVars + ");"
	_, err := s.db.Exec(sql)
	if err != nil {
		return err
	}

	sql = "INSERT INTO [" + name + "] (" + sqlVars + ") VALUES (" + sqlParams + ");"
	stmt, err := s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[add] = stmt

	sql = "SELECT " + sqlVars + " FROM [" + name + "] WHERE [" + fields[id].name + "] = ? LIMIT 1;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[get] = stmt

	sql = "UPDATE [" + name + "] SET " + setSQLParams + " WHERE [" + fields[id].name + "] = ?;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[update] = stmt

	sql = "DELETE FROM [" + name + "] WHERE [" + fields[id].name + "] = ?;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[remove] = stmt

	sql = "SELECT " + sqlVars + ", [" + fields[id].name + "] FROM [" + name + "] ORDER BY [" + fields[id].name + "] LIMIT ? OFFSET ?;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[getPage] = stmt

	sql = "SELECT COUNT(1) FROM [" + name + "];"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[count] = stmt

	s.types[name] = typeInfo{
		primary:    id,
		fields:     fields,
		statements: statements,
	}
	return nil
}

// Errors

var (
	DBClosed        = errors.New("database already closed")
	NoPointerStruct = errors.New("given variable is not a pointer to a struct")
	NoKey           = errors.New("could not determine key")
	DuplicateColumn = errors.New("duplicate column name found")
)
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"strconv"
	//"strconv"
	"encoding/json"
	"time"
	//"strings"
	"github.com/hyperledger/fabric/core/chaincode/shim"
)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

var dispatchOrderIndexstr = "_dispatchOrderindex"

//==============================================================================================================================
//	 Status types - contract lifecycle is broken down into 5 statuses, this is part of the business logic to determine what can
//					be done to the vehicle at points in it's lifecycle
//==============================================================================================================================
const STATE_OBD_REQUEST_CREATED = 0
const STATE_READY_FOR_DISPATCH = 1
const STATE_ARRIVAL_OF_TRANSPORTER = 2
const STATE_READY_FOR_SHIPMENT = 3
const STATE_IN_TRANSIT = 4
const STATE_SHIPMENT_DELIVERED = 5
const STATE_AMENDED = 6
const STATE_DROPPED = 7

const SELLER = "seller"
const TRANSPORTER = "transporter"
const BUYER = "lease_company"

type DispatchOrderObject struct {
	dispatchOrderId string
	stage           string
	customer        string
	timeStamp       string // This is the time stamp
}

var tables = []string{"AssetTable", "TransactionHistory", "DocumentTable"}

func GetNumberOfKeys(tname string) int {
	TableMap := map[string]int{
		"AssetTable":         2,
		"TransactionHistory": 3,
		"DocumentTable":      2,
	}
	return TableMap[tname]
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

// Init initializes the chain and two tables - one for asset and other for transaction history
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {

	fmt.Println("Application Init")
	var err error

	for _, val := range tables {
		err = stub.DeleteTable(val)
		if err != nil {
			return nil, fmt.Errorf("Init(): DeleteTable of %s  Failed ", val)
		}
		err = InitLedger(stub, val)
		if err != nil {
			return nil, fmt.Errorf("Init(): InitLedger of %s  Failed ", val)
		}
	}

	err = stub.PutState("_dispatchOrderindex", []byte(dispatchOrderIndexstr))
	if err != nil {
		return nil, err
	}

	fmt.Println("Init() Initialization Complete  : ", args)
	return []byte("Init(): Initialization Complete"), nil
}

func InitLedger(stub shim.ChaincodeStubInterface, tableName string) error {

	// Generic Table Creation Function - requires Table Name and Table Key Entry
	// Create Table - Get number of Keys the tables supports
	// This version assumes all Keys are String and the Data is Bytes

	nKeys := GetNumberOfKeys(tableName)
	if nKeys < 1 {
		fmt.Println("Atleast 1 Key must be provided \n")
		fmt.Println("Auction_Application: Failed creating Table ", tableName)
		return errors.New("Auction_Application: Failed creating Table " + tableName)
	}

	var columnDefsForTbl []*shim.ColumnDefinition

	for i := 0; i < nKeys; i++ {
		columnDef := shim.ColumnDefinition{Name: "keyName" + strconv.Itoa(i), Type: shim.ColumnDefinition_STRING, Key: true}
		columnDefsForTbl = append(columnDefsForTbl, &columnDef)
	}

	columnLastTblDef := shim.ColumnDefinition{Name: "Details", Type: shim.ColumnDefinition_BYTES, Key: false}
	columnDefsForTbl = append(columnDefsForTbl, &columnLastTblDef)

	// Create the Table (Nil is returned if the Table exists or if the table is created successfully
	err := stub.CreateTable(tableName, columnDefsForTbl)

	if err != nil {
		fmt.Println("Auction_Application: Failed creating Table ", tableName)
		return errors.New("Auction_Application: Failed creating Table " + tableName)
	}

	return err
}

// Invoke is our entry point to invoke a chaincode function
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("invoke is running " + function)

	if function == "createDispatchOrder" {
		return t.createDispatchOrder(stub, args)
	}
	fmt.Println("invoke did not find func: " + function) //error

	return nil, errors.New("Received unknown function invocation: " + function)
}

// Query queries the hyperledger
func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("query is running " + function)

	// Handle different functions
	if function == "keys" {
		return t.getAllKeys(stub, args)
	} else if function == "read" { //read a contract
		return t.read(stub, args)
	}

	fmt.Println("query did not find func: " + function) //error
	return nil, errors.New("Received unknown function query " + function)
}

func (t *SimpleChaincode) createDispatchOrder(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var err error

	//convert the arguments into an Diapatch order Object
	dispatchObject, err := CreateDispatchOrderObject(args[0:])
	if err != nil {
		fmt.Println("createDispatchOrder(): Cannot create dispatch object ")
		return nil, errors.New("createDispatchOrder(): Cannot create dipatch object")
	}

	// check if the DispatchOrder already exists
	contractAsBytes, err := stub.GetState(dispatchObject.dispatchOrderId)
	if err != nil {
		fmt.Println("createDispatchOrder() : failed to get contract")
		return nil, errors.New("Failed to get dispatchOrder")
	}
	if contractAsBytes != nil {
		fmt.Println("initContract() : contract already exists for ", dispatchObject.dispatchOrderId)
		jsonResp := "{\"Error\":\"Failed - contract already exists " + dispatchObject.dispatchOrderId + "\"}"
		return nil, errors.New(jsonResp)
	}

	buff, err := doToJSON(dispatchObject)
	if err != nil {
		errorStr := "initContract() : Failed Cannot create object buffer for write : " + args[0]
		fmt.Println(errorStr)
		return nil, errors.New(errorStr)
	}
	fmt.Println("createDispatchOrder() : buffer", buff)
	err = stub.PutState(args[0], buff)
	if err != nil {
		fmt.Println("initContract() : write error while inserting record\n")
		return nil, errors.New("initContract() : write error while inserting record : " + err.Error())
	}

	// make an entry into transaction history table

	return nil, nil
}

// read function return value
func (t *SimpleChaincode) readContract(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var name, jsonResp string
	var err error

	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting name of the var to query")
	}

	name = args[0]
	fmt.Println("document id to be queried is " + name)
	valAsbytes, err := stub.GetState(name)
	if err != nil {
		jsonResp = "{\"Error\":\"Failed to get state for " + name + "\"}"
		return nil, errors.New(jsonResp)
	}
	if valAsbytes == nil {
		jsonResp = "{\"null object\":\"in ledger for key " + name + "\"}"
		return nil, errors.New(jsonResp)
	}
	fmt.Println("read contract output ", valAsbytes)
	return valAsbytes, nil
}

// read - query function to read key/value pair
func (t *SimpleChaincode) read(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var key, jsonResp string
	var err error

	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting name of the key to query")
	}

	key = args[0]
	valAsbytes, err := stub.GetState(key)
	if err != nil {
		jsonResp = "{\"Error\":\"Failed to get state for " + key + "\"}"
		return nil, errors.New(jsonResp)
	}
	fmt.Println("read contract output ", valAsbytes)
	return valAsbytes, nil
}

func (t *SimpleChaincode) getAllKeys(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {

	if len(args) < 2 {
		return nil, errors.New("put operation must include two arguments, a key and value")
	}

	startKey := args[0]
	endKey := args[1]

	keysIter, err := stub.RangeQueryState(startKey, endKey)

	if err != nil {
		return nil, errors.New(fmt.Sprintf("keys operation failed. Error accessing state: %s", err))
	}
	defer keysIter.Close()
	var keys []string
	for keysIter.HasNext() {
		response, _, iterErr := keysIter.Next()
		if iterErr != nil {
			return nil, errors.New(fmt.Sprintf("keys operation failed. Error accessing state: %s", err))
		}
		keys = append(keys, response)
	}

	for key, value := range keys {
		fmt.Printf("key %d contains %s\n", key, value)
	}

	jsonKeys, err := json.Marshal(keys)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("keys operation failed. Error accessing state: %s", err))
	}

	return jsonKeys, nil
}

// CreateContractObject creates an contract
func CreateDispatchOrderObject(args []string) (DispatchOrderObject, error) {
	// S001 LHTMO bosch
	var err error
	var myDispatchOrder DispatchOrderObject

	// Check there are 31 Arguments provided as per the the struct, time is computed
	if len(args) != 3 {
		fmt.Println("CreateDispatchOrderObject(): Incorrect number of arguments. Expecting 31 ")
		return myDispatchOrder, errors.New("CreateDispatchOrderObject(): Incorrect number of arguments. Expecting 31 ")
	}

	//check whether the dispatch order already exists
	myDispatchOrder = DispatchOrderObject{args[0], strconv.Itoa(STATE_OBD_REQUEST_CREATED), args[2], time.Now().Format("20060102150405")}
	if err != nil {
		fmt.Println(err)
		return myDispatchOrder, err
	}
	fmt.Println("CreateDispatchOrderObject(): dispatch Object created: ", myDispatchOrder.dispatchOrderId, myDispatchOrder.stage, myDispatchOrder.customer, myDispatchOrder.timeStamp)
	return myDispatchOrder, nil
}

// doToJSON Converts an dispatch Object to a JSON String
func doToJSON(c DispatchOrderObject) ([]byte, error) {
	cjson, err := json.Marshal(c)
	if err != nil {
		fmt.Println(err)
		return nil, err
	}
	fmt.Println("dispatch object as bytes ", cjson)
	return cjson, nil
}
<commit_msg>last attempt<commit_after>package main

import (
	"errors"
	"fmt"
	"strconv"
	//"strconv"
	"encoding/json"
	"time"
	//"strings"
	"github.com/hyperledger/fabric/core/chaincode/shim"
)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

var dispatchOrderIndexstr = "_dispatchOrderindex"

//==============================================================================================================================
//	 Status types - contract lifecycle is broken down into 5 statuses, this is part of the business logic to determine what can
//					be done to the vehicle at points in it's lifecycle
//==============================================================================================================================
const STATE_OBD_REQUEST_CREATED = 0
const STATE_READY_FOR_DISPATCH = 1
const STATE_ARRIVAL_OF_TRANSPORTER = 2
const STATE_READY_FOR_SHIPMENT = 3
const STATE_IN_TRANSIT = 4
const STATE_SHIPMENT_DELIVERED = 5
const STATE_AMENDED = 6
const STATE_DROPPED = 7

const SELLER = "seller"
const TRANSPORTER = "transporter"
const BUYER = "lease_company"

type DispatchOrderObject struct {
	dispatchOrderId string
	stage           string
	customer        string
	timeStamp       string // This is the time stamp
}

var tables = []string{"AssetTable", "TransactionHistory", "DocumentTable"}

func GetNumberOfKeys(tname string) int {
	TableMap := map[string]int{
		"AssetTable":         2,
		"TransactionHistory": 3,
		"DocumentTable":      2,
	}
	return TableMap[tname]
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

// Init initializes the chain and two tables - one for asset and other for transaction history
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {

	fmt.Println("Application Init")
	var err error

	for _, val := range tables {
		err = stub.DeleteTable(val)
		if err != nil {
			return nil, fmt.Errorf("Init(): DeleteTable of %s  Failed ", val)
		}
		err = InitLedger(stub, val)
		if err != nil {
			return nil, fmt.Errorf("Init(): InitLedger of %s  Failed ", val)
		}
	}

	err = stub.PutState("_dispatchOrderindex", []byte(dispatchOrderIndexstr))
	if err != nil {
		return nil, err
	}

	fmt.Println("Init() Initialization Complete  : ", args)
	return []byte("Init(): Initialization Complete"), nil
}

func InitLedger(stub shim.ChaincodeStubInterface, tableName string) error {

	// Generic Table Creation Function - requires Table Name and Table Key Entry
	// Create Table - Get number of Keys the tables supports
	// This version assumes all Keys are String and the Data is Bytes

	nKeys := GetNumberOfKeys(tableName)
	if nKeys < 1 {
		fmt.Println("Atleast 1 Key must be provided \n")
		fmt.Println("Auction_Application: Failed creating Table ", tableName)
		return errors.New("Auction_Application: Failed creating Table " + tableName)
	}

	var columnDefsForTbl []*shim.ColumnDefinition

	for i := 0; i < nKeys; i++ {
		columnDef := shim.ColumnDefinition{Name: "keyName" + strconv.Itoa(i), Type: shim.ColumnDefinition_STRING, Key: true}
		columnDefsForTbl = append(columnDefsForTbl, &columnDef)
	}

	columnLastTblDef := shim.ColumnDefinition{Name: "Details", Type: shim.ColumnDefinition_BYTES, Key: false}
	columnDefsForTbl = append(columnDefsForTbl, &columnLastTblDef)

	// Create the Table (Nil is returned if the Table exists or if the table is created successfully
	err := stub.CreateTable(tableName, columnDefsForTbl)

	if err != nil {
		fmt.Println("Auction_Application: Failed creating Table ", tableName)
		return errors.New("Auction_Application: Failed creating Table " + tableName)
	}

	return err
}

// Invoke is our entry point to invoke a chaincode function
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("invoke is running " + function)

	if function == "createDispatchOrder" {
		return t.createDispatchOrder(stub, args)
	}
	fmt.Println("invoke did not find func: " + function) //error

	return nil, errors.New("Received unknown function invocation: " + function)
}

// Query queries the hyperledger
func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("query is running " + function)

	// Handle different functions
	if function == "keys" {
		return t.getAllKeys(stub, args)
	} else if function == "read" { //read a contract
		return t.read(stub, args)
	}

	fmt.Println("query did not find func: " + function) //error
	return nil, errors.New("Received unknown function query " + function)
}

func (t *SimpleChaincode) createDispatchOrder(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var err error

	//convert the arguments into an Diapatch order Object
	dispatchObject, err := CreateDispatchOrderObject(args[0:])
	if err != nil {
		fmt.Println("createDispatchOrder(): Cannot create dispatch object ")
		return nil, errors.New("createDispatchOrder(): Cannot create dipatch object")
	}

	// check if the DispatchOrder already exists
	contractAsBytes, err := stub.GetState(dispatchObject.dispatchOrderId)
	if err != nil {
		fmt.Println("createDispatchOrder() : failed to get contract")
		return nil, errors.New("Failed to get dispatchOrder")
	}
	if contractAsBytes != nil {
		fmt.Println("initContract() : contract already exists for ", dispatchObject.dispatchOrderId)
		jsonResp := "{\"Error\":\"Failed - contract already exists " + dispatchObject.dispatchOrderId + "\"}"
		return nil, errors.New(jsonResp)
	}

	buff, err := doToJSON(dispatchObject)
	if err != nil {
		errorStr := "initContract() : Failed Cannot create object buffer for write : " + args[0]
		fmt.Println(errorStr)
		return nil, errors.New(errorStr)
	}
	fmt.Println("createDispatchOrder() : buffer", buff)
	err = stub.PutState(args[0], buff)
	if err != nil {
		fmt.Println("initContract() : write error while inserting record\n")
		return nil, errors.New("initContract() : write error while inserting record : " + err.Error())
	}

	// make an entry into transaction history table

	return nil, nil
}

// read function return value
func (t *SimpleChaincode) readContract(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var name, jsonResp string
	var err error

	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting name of the var to query")
	}

	name = args[0]
	fmt.Println("document id to be queried is " + name)
	valAsbytes, err := stub.GetState(name)
	if err != nil {
		jsonResp = "{\"Error\":\"Failed to get state for " + name + "\"}"
		return nil, errors.New(jsonResp)
	}
	if valAsbytes == nil {
		jsonResp = "{\"null object\":\"in ledger for key " + name + "\"}"
		return nil, errors.New(jsonResp)
	}
	fmt.Println("read contract output ", valAsbytes)
	return valAsbytes, nil
}

// read - query function to read key/value pair
func (t *SimpleChaincode) read(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var key, jsonResp string
	var err error

	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting name of the key to query")
	}

	key = args[0]
	valAsbytes, err := stub.GetState(key)
	if err != nil {
		jsonResp = "{\"Error\":\"Failed to get state for " + key + "\"}"
		return nil, errors.New(jsonResp)
	}
	fmt.Println("read contract output ", valAsbytes)
	jsonKeys, err := json.Marshal(valAsbytes)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("keys operation failed. Error accessing state: %s", err))
	}

	return jsonKeys, nil
}

func (t *SimpleChaincode) getAllKeys(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {

	if len(args) < 2 {
		return nil, errors.New("put operation must include two arguments, a key and value")
	}

	startKey := args[0]
	endKey := args[1]

	keysIter, err := stub.RangeQueryState(startKey, endKey)

	if err != nil {
		return nil, errors.New(fmt.Sprintf("keys operation failed. Error accessing state: %s", err))
	}
	defer keysIter.Close()
	var keys []string
	for keysIter.HasNext() {
		response, _, iterErr := keysIter.Next()
		if iterErr != nil {
			return nil, errors.New(fmt.Sprintf("keys operation failed. Error accessing state: %s", err))
		}
		keys = append(keys, response)
	}

	for key, value := range keys {
		fmt.Printf("key %d contains %s\n", key, value)
	}

	jsonKeys, err := json.Marshal(keys)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("keys operation failed. Error accessing state: %s", err))
	}

	return jsonKeys, nil
}

// CreateContractObject creates an contract
func CreateDispatchOrderObject(args []string) (DispatchOrderObject, error) {
	// S001 LHTMO bosch
	var err error
	var myDispatchOrder DispatchOrderObject

	// Check there are 31 Arguments provided as per the the struct, time is computed
	if len(args) != 3 {
		fmt.Println("CreateDispatchOrderObject(): Incorrect number of arguments. Expecting 31 ")
		return myDispatchOrder, errors.New("CreateDispatchOrderObject(): Incorrect number of arguments. Expecting 31 ")
	}

	//check whether the dispatch order already exists
	myDispatchOrder = DispatchOrderObject{args[0], strconv.Itoa(STATE_OBD_REQUEST_CREATED), args[2], time.Now().Format("20060102150405")}
	if err != nil {
		fmt.Println(err)
		return myDispatchOrder, err
	}
	fmt.Println("CreateDispatchOrderObject(): dispatch Object created: ", myDispatchOrder.dispatchOrderId, myDispatchOrder.stage, myDispatchOrder.customer, myDispatchOrder.timeStamp)
	return myDispatchOrder, nil
}

// doToJSON Converts an dispatch Object to a JSON String
func doToJSON(c DispatchOrderObject) ([]byte, error) {
	cjson, err := json.Marshal(c)
	if err != nil {
		fmt.Println(err)
		return nil, err
	}
	fmt.Println("dispatch object as bytes ", cjson)
	return cjson, nil
}
<|endoftext|>


<commit_before>package matrix

import ()

func (matrix *matrix) Scroll(bitmask [][]bool, color [3]byte, y int, x int, offset int, length int) {
	s := len(bitmask[0])
	if s < length {
		matrix.DrawBitmask(bitmask, color, y, x)
		return
	}
	// Seamlessly loop the bitmap around
	offset = offset % s
	size := s - offset
	if size >= length {
		matrix.DrawBitmask(matrix.OffsetBitmask(bitmask, offset, length), color, y, x)
	} else {
		matrix.DrawBitmask(matrix.OffsetBitmask(bitmask, offset, length), color, y, x)
		matrix.DrawBitmask(matrix.OffsetBitmask(bitmask, 0, length-size), color, y, x+size)
	}
}
<commit_msg>Add ScrollPlasma<commit_after>package matrix

import ()

func (matrix *matrix) Scroll(bitmask [][]bool, color [3]byte, y, x, offset, length int) {
	matrix.DrawBitmask(matrix.getScrollBitmask(bitmask, y, x, offset, length), color, y, x)
}

func (matrix *matrix) ScrollPlasma(bitmask [][]bool, y, x, offset, length int) {
	matrix.DrawBitmapMask(matrix.PlasmaBitmap(offset),
		matrix.getScrollBitmask(bitmask, y, x, offset, length), y, x)
}

func (matrix *matrix) getScrollBitmask(bitmask [][]bool, y, x, offset, length int) [][]bool {
	s := len(bitmask[0])
	if s < length {
		return bitmask
	}
	// Seamlessly loop the bitmap around
	offset = offset % s
	size := s - offset
	if size >= length {
		return matrix.OffsetBitmask(bitmask, offset, length)
	} else {
		ret := make([][]bool, len(bitmask))
		for r, row := range matrix.OffsetBitmask(bitmask, offset, length) {
			ret[r] = row
		}
		for r, row := range matrix.OffsetBitmask(bitmask, 0, length-size) {
			ret[r] = append(ret[r], row...)
		}
		return ret
	}
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"math/big"
	"regexp"
	"strings"
	"sync"

	"github.com/golang/glog"

	ethcommon "github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/crypto"

	"github.com/livepeer/go-livepeer/common"
	"github.com/livepeer/go-livepeer/core"
	"github.com/livepeer/go-livepeer/drivers"
	"github.com/livepeer/go-livepeer/monitor"
	"github.com/livepeer/go-livepeer/pm"

	"github.com/livepeer/lpms/stream"
)

func selectOrchestrator(n *core.LivepeerNode, cpl core.PlaylistManager) (*BroadcastSession, error) {

	if n.OrchestratorPool == nil {
		glog.Info("No orchestrators specified; not transcoding")
		return nil, ErrDiscovery
	}

	rpcBcast := core.NewBroadcaster(n)

	tinfos, err := n.OrchestratorPool.GetOrchestrators(1)
	if len(tinfos) <= 0 {
		glog.Info("No orchestrators found; not transcoding. Error: ", err)
		return nil, ErrNoOrchs
	}
	if err != nil {
		return nil, err
	}
	tinfo := tinfos[0]

	var sessionID string

	if n.Sender != nil {
		protoParams := tinfo.TicketParams
		params := pm.TicketParams{
			Recipient:         ethcommon.BytesToAddress(protoParams.Recipient),
			FaceValue:         new(big.Int).SetBytes(protoParams.FaceValue),
			WinProb:           new(big.Int).SetBytes(protoParams.WinProb),
			RecipientRandHash: ethcommon.BytesToHash(protoParams.RecipientRandHash),
			Seed:              new(big.Int).SetBytes(protoParams.Seed),
		}

		sessionID = n.Sender.StartSession(params)
	}

	// set OSes
	var orchOS drivers.OSSession
	if len(tinfo.Storage) > 0 {
		orchOS = drivers.NewSession(tinfo.Storage[0])
	}

	return &BroadcastSession{
		Broadcaster:      rpcBcast,
		ManifestID:       cpl.ManifestID(),
		Profiles:         BroadcastJobVideoProfiles,
		OrchestratorInfo: tinfo,
		OrchestratorOS:   orchOS,
		BroadcasterOS:    cpl.GetOSSession(),
		Sender:           n.Sender,
		PMSessionID:      sessionID,
	}, nil
}

func processSegment(cxn *rtmpConnection, seg *stream.HLSSegment) {

	nonce := cxn.nonce
	rtmpStrm := cxn.stream
	cpl := cxn.pl
	mid := cxn.mid
	vProfile := cxn.profile

	cxn.lock.RLock()
	sess := cxn.sess
	cxn.lock.RUnlock()

	if monitor.Enabled {
		monitor.LogSegmentEmerged(nonce, seg.SeqNo)
	}

	seg.Name = "" // hijack seg.Name to convey the uploaded URI
	name := fmt.Sprintf("%s/%d.ts", vProfile.Name, seg.SeqNo)
	uri, err := cpl.GetOSSession().SaveData(name, seg.Data)
	if err != nil {
		glog.Errorf("Error saving segment %d: %v", seg.SeqNo, err)
		if monitor.Enabled {
			monitor.LogSegmentUploadFailed(nonce, seg.SeqNo, err.Error())
		}
		return
	}
	if cpl.GetOSSession().IsExternal() {
		seg.Name = uri // hijack seg.Name to convey the uploaded URI
	}
	err = cpl.InsertHLSSegment(vProfile, seg.SeqNo, uri, seg.Duration)
	if monitor.Enabled {
		monitor.LogSourceSegmentAppeared(nonce, seg.SeqNo, string(mid), vProfile.Name)
		glog.V(6).Infof("Appeared segment %d", seg.SeqNo)
	}
	if err != nil {
		glog.Errorf("Error inserting segment %d: %v", seg.SeqNo, err)
		if monitor.Enabled {
			monitor.LogSegmentUploadFailed(nonce, seg.SeqNo, err.Error())
		}
	}

	// Return early under a few circumstances:
	// View-only (non-transcoded) streams or mid-failover
	if sess == nil {
		return
	}

	// Process the rest of the segment asynchronously - transcode
	go func() {
		// storage the orchestrator prefers
		if ios := sess.OrchestratorOS; ios != nil {
			// XXX handle case when orch expects direct upload
			uri, err := ios.SaveData(name, seg.Data)
			if err != nil {
				glog.Error("Error saving segment to OS ", err)
				if monitor.Enabled {
					monitor.LogSegmentUploadFailed(nonce, seg.SeqNo, err.Error())
				}
				return
			}
			seg.Name = uri // hijack seg.Name to convey the uploaded URI
		}

		// send segment to the orchestrator
		glog.V(common.DEBUG).Infof("Submitting segment %d", seg.SeqNo)

		res, err := SubmitSegment(sess, seg, nonce)
		if err != nil {
			if shouldStopStream(err) {
				glog.Warningf("Stopping current stream due to: %v", err)
				rtmpStrm.Close()
				return
			}
			if shouldStopSession(err) {
				cxn.needOrch <- struct{}{}
			}
			return
		}

		// download transcoded segments from the transcoder
		gotErr := false // only send one error msg per segment list
		errFunc := func(subType, url string, err error) {
			glog.Errorf("%v error with segment %v: %v (URL: %v)", subType, seg.SeqNo, err, url)
			if monitor.Enabled && !gotErr {
				monitor.LogSegmentTranscodeFailed(subType, nonce, seg.SeqNo, err)
				gotErr = true
			}
		}

		segHashes := make([][]byte, len(res.Segments))
		n := len(res.Segments)
		segHashLock := &sync.Mutex{}
		cond := sync.NewCond(segHashLock)

		dlFunc := func(url string, i int) {
			defer func() {
				cond.L.Lock()
				n--
				if n == 0 {
					cond.Signal()
				}
				cond.L.Unlock()
			}()

			if bos := sess.BroadcasterOS; bos != nil && !drivers.IsOwnExternal(url) {
				data, err := drivers.GetSegmentData(url)
				if err != nil {
					errFunc("Download", url, err)
					return
				}
				name := fmt.Sprintf("%s/%d.ts", sess.Profiles[i].Name, seg.SeqNo)
				newUrl, err := bos.SaveData(name, data)
				if err != nil {
					errFunc("SaveData", url, err)
					return
				}
				url = newUrl

				hash := crypto.Keccak256(data)
				segHashLock.Lock()
				segHashes[i] = hash
				segHashLock.Unlock()
			}

			if monitor.Enabled {
				monitor.LogTranscodedSegmentAppeared(nonce, seg.SeqNo, sess.Profiles[i].Name)
			}
			err = cpl.InsertHLSSegment(&sess.Profiles[i], seg.SeqNo, url, seg.Duration)
			if err != nil {
				errFunc("Playlist", url, err)
				return
			}
		}

		for i, v := range res.Segments {
			go dlFunc(v.Url, i)
		}

		cond.L.Lock()
		for n != 0 {
			cond.Wait()
		}
		cond.L.Unlock()

		ticketParams := sess.OrchestratorInfo.GetTicketParams()
		if !pm.VerifySig(ethcommon.BytesToAddress(ticketParams.Recipient), crypto.Keccak256(segHashes...), res.Sig) {
			glog.Error("Sig check failed for segment ", seg.SeqNo)
			return
		}

		glog.V(common.DEBUG).Info("Successfully validated segment ", seg.SeqNo)
	}()
}

var sessionErrStrings = []string{"dial tcp", "unexpected EOF", core.ErrOrchBusy.Error(), core.ErrOrchCap.Error()}

func generateSessionErrors() *regexp.Regexp {
	// Given a list [err1, err2, err3] generates a regexp `(err1)|(err2)|(err3)`
	groups := []string{}
	for _, v := range sessionErrStrings {
		groups = append(groups, fmt.Sprintf("(%v)", v))
	}
	return regexp.MustCompile(strings.Join(groups, "|"))
}

var sessionErrRegex = generateSessionErrors()

func shouldStopSession(err error) bool {
	return sessionErrRegex.MatchString(err.Error())
}
<commit_msg>broadcaster: Fix result sig check in offchain mode.<commit_after>package server

import (
	"fmt"
	"math/big"
	"regexp"
	"strings"
	"sync"

	"github.com/golang/glog"

	ethcommon "github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/crypto"

	"github.com/livepeer/go-livepeer/common"
	"github.com/livepeer/go-livepeer/core"
	"github.com/livepeer/go-livepeer/drivers"
	"github.com/livepeer/go-livepeer/monitor"
	"github.com/livepeer/go-livepeer/pm"

	"github.com/livepeer/lpms/stream"
)

func selectOrchestrator(n *core.LivepeerNode, cpl core.PlaylistManager) (*BroadcastSession, error) {

	if n.OrchestratorPool == nil {
		glog.Info("No orchestrators specified; not transcoding")
		return nil, ErrDiscovery
	}

	rpcBcast := core.NewBroadcaster(n)

	tinfos, err := n.OrchestratorPool.GetOrchestrators(1)
	if len(tinfos) <= 0 {
		glog.Info("No orchestrators found; not transcoding. Error: ", err)
		return nil, ErrNoOrchs
	}
	if err != nil {
		return nil, err
	}
	tinfo := tinfos[0]

	var sessionID string

	if n.Sender != nil {
		protoParams := tinfo.TicketParams
		params := pm.TicketParams{
			Recipient:         ethcommon.BytesToAddress(protoParams.Recipient),
			FaceValue:         new(big.Int).SetBytes(protoParams.FaceValue),
			WinProb:           new(big.Int).SetBytes(protoParams.WinProb),
			RecipientRandHash: ethcommon.BytesToHash(protoParams.RecipientRandHash),
			Seed:              new(big.Int).SetBytes(protoParams.Seed),
		}

		sessionID = n.Sender.StartSession(params)
	}

	// set OSes
	var orchOS drivers.OSSession
	if len(tinfo.Storage) > 0 {
		orchOS = drivers.NewSession(tinfo.Storage[0])
	}

	return &BroadcastSession{
		Broadcaster:      rpcBcast,
		ManifestID:       cpl.ManifestID(),
		Profiles:         BroadcastJobVideoProfiles,
		OrchestratorInfo: tinfo,
		OrchestratorOS:   orchOS,
		BroadcasterOS:    cpl.GetOSSession(),
		Sender:           n.Sender,
		PMSessionID:      sessionID,
	}, nil
}

func processSegment(cxn *rtmpConnection, seg *stream.HLSSegment) {

	nonce := cxn.nonce
	rtmpStrm := cxn.stream
	cpl := cxn.pl
	mid := cxn.mid
	vProfile := cxn.profile

	cxn.lock.RLock()
	sess := cxn.sess
	cxn.lock.RUnlock()

	if monitor.Enabled {
		monitor.LogSegmentEmerged(nonce, seg.SeqNo)
	}

	seg.Name = "" // hijack seg.Name to convey the uploaded URI
	name := fmt.Sprintf("%s/%d.ts", vProfile.Name, seg.SeqNo)
	uri, err := cpl.GetOSSession().SaveData(name, seg.Data)
	if err != nil {
		glog.Errorf("Error saving segment %d: %v", seg.SeqNo, err)
		if monitor.Enabled {
			monitor.LogSegmentUploadFailed(nonce, seg.SeqNo, err.Error())
		}
		return
	}
	if cpl.GetOSSession().IsExternal() {
		seg.Name = uri // hijack seg.Name to convey the uploaded URI
	}
	err = cpl.InsertHLSSegment(vProfile, seg.SeqNo, uri, seg.Duration)
	if monitor.Enabled {
		monitor.LogSourceSegmentAppeared(nonce, seg.SeqNo, string(mid), vProfile.Name)
		glog.V(6).Infof("Appeared segment %d", seg.SeqNo)
	}
	if err != nil {
		glog.Errorf("Error inserting segment %d: %v", seg.SeqNo, err)
		if monitor.Enabled {
			monitor.LogSegmentUploadFailed(nonce, seg.SeqNo, err.Error())
		}
	}

	// Return early under a few circumstances:
	// View-only (non-transcoded) streams or mid-failover
	if sess == nil {
		return
	}

	// Process the rest of the segment asynchronously - transcode
	go func() {
		// storage the orchestrator prefers
		if ios := sess.OrchestratorOS; ios != nil {
			// XXX handle case when orch expects direct upload
			uri, err := ios.SaveData(name, seg.Data)
			if err != nil {
				glog.Error("Error saving segment to OS ", err)
				if monitor.Enabled {
					monitor.LogSegmentUploadFailed(nonce, seg.SeqNo, err.Error())
				}
				return
			}
			seg.Name = uri // hijack seg.Name to convey the uploaded URI
		}

		// send segment to the orchestrator
		glog.V(common.DEBUG).Infof("Submitting segment %d", seg.SeqNo)

		res, err := SubmitSegment(sess, seg, nonce)
		if err != nil {
			if shouldStopStream(err) {
				glog.Warningf("Stopping current stream due to: %v", err)
				rtmpStrm.Close()
				return
			}
			if shouldStopSession(err) {
				cxn.needOrch <- struct{}{}
			}
			return
		}

		// download transcoded segments from the transcoder
		gotErr := false // only send one error msg per segment list
		errFunc := func(subType, url string, err error) {
			glog.Errorf("%v error with segment %v: %v (URL: %v)", subType, seg.SeqNo, err, url)
			if monitor.Enabled && !gotErr {
				monitor.LogSegmentTranscodeFailed(subType, nonce, seg.SeqNo, err)
				gotErr = true
			}
		}

		segHashes := make([][]byte, len(res.Segments))
		n := len(res.Segments)
		segHashLock := &sync.Mutex{}
		cond := sync.NewCond(segHashLock)

		dlFunc := func(url string, i int) {
			defer func() {
				cond.L.Lock()
				n--
				if n == 0 {
					cond.Signal()
				}
				cond.L.Unlock()
			}()

			if bos := sess.BroadcasterOS; bos != nil && !drivers.IsOwnExternal(url) {
				data, err := drivers.GetSegmentData(url)
				if err != nil {
					errFunc("Download", url, err)
					return
				}
				name := fmt.Sprintf("%s/%d.ts", sess.Profiles[i].Name, seg.SeqNo)
				newUrl, err := bos.SaveData(name, data)
				if err != nil {
					errFunc("SaveData", url, err)
					return
				}
				url = newUrl

				hash := crypto.Keccak256(data)
				segHashLock.Lock()
				segHashes[i] = hash
				segHashLock.Unlock()
			}

			if monitor.Enabled {
				monitor.LogTranscodedSegmentAppeared(nonce, seg.SeqNo, sess.Profiles[i].Name)
			}
			err = cpl.InsertHLSSegment(&sess.Profiles[i], seg.SeqNo, url, seg.Duration)
			if err != nil {
				errFunc("Playlist", url, err)
				return
			}
		}

		for i, v := range res.Segments {
			go dlFunc(v.Url, i)
		}

		cond.L.Lock()
		for n != 0 {
			cond.Wait()
		}
		cond.L.Unlock()

		ticketParams := sess.OrchestratorInfo.GetTicketParams()
		if ticketParams != nil && // may be nil in offchain mode
			!pm.VerifySig(ethcommon.BytesToAddress(ticketParams.Recipient), crypto.Keccak256(segHashes...), res.Sig) {
			glog.Error("Sig check failed for segment ", seg.SeqNo)
			return
		}

		glog.V(common.DEBUG).Info("Successfully validated segment ", seg.SeqNo)
	}()
}

var sessionErrStrings = []string{"dial tcp", "unexpected EOF", core.ErrOrchBusy.Error(), core.ErrOrchCap.Error()}

func generateSessionErrors() *regexp.Regexp {
	// Given a list [err1, err2, err3] generates a regexp `(err1)|(err2)|(err3)`
	groups := []string{}
	for _, v := range sessionErrStrings {
		groups = append(groups, fmt.Sprintf("(%v)", v))
	}
	return regexp.MustCompile(strings.Join(groups, "|"))
}

var sessionErrRegex = generateSessionErrors()

func shouldStopSession(err error) bool {
	return sessionErrRegex.MatchString(err.Error())
}
<|endoftext|>


<commit_before>package game

import (
	"fmt"
)

// Game parameters
const (
	endGameAtRound = 5

	ActionHandSize = 7
	PeopleHandSize = 5
)

// Handle handles an action.
func (s *State) Handle(a *Action, playerID int) error {
	s.Lock()
	defer s.Unlock()
	defer s.notify()

	// Everyone can always do nothing.
	if a.Act == ActNoOp {
		return nil
	}

	switch s.State {
	case StateLobby:
		switch a.Act {
		case ActStartGame:
			// Anyone can start the game if there are 2 or more players.
			if len(s.Players) < 2 {
				return fmt.Errorf("too few players for game [%d<2]", len(s.Players))
			}
			s.State = StateInGame
			s.startGame()

		default:
			return fmt.Errorf("bad action for StateLobby [%d]", a.Act)
		}
	case StateInGame:
		switch a.Act {
		case ActPlayCard:
			if playerID != s.WhoseTurn {
				return fmt.Errorf("not your turn [%d!=%d]", playerID, s.WhoseTurn)
			}
			// TODO: check the card is valid play
			// TODO: compute effects
		case ActDiscard:
			if playerID != s.WhoseTurn {
				return fmt.Errorf("not your turn [%d!=%d]", playerID, s.WhoseTurn)
			}
			// TODO: check the card is valid discard
			// TODO: compute effects / draw new card
		default:
			return fmt.Errorf("bad action for StateInGame [%d]", a.Act)
		}
		s.advance()

	case StateGameOver:
		switch a.Act {
		case ActReturnToLobby:
			// Anyone can return to the lobby when the game is over.
			s.State = StateLobby
		default:
			return fmt.Errorf("bad action for StateGameOver [%d]", a.Act)
		}
	}
	return nil
}

// advance advances whose-turn to the next player, and game clock
// MUST GUARD WITH LOCK
func (s *State) advance() {
	n := s.nextPlayer(s.WhoseTurn)
	if n < s.WhoseTurn {
		s.Clock++
	}
	s.WhoseTurn = n
	if s.Clock == endGameAtRound {
		s.State = StateGameOver
	}
}

// AddPlayer adds a player.
func (s *State) AddPlayer() (int, error) {
	s.Lock()
	defer s.Unlock()
	if s.State != StateLobby {
		return -1, fmt.Errorf("game not in lobby state [%d!=%d]", s.State, StateLobby)
	}
	id := s.nextID
	s.Players[id] = &Player{
		Name: fmt.Sprintf("Player %d", id),
	}
	s.nextID++
	s.notify()
	return id, nil
}

// RemovePlayer quits a player.
func (s *State) RemovePlayer(id int) error {
	s.Lock()
	defer s.Unlock()
	if s.Players[id] == nil {
		return fmt.Errorf("id %d not present", id)
	}
	delete(s.Players, id)

	switch len(s.Players) {
	case 1:
		if s.State == StateInGame {
			// If there's one player remaining, they win.
			s.State = StateGameOver
		}
	case 0:
		// If there are no players remaining, go back to lobby.
		s.State = StateLobby

	default:
		// Go to the next player
		if s.WhoseTurn == id {
			s.advance()
		}
	}
	s.notify()
	return nil
}

// MUST GUARD WITH LOCK
func (s *State) nextPlayer(after int) int {
	min, sup := (1<<31)-1, (1<<31)-1
	// It's gotta be linear in Players to find the next one when wrapping around.
	for id := range s.Players {
		if id < min {
			min = id
		}
		if id > after && id < sup {
			sup = id
		}
	}
	if sup == (1<<31)-1 {
		return min
	}
	return sup
}

// MUST GUARD WITH LOCK
func (s *State) startGame() {
	s.Clock = 0
	s.WhoseTurn = -1
	s.advance()

	s.deck = s.baseDeck.Instance()
	s.deck.Shuffle()
	for _, p := range s.Players {
		p.Hand = &Hand{
			Actions: s.deck.DrawActions(ActionHandSize),
			People:  s.deck.DrawPeople(PeopleHandSize),
		}
	}
}
<commit_msg>Lol use a const<commit_after>package game

import (
	"fmt"
)

// Game parameters
const (
	endGameAtRound = 5

	ActionHandSize = 7
	PeopleHandSize = 5
)

// Handle handles an action.
func (s *State) Handle(a *Action, playerID int) error {
	s.Lock()
	defer s.Unlock()
	defer s.notify()

	// Everyone can always do nothing.
	if a.Act == ActNoOp {
		return nil
	}

	switch s.State {
	case StateLobby:
		switch a.Act {
		case ActStartGame:
			// Anyone can start the game if there are 2 or more players.
			if len(s.Players) < 2 {
				return fmt.Errorf("too few players for game [%d<2]", len(s.Players))
			}
			s.State = StateInGame
			s.startGame()

		default:
			return fmt.Errorf("bad action for StateLobby [%d]", a.Act)
		}
	case StateInGame:
		switch a.Act {
		case ActPlayCard:
			if playerID != s.WhoseTurn {
				return fmt.Errorf("not your turn [%d!=%d]", playerID, s.WhoseTurn)
			}
			// TODO: check the card is valid play
			// TODO: compute effects
		case ActDiscard:
			if playerID != s.WhoseTurn {
				return fmt.Errorf("not your turn [%d!=%d]", playerID, s.WhoseTurn)
			}
			// TODO: check the card is valid discard
			// TODO: compute effects / draw new card
		default:
			return fmt.Errorf("bad action for StateInGame [%d]", a.Act)
		}
		s.advance()

	case StateGameOver:
		switch a.Act {
		case ActReturnToLobby:
			// Anyone can return to the lobby when the game is over.
			s.State = StateLobby
		default:
			return fmt.Errorf("bad action for StateGameOver [%d]", a.Act)
		}
	}
	return nil
}

// advance advances whose-turn to the next player, and game clock
// MUST GUARD WITH LOCK
func (s *State) advance() {
	n := s.nextPlayer(s.WhoseTurn)
	if n < s.WhoseTurn {
		s.Clock++
	}
	s.WhoseTurn = n
	if s.Clock == endGameAtRound {
		s.State = StateGameOver
	}
}

// AddPlayer adds a player.
func (s *State) AddPlayer() (int, error) {
	s.Lock()
	defer s.Unlock()
	if s.State != StateLobby {
		return -1, fmt.Errorf("game not in lobby state [%d!=%d]", s.State, StateLobby)
	}
	id := s.nextID
	s.Players[id] = &Player{
		Name: fmt.Sprintf("Player %d", id),
	}
	s.nextID++
	s.notify()
	return id, nil
}

// RemovePlayer quits a player.
func (s *State) RemovePlayer(id int) error {
	s.Lock()
	defer s.Unlock()
	if s.Players[id] == nil {
		return fmt.Errorf("id %d not present", id)
	}
	delete(s.Players, id)

	switch len(s.Players) {
	case 1:
		if s.State == StateInGame {
			// If there's one player remaining, they win.
			s.State = StateGameOver
		}
	case 0:
		// If there are no players remaining, go back to lobby.
		s.State = StateLobby

	default:
		// Go to the next player
		if s.WhoseTurn == id {
			s.advance()
		}
	}
	s.notify()
	return nil
}

// MUST GUARD WITH LOCK
func (s *State) nextPlayer(after int) int {
	const bigint = (1 << 31) - 1
	min, sup := bigint, bigint
	// It's gotta be linear in Players to find the next one when wrapping around.
	for id := range s.Players {
		if id < min {
			min = id
		}
		if id > after && id < sup {
			sup = id
		}
	}
	if sup == bigint {
		return min
	}
	return sup
}

// MUST GUARD WITH LOCK
func (s *State) startGame() {
	s.Clock = 0
	s.WhoseTurn = -1
	s.advance()

	s.deck = s.baseDeck.Instance()
	s.deck.Shuffle()
	for _, p := range s.Players {
		p.Hand = &Hand{
			Actions: s.deck.DrawActions(ActionHandSize),
			People:  s.deck.DrawPeople(PeopleHandSize),
		}
	}
}
<|endoftext|>