<commit_before>package execution

import (
	"pfi/sensorbee/sensorbee/bql/parser"
	"pfi/sensorbee/sensorbee/bql/udf"
	"pfi/sensorbee/sensorbee/core"
	"pfi/sensorbee/sensorbee/data"
	"reflect"
	"time"
)

type filterIstreamPlan struct {
	commonExecutionPlan
	relAlias    string
	windowSize  int64
	prevResults []data.Map
	procItems   int64
}

// CanBuildFilterIstreamPlan checks whether the given statement
// allows to use a filterIstreamPlan.
func CanBuildFilterIstreamPlan(lp *LogicalPlan, reg udf.FunctionRegistry) bool {
	if len(lp.Relations) != 1 {
		return false
	}
	rangeUnit := lp.Relations[0].Unit
	for _, proj := range lp.Projections {
		// this implementation assumes that after we evaluated an
		// expression on a row, its value will never change. this
		// means that we cannot deal with volatile or stable
		// functions in this plan.
		if proj.expr.Volatility() != Immutable {
			return false
		}
	}
	return !lp.GroupingStmt &&
		lp.EmitterType == parser.Istream &&
		rangeUnit == parser.Tuples
}

// filterIstreamPlan is a fast and simple plan for the case where the
// BQL statement has an Istream emitter, a TUPLES range clause and only
// a WHERE clause (no GROUP BY/aggregate functions). In that case we can
// perform the check with less memory and faster than the default plan.
func NewFilterIstreamPlan(lp *LogicalPlan, reg udf.FunctionRegistry) (ExecutionPlan, error) {
	// prepare projection components
	projs, err := prepareProjections(lp.Projections, reg)
	if err != nil {
		return nil, err
	}
	// compute evaluator for the filter
	filter, err := prepareFilter(lp.Filter, reg)
	if err != nil {
		return nil, err
	}
	rangeValue := lp.Relations[0].Value
	return &filterIstreamPlan{commonExecutionPlan{
		projections: projs,
		filter:      filter,
	}, lp.Relations[0].Alias, rangeValue, make([]data.Map, 0, rangeValue), 0}, nil
}

func (ep *filterIstreamPlan) Process(input *core.Tuple) ([]data.Map, error) {
	// remove the oldest item from prevResults after it has once
	// been filled completely
	if ep.procItems >= ep.windowSize {
		if len(ep.prevResults) >= 1 {
			ep.prevResults = ep.prevResults[1:]
		}
	} else {
		ep.procItems++
	}

	// nest the data in a one-element map using the alias as the key
	input.Data = data.Map{ep.relAlias: input.Data}
	setMetadata(input.Data, ep.relAlias, input)

	// evaluate filter condition and convert to bool
	if ep.filter != nil {
		filterResult, err := ep.filter.Eval(input.Data)
		if err != nil {
			return nil, err
		}
		filterResultBool, err := data.ToBool(filterResult)
		if err != nil {
			return nil, err
		}
		// if it evaluated to false, do not further process this tuple
		if !filterResultBool {
			return nil, nil
		}
	}
	// otherwise, compute all the expressions
	result := data.Map(make(map[string]data.Value, len(ep.projections)))
	for _, proj := range ep.projections {
		value, err := proj.evaluator.Eval(input.Data)
		if err != nil {
			return nil, err
		}
		if err := assignOutputValue(result, proj.alias, value); err != nil {
			return nil, err
		}
	}
	// check if the item is in the previous results
	alreadyEmitted := false
	for _, item := range ep.prevResults {
		if reflect.DeepEqual(result, item) {
			alreadyEmitted = true
			break
		}
	}
	// in any way, append it the the previous results
	ep.prevResults = append(ep.prevResults, result)
	if alreadyEmitted {
		return nil, nil
	}
	return []data.Map{result}, nil
}
<commit_msg>Remove unused import<commit_after>package execution

import (
	"pfi/sensorbee/sensorbee/bql/parser"
	"pfi/sensorbee/sensorbee/bql/udf"
	"pfi/sensorbee/sensorbee/core"
	"pfi/sensorbee/sensorbee/data"
	"reflect"
)

type filterIstreamPlan struct {
	commonExecutionPlan
	relAlias    string
	windowSize  int64
	prevResults []data.Map
	procItems   int64
}

// CanBuildFilterIstreamPlan checks whether the given statement
// allows to use a filterIstreamPlan.
func CanBuildFilterIstreamPlan(lp *LogicalPlan, reg udf.FunctionRegistry) bool {
	if len(lp.Relations) != 1 {
		return false
	}
	rangeUnit := lp.Relations[0].Unit
	for _, proj := range lp.Projections {
		// this implementation assumes that after we evaluated an
		// expression on a row, its value will never change. this
		// means that we cannot deal with volatile or stable
		// functions in this plan.
		if proj.expr.Volatility() != Immutable {
			return false
		}
	}
	return !lp.GroupingStmt &&
		lp.EmitterType == parser.Istream &&
		rangeUnit == parser.Tuples
}

// filterIstreamPlan is a fast and simple plan for the case where the
// BQL statement has an Istream emitter, a TUPLES range clause and only
// a WHERE clause (no GROUP BY/aggregate functions). In that case we can
// perform the check with less memory and faster than the default plan.
func NewFilterIstreamPlan(lp *LogicalPlan, reg udf.FunctionRegistry) (ExecutionPlan, error) {
	// prepare projection components
	projs, err := prepareProjections(lp.Projections, reg)
	if err != nil {
		return nil, err
	}
	// compute evaluator for the filter
	filter, err := prepareFilter(lp.Filter, reg)
	if err != nil {
		return nil, err
	}
	rangeValue := lp.Relations[0].Value
	return &filterIstreamPlan{commonExecutionPlan{
		projections: projs,
		filter:      filter,
	}, lp.Relations[0].Alias, rangeValue, make([]data.Map, 0, rangeValue), 0}, nil
}

func (ep *filterIstreamPlan) Process(input *core.Tuple) ([]data.Map, error) {
	// remove the oldest item from prevResults after it has once
	// been filled completely
	if ep.procItems >= ep.windowSize {
		if len(ep.prevResults) >= 1 {
			ep.prevResults = ep.prevResults[1:]
		}
	} else {
		ep.procItems++
	}

	// nest the data in a one-element map using the alias as the key
	input.Data = data.Map{ep.relAlias: input.Data}
	setMetadata(input.Data, ep.relAlias, input)

	// evaluate filter condition and convert to bool
	if ep.filter != nil {
		filterResult, err := ep.filter.Eval(input.Data)
		if err != nil {
			return nil, err
		}
		filterResultBool, err := data.ToBool(filterResult)
		if err != nil {
			return nil, err
		}
		// if it evaluated to false, do not further process this tuple
		if !filterResultBool {
			return nil, nil
		}
	}
	// otherwise, compute all the expressions
	result := data.Map(make(map[string]data.Value, len(ep.projections)))
	for _, proj := range ep.projections {
		value, err := proj.evaluator.Eval(input.Data)
		if err != nil {
			return nil, err
		}
		if err := assignOutputValue(result, proj.alias, value); err != nil {
			return nil, err
		}
	}
	// check if the item is in the previous results
	alreadyEmitted := false
	for _, item := range ep.prevResults {
		if reflect.DeepEqual(result, item) {
			alreadyEmitted = true
			break
		}
	}
	// in any way, append it the the previous results
	ep.prevResults = append(ep.prevResults, result)
	if alreadyEmitted {
		return nil, nil
	}
	return []data.Map{result}, nil
}
<|endoftext|>


<commit_before>package integrate

import (
	"os"
	"path/filepath"
	"sync"
	"testing"
	"time"

	"github.com/sourcegraph/jsonrpc2"
	"github.com/stretchr/testify/assert"

	"github.com/itchio/butler/butlerd"
	"github.com/itchio/butler/butlerd/messages"
	itchio "github.com/itchio/go-itchio"
	"github.com/itchio/httpkit/progress"
)

func Test_InstallCancel(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping test in short mode")
	}

	rc, h, cancel := connect(t)
	defer cancel()

	authenticate(t, rc)
	setupTmpInstallLocation(t, h, rc)

	_, err := messages.NetworkSetBandwidthThrottle.TestCall(rc, butlerd.NetworkSetBandwidthThrottleParams{
		Enabled: true,
		Rate:    32 * 1024,
	})
	must(t, err)

	defer func() {
		_, err := messages.NetworkSetBandwidthThrottle.TestCall(rc, butlerd.NetworkSetBandwidthThrottleParams{
			Enabled: false,
		})
		must(t, err)
	}()

	{
		// itch-test-account/big-assets
		game := getGame(t, h, rc, 243485)

		queueRes, err := messages.InstallQueue.TestCall(rc, butlerd.InstallQueueParams{
			Game:              game,
			InstallLocationID: "tmp",
		})
		must(t, err)

		pidFilePath := filepath.Join(queueRes.StagingFolder, "operate-pid.json")

		var lastProgressValue float64
		printProgress := func(params butlerd.ProgressNotification) {
			t.Logf("%.2f%% done @ %s / s ETA %s", params.Progress*100, progress.FormatBytes(int64(params.BPS)), time.Duration(params.ETA*float64(time.Second)))
			lastProgressValue = params.Progress
		}

		gracefulCancelOnce := &sync.Once{}

		messages.Progress.Register(h, func(rc *butlerd.RequestContext, params butlerd.ProgressNotification) {
			printProgress(params)

			if params.Progress > 0.2 {
				_, err := os.Stat(pidFilePath)
				assert.NoError(t, err, "pid file exists before we graceful cancel")

				gracefulCancelOnce.Do(func() {
					delete(h.notificationHandlers, messages.Progress.Method())

					t.Logf("Calling graceful cancel")
					messages.InstallCancel.TestCall(rc, butlerd.InstallCancelParams{
						ID: queueRes.ID,
					})
					t.Logf("Graceful cancel called")
				})
			}
		})

		t.Logf("Queued %s", queueRes.InstallFolder)

		_, err = messages.InstallPerform.TestCall(rc, butlerd.InstallPerformParams{
			ID:            queueRes.ID,
			StagingFolder: queueRes.StagingFolder,
		})

		t.Logf("Last progress before graceful cancel: %.2f%%", lastProgressValue*100)
		t.Logf("Making sure we've been cancelled...")
		assert.Error(t, err)
		je := err.(*jsonrpc2.Error)
		assert.EqualValues(t, butlerd.CodeOperationCancelled, je.Code)

		t.Logf("Resuming while offline...")
		_, err = messages.NetworkSetSimulateOffline.TestCall(rc, butlerd.NetworkSetSimulateOfflineParams{
			Enabled: true,
		})
		must(t, err)

		_, err = messages.InstallPerform.TestCall(rc, butlerd.InstallPerformParams{
			ID:            queueRes.ID,
			StagingFolder: queueRes.StagingFolder,
		})
		assert.Error(t, err)
		je = err.(*jsonrpc2.Error)
		assert.EqualValues(t, butlerd.CodeNetworkDisconnected, je.Code)

		_, err = messages.NetworkSetSimulateOffline.TestCall(rc, butlerd.NetworkSetSimulateOfflineParams{
			Enabled: false,
		})
		must(t, err)

		t.Logf("Resuming after graceful cancel...")

		hardCancelOnce := &sync.Once{}

		messages.Progress.Register(h, func(rc *butlerd.RequestContext, params butlerd.ProgressNotification) {
			printProgress(params)

			if params.Progress > 0.5 {
				hardCancelOnce.Do(func() {
					t.Logf("Sending hard cancel")
					delete(h.notificationHandlers, messages.Progress.Method())
					t.Logf("Calling cancel")
					cancel()
					t.Logf("Okay, we called cancel")
				})
			}
		})

		_, err = messages.InstallPerform.TestCall(rc, butlerd.InstallPerformParams{
			ID:            queueRes.ID,
			StagingFolder: queueRes.StagingFolder,
		})

		t.Logf("Last progress before hard cancel: %.2f%%", lastProgressValue*100)
		assert.Error(t, err)

		t.Logf("Waiting for pid file to disappear...")
		pidFileDisappeared := false
		beforePidDisappear := time.Now()
		for i := 0; i < 100; i++ {
			_, err := os.Stat(pidFilePath)
			if err != nil && os.IsNotExist(err) {
				// good!
				pidFileDisappeared = true
				break
			}
			time.Sleep(100 * time.Millisecond)
		}
		assert.True(t, pidFileDisappeared, "pid file should disappear after cancellation (even hard)")
		t.Logf("PID file disappeared in %s", time.Since(beforePidDisappear))

		t.Logf("Resuming after hard cancel...")
		rc, h, cancel = connect(t)

		messages.Progress.Register(h, func(rc *butlerd.RequestContext, params butlerd.ProgressNotification) {
			printProgress(params)
		})

		_, err = messages.InstallPerform.TestCall(rc, butlerd.InstallPerformParams{
			ID:            queueRes.ID,
			StagingFolder: queueRes.StagingFolder,
		})
		assert.NoError(t, err)
	}
}

var setupTmpOnce sync.Once

func setupTmpInstallLocation(t *testing.T, h *handler, rc *butlerd.RequestContext) {
	setupTmpOnce.Do(func() {
		wd, err := os.Getwd()
		must(t, err)

		tmpPath := filepath.Join(wd, "tmp")
		must(t, os.RemoveAll(tmpPath))

		_, err = messages.InstallLocationsAdd.TestCall(rc, butlerd.InstallLocationsAddParams{
			ID:   "tmp",
			Path: filepath.Join(wd, "tmp"),
		})
		must(t, err)
	})
}

func getGame(t *testing.T, h *handler, rc *butlerd.RequestContext, gameID int64) *itchio.Game {
	gameRes, err := messages.FetchGame.TestCall(rc, butlerd.FetchGameParams{
		GameID: gameID,
	})
	must(t, err)
	return gameRes.Game
}
<commit_msg>Fix integration tests (broken by install location add check)<commit_after>package integrate

import (
	"os"
	"path/filepath"
	"sync"
	"testing"
	"time"

	"github.com/sourcegraph/jsonrpc2"
	"github.com/stretchr/testify/assert"

	"github.com/itchio/butler/butlerd"
	"github.com/itchio/butler/butlerd/messages"
	itchio "github.com/itchio/go-itchio"
	"github.com/itchio/httpkit/progress"
)

func Test_InstallCancel(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping test in short mode")
	}

	rc, h, cancel := connect(t)
	defer cancel()

	authenticate(t, rc)
	setupTmpInstallLocation(t, h, rc)

	_, err := messages.NetworkSetBandwidthThrottle.TestCall(rc, butlerd.NetworkSetBandwidthThrottleParams{
		Enabled: true,
		Rate:    32 * 1024,
	})
	must(t, err)

	defer func() {
		_, err := messages.NetworkSetBandwidthThrottle.TestCall(rc, butlerd.NetworkSetBandwidthThrottleParams{
			Enabled: false,
		})
		must(t, err)
	}()

	{
		// itch-test-account/big-assets
		game := getGame(t, h, rc, 243485)

		queueRes, err := messages.InstallQueue.TestCall(rc, butlerd.InstallQueueParams{
			Game:              game,
			InstallLocationID: "tmp",
		})
		must(t, err)

		pidFilePath := filepath.Join(queueRes.StagingFolder, "operate-pid.json")

		var lastProgressValue float64
		printProgress := func(params butlerd.ProgressNotification) {
			t.Logf("%.2f%% done @ %s / s ETA %s", params.Progress*100, progress.FormatBytes(int64(params.BPS)), time.Duration(params.ETA*float64(time.Second)))
			lastProgressValue = params.Progress
		}

		gracefulCancelOnce := &sync.Once{}

		messages.Progress.Register(h, func(rc *butlerd.RequestContext, params butlerd.ProgressNotification) {
			printProgress(params)

			if params.Progress > 0.2 {
				_, err := os.Stat(pidFilePath)
				assert.NoError(t, err, "pid file exists before we graceful cancel")

				gracefulCancelOnce.Do(func() {
					delete(h.notificationHandlers, messages.Progress.Method())

					t.Logf("Calling graceful cancel")
					messages.InstallCancel.TestCall(rc, butlerd.InstallCancelParams{
						ID: queueRes.ID,
					})
					t.Logf("Graceful cancel called")
				})
			}
		})

		t.Logf("Queued %s", queueRes.InstallFolder)

		_, err = messages.InstallPerform.TestCall(rc, butlerd.InstallPerformParams{
			ID:            queueRes.ID,
			StagingFolder: queueRes.StagingFolder,
		})

		t.Logf("Last progress before graceful cancel: %.2f%%", lastProgressValue*100)
		t.Logf("Making sure we've been cancelled...")
		assert.Error(t, err)
		je := err.(*jsonrpc2.Error)
		assert.EqualValues(t, butlerd.CodeOperationCancelled, je.Code)

		t.Logf("Resuming while offline...")
		_, err = messages.NetworkSetSimulateOffline.TestCall(rc, butlerd.NetworkSetSimulateOfflineParams{
			Enabled: true,
		})
		must(t, err)

		_, err = messages.InstallPerform.TestCall(rc, butlerd.InstallPerformParams{
			ID:            queueRes.ID,
			StagingFolder: queueRes.StagingFolder,
		})
		assert.Error(t, err)
		je = err.(*jsonrpc2.Error)
		assert.EqualValues(t, butlerd.CodeNetworkDisconnected, je.Code)

		_, err = messages.NetworkSetSimulateOffline.TestCall(rc, butlerd.NetworkSetSimulateOfflineParams{
			Enabled: false,
		})
		must(t, err)

		t.Logf("Resuming after graceful cancel...")

		hardCancelOnce := &sync.Once{}

		messages.Progress.Register(h, func(rc *butlerd.RequestContext, params butlerd.ProgressNotification) {
			printProgress(params)

			if params.Progress > 0.5 {
				hardCancelOnce.Do(func() {
					t.Logf("Sending hard cancel")
					delete(h.notificationHandlers, messages.Progress.Method())
					t.Logf("Calling cancel")
					cancel()
					t.Logf("Okay, we called cancel")
				})
			}
		})

		_, err = messages.InstallPerform.TestCall(rc, butlerd.InstallPerformParams{
			ID:            queueRes.ID,
			StagingFolder: queueRes.StagingFolder,
		})

		t.Logf("Last progress before hard cancel: %.2f%%", lastProgressValue*100)
		assert.Error(t, err)

		t.Logf("Waiting for pid file to disappear...")
		pidFileDisappeared := false
		beforePidDisappear := time.Now()
		for i := 0; i < 100; i++ {
			_, err := os.Stat(pidFilePath)
			if err != nil && os.IsNotExist(err) {
				// good!
				pidFileDisappeared = true
				break
			}
			time.Sleep(100 * time.Millisecond)
		}
		assert.True(t, pidFileDisappeared, "pid file should disappear after cancellation (even hard)")
		t.Logf("PID file disappeared in %s", time.Since(beforePidDisappear))

		t.Logf("Resuming after hard cancel...")
		rc, h, cancel = connect(t)

		messages.Progress.Register(h, func(rc *butlerd.RequestContext, params butlerd.ProgressNotification) {
			printProgress(params)
		})

		_, err = messages.InstallPerform.TestCall(rc, butlerd.InstallPerformParams{
			ID:            queueRes.ID,
			StagingFolder: queueRes.StagingFolder,
		})
		assert.NoError(t, err)
	}
}

var setupTmpOnce sync.Once

func setupTmpInstallLocation(t *testing.T, h *handler, rc *butlerd.RequestContext) {
	setupTmpOnce.Do(func() {
		wd, err := os.Getwd()
		must(t, err)

		tmpPath := filepath.Join(wd, "tmp")
		must(t, os.RemoveAll(tmpPath))
		must(t, os.MkdirAll(tmpPath, 0755))

		_, err = messages.InstallLocationsAdd.TestCall(rc, butlerd.InstallLocationsAddParams{
			ID:   "tmp",
			Path: filepath.Join(wd, "tmp"),
		})
		must(t, err)
	})
}

func getGame(t *testing.T, h *handler, rc *butlerd.RequestContext, gameID int64) *itchio.Game {
	gameRes, err := messages.FetchGame.TestCall(rc, butlerd.FetchGameParams{
		GameID: gameID,
	})
	must(t, err)
	return gameRes.Game
}
<|endoftext|>


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

package main

import (
	"fmt"
	"io/ioutil"
	"path"
	"regexp"

	"github.com/Jeffail/gabs"
)

var nameRegex = regexp.MustCompile("^\\w+$")

type launcherConfig struct {
	data      *gabs.Container
	filename  string
	nameRegex *regexp.Regexp
}

func newLauncherConfig() (*launcherConfig, error) {
	lc := new(launcherConfig)
	lc.filename = path.Join(env().MinecraftDir, "launcher_profiles.json")
	lc.data = gabs.New()

	if fileExists(lc.filename) {
		rawdata, err := ioutil.ReadFile(lc.filename)
		if err != nil {
			return nil, err
		}

		lc.data, err = gabs.ParseJSON(rawdata)
		if err != nil {
			return nil, err
		}
	}
	return lc, nil
}

func (lc *launcherConfig) createProfile(name, version, gameDir string) error {
	if !nameRegex.MatchString(name) {
		return fmt.Errorf("invalid profile name: %s", name)
	}
	path := "profiles." + name
	lc.data.SetP(name, path+".name")
	lc.data.SetP(version, path+".lastVersionId")
	lc.data.SetP(gameDir, path+".gameDir")
	return nil
}

func (lc *launcherConfig) save() error {
	return ioutil.WriteFile(lc.filename, lc.data.Bytes(), 0644)
}
<commit_msg>Fix #8; regex was unnecessarily restrictive<commit_after>// ***************************************************************************
//
//  Copyright 2017 David (Dizzy) Smith, dizzyd@dizzyd.com
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
// ***************************************************************************

package main

import (
	"fmt"
	"io/ioutil"
	"path"
	"regexp"

	"github.com/Jeffail/gabs"
)

var nameRegex = regexp.MustCompile("^[\\w][\\w-.]*$")

type launcherConfig struct {
	data      *gabs.Container
	filename  string
	nameRegex *regexp.Regexp
}

func newLauncherConfig() (*launcherConfig, error) {
	lc := new(launcherConfig)
	lc.filename = path.Join(env().MinecraftDir, "launcher_profiles.json")
	lc.data = gabs.New()

	if fileExists(lc.filename) {
		rawdata, err := ioutil.ReadFile(lc.filename)
		if err != nil {
			return nil, err
		}

		lc.data, err = gabs.ParseJSON(rawdata)
		if err != nil {
			return nil, err
		}
	}
	return lc, nil
}

func (lc *launcherConfig) createProfile(name, version, gameDir string) error {
	if !nameRegex.MatchString(name) {
		return fmt.Errorf("invalid profile name: %s", name)
	}
	path := "profiles." + name
	lc.data.SetP(name, path+".name")
	lc.data.SetP(version, path+".lastVersionId")
	lc.data.SetP(gameDir, path+".gameDir")
	return nil
}

func (lc *launcherConfig) save() error {
	return writeJSON(lc.data, lc.filename)
}
<|endoftext|>


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

package ogletest

import (
)

// LessThan returns a matcher that matches integer, floating point, or strings
// values v such that v < x. Comparison is not defined between numeric and
// string types, but is defined between all integer and floating point types.
//
// x must itself be an integer, floating point, or string type; otherwise,
// LessThan will panic.
func LessThan(x interface{}) Matcher {
	// TODO
	return Equals(x)
}
<commit_msg>Added type checking.<commit_after>// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ogletest

import (
	"fmt"
	"reflect"
)

// LessThan returns a matcher that matches integer, floating point, or strings
// values v such that v < x. Comparison is not defined between numeric and
// string types, but is defined between all integer and floating point types.
//
// x must itself be an integer, floating point, or string type; otherwise,
// LessThan will panic.
func LessThan(x interface{}) Matcher {
	v := reflect.ValueOf(x)
	kind := v.Kind()

	switch {
	case isInteger(v):
	case isFloat(v):
	case kind == reflect.String:

	default:
		panic(fmt.Sprintf("LessThan: unexpected kind %v", kind))
	}

	// TODO
	return Equals(x)
}

type lessThanMatcher struct {
	limit reflect.Value
}
<|endoftext|>


<commit_before>package yaml

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"

	"gopkg.in/yaml.v2"

	"github.com/qor/i18n"
)

// New new YAML backend for I18n
func New(paths ...string) i18n.Backend {
	backend := &Backend{}

	for _, p := range paths {
		if file, err := os.Open(p); err == nil {
			defer file.Close()
			if fileInfo, err := file.Stat(); err == nil {
				if fileInfo.IsDir() {
					yamlFiles, _ := filepath.Glob(path.Join(p, "*.yaml"))
					backend.files = append(backend.files, yamlFiles...)

					ymlFiles, _ := filepath.Glob(path.Join(p, "*.yml"))
					backend.files = append(backend.files, ymlFiles...)
				} else if fileInfo.Mode().IsRegular() {
					backend.files = append(backend.files, p)
				}
			}
		}
	}

	return backend
}

// Backend YAML backend
type Backend struct {
	files []string
}

func loadTranslationsFromYaml(locale string, value interface{}, scopes []string) (translations []*i18n.Translation) {
	switch v := value.(type) {
	case yaml.MapSlice:
		for _, s := range v {
			results := loadTranslationsFromYaml(locale, s.Value, append(scopes, fmt.Sprint(s.Key)))
			translations = append(translations, results...)
		}
	default:
		var translation = &i18n.Translation{
			Locale: locale,
			Key:    strings.Join(scopes, "."),
			Value:  fmt.Sprint(v),
		}
		translations = append(translations, translation)
	}
	return
}

// LoadYAMLContent load YAML content
func (backend *Backend) LoadYAMLContent(content []byte) (translations []*i18n.Translation, err error) {
	var slice yaml.MapSlice

	if err = yaml.Unmarshal(content, &slice); err == nil {
		for _, item := range slice {
			translations = append(translations, loadTranslationsFromYaml(item.Key.(string) /* locale */, item.Value, []string{})...)
		}
	}

	return translations, err
}

// LoadTranslations load translations from YAML backend
func (backend *Backend) LoadTranslations() (translations []*i18n.Translation) {
	for _, file := range backend.files {
		if content, err := ioutil.ReadFile(file); err == nil {
			if results, err := backend.LoadYAMLContent(content); err == nil {
				translations = append(translations, results...)
			}
		} else {
			panic(err)
		}
	}
	return translations
}

// SaveTranslation save translation into YAML backend, not implemented
func (backend *Backend) SaveTranslation(t *i18n.Translation) error {
	return errors.New("not implemented")
}

// DeleteTranslation delete translation into YAML backend, not implemented
func (backend *Backend) DeleteTranslation(t *i18n.Translation) error {
	return errors.New("not implemented")
}
<commit_msg>YAML return itself other than Backend interface<commit_after>package yaml

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/qor/i18n"
	"gopkg.in/yaml.v2"
)

var _ i18n.Backend = &Backend{}

// New new YAML backend for I18n
func New(paths ...string) *Backend {
	backend := &Backend{}

	for _, p := range paths {
		if file, err := os.Open(p); err == nil {
			defer file.Close()
			if fileInfo, err := file.Stat(); err == nil {
				if fileInfo.IsDir() {
					yamlFiles, _ := filepath.Glob(path.Join(p, "*.yaml"))
					backend.files = append(backend.files, yamlFiles...)

					ymlFiles, _ := filepath.Glob(path.Join(p, "*.yml"))
					backend.files = append(backend.files, ymlFiles...)
				} else if fileInfo.Mode().IsRegular() {
					backend.files = append(backend.files, p)
				}
			}
		}
	}

	return backend
}

// Backend YAML backend
type Backend struct {
	files []string
}

func loadTranslationsFromYaml(locale string, value interface{}, scopes []string) (translations []*i18n.Translation) {
	switch v := value.(type) {
	case yaml.MapSlice:
		for _, s := range v {
			results := loadTranslationsFromYaml(locale, s.Value, append(scopes, fmt.Sprint(s.Key)))
			translations = append(translations, results...)
		}
	default:
		var translation = &i18n.Translation{
			Locale: locale,
			Key:    strings.Join(scopes, "."),
			Value:  fmt.Sprint(v),
		}
		translations = append(translations, translation)
	}
	return
}

// LoadYAMLContent load YAML content
func (backend *Backend) LoadYAMLContent(content []byte) (translations []*i18n.Translation, err error) {
	var slice yaml.MapSlice

	if err = yaml.Unmarshal(content, &slice); err == nil {
		for _, item := range slice {
			translations = append(translations, loadTranslationsFromYaml(item.Key.(string) /* locale */, item.Value, []string{})...)
		}
	}

	return translations, err
}

// LoadTranslations load translations from YAML backend
func (backend *Backend) LoadTranslations() (translations []*i18n.Translation) {
	for _, file := range backend.files {
		if content, err := ioutil.ReadFile(file); err == nil {
			if results, err := backend.LoadYAMLContent(content); err == nil {
				translations = append(translations, results...)
			}
		} else {
			panic(err)
		}
	}
	return translations
}

// SaveTranslation save translation into YAML backend, not implemented
func (backend *Backend) SaveTranslation(t *i18n.Translation) error {
	return errors.New("not implemented")
}

// DeleteTranslation delete translation into YAML backend, not implemented
func (backend *Backend) DeleteTranslation(t *i18n.Translation) error {
	return errors.New("not implemented")
}
<|endoftext|>


<commit_before>package moves

import (
	"errors"
	"github.com/jkomoros/boardgame"
	"github.com/jkomoros/boardgame/moves/moveinterfaces"
	"strconv"
)

//We can keep these private because embedders already will have the interface
//satisfied so don't need to be confused by them.
type roundRobinStarterPlayer interface {
	RoundRobinStarterPlayer(state boardgame.State) boardgame.PlayerIndex
}
type roundRobinPlayerConditionMet interface {
	//RoundRobinPlayerConditionMet should return whether the condition for the
	//round robin to be over has been met for this player.
	RoundRobinPlayerConditionMet(playerState boardgame.PlayerState) bool
}

/*

RoundRobin is a complicated type of move because a lot of complicated logic
has to be included in its Legal() and Apply(). It doesn't use
TargetPlayerIndex, because DefaultsForState doesn't get a chance to modify the
underlying state, and figuring out the next index to apply on is a non-trivial
calculation.

In addition, we don't want an explicit StartRoundRobin move, which means that
RoundRobin itself is responsible for signaling that it is active and when it
is done.

The first round robin sees that HasStartedRoundRobin is false, and is
therefore legal. (It also verifies that the last round robin is not equal to
its move name, to avoid situations where the round robin is over and it just
starts back up again).

RoundRobinLastPlayer is alway set to the last round robin
move that was applied.

The first Apply() sets up the RoundRobin by configuring all of the various
properties and signaling thta RoundRobinHasStarted is true.

RoundRobin.Legal() and RoundRobin.Apply() both have to keep track of the next
player to apply to. It is the LastPlayer, played forward until we find a
player whose ConditionMet returns nil.

When we apply it, we apply the action and update LastPlayer to this player
index. If searching forward fro the LastPlayer to our index either ended on
the original player or passed over them, then RoundCount is incremented.

After applying, we check the legal logic again. If we're the last move in the
round robin we finalize it by setting the properties to signal the round robin
is over.

*/

/*

RoundRobin is a type of move that goes around every player one by one and does
some action. Other moves in this package embed RoundRobin, and it's more
common to use those directly.

Round Robin moves start at a given player and goes around. It will skip
players for whom move.RoundRobinPlayerConditionMet() has already returned
true. When it finds a player whose end condition is not met, it will apply
RoundRobinAction() to them, and then advance to the next player. Every time it
makes a circuit around the list of players, it will increment
RoundRobinRoundCount. By default, once all players have had their player
conditions met (that is, no player is legal to select) the round robin's will
be done applying: its ConditionMet will return nil.

Round Robin keeps track of various properties on the gameState by using the
RoundRobinProperties interface. Generally it's easiest to simply embed
moveinterfaces.RoundRobinBaseGameState in your GameState anonymously to
implement the interface automatically.

The embeding move should implement moveinterfaces.RoundRobinActioner.

*/
type RoundRobin struct {
	ApplyUntil
}

//RoundRobinStarterPlayer by default will return delegate.CurrentPlayer.
//Override this method if you want a different starter.
func (s *RoundRobin) RoundRobinStarterPlayer(state boardgame.State) boardgame.PlayerIndex {
	return state.Game().Manager().Delegate().CurrentPlayerIndex(state)
}

//ConditionMet  goes around and returns nil if all players have had their
//player condition met, meaning that there are no more legal players to
//select. Because this condition is almost always an important base no matter
//the other conditions you are considering (it's not possible to select
//players who have already had their player condition met), if you override
//CondtionMet you should also call this implementation.
func (r *RoundRobin) ConditionMet(state boardgame.State) error {
	conditionsMet, ok := r.TopLevelStruct().(roundRobinPlayerConditionMet)

	if !ok {
		//This should be extremely rare since we ourselves have the right method.
		return errors.New("RoundRobin top level struct unexpectedly did not have RoundRobinPlayerConditionMet method")
	}

	for i, player := range state.PlayerStates() {
		if !conditionsMet.RoundRobinPlayerConditionMet(player) {
			return errors.New("Player " + strconv.Itoa(i) + " does not have their player condition met.")
		}
	}

	return nil
}

//RoundRobinPlayerConditionMet is called for each playerState. When advancing
//to the next player, round robin will only pick a player whose condition has
//not yet been met. Once all players have their PlayerconditionMet, then the
//RoundRobin's ConditionMet will return nil, signaling that the RoundRobin is
//done. By default this will return false. If you will use RoundRobin directly
//(as opposed to RoundRobinNumRounds) you will want to override this otherwise
//it will get in an infinite loop.
func (r *RoundRobin) RoundRobinPlayerConditionMet(playerState boardgame.PlayerState) bool {
	return false
}

//roundRobinHasStarted returns true if the gameState RoundRobin properties are not their sentinel values.
func (r *RoundRobin) roundRobinHasStarted(state boardgame.State) bool {
	roundRobiner, ok := state.GameState().(moveinterfaces.RoundRobinProperties)

	if !ok {
		return false
	}

	return roundRobiner.RoundRobinHasStarted()
}

//startRoundRobin should be called if roundRobinHasStarted is false.
func (r *RoundRobin) startRoundRobin(state boardgame.MutableState) error {

	roundRobiner, ok := state.GameState().(moveinterfaces.RoundRobinProperties)

	if !ok {
		return errors.New("GameState unexpectedly did not implement RoundRobiner interface")
	}

	starter, ok := r.TopLevelStruct().(roundRobinStarterPlayer)

	if !ok {
		//This should be extremely rare, because if we're embedded in it then
		//the struct should have it.
		return errors.New("The top level struct unexpectedly didn't have RoundRobinStarterPlayer")
	}

	starterPlayer := starter.RoundRobinStarterPlayer(state)

	roundRobiner.SetRoundRobinLastPlayer(starterPlayer.Previous(state))
	roundRobiner.SetRoundRobinStarterPlayer(starterPlayer)
	roundRobiner.SetRoundRobinRoundCount(0)
	roundRobiner.SetRoundRobinHasStarted(true)

	return nil
}

//finishRoundRobin should be called in the Apply method of the last round robin move.
func (r *RoundRobin) finishRoundRobin(state boardgame.MutableState) error {
	roundRobiner, ok := state.GameState().(moveinterfaces.RoundRobinProperties)

	if !ok {
		return errors.New("GameState unexpectedly did not implement RoundRobiner interface")
	}

	roundRobiner.SetRoundRobinHasStarted(false)

	return nil
}

//nextPlayerIndex returns the next playerIndex that the round robin will
//operate on.  Also returns roundSkip true if the player we end on is the
//first player in the round robin, or if we skipped over them to find the next
//valid player.
func (r *RoundRobin) nextPlayerIndex(state boardgame.State) (player boardgame.PlayerIndex, roundSkip bool) {

	var currentPlayer boardgame.PlayerIndex

	roundRobiner, ok := state.GameState().(moveinterfaces.RoundRobinProperties)

	if !ok {
		return boardgame.ObserverPlayerIndex, true
	}

	if r.roundRobinHasStarted(state) {

		currentPlayer = roundRobiner.RoundRobinLastPlayer()
	} else {

		starterPlayer, ok := r.TopLevelStruct().(roundRobinStarterPlayer)

		if !ok {
			return boardgame.ObserverPlayerIndex, true
		}

		currentPlayer = starterPlayer.RoundRobinStarterPlayer(state).Previous(state)
	}

	//If the PlayerConditionMet for that player is already true, we know that
	//we shouldn't land on them. Cycle around until we find one for which
	//PlayerConditionMet returns false, unless none of them are true, in which
	//case just leave it with the original target.

	//RoundRobin moves whose Finished() routine look for something other than
	//PlayerConditionMet will still work fine, because their
	//PlayerConditionMet will always return false.

	conditionsMet, ok := r.TopLevelStruct().(roundRobinPlayerConditionMet)

	if !ok {
		//This should be extremely rare since we ourselves have the right method.
		return boardgame.ObserverPlayerIndex, true
	}

	counter := 0
	roundSkip = false

	//Advance around, but if we loop back just leave it.
	for counter <= len(state.PlayerStates()) {

		currentPlayer = currentPlayer.Next(state)

		if currentPlayer == roundRobiner.RoundRobinStarterPlayer() {
			roundSkip = true
		}

		if !conditionsMet.RoundRobinPlayerConditionMet(state.PlayerStates()[currentPlayer]) {
			break
		}

		counter++
	}

	if counter > len(state.PlayerStates()) {
		//No players are legal
		return currentPlayer, true
	}

	return currentPlayer, roundSkip

}

func (r *RoundRobin) ValidConfiguration(exampleState boardgame.MutableState) error {

	if err := r.ApplyUntil.ValidConfiguration(exampleState); err != nil {
		return err
	}

	if _, ok := exampleState.GameState().(moveinterfaces.RoundRobinProperties); !ok {
		return errors.New("GameState does not implement RoundRobiner interface")
	}

	if _, ok := r.TopLevelStruct().(roundRobinStarterPlayer); !ok {
		return errors.New("Embedding move doesn't implement RoundRobinStarterPlayer")
	}

	embeddingMove := r.TopLevelStruct()

	if _, ok := embeddingMove.(moveinterfaces.RoundRobinActioner); !ok {
		return errors.New("Embedding move doesn't implement RoundRobinActioner")
	}

	return nil
}

//lastMoveName returns the name of the last move that was applied to this game.
func (r *RoundRobin) lastMoveName(state boardgame.State) string {
	moves := state.Game().MoveRecords(state.Version())

	if len(moves) == 0 {
		return ""
	}

	return moves[len(moves)-1].Name
}

//Legal is a complex implementation because it needs to figure out when to
//start the round robin. In general you do not want to override this.
func (r *RoundRobin) Legal(state boardgame.State, proposer boardgame.PlayerIndex) error {

	//We run the base legal first to see if this phase is even legal for us.
	//We can't run ApplyUntil until later, because it will say it's not legal
	//if the round robin hasn't started.
	if err := r.Base.Legal(state, proposer); err != nil {
		return err
	}

	if !r.roundRobinHasStarted(state) {

		//If the round robin hasn't started, it's legal to start--as long as
		//the last move applied was not us (otherwise we'd just infinite loop
		//in them).

		if r.TopLevelStruct().Info().Type().Name() == r.lastMoveName(state) {
			return errors.New("Can't start this round robin move because the last move was also part of this round robin.")
		}

		return nil
	}

	//We do ApplyUntil down here because it will check ConditionMet, which
	//will be true before the game starts in many cases.
	if err := r.ApplyUntil.Legal(state, proposer); err != nil {
		return err
	}

	//If the round robin has started, then it's legal--as soon as it isn't
	//legal, we've turned off the round robing.

	return nil
}

//Apply is a complex implementation because it needs to figure out when the
//round is already over and handle complicated signalling about who the next
//player is. In general you do not want to override this.
func (r *RoundRobin) Apply(state boardgame.MutableState) error {

	if !r.roundRobinHasStarted(state) {
		if err := r.startRoundRobin(state); err != nil {
			return errors.New("Couldn't start round robin: " + err.Error())
		}
	}

	conditionMetter, ok := r.TopLevelStruct().(conditionMetter)

	if !ok {
		return errors.New("Top level struct unexpectedly did not implement condition met")
	}

	if conditionMetter.ConditionMet(state) == nil {
		return errors.New("The round robin was found to be finished in our Apply, but it should have been marked finished before!")
	}

	nextPlayer, _ := r.nextPlayerIndex(state)

	actioner, ok := r.TopLevelStruct().(moveinterfaces.RoundRobinActioner)

	if !ok {
		return errors.New("Embedding move doesn't implement RoundRobinActioner")
	}

	playerToAction := state.MutablePlayerStates()[nextPlayer]

	if err := actioner.RoundRobinAction(playerToAction); err != nil {
		return errors.New("RoundRobinAction returned error: " + err.Error())
	}

	roundRobiner, ok := state.GameState().(moveinterfaces.RoundRobinProperties)

	if !ok {
		return errors.New("GameState does not implement RoundRobiner interface")
	}

	roundRobiner.SetRoundRobinLastPlayer(nextPlayer)

	nextPlayer, roundSkip := r.nextPlayerIndex(state)

	if roundSkip {
		roundRobiner.SetRoundRobinRoundCount(roundRobiner.RoundRobinRoundCount() + 1)
	}

	if conditionMetter.ConditionMet(state) == nil {
		if err := r.finishRoundRobin(state); err != nil {
			return errors.New("Couldn't finish round robin when it was done: " + err.Error())
		}
	}

	return nil

}

type numRoundser interface {
	NumRounds() int
}

//RoundRobinNumRounds is a subclass of RoundRobin whose ConditionMet checks
//whether RoundRobinRoundCount is greater than or equal to NumRounds(), and if
//it is ends immediately. NumRounds() defaults to 1; if you want to have
//multiple rounds, override NumRounds().
type RoundRobinNumRounds struct {
	RoundRobin
}

func (r *RoundRobinNumRounds) ValidConfiguration(exampleState boardgame.MutableState) error {
	if err := r.RoundRobin.ValidConfiguration(exampleState); err != nil {
		return err
	}
	if _, ok := r.TopLevelStruct().(numRoundser); !ok {
		return errors.New("EmbeddingMove unexpectedly did not implement NumRounds!")
	}
	return nil
}

//NumRounds should return the RoundRobinRoundCount that we are targeting. As
//soon as that RoundCount is reached, our ConditionMet will start returning
//nil, signaling the Round Robin is over. Defaults to 1.
func (r *RoundRobinNumRounds) NumRounds() int {
	return 1
}

//ConditionMet will check if the round count has been reached; if it has it
//will return nil immediately. Otherwise it will fall back on RoundRobin's
//base ConditionMet, returning nil if no players are left to act upon.
func (r *RoundRobinNumRounds) ConditionMet(state boardgame.State) error {
	numRounds, ok := r.TopLevelStruct().(numRoundser)

	if !ok {
		//Unexpected!
		return nil
	}

	roundRobiner, ok := state.GameState().(moveinterfaces.RoundRobinProperties)

	if !ok {
		return nil
	}

	if roundRobiner.RoundRobinRoundCount() >= numRounds.NumRounds() {
		return nil
	}

	return r.RoundRobin.ConditionMet(state)

}
<commit_msg>RoundRobinPlayerConditionMet --> PlayerConditionMet, because it will be used in other subclassing scenarios and the RoundRobin is unnecessary and distracting. Part of #516.<commit_after>package moves

import (
	"errors"
	"github.com/jkomoros/boardgame"
	"github.com/jkomoros/boardgame/moves/moveinterfaces"
	"strconv"
)

//We can keep these private because embedders already will have the interface
//satisfied so don't need to be confused by them.
type roundRobinStarterPlayer interface {
	RoundRobinStarterPlayer(state boardgame.State) boardgame.PlayerIndex
}
type playerConditionMet interface {
	//PlayerConditionMet should return whether the condition for the round
	//robin to be over has been met for this player.
	PlayerConditionMet(playerState boardgame.PlayerState) bool
}

/*

RoundRobin is a complicated type of move because a lot of complicated logic
has to be included in its Legal() and Apply(). It doesn't use
TargetPlayerIndex, because DefaultsForState doesn't get a chance to modify the
underlying state, and figuring out the next index to apply on is a non-trivial
calculation.

In addition, we don't want an explicit StartRoundRobin move, which means that
RoundRobin itself is responsible for signaling that it is active and when it
is done.

The first round robin sees that HasStartedRoundRobin is false, and is
therefore legal. (It also verifies that the last round robin is not equal to
its move name, to avoid situations where the round robin is over and it just
starts back up again).

RoundRobinLastPlayer is alway set to the last round robin
move that was applied.

The first Apply() sets up the RoundRobin by configuring all of the various
properties and signaling thta RoundRobinHasStarted is true.

RoundRobin.Legal() and RoundRobin.Apply() both have to keep track of the next
player to apply to. It is the LastPlayer, played forward until we find a
player whose ConditionMet returns nil.

When we apply it, we apply the action and update LastPlayer to this player
index. If searching forward fro the LastPlayer to our index either ended on
the original player or passed over them, then RoundCount is incremented.

After applying, we check the legal logic again. If we're the last move in the
round robin we finalize it by setting the properties to signal the round robin
is over.

*/

/*

RoundRobin is a type of move that goes around every player one by one and does
some action. Other moves in this package embed RoundRobin, and it's more
common to use those directly.

Round Robin moves start at a given player and goes around. It will skip
players for whom move.RoundRobinPlayerConditionMet() has already returned
true. When it finds a player whose end condition is not met, it will apply
RoundRobinAction() to them, and then advance to the next player. Every time it
makes a circuit around the list of players, it will increment
RoundRobinRoundCount. By default, once all players have had their player
conditions met (that is, no player is legal to select) the round robin's will
be done applying: its ConditionMet will return nil.

Round Robin keeps track of various properties on the gameState by using the
RoundRobinProperties interface. Generally it's easiest to simply embed
moveinterfaces.RoundRobinBaseGameState in your GameState anonymously to
implement the interface automatically.

The embeding move should implement moveinterfaces.RoundRobinActioner.

*/
type RoundRobin struct {
	ApplyUntil
}

//RoundRobinStarterPlayer by default will return delegate.CurrentPlayer.
//Override this method if you want a different starter.
func (s *RoundRobin) RoundRobinStarterPlayer(state boardgame.State) boardgame.PlayerIndex {
	return state.Game().Manager().Delegate().CurrentPlayerIndex(state)
}

//ConditionMet  goes around and returns nil if all players have had their
//player condition met, meaning that there are no more legal players to
//select. Because this condition is almost always an important base no matter
//the other conditions you are considering (it's not possible to select
//players who have already had their player condition met), if you override
//CondtionMet you should also call this implementation.
func (r *RoundRobin) ConditionMet(state boardgame.State) error {
	conditionsMet, ok := r.TopLevelStruct().(playerConditionMet)

	if !ok {
		//This should be extremely rare since we ourselves have the right method.
		return errors.New("RoundRobin top level struct unexpectedly did not have PlayerConditionMet method")
	}

	for i, player := range state.PlayerStates() {
		if !conditionsMet.PlayerConditionMet(player) {
			return errors.New("Player " + strconv.Itoa(i) + " does not have their player condition met.")
		}
	}

	return nil
}

//PlayerConditionMet is called for each playerState. When advancing
//to the next player, round robin will only pick a player whose condition has
//not yet been met. Once all players have their PlayerconditionMet, then the
//RoundRobin's ConditionMet will return nil, signaling that the RoundRobin is
//done. By default this will return false. If you will use RoundRobin directly
//(as opposed to RoundRobinNumRounds) you will want to override this otherwise
//it will get in an infinite loop.
func (r *RoundRobin) PlayerConditionMet(playerState boardgame.PlayerState) bool {
	return false
}

//roundRobinHasStarted returns true if the gameState RoundRobin properties are not their sentinel values.
func (r *RoundRobin) roundRobinHasStarted(state boardgame.State) bool {
	roundRobiner, ok := state.GameState().(moveinterfaces.RoundRobinProperties)

	if !ok {
		return false
	}

	return roundRobiner.RoundRobinHasStarted()
}

//startRoundRobin should be called if roundRobinHasStarted is false.
func (r *RoundRobin) startRoundRobin(state boardgame.MutableState) error {

	roundRobiner, ok := state.GameState().(moveinterfaces.RoundRobinProperties)

	if !ok {
		return errors.New("GameState unexpectedly did not implement RoundRobiner interface")
	}

	starter, ok := r.TopLevelStruct().(roundRobinStarterPlayer)

	if !ok {
		//This should be extremely rare, because if we're embedded in it then
		//the struct should have it.
		return errors.New("The top level struct unexpectedly didn't have RoundRobinStarterPlayer")
	}

	starterPlayer := starter.RoundRobinStarterPlayer(state)

	roundRobiner.SetRoundRobinLastPlayer(starterPlayer.Previous(state))
	roundRobiner.SetRoundRobinStarterPlayer(starterPlayer)
	roundRobiner.SetRoundRobinRoundCount(0)
	roundRobiner.SetRoundRobinHasStarted(true)

	return nil
}

//finishRoundRobin should be called in the Apply method of the last round robin move.
func (r *RoundRobin) finishRoundRobin(state boardgame.MutableState) error {
	roundRobiner, ok := state.GameState().(moveinterfaces.RoundRobinProperties)

	if !ok {
		return errors.New("GameState unexpectedly did not implement RoundRobiner interface")
	}

	roundRobiner.SetRoundRobinHasStarted(false)

	return nil
}

//nextPlayerIndex returns the next playerIndex that the round robin will
//operate on.  Also returns roundSkip true if the player we end on is the
//first player in the round robin, or if we skipped over them to find the next
//valid player.
func (r *RoundRobin) nextPlayerIndex(state boardgame.State) (player boardgame.PlayerIndex, roundSkip bool) {

	var currentPlayer boardgame.PlayerIndex

	roundRobiner, ok := state.GameState().(moveinterfaces.RoundRobinProperties)

	if !ok {
		return boardgame.ObserverPlayerIndex, true
	}

	if r.roundRobinHasStarted(state) {

		currentPlayer = roundRobiner.RoundRobinLastPlayer()
	} else {

		starterPlayer, ok := r.TopLevelStruct().(roundRobinStarterPlayer)

		if !ok {
			return boardgame.ObserverPlayerIndex, true
		}

		currentPlayer = starterPlayer.RoundRobinStarterPlayer(state).Previous(state)
	}

	//If the PlayerConditionMet for that player is already true, we know that
	//we shouldn't land on them. Cycle around until we find one for which
	//PlayerConditionMet returns false, unless none of them are true, in which
	//case just leave it with the original target.

	//RoundRobin moves whose Finished() routine look for something other than
	//PlayerConditionMet will still work fine, because their
	//PlayerConditionMet will always return false.

	conditionsMet, ok := r.TopLevelStruct().(playerConditionMet)

	if !ok {
		//This should be extremely rare since we ourselves have the right method.
		return boardgame.ObserverPlayerIndex, true
	}

	counter := 0
	roundSkip = false

	//Advance around, but if we loop back just leave it.
	for counter <= len(state.PlayerStates()) {

		currentPlayer = currentPlayer.Next(state)

		if currentPlayer == roundRobiner.RoundRobinStarterPlayer() {
			roundSkip = true
		}

		if !conditionsMet.PlayerConditionMet(state.PlayerStates()[currentPlayer]) {
			break
		}

		counter++
	}

	if counter > len(state.PlayerStates()) {
		//No players are legal
		return currentPlayer, true
	}

	return currentPlayer, roundSkip

}

func (r *RoundRobin) ValidConfiguration(exampleState boardgame.MutableState) error {

	if err := r.ApplyUntil.ValidConfiguration(exampleState); err != nil {
		return err
	}

	if _, ok := exampleState.GameState().(moveinterfaces.RoundRobinProperties); !ok {
		return errors.New("GameState does not implement RoundRobiner interface")
	}

	if _, ok := r.TopLevelStruct().(roundRobinStarterPlayer); !ok {
		return errors.New("Embedding move doesn't implement RoundRobinStarterPlayer")
	}

	embeddingMove := r.TopLevelStruct()

	if _, ok := embeddingMove.(moveinterfaces.RoundRobinActioner); !ok {
		return errors.New("Embedding move doesn't implement RoundRobinActioner")
	}

	return nil
}

//lastMoveName returns the name of the last move that was applied to this game.
func (r *RoundRobin) lastMoveName(state boardgame.State) string {
	moves := state.Game().MoveRecords(state.Version())

	if len(moves) == 0 {
		return ""
	}

	return moves[len(moves)-1].Name
}

//Legal is a complex implementation because it needs to figure out when to
//start the round robin. In general you do not want to override this.
func (r *RoundRobin) Legal(state boardgame.State, proposer boardgame.PlayerIndex) error {

	//We run the base legal first to see if this phase is even legal for us.
	//We can't run ApplyUntil until later, because it will say it's not legal
	//if the round robin hasn't started.
	if err := r.Base.Legal(state, proposer); err != nil {
		return err
	}

	if !r.roundRobinHasStarted(state) {

		//If the round robin hasn't started, it's legal to start--as long as
		//the last move applied was not us (otherwise we'd just infinite loop
		//in them).

		if r.TopLevelStruct().Info().Type().Name() == r.lastMoveName(state) {
			return errors.New("Can't start this round robin move because the last move was also part of this round robin.")
		}

		return nil
	}

	//We do ApplyUntil down here because it will check ConditionMet, which
	//will be true before the game starts in many cases.
	if err := r.ApplyUntil.Legal(state, proposer); err != nil {
		return err
	}

	//If the round robin has started, then it's legal--as soon as it isn't
	//legal, we've turned off the round robing.

	return nil
}

//Apply is a complex implementation because it needs to figure out when the
//round is already over and handle complicated signalling about who the next
//player is. In general you do not want to override this.
func (r *RoundRobin) Apply(state boardgame.MutableState) error {

	if !r.roundRobinHasStarted(state) {
		if err := r.startRoundRobin(state); err != nil {
			return errors.New("Couldn't start round robin: " + err.Error())
		}
	}

	conditionMetter, ok := r.TopLevelStruct().(conditionMetter)

	if !ok {
		return errors.New("Top level struct unexpectedly did not implement condition met")
	}

	if conditionMetter.ConditionMet(state) == nil {
		return errors.New("The round robin was found to be finished in our Apply, but it should have been marked finished before!")
	}

	nextPlayer, _ := r.nextPlayerIndex(state)

	actioner, ok := r.TopLevelStruct().(moveinterfaces.RoundRobinActioner)

	if !ok {
		return errors.New("Embedding move doesn't implement RoundRobinActioner")
	}

	playerToAction := state.MutablePlayerStates()[nextPlayer]

	if err := actioner.RoundRobinAction(playerToAction); err != nil {
		return errors.New("RoundRobinAction returned error: " + err.Error())
	}

	roundRobiner, ok := state.GameState().(moveinterfaces.RoundRobinProperties)

	if !ok {
		return errors.New("GameState does not implement RoundRobiner interface")
	}

	roundRobiner.SetRoundRobinLastPlayer(nextPlayer)

	nextPlayer, roundSkip := r.nextPlayerIndex(state)

	if roundSkip {
		roundRobiner.SetRoundRobinRoundCount(roundRobiner.RoundRobinRoundCount() + 1)
	}

	if conditionMetter.ConditionMet(state) == nil {
		if err := r.finishRoundRobin(state); err != nil {
			return errors.New("Couldn't finish round robin when it was done: " + err.Error())
		}
	}

	return nil

}

type numRoundser interface {
	NumRounds() int
}

//RoundRobinNumRounds is a subclass of RoundRobin whose ConditionMet checks
//whether RoundRobinRoundCount is greater than or equal to NumRounds(), and if
//it is ends immediately. NumRounds() defaults to 1; if you want to have
//multiple rounds, override NumRounds().
type RoundRobinNumRounds struct {
	RoundRobin
}

func (r *RoundRobinNumRounds) ValidConfiguration(exampleState boardgame.MutableState) error {
	if err := r.RoundRobin.ValidConfiguration(exampleState); err != nil {
		return err
	}
	if _, ok := r.TopLevelStruct().(numRoundser); !ok {
		return errors.New("EmbeddingMove unexpectedly did not implement NumRounds!")
	}
	return nil
}

//NumRounds should return the RoundRobinRoundCount that we are targeting. As
//soon as that RoundCount is reached, our ConditionMet will start returning
//nil, signaling the Round Robin is over. Defaults to 1.
func (r *RoundRobinNumRounds) NumRounds() int {
	return 1
}

//ConditionMet will check if the round count has been reached; if it has it
//will return nil immediately. Otherwise it will fall back on RoundRobin's
//base ConditionMet, returning nil if no players are left to act upon.
func (r *RoundRobinNumRounds) ConditionMet(state boardgame.State) error {
	numRounds, ok := r.TopLevelStruct().(numRoundser)

	if !ok {
		//Unexpected!
		return nil
	}

	roundRobiner, ok := state.GameState().(moveinterfaces.RoundRobinProperties)

	if !ok {
		return nil
	}

	if roundRobiner.RoundRobinRoundCount() >= numRounds.NumRounds() {
		return nil
	}

	return r.RoundRobin.ConditionMet(state)

}
<|endoftext|>


<commit_before>// Copyright 2016 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package layers

import (
	"encoding/binary"

	"github.com/google/gopacket"
)

// Geneve is specifed here https://tools.ietf.org/html/draft-ietf-nvo3-geneve-03
// Geneve Header:
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |Ver|  Opt Len  |O|C|    Rsvd.  |          Protocol Type        |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |        Virtual Network Identifier (VNI)       |    Reserved   |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                    Variable Length Options                    |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type Geneve struct {
	BaseLayer
	Version        uint8        // 2 bits
	OptionsLength  uint8        // 6 bits
	OAMPacket      bool         // 1 bits
	CriticalOption bool         // 1 bits
	Protocol       EthernetType // 16 bits
	VNI            uint32       // 24bits
	Options        []*GeneveOption
}

// Geneve Tunnel Options
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |          Option Class         |      Type     |R|R|R| Length  |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                      Variable Option Data                     |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type GeneveOption struct {
	Class  uint16 // 16 bits
	Type   uint8  // 8 bits
	Flags  uint8  // 3 bits
	Length uint8  // 5 bits
	Data   []byte
}

// LayerType returns LayerTypeGeneve
func (gn *Geneve) LayerType() gopacket.LayerType { return LayerTypeGeneve }

func decodeGeneveOption(data []byte, gn *Geneve) (*GeneveOption, uint8) {
	opt := &GeneveOption{}

	opt.Class = binary.BigEndian.Uint16(data[0:1])
	opt.Type = data[2]
	opt.Flags = data[3] >> 4
	opt.Length = data[3] & 0xf

	copy(opt.Data, data[4:opt.Length])

	return opt, 4 + opt.Length
}

func (gn *Geneve) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
	gn.Version = data[0] >> 7
	gn.OptionsLength = data[0] & 0x3f

	gn.OAMPacket = data[1]&0x80 > 0
	gn.CriticalOption = data[1]&0x40 > 0
	gn.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4]))

	var buf [4]byte
	copy(buf[1:], data[4:7])
	gn.VNI = binary.BigEndian.Uint32(buf[:])

	offset, length := uint8(8), gn.OptionsLength
	for length > 0 {
		opt, len := decodeGeneveOption(data[offset:], gn)
		gn.Options = append(gn.Options, opt)

		length -= len
		offset += len
	}

	gn.BaseLayer = BaseLayer{data[:offset], data[offset:]}

	return nil
}

func (gn *Geneve) NextLayerType() gopacket.LayerType {
	return gn.Protocol.LayerType()
}

func decodeGeneve(data []byte, p gopacket.PacketBuilder) error {
	gn := &Geneve{}
	return decodingLayerDecoder(gn, data, p)
}
<commit_msg>handle truncated geneve packets<commit_after>// Copyright 2016 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package layers

import (
	"encoding/binary"
	"errors"

	"github.com/google/gopacket"
)

// Geneve is specifed here https://tools.ietf.org/html/draft-ietf-nvo3-geneve-03
// Geneve Header:
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |Ver|  Opt Len  |O|C|    Rsvd.  |          Protocol Type        |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |        Virtual Network Identifier (VNI)       |    Reserved   |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                    Variable Length Options                    |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type Geneve struct {
	BaseLayer
	Version        uint8        // 2 bits
	OptionsLength  uint8        // 6 bits
	OAMPacket      bool         // 1 bits
	CriticalOption bool         // 1 bits
	Protocol       EthernetType // 16 bits
	VNI            uint32       // 24bits
	Options        []*GeneveOption
}

// Geneve Tunnel Options
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |          Option Class         |      Type     |R|R|R| Length  |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                      Variable Option Data                     |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type GeneveOption struct {
	Class  uint16 // 16 bits
	Type   uint8  // 8 bits
	Flags  uint8  // 3 bits
	Length uint8  // 5 bits
	Data   []byte
}

// LayerType returns LayerTypeGeneve
func (gn *Geneve) LayerType() gopacket.LayerType { return LayerTypeGeneve }

func decodeGeneveOption(data []byte, gn *Geneve) (*GeneveOption, uint8) {
	opt := &GeneveOption{}

	opt.Class = binary.BigEndian.Uint16(data[0:1])
	opt.Type = data[2]
	opt.Flags = data[3] >> 4
	opt.Length = data[3] & 0xf

	copy(opt.Data, data[4:opt.Length])

	return opt, 4 + opt.Length
}

func (gn *Geneve) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
	if len(data) < 7 {
		df.SetTruncated()
		return errors.New("geneve packet too short")
	}

	gn.Version = data[0] >> 7
	gn.OptionsLength = data[0] & 0x3f

	gn.OAMPacket = data[1]&0x80 > 0
	gn.CriticalOption = data[1]&0x40 > 0
	gn.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4]))

	var buf [4]byte
	copy(buf[1:], data[4:7])
	gn.VNI = binary.BigEndian.Uint32(buf[:])

	offset, length := uint8(8), gn.OptionsLength
	if len(data) < int(length+7) {
		df.SetTruncated()
		return errors.New("geneve packet too short")
	}

	for length > 0 {
		opt, len := decodeGeneveOption(data[offset:], gn)
		gn.Options = append(gn.Options, opt)

		length -= len
		offset += len
	}

	gn.BaseLayer = BaseLayer{data[:offset], data[offset:]}

	return nil
}

func (gn *Geneve) NextLayerType() gopacket.LayerType {
	return gn.Protocol.LayerType()
}

func decodeGeneve(data []byte, p gopacket.PacketBuilder) error {
	gn := &Geneve{}
	return decodingLayerDecoder(gn, data, p)
}
<|endoftext|>


<commit_before>package rpc

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"hash/crc32"
	"io"
	"math"

	hdfs "github.com/colinmarc/hdfs/protocol/hadoop_hdfs"
	"github.com/golang/protobuf/proto"
)

const (
	outboundPacketSize = 65536
	outboundChunkSize  = 512
	maxPacketsInQueue  = 5
)

// blockWriteStream writes data out to a datanode, and reads acks back.
type blockWriteStream struct {
	block *hdfs.LocatedBlockProto

	conn   io.ReadWriter
	buf    bytes.Buffer
	offset int64
	closed bool

	packets chan outboundPacket
	seqno   int

	ackError        error
	acksDone        chan struct{}
	lastPacketSeqno int
}

type outboundPacket struct {
	seqno     int
	offset    int64
	last      bool
	checksums []byte
	data      []byte
}

type ackError struct {
	pipelineIndex int
	seqno         int
	status        hdfs.Status
}

func (ae ackError) Error() string {
	return fmt.Sprintf("Ack error from datanode: %s", ae.status.String())
}

var ErrInvalidSeqno = errors.New("Invalid ack sequence number")

func newBlockWriteStream(conn io.ReadWriter) *blockWriteStream {
	s := &blockWriteStream{
		conn:     conn,
		offset:   0,
		seqno:    1,
		packets:  make(chan outboundPacket, maxPacketsInQueue),
		acksDone: make(chan struct{}),
	}

	// Ack packets in the background.
	go func() {
		s.ackPackets()
		close(s.acksDone)
	}()

	return s
}

// func newBlockWriteStreamForRecovery(conn io.ReadWriter, oldWriteStream *blockWriteStream) {
// 	s := &blockWriteStream{
// 		conn: conn,
// 		buf: oldWriteStream.buf,
// 		packets: oldWriteStream.packets,
// 		offset: oldWriteStream.offset,
// 		seqno: oldWriteStream.seqno,
// 		packets
// 	}

// 	go s.ackPackets()
// 	return s
// }

func (s *blockWriteStream) Write(b []byte) (int, error) {
	if s.closed {
		return 0, io.ErrClosedPipe
	}

	if s.ackError != nil {
		return 0, s.ackError
	}

	n, _ := s.buf.Write(b)
	err := s.flush(false)
	return n, err
}

// finish flushes the rest of the buffered bytes, and then sends a final empty
// packet signifying the end of the block.
func (s *blockWriteStream) finish() error {
	if s.closed {
		return nil
	}
	s.closed = true

	if s.ackError != nil {
		return s.ackError
	}

	err := s.flush(true)
	if err != nil {
		return err
	}

	// The last packet has no data; it's just a marker that the block is finished.
	lastPacket := outboundPacket{
		seqno:     s.seqno,
		offset:    s.offset,
		last:      true,
		checksums: []byte{},
		data:      []byte{},
	}
	s.packets <- lastPacket
	err = s.writePacket(lastPacket)
	if err != nil {
		return err
	}
	close(s.packets)

	// Check one more time for any ack errors.
	<-s.acksDone
	if s.ackError != nil {
		return s.ackError
	}

	return nil
}

// flush parcels out the buffered bytes into packets, which it then flushes to
// the datanode. We keep around a reference to the packet, in case the ack
// fails, and we need to send it again later.
func (s *blockWriteStream) flush(force bool) error {
	for s.buf.Len() > 0 && (force || s.buf.Len() >= outboundPacketSize) {
		packet := s.makePacket()
		s.packets <- packet
		s.offset += int64(len(packet.data))
		s.seqno++

		err := s.writePacket(packet)
		if err != nil {
			return err
		}
	}

	return nil
}

func (s *blockWriteStream) makePacket() outboundPacket {
	packetLength := outboundPacketSize
	if s.buf.Len() < outboundPacketSize {
		packetLength = s.buf.Len()
	}

	numChunks := int(math.Ceil(float64(packetLength) / float64(outboundChunkSize)))
	packet := outboundPacket{
		seqno:     s.seqno,
		offset:    s.offset,
		last:      false,
		checksums: make([]byte, numChunks*4),
		data:      make([]byte, packetLength),
	}

	// TODO: we shouldn't actually need this extra copy. We should also be able
	// to "reuse" packets.
	io.ReadFull(&s.buf, packet.data)

	// Fill in the checksum for each chunk of data.
	for i := 0; i < numChunks; i++ {
		chunkOff := i * outboundChunkSize
		chunkEnd := chunkOff + outboundChunkSize
		if chunkEnd >= len(packet.data) {
			chunkEnd = len(packet.data)
		}

		checksum := crc32.Checksum(packet.data[chunkOff:chunkEnd], crc32.IEEETable)
		binary.BigEndian.PutUint32(packet.checksums[i*4:], checksum)
	}

	return packet
}

// ackPackets is meant to run in the background, reading acks and setting
// ackError if one fails.
func (s *blockWriteStream) ackPackets() {
	reader := bufio.NewReader(s.conn)

	for {
		p, ok := <-s.packets
		if !ok {
			// All packets all acked.
			return
		}

		ack := &hdfs.PipelineAckProto{}

		// If we fail to read the ack at all, that counts as a failure from the
		// first datanode (the one we're connected to).
		err := readPrefixedMessage(reader, ack)
		if err != nil {
			s.ackError = err
			return
		}

		seqno := int(ack.GetSeqno())
		for i, status := range ack.GetStatus() {
			if status != hdfs.Status_SUCCESS {
				s.ackError = ackError{status: status, seqno: seqno, pipelineIndex: i}
				return
			}
		}

		if seqno != p.seqno {
			s.ackError = ErrInvalidSeqno
			return
		}
	}
}

// A packet for the datanode:
// +-----------------------------------------------------------+
// |  uint32 length of the packet                              |
// +-----------------------------------------------------------+
// |  size of the PacketHeaderProto, uint16                    |
// +-----------------------------------------------------------+
// |  PacketHeaderProto                                        |
// +-----------------------------------------------------------+
// |  N checksums, 4 bytes each                                |
// +-----------------------------------------------------------+
// |  N chunks of payload data                                 |
// +-----------------------------------------------------------+
func (s *blockWriteStream) writePacket(p outboundPacket) error {
	headerInfo := &hdfs.PacketHeaderProto{
		OffsetInBlock:     proto.Int64(p.offset),
		Seqno:             proto.Int64(int64(p.seqno)),
		LastPacketInBlock: proto.Bool(p.last),
		DataLen:           proto.Int32(int32(len(p.data))),
	}

	header := make([]byte, 6)
	infoBytes, err := proto.Marshal(headerInfo)
	if err != nil {
		return err
	}

	// Don't ask me why this doesn't include the header proto...
	totalLength := len(p.data) + len(p.checksums) + 4
	binary.BigEndian.PutUint32(header, uint32(totalLength))
	binary.BigEndian.PutUint16(header[4:], uint16(len(infoBytes)))
	header = append(header, infoBytes...)

	_, err = s.conn.Write(header)
	if err != nil {
		return err
	}

	_, err = s.conn.Write(p.checksums)
	if err != nil {
		return err
	}

	_, err = s.conn.Write(p.data)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Fix possible race in blockWriteStream<commit_after>package rpc

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"hash/crc32"
	"io"
	"math"

	hdfs "github.com/colinmarc/hdfs/protocol/hadoop_hdfs"
	"github.com/golang/protobuf/proto"
)

const (
	outboundPacketSize = 65536
	outboundChunkSize  = 512
	maxPacketsInQueue  = 5
)

// blockWriteStream writes data out to a datanode, and reads acks back.
type blockWriteStream struct {
	block *hdfs.LocatedBlockProto

	conn   io.ReadWriter
	buf    bytes.Buffer
	offset int64
	closed bool

	packets chan outboundPacket
	seqno   int

	ackError        error
	acksDone        chan struct{}
	lastPacketSeqno int
}

type outboundPacket struct {
	seqno     int
	offset    int64
	last      bool
	checksums []byte
	data      []byte
}

type ackError struct {
	pipelineIndex int
	seqno         int
	status        hdfs.Status
}

func (ae ackError) Error() string {
	return fmt.Sprintf("Ack error from datanode: %s", ae.status.String())
}

var ErrInvalidSeqno = errors.New("Invalid ack sequence number")

func newBlockWriteStream(conn io.ReadWriter) *blockWriteStream {
	s := &blockWriteStream{
		conn:     conn,
		offset:   0,
		seqno:    1,
		packets:  make(chan outboundPacket, maxPacketsInQueue),
		acksDone: make(chan struct{}),
	}

	// Ack packets in the background.
	go func() {
		s.ackPackets()
		close(s.acksDone)
	}()

	return s
}

// func newBlockWriteStreamForRecovery(conn io.ReadWriter, oldWriteStream *blockWriteStream) {
// 	s := &blockWriteStream{
// 		conn: conn,
// 		buf: oldWriteStream.buf,
// 		packets: oldWriteStream.packets,
// 		offset: oldWriteStream.offset,
// 		seqno: oldWriteStream.seqno,
// 		packets
// 	}

// 	go s.ackPackets()
// 	return s
// }

func (s *blockWriteStream) Write(b []byte) (int, error) {
	if s.closed {
		return 0, io.ErrClosedPipe
	}

	if s.ackError != nil {
		return 0, s.ackError
	}

	n, _ := s.buf.Write(b)
	err := s.flush(false)
	return n, err
}

// finish flushes the rest of the buffered bytes, and then sends a final empty
// packet signifying the end of the block.
func (s *blockWriteStream) finish() error {
	if s.closed {
		return nil
	}
	s.closed = true

	if s.ackError != nil {
		return s.ackError
	}

	err := s.flush(true)
	if err != nil {
		return err
	}

	// The last packet has no data; it's just a marker that the block is finished.
	lastPacket := outboundPacket{
		seqno:     s.seqno,
		offset:    s.offset,
		last:      true,
		checksums: []byte{},
		data:      []byte{},
	}
	s.packets <- lastPacket
	err = s.writePacket(lastPacket)
	if err != nil {
		return err
	}
	close(s.packets)

	// Check one more time for any ack errors.
	<-s.acksDone
	if s.ackError != nil {
		return s.ackError
	}

	return nil
}

// flush parcels out the buffered bytes into packets, which it then flushes to
// the datanode. We keep around a reference to the packet, in case the ack
// fails, and we need to send it again later.
func (s *blockWriteStream) flush(force bool) error {
	for s.buf.Len() > 0 && (force || s.buf.Len() >= outboundPacketSize) {
		packet := s.makePacket()
		s.packets <- packet
		s.offset += int64(len(packet.data))
		s.seqno++

		err := s.writePacket(packet)
		if err != nil {
			return err
		}
	}

	return nil
}

func (s *blockWriteStream) makePacket() outboundPacket {
	packetLength := outboundPacketSize
	if s.buf.Len() < outboundPacketSize {
		packetLength = s.buf.Len()
	}

	numChunks := int(math.Ceil(float64(packetLength) / float64(outboundChunkSize)))
	packet := outboundPacket{
		seqno:     s.seqno,
		offset:    s.offset,
		last:      false,
		checksums: make([]byte, numChunks*4),
		data:      make([]byte, packetLength),
	}

	// TODO: we shouldn't actually need this extra copy. We should also be able
	// to "reuse" packets.
	io.ReadFull(&s.buf, packet.data)

	// Fill in the checksum for each chunk of data.
	for i := 0; i < numChunks; i++ {
		chunkOff := i * outboundChunkSize
		chunkEnd := chunkOff + outboundChunkSize
		if chunkEnd >= len(packet.data) {
			chunkEnd = len(packet.data)
		}

		checksum := crc32.Checksum(packet.data[chunkOff:chunkEnd], crc32.IEEETable)
		binary.BigEndian.PutUint32(packet.checksums[i*4:], checksum)
	}

	return packet
}

// ackPackets is meant to run in the background, reading acks and setting
// ackError if one fails.
func (s *blockWriteStream) ackPackets() {
	reader := bufio.NewReader(s.conn)

	for {
		p, ok := <-s.packets
		if !ok {
			// All packets all acked.
			return
		}

		// If we fail to read the ack at all, that counts as a failure from the
		// first datanode (the one we're connected to).
		ack := &hdfs.PipelineAckProto{}
		err := readPrefixedMessage(reader, ack)
		if err != nil {
			s.ackError = err
			break
		}

		seqno := int(ack.GetSeqno())
		for i, status := range ack.GetStatus() {
			if status != hdfs.Status_SUCCESS {
				s.ackError = ackError{status: status, seqno: seqno, pipelineIndex: i}
				break
			}
		}

		if seqno != p.seqno {
			s.ackError = ErrInvalidSeqno
			break
		}
	}

	// Once we've seen an error, just keep reading packets off the channel (but
	// not off the socket) until the writing thread figures it out. If we don't,
	// the upstream thread could deadlock waiting for the channel to have space.
	for _ = range s.packets {
	}
}

// A packet for the datanode:
// +-----------------------------------------------------------+
// |  uint32 length of the packet                              |
// +-----------------------------------------------------------+
// |  size of the PacketHeaderProto, uint16                    |
// +-----------------------------------------------------------+
// |  PacketHeaderProto                                        |
// +-----------------------------------------------------------+
// |  N checksums, 4 bytes each                                |
// +-----------------------------------------------------------+
// |  N chunks of payload data                                 |
// +-----------------------------------------------------------+
func (s *blockWriteStream) writePacket(p outboundPacket) error {
	headerInfo := &hdfs.PacketHeaderProto{
		OffsetInBlock:     proto.Int64(p.offset),
		Seqno:             proto.Int64(int64(p.seqno)),
		LastPacketInBlock: proto.Bool(p.last),
		DataLen:           proto.Int32(int32(len(p.data))),
	}

	header := make([]byte, 6)
	infoBytes, err := proto.Marshal(headerInfo)
	if err != nil {
		return err
	}

	// Don't ask me why this doesn't include the header proto...
	totalLength := len(p.data) + len(p.checksums) + 4
	binary.BigEndian.PutUint32(header, uint32(totalLength))
	binary.BigEndian.PutUint16(header[4:], uint16(len(infoBytes)))
	header = append(header, infoBytes...)

	_, err = s.conn.Write(header)
	if err != nil {
		return err
	}

	_, err = s.conn.Write(p.checksums)
	if err != nil {
		return err
	}

	_, err = s.conn.Write(p.data)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package layout

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/goccy/go-yaml"
	tele "gopkg.in/tucnak/telebot.v3"
)

type Settings struct {
	URL     string
	Token   string
	Updates int

	LocalesDir string `yaml:"locales_dir"`
	TokenEnv   string `yaml:"token_env"`
	ParseMode  string `yaml:"parse_mode"`

	Webhook    *tele.Webhook    `yaml:"webhook"`
	LongPoller *tele.LongPoller `yaml:"long_poller"`
}

func (lt *Layout) UnmarshalYAML(data []byte) error {
	var aux struct {
		Settings *Settings
		Config   map[string]interface{}
		Buttons  yaml.MapSlice
		Markups  yaml.MapSlice
		Results  yaml.MapSlice
		Locales  map[string]map[string]string
	}
	if err := yaml.Unmarshal(data, &aux); err != nil {
		return err
	}

	lt.Config = &Config{
		v: aux.Config,
	}

	if pref := aux.Settings; pref != nil {
		lt.pref = &tele.Settings{
			URL:       pref.URL,
			Token:     pref.Token,
			Updates:   pref.Updates,
			ParseMode: pref.ParseMode,
		}

		if pref.TokenEnv != "" {
			lt.pref.Token = os.Getenv(pref.TokenEnv)
		}

		if pref.Webhook != nil {
			lt.pref.Poller = pref.Webhook
		} else if pref.LongPoller != nil {
			lt.pref.Poller = pref.LongPoller
		}
	}

	lt.buttons = make(map[string]Button, len(aux.Buttons))
	for _, item := range aux.Buttons {
		k, v := item.Key.(string), item.Value

		// 1. Shortened reply button

		if v, ok := v.(string); ok {
			lt.buttons[k] = Button{Text: v}
			continue
		}

		// 2. Extended reply or inline button

		data, err := yaml.Marshal(v)
		if err != nil {
			return err
		}

		var btn struct {
			Button `yaml:",inline"`
			Data   interface{} `yaml:"data"`
		}
		if err := yaml.Unmarshal(data, &btn); err != nil {
			return err
		}

		if btn.Data != nil {
			if a, ok := btn.Data.([]interface{}); ok {
				s := make([]string, len(a))
				for i, v := range a {
					s[i] = fmt.Sprint(v)
				}
				btn.Button.Data = strings.Join(s, "|")
			} else if s, ok := btn.Data.(string); ok {
				btn.Button.Data = s
			} else {
				return fmt.Errorf("telebot/layout: invalid callback_data for %s button", k)
			}
		}

		lt.buttons[k] = btn.Button
	}

	lt.markups = make(map[string]Markup, len(aux.Markups))
	for _, item := range aux.Markups {
		k, v := item.Key.(string), item.Value

		data, err := yaml.Marshal(v)
		if err != nil {
			return err
		}

		var shortenedMarkup [][]string
		if yaml.Unmarshal(data, &shortenedMarkup) == nil {
			// 1. Shortened reply or inline markup

			kb := make([][]Button, len(shortenedMarkup))
			for i, btns := range shortenedMarkup {
				row := make([]Button, len(btns))
				for j, btn := range btns {
					b, ok := lt.buttons[btn]
					if !ok {
						return fmt.Errorf("telebot/layout: no %s button for %s markup", btn, k)
					}
					row[j] = b
				}
				kb[i] = row
			}

			data, err := yaml.Marshal(kb)
			if err != nil {
				return err
			}

			tmpl, err := template.New(k).Funcs(lt.funcs).Parse(string(data))
			if err != nil {
				return err
			}

			markup := Markup{keyboard: tmpl}
			for _, row := range kb {
				for _, btn := range row {
					inline := btn.URL != "" ||
						btn.Unique != "" ||
						btn.InlineQuery != "" ||
						btn.InlineQueryChat != ""

					if markup.inline == nil {
						markup.inline = &inline
					} else if *markup.inline != inline {
						return fmt.Errorf("telebot/layout: mixed reply and inline buttons in %s markup", k)
					}
				}
			}

			lt.markups[k] = markup
		} else {
			// 2. Extended reply markup

			var markup struct {
				Markup   `yaml:",inline"`
				Keyboard [][]string `yaml:"keyboard"`
			}
			if err := yaml.Unmarshal(data, &markup); err != nil {
				return err
			}

			kb := make([][]tele.ReplyButton, len(markup.Keyboard))
			for i, btns := range markup.Keyboard {
				row := make([]tele.ReplyButton, len(btns))
				for j, btn := range btns {
					row[j] = *lt.buttons[btn].Reply()
				}
				kb[i] = row
			}

			data, err := yaml.Marshal(kb)
			if err != nil {
				return err
			}

			tmpl, err := template.New(k).Funcs(lt.funcs).Parse(string(data))
			if err != nil {
				return err
			}

			markup.inline = new(bool)
			markup.Markup.keyboard = tmpl
			lt.markups[k] = markup.Markup
		}
	}

	lt.results = make(map[string]Result, len(aux.Results))
	for _, item := range aux.Results {
		k, v := item.Key.(string), item.Value

		data, err := yaml.Marshal(v)
		if err != nil {
			return err
		}

		tmpl, err := template.New(k).Funcs(lt.funcs).Parse(string(data))
		if err != nil {
			return err
		}

		var result Result
		if err := yaml.Unmarshal(data, &result); err != nil {
			return err
		}

		result.result = tmpl
		lt.results[k] = result
	}

	if aux.Locales == nil {
		if aux.Settings.LocalesDir == "" {
			aux.Settings.LocalesDir = "locales"
		}
		return lt.parseLocales(aux.Settings.LocalesDir)
	}

	return nil
}

func (lt *Layout) parseLocales(dir string) error {
	lt.locales = make(map[string]*template.Template)

	return filepath.Walk(dir, func(path string, fi os.FileInfo, _ error) error {
		if fi == nil || fi.IsDir() {
			return nil
		}

		data, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}

		var texts map[string]string
		if err := yaml.Unmarshal(data, &texts); err != nil {
			return err
		}

		name := fi.Name()
		name = strings.TrimSuffix(name, filepath.Ext(name))

		tmpl := template.New(name)
		for key, text := range texts {
			text = strings.Trim(text, "\r\n")
			tmpl, err = tmpl.New(key).Funcs(lt.funcs).Parse(text)
			if err != nil {
				return err
			}
		}

		lt.locales[name] = tmpl
		return nil
	})
}
<commit_msg>layout: make locale templates association correct<commit_after>package layout

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/goccy/go-yaml"
	tele "gopkg.in/tucnak/telebot.v3"
)

type Settings struct {
	URL     string
	Token   string
	Updates int

	LocalesDir string `yaml:"locales_dir"`
	TokenEnv   string `yaml:"token_env"`
	ParseMode  string `yaml:"parse_mode"`

	Webhook    *tele.Webhook    `yaml:"webhook"`
	LongPoller *tele.LongPoller `yaml:"long_poller"`
}

func (lt *Layout) UnmarshalYAML(data []byte) error {
	var aux struct {
		Settings *Settings
		Config   map[string]interface{}
		Buttons  yaml.MapSlice
		Markups  yaml.MapSlice
		Results  yaml.MapSlice
		Locales  map[string]map[string]string
	}
	if err := yaml.Unmarshal(data, &aux); err != nil {
		return err
	}

	lt.Config = &Config{
		v: aux.Config,
	}

	if pref := aux.Settings; pref != nil {
		lt.pref = &tele.Settings{
			URL:       pref.URL,
			Token:     pref.Token,
			Updates:   pref.Updates,
			ParseMode: pref.ParseMode,
		}

		if pref.TokenEnv != "" {
			lt.pref.Token = os.Getenv(pref.TokenEnv)
		}

		if pref.Webhook != nil {
			lt.pref.Poller = pref.Webhook
		} else if pref.LongPoller != nil {
			lt.pref.Poller = pref.LongPoller
		}
	}

	lt.buttons = make(map[string]Button, len(aux.Buttons))
	for _, item := range aux.Buttons {
		k, v := item.Key.(string), item.Value

		// 1. Shortened reply button

		if v, ok := v.(string); ok {
			lt.buttons[k] = Button{Text: v}
			continue
		}

		// 2. Extended reply or inline button

		data, err := yaml.Marshal(v)
		if err != nil {
			return err
		}

		var btn struct {
			Button `yaml:",inline"`
			Data   interface{} `yaml:"data"`
		}
		if err := yaml.Unmarshal(data, &btn); err != nil {
			return err
		}

		if btn.Data != nil {
			if a, ok := btn.Data.([]interface{}); ok {
				s := make([]string, len(a))
				for i, v := range a {
					s[i] = fmt.Sprint(v)
				}
				btn.Button.Data = strings.Join(s, "|")
			} else if s, ok := btn.Data.(string); ok {
				btn.Button.Data = s
			} else {
				return fmt.Errorf("telebot/layout: invalid callback_data for %s button", k)
			}
		}

		lt.buttons[k] = btn.Button
	}

	lt.markups = make(map[string]Markup, len(aux.Markups))
	for _, item := range aux.Markups {
		k, v := item.Key.(string), item.Value

		data, err := yaml.Marshal(v)
		if err != nil {
			return err
		}

		var shortenedMarkup [][]string
		if yaml.Unmarshal(data, &shortenedMarkup) == nil {
			// 1. Shortened reply or inline markup

			kb := make([][]Button, len(shortenedMarkup))
			for i, btns := range shortenedMarkup {
				row := make([]Button, len(btns))
				for j, btn := range btns {
					b, ok := lt.buttons[btn]
					if !ok {
						return fmt.Errorf("telebot/layout: no %s button for %s markup", btn, k)
					}
					row[j] = b
				}
				kb[i] = row
			}

			data, err := yaml.Marshal(kb)
			if err != nil {
				return err
			}

			tmpl, err := template.New(k).Funcs(lt.funcs).Parse(string(data))
			if err != nil {
				return err
			}

			markup := Markup{keyboard: tmpl}
			for _, row := range kb {
				for _, btn := range row {
					inline := btn.URL != "" ||
						btn.Unique != "" ||
						btn.InlineQuery != "" ||
						btn.InlineQueryChat != ""

					if markup.inline == nil {
						markup.inline = &inline
					} else if *markup.inline != inline {
						return fmt.Errorf("telebot/layout: mixed reply and inline buttons in %s markup", k)
					}
				}
			}

			lt.markups[k] = markup
		} else {
			// 2. Extended reply markup

			var markup struct {
				Markup   `yaml:",inline"`
				Keyboard [][]string `yaml:"keyboard"`
			}
			if err := yaml.Unmarshal(data, &markup); err != nil {
				return err
			}

			kb := make([][]tele.ReplyButton, len(markup.Keyboard))
			for i, btns := range markup.Keyboard {
				row := make([]tele.ReplyButton, len(btns))
				for j, btn := range btns {
					row[j] = *lt.buttons[btn].Reply()
				}
				kb[i] = row
			}

			data, err := yaml.Marshal(kb)
			if err != nil {
				return err
			}

			tmpl, err := template.New(k).Funcs(lt.funcs).Parse(string(data))
			if err != nil {
				return err
			}

			markup.inline = new(bool)
			markup.Markup.keyboard = tmpl
			lt.markups[k] = markup.Markup
		}
	}

	lt.results = make(map[string]Result, len(aux.Results))
	for _, item := range aux.Results {
		k, v := item.Key.(string), item.Value

		data, err := yaml.Marshal(v)
		if err != nil {
			return err
		}

		tmpl, err := template.New(k).Funcs(lt.funcs).Parse(string(data))
		if err != nil {
			return err
		}

		var result Result
		if err := yaml.Unmarshal(data, &result); err != nil {
			return err
		}

		result.result = tmpl
		lt.results[k] = result
	}

	if aux.Locales == nil {
		if aux.Settings.LocalesDir == "" {
			aux.Settings.LocalesDir = "locales"
		}
		return lt.parseLocales(aux.Settings.LocalesDir)
	}

	return nil
}

func (lt *Layout) parseLocales(dir string) error {
	lt.locales = make(map[string]*template.Template)

	return filepath.Walk(dir, func(path string, fi os.FileInfo, _ error) error {
		if fi == nil || fi.IsDir() {
			return nil
		}

		data, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}

		var texts map[string]string
		if err := yaml.Unmarshal(data, &texts); err != nil {
			return err
		}

		name := fi.Name()
		name = strings.TrimSuffix(name, filepath.Ext(name))

		tmpl := template.New(name).Funcs(lt.funcs)
		for key, text := range texts {
			_, err = tmpl.New(key).Parse(strings.Trim(text, "\r\n"))
			if err != nil {
				return err
			}
		}

		lt.locales[name] = tmpl
		return nil
	})
}
<|endoftext|>


<commit_before>package sia

// wallet.go contains things like signatures and scans the blockchain for
// available funds that can be spent.

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
)

type Wallet struct {
	SecretKey *ecdsa.PrivateKey

	// OwnedOutputs is a list of outputs that can spent using the secret
	// key.
	OwnedOutputs map[OutputID]Output
}

func CreateWallet() (w *Wallet, err error) {
	w = new(Wallet)

	curve := elliptic.P256()
	w.SecretKey, err = ecdsa.GenerateKey(curve, rand.Reader)
	return
}

func (w *Wallet) GetAddress() (ca CoinAddress) {
	var sc SpendConditions
	sc.NumSignatures = 1
	sc.PublicKeys = make([]PublicKey, 1)
	sc.PublicKeys[0] = PublicKey(w.SecretKey.PublicKey)

	// ca = HashStruct(SpendConditions)
	// SpendConditions should be a merkle tree.

	return
}
<commit_msg>make the spend conditions a part of the wallet struct<commit_after>package sia

// wallet.go contains things like signatures and scans the blockchain for
// available funds that can be spent.

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
)

type Wallet struct {
	SecretKey *ecdsa.PrivateKey

	SpendConditions SpendConditions

	// OwnedOutputs is a list of outputs that can spent using the secret
	// key.
	// OwnedOutputs map[OutputID]Output
}

func CreateWallet() (w *Wallet, err error) {
	w = new(Wallet)

	curve := elliptic.P256()
	w.SecretKey, err = ecdsa.GenerateKey(curve, rand.Reader)

	w.SpendConditions.NumSignatures = 1
	w.SpendConditions.PublicKeys = make([]PublicKey, 1)
	w.SpendConditions.PublicKeys[0] = PublicKey(w.SecretKey.PublicKey)

	return
}

func (w *Wallet) GetAddress() (ca CoinAddress) {
	// ca = HashStruct(w.SpendConditions)
	return
}
<|endoftext|>


<commit_before>package jsonrpc

import (
	"encoding/json"
	"errors"

	"github.com/nbusy/neptulon"
)

// Client is a client implementation for JSON-RPC 2.0 protocol for Neptulon framework.
// Client implementations in other programming languages might be provided in separate repositories so check the documentation.
type Client struct {
	conn *neptulon.Conn
}

// Dial creates a new client connection to a given network address with optional CA and/or a client certificate (PEM encoded X.509 cert/key).
// Debug mode logs all raw TCP communication.
func Dial(addr string, ca []byte, clientCert []byte, clientCertKey []byte, debug bool) (*Client, error) {
	c, err := neptulon.Dial(addr, ca, clientCert, clientCertKey, debug)
	if err != nil {
		return nil, err
	}

	return &Client{conn: c}, nil
}

// SetReadDeadline set the read deadline for the connection in seconds.
func (c *Client) SetReadDeadline(seconds int) {
	c.conn.SetReadDeadline(seconds)
}

// ReadMsg reads a message off of a client connection and returns a request, response, or notification message depending on what server sent.
// Optionally, you can specify a result type so returned JSON-RPC response result will be serialized in that type.
func (c *Client) ReadMsg(resultType interface{}) (req *Request, res *Response, not *Notification, err error) {
	_, data, err := c.conn.Read()
	if err != nil {
		return
	}

	msg := message{Result: resultType}
	if err = json.Unmarshal(data, &msg); err != nil {
		return
	}

	// if incoming message is a request or response
	if msg.ID != "" {
		// if incoming message is a request
		if msg.Method != "" {
			req = &Request{ID: msg.ID, Method: msg.Method, Params: msg.Params}
			return
		}

		// if incoming message is a response
		res = &Response{ID: msg.ID, Result: msg.Result, Error: msg.Error}
		return
	}

	// if incoming message is a notification
	if msg.Method != "" {
		not = &Notification{Method: msg.Method, Params: msg.Params}
	}

	err = errors.New("Received a malformed message.")
	return
}

// WriteRequest writes a JSON-RPC request to a client connection with structured params object and auto generated request ID.
func (c *Client) WriteRequest(method string, params interface{}) (reqID string, err error) {
	id, err := neptulon.GenUID()
	if err != nil {
		return "", err
	}

	return id, c.WriteMsg(Request{ID: id, Method: method, Params: params})
}

// WriteRequestArr writes a JSON-RPC request to a client connection with array params and auto generated request ID.
func (c *Client) WriteRequestArr(method string, params ...interface{}) (reqID string, err error) {
	id, err := neptulon.GenUID()
	if err != nil {
		return "", err
	}

	return id, c.WriteMsg(Request{ID: id, Method: method, Params: params})
}

// WriteMsg writes any JSON-RPC message to a client connection.
func (c *Client) WriteMsg(msg interface{}) error {
	data, err := json.Marshal(msg)
	if err != nil {
		return err
	}

	if _, err := c.conn.Write(data); err != nil {
		return err
	}

	return nil
}

// Close closes a client connection.
func (c *Client) Close() error {
	return c.conn.Close()
}
<commit_msg>clarify client.ReadMsg docs<commit_after>package jsonrpc

import (
	"encoding/json"
	"errors"

	"github.com/nbusy/neptulon"
)

// Client is a client implementation for JSON-RPC 2.0 protocol for Neptulon framework.
// Client implementations in other programming languages might be provided in separate repositories so check the documentation.
type Client struct {
	conn *neptulon.Conn
}

// Dial creates a new client connection to a given network address with optional CA and/or a client certificate (PEM encoded X.509 cert/key).
// Debug mode logs all raw TCP communication.
func Dial(addr string, ca []byte, clientCert []byte, clientCertKey []byte, debug bool) (*Client, error) {
	c, err := neptulon.Dial(addr, ca, clientCert, clientCertKey, debug)
	if err != nil {
		return nil, err
	}

	return &Client{conn: c}, nil
}

// SetReadDeadline set the read deadline for the connection in seconds.
func (c *Client) SetReadDeadline(seconds int) {
	c.conn.SetReadDeadline(seconds)
}

// ReadMsg reads a message off of a client connection and returns a request, response, or notification message depending on what server sent.
// Optionally, you can pass in a data structure that the returned JSON-RPC response result data will be serialized into. Otherwise the response result data will be a map.
func (c *Client) ReadMsg(resultType interface{}) (req *Request, res *Response, not *Notification, err error) {
	_, data, err := c.conn.Read()
	if err != nil {
		return
	}

	msg := message{Result: resultType}
	if err = json.Unmarshal(data, &msg); err != nil {
		return
	}

	// if incoming message is a request or response
	if msg.ID != "" {
		// if incoming message is a request
		if msg.Method != "" {
			req = &Request{ID: msg.ID, Method: msg.Method, Params: msg.Params}
			return
		}

		// if incoming message is a response
		res = &Response{ID: msg.ID, Result: msg.Result, Error: msg.Error}
		return
	}

	// if incoming message is a notification
	if msg.Method != "" {
		not = &Notification{Method: msg.Method, Params: msg.Params}
	}

	err = errors.New("Received a malformed message.")
	return
}

// WriteRequest writes a JSON-RPC request to a client connection with structured params object and auto generated request ID.
func (c *Client) WriteRequest(method string, params interface{}) (reqID string, err error) {
	id, err := neptulon.GenUID()
	if err != nil {
		return "", err
	}

	return id, c.WriteMsg(Request{ID: id, Method: method, Params: params})
}

// WriteRequestArr writes a JSON-RPC request to a client connection with array params and auto generated request ID.
func (c *Client) WriteRequestArr(method string, params ...interface{}) (reqID string, err error) {
	id, err := neptulon.GenUID()
	if err != nil {
		return "", err
	}

	return id, c.WriteMsg(Request{ID: id, Method: method, Params: params})
}

// WriteMsg writes any JSON-RPC message to a client connection.
func (c *Client) WriteMsg(msg interface{}) error {
	data, err := json.Marshal(msg)
	if err != nil {
		return err
	}

	if _, err := c.conn.Write(data); err != nil {
		return err
	}

	return nil
}

// Close closes a client connection.
func (c *Client) Close() error {
	return c.conn.Close()
}
<|endoftext|>


<commit_before>package main

var global = scope {
	"nil": Nil,

	// Arithmetic
	"+": builtinAdd,
	"-": builtinSub,
	"*": builtinMul,
	"/": builtinDiv,
	"%": builtinMod,
}
<commit_msg>Stub of 'define'<commit_after>package main

var global = scope {
	"nil": Nil,

	// Arithmetic
	"+": builtinAdd,
	"-": builtinSub,
	"*": builtinMul,
	"/": builtinDiv,
	"%": builtinMod,

	"define": builtinDefine,
}

func builtinDefine(ss []sexpr) sexpr {
	return Nil
}
<|endoftext|>