<commit_before>package jsonrpc

import (
	"github.com/cenkalti/rpc2"
	"net"
	"testing"
	"time"
)

const (
	network = "tcp4"
	addr    = "127.0.0.1:5000"
)

func TestJSONRPC(t *testing.T) {
	type Args struct{ A, B int }
	type Reply int

	lis, err := net.Listen(network, addr)
	if err != nil {
		t.Fatal(err)
	}

	srv := rpc2.NewServer()
	srv.Handle("add", func(client *rpc2.Client, args *Args, reply *Reply) error {
		*reply = Reply(args.A + args.B)

		var rep Reply
		err := client.Call("mult", Args{2, 3}, &rep)
		if err != nil {
			t.Fatal(err)
		}

		if rep != 6 {
			t.Fatalf("not expected: %d", rep)
		}

		return nil
	})
	srv.Handle("addPos", func(client *rpc2.Client, args []interface{}, result *float64) error {
		*result = args[0].(float64) + args[1].(float64)
		return nil
	})
	number := make(chan int, 1)
	srv.Handle("set", func(client *rpc2.Client, i int, _ *struct{}) error {
		number <- i
		return nil
	})

	go func() {
		conn, err := lis.Accept()
		if err != nil {
			t.Fatal(err)
		}
		srv.ServeCodec(NewJSONCodec(conn))
	}()

	conn, err := net.Dial(network, addr)
	if err != nil {
		t.Fatal(err)
	}

	clt := rpc2.NewClientWithCodec(NewJSONCodec(conn))
	clt.Handle("mult", func(client *rpc2.Client, args *Args, reply *Reply) error {
		*reply = Reply(args.A * args.B)
		return nil
	})
	go clt.Run()

	// Test Call.
	var rep Reply
	err = clt.Call("add", Args{1, 2}, &rep)
	if err != nil {
		t.Fatal(err)
	}
	if rep != 3 {
		t.Fatalf("not expected: %d", rep)
	}

	// Test notification.
	err = clt.Notify("set", 6)
	if err != nil {
		t.Fatal(err)
	}
	select {
	case i := <-number:
		if i != 6 {
			t.Fatalf("unexpected number: %d", i)
		}
	case <-time.After(time.Second):
		t.Fatal("did not get notification")
	}

	// Test undefined method.
	err = clt.Call("foo", 1, &rep)
	if err.Error() != "rpc2: can't find method foo" {
		t.Fatal(err)
	}

	// Test Positional arguments.
	var result float64
	err = clt.Call("addPos", []interface{}{1, 2}, &result)
	if err != nil {
		t.Fatal(err)
	}
	if result != 3 {
		t.Fatalf("not expected: %d", result)
	}
}
<commit_msg>fix string formatting in test<commit_after>package jsonrpc

import (
	"github.com/cenkalti/rpc2"
	"net"
	"testing"
	"time"
)

const (
	network = "tcp4"
	addr    = "127.0.0.1:5000"
)

func TestJSONRPC(t *testing.T) {
	type Args struct{ A, B int }
	type Reply int

	lis, err := net.Listen(network, addr)
	if err != nil {
		t.Fatal(err)
	}

	srv := rpc2.NewServer()
	srv.Handle("add", func(client *rpc2.Client, args *Args, reply *Reply) error {
		*reply = Reply(args.A + args.B)

		var rep Reply
		err := client.Call("mult", Args{2, 3}, &rep)
		if err != nil {
			t.Fatal(err)
		}

		if rep != 6 {
			t.Fatalf("not expected: %d", rep)
		}

		return nil
	})
	srv.Handle("addPos", func(client *rpc2.Client, args []interface{}, result *float64) error {
		*result = args[0].(float64) + args[1].(float64)
		return nil
	})
	number := make(chan int, 1)
	srv.Handle("set", func(client *rpc2.Client, i int, _ *struct{}) error {
		number <- i
		return nil
	})

	go func() {
		conn, err := lis.Accept()
		if err != nil {
			t.Fatal(err)
		}
		srv.ServeCodec(NewJSONCodec(conn))
	}()

	conn, err := net.Dial(network, addr)
	if err != nil {
		t.Fatal(err)
	}

	clt := rpc2.NewClientWithCodec(NewJSONCodec(conn))
	clt.Handle("mult", func(client *rpc2.Client, args *Args, reply *Reply) error {
		*reply = Reply(args.A * args.B)
		return nil
	})
	go clt.Run()

	// Test Call.
	var rep Reply
	err = clt.Call("add", Args{1, 2}, &rep)
	if err != nil {
		t.Fatal(err)
	}
	if rep != 3 {
		t.Fatalf("not expected: %d", rep)
	}

	// Test notification.
	err = clt.Notify("set", 6)
	if err != nil {
		t.Fatal(err)
	}
	select {
	case i := <-number:
		if i != 6 {
			t.Fatalf("unexpected number: %d", i)
		}
	case <-time.After(time.Second):
		t.Fatal("did not get notification")
	}

	// Test undefined method.
	err = clt.Call("foo", 1, &rep)
	if err.Error() != "rpc2: can't find method foo" {
		t.Fatal(err)
	}

	// Test Positional arguments.
	var result float64
	err = clt.Call("addPos", []interface{}{1, 2}, &result)
	if err != nil {
		t.Fatal(err)
	}
	if result != 3 {
		t.Fatalf("not expected: %f", result)
	}
}
<|endoftext|>


<commit_before>package model

import (
	"fmt"
	"time"
)

const (
	CreationTimeFormat = time.RFC822
)

func TimestampFormat(timestamp time.Time) string {
	age := time.Now().Sub(timestamp)
	ageString := age.String()
	switch {
	case age.Hours() > 365*24:
		years := uint64(age.Hours()) / (365 * 24)
		days := uint64(age.Hours()) % (365 * 24)
		ageString = fmt.Sprintf("%dy-%03d", years, days)
	case age.Hours() > 24:
		days := uint64(age.Hours()) / 24
		hours := uint64(age.Hours()) % 24
		ageString = fmt.Sprintf("%dd-%02dh", days, hours)
	case age.Hours() <= 24 && age.Hours() > 1:
		hours := uint64(age.Hours())
		minutes := uint64(age.Minutes()) % 60
		ageString = fmt.Sprintf("%dh-%02dm", hours, minutes)
	case age.Hours() < 1 && age.Minutes() > 1:
		minutes := uint64(age.Minutes())
		seconds := uint64(age.Seconds()) % 60
		ageString = fmt.Sprintf("%dm-%02ds", minutes, seconds)
	default:
		seconds := uint64(age.Seconds())
		ageString = fmt.Sprintf("%ds", seconds)
	}
	return ageString
}
<commit_msg>fix age formatting<commit_after>package model

import (
	"fmt"
	"time"
)

const (
	CreationTimeFormat = time.RFC822
)

func TimestampFormat(timestamp time.Time) string {
	age := time.Now().Sub(timestamp)
	ageString := age.String()
	const year = 365 * 24
	switch {
	case age.Hours() > year:
		years := uint64(age.Hours()) / year
		days := uint64(age.Hours()) % year
		ageString = fmt.Sprintf("%dy-%03dd", years, days)
	case age.Hours() > 24 && age.Hours() < 365*24:
		days := uint64(age.Hours()) / 24
		hours := uint64(age.Hours()) % 24
		ageString = fmt.Sprintf("%dd-%02dh", days, hours)
	case age.Hours() <= 24 && age.Hours() > 1:
		hours := uint64(age.Hours())
		minutes := uint64(age.Minutes()) % 60
		ageString = fmt.Sprintf("%dh-%02dm", hours, minutes)
	case age.Hours() < 1 && age.Minutes() > 1:
		minutes := uint64(age.Minutes())
		seconds := uint64(age.Seconds()) % 60
		ageString = fmt.Sprintf("%dm-%02ds", minutes, seconds)
	default:
		seconds := uint64(age.Seconds())
		ageString = fmt.Sprintf("%ds", seconds)
	}
	return ageString
}
<|endoftext|>


<commit_before>package qemu

import (
	"fmt"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"code.cloudfoundry.org/bytefmt"
	"github.com/digitalocean/go-qemu/qmp"
	"github.com/digitalocean/go-qemu/qmp/raw"
	uuid "github.com/satori/go.uuid"
	"github.com/shirou/gopsutil/process"
)

type Qemu struct {
	proc *process.Process

	// args
	id      *uuid.UUID
	qmpPath string
	isKVM   bool

	qmp qmp.Monitor
	m   *raw.Monitor
}

func OpenQemu(id *uuid.UUID) (*Qemu, error) {
	q := &Qemu{
		id:    id,
		isKVM: true,
	}

	if err := q.init(); err != nil {
		return nil, err
	}

	return q, nil
}

func (q *Qemu) Close() error {
	if q.m == nil {
		return nil
	}

	return q.qmp.Disconnect()
}

func (q Qemu) Reset() error {
	return q.m.SystemReset()
}

func (q Qemu) Shutdown() error {
	return q.m.SystemPowerdown()
}

func (q Qemu) Boot() error {
	if err := q.m.Cont(); err != nil {
		return err
	}

	return q.m.SystemWakeup()
}

type Status int

const (
	StatusDebug         Status = Status(raw.RunStateDebug)
	StatusFinishMigrate Status = Status(raw.RunStateFinishMigrate)
	StatusGuestPanicked Status = Status(raw.RunStateGuestPanicked)
	StatusIOError       Status = Status(raw.RunStateIOError)
	StatusInMigrate     Status = Status(raw.RunStateInmigrate)
	StatusInternalError Status = Status(raw.RunStateInternalError)
	StatusPaused        Status = Status(raw.RunStatePaused)
	StatusPostMigrate   Status = Status(raw.RunStatePostmigrate)
	StatusPreLaunch     Status = Status(raw.RunStatePrelaunch)
	StatusRestoreVM     Status = Status(raw.RunStateRestoreVM)
	StatusRunning       Status = Status(raw.RunStateRunning)
	StatusSaveVM        Status = Status(raw.RunStateSaveVM)
	StatusShutdown      Status = Status(raw.RunStateShutdown)
	StatusSuspended     Status = Status(raw.RunStateSuspended)
	StatusWatchdog      Status = Status(raw.RunStateWatchdog)
)

func (q Qemu) Status() (Status, error) {
	s, err := q.m.QueryStatus()
	if err != nil {
		if strings.Contains(err.Error(), "not running") {
			return StatusShutdown, nil
		}

		return 0, err
	}

	return Status(s.Status), nil
}

func (q Qemu) IsRunning() bool {
	if q.proc == nil {
		return false
	}

	return true
}

func (q *Qemu) Delete() error {
	if q.proc != nil {
		if err := q.proc.Kill(); err != nil {
			return fmt.Errorf("Failed to kill process: err='%s'", err.Error())
		}

		q.proc = nil
	}

	if err := os.Remove(q.qmpPath); err != nil {
		return fmt.Errorf("Failed to delete QMP socket: err='%s'", err.Error())
	}

	return nil
}

func (q Qemu) getVNCPort() uint16 {
	for p := 5900; p < 65536; p++ {
		l, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", p))
		if err == nil {
			defer l.Close()

			return uint16(p)
		}
	}

	return 0
}

func (q *Qemu) Start(name, qmpPath string, vncWebsocketPort, vcpus uint32, memory uint64) error {
	qmpPath, err := filepath.Abs(qmpPath)
	if err != nil {
		return fmt.Errorf("Failed to get absolute path of qmpPath: err='%s'", err.Error())
	}

	args := []string{
		"qemu-system-x86_64",

		// -- QEMU metadata --
		"-uuid",
		q.id.String(),
		"-name",
		fmt.Sprintf("guest=%s,debug-threads=on", name),
		"-msg",
		"timestamp=on",

		// Config
		// "-daemonize",
		"-nodefaults",     // Don't create default devices
		"-no-user-config", // The "-no-user-config" option makes QEMU not load any of the user-provided config files on sysconfdir
		"-S",              // Do not start CPU at startup
		"-no-shutdown",    // Don't exit QEMU on guest shutdown

		// QMP
		"-chardev",
		fmt.Sprintf("socket,id=charmonitor,path=%s,server,nowait", qmpPath),
		"-mon",
		"chardev=charmonitor,id=monitor,mode=control",

		// -- BIOS --
		// boot priority
		"-boot",
		"menu=on,strict=on",

		// keyboard
		"-k",
		"en-us",

		// VNC
		"-vnc",
		fmt.Sprintf("127.0.0.1:%d,websocket=%d", q.getVNCPort()-5900, vncWebsocketPort), // TODO: ぶつからないようにポートを設定する必要がある、現状一台しか立たない

		// clock
		"-rtc",
		"base=utc,driftfix=slew",
		"-global",
		"kvm-pit.lost_tick_policy=delay",
		"-no-hpet",

		// CPU
		// TODO: 必要があればmonitorを操作してhotaddできるようにする
		// TODO: スケジューリングが可能かどうか調べる
		"-smp",
		fmt.Sprintf("%d,sockets=1,cores=%d,threads=1", vcpus, vcpus),
		"-cpu",
		"host",
		"-enable-kvm",

		// Memory
		"-m",
		fmt.Sprintf("%s", bytefmt.ByteSize(memory)),
		// "-device",
		// "virtio-balloon-pci,id=balloon0,bus=pci.0", // dynamic configurations
		"-realtime",
		"mlock=off",

		// VGA controller
		"-device",
		"VGA,id=video0,bus=pci.0",

		// SCSI controller
		"-device",
		"lsi53c895a,bus=pci.0,id=scsi0",
	}

	if !q.isKVM {
		// remove "-cpu", "host" and "-enable-kvm", because kvm is disable
		args = append(args[:29], args[32:]...)
	}

	cmd := exec.Command(args[0], args[1:]...)
	if err := cmd.Start(); err != nil { // TODO: combine でもいいかもしれない
		return fmt.Errorf("Failed to start process: args='%s', err='%s'", args, err.Error())
	}

	done := make(chan error)
	go func() {
		done <- cmd.Wait()
	}()

	select {
	case <-time.After(3 * time.Second):
		break

	case err := <-done:
		if err != nil {
			return fmt.Errorf("Failed to run process: args='%s', err='%s'", args, err.Error()) // stderrを表示できるようにする必要がある
		}
	}

	if err := q.init(); err != nil {
		return fmt.Errorf("Failed to initialize: args='%s', err='%s'", args, err.Error())
	}
	return nil
}
<commit_msg>[pkg/qemu] daemonize qemu<commit_after>package qemu

import (
	"fmt"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"code.cloudfoundry.org/bytefmt"
	"github.com/digitalocean/go-qemu/qmp"
	"github.com/digitalocean/go-qemu/qmp/raw"
	uuid "github.com/satori/go.uuid"
	"github.com/shirou/gopsutil/process"
)

type Qemu struct {
	proc *process.Process

	// args
	id      *uuid.UUID
	qmpPath string
	isKVM   bool

	qmp qmp.Monitor
	m   *raw.Monitor
}

func OpenQemu(id *uuid.UUID) (*Qemu, error) {
	q := &Qemu{
		id:    id,
		isKVM: true,
	}

	if err := q.init(); err != nil {
		return nil, err
	}

	return q, nil
}

func (q *Qemu) Close() error {
	if q.m == nil {
		return nil
	}

	return q.qmp.Disconnect()
}

func (q Qemu) Reset() error {
	return q.m.SystemReset()
}

func (q Qemu) Shutdown() error {
	return q.m.SystemPowerdown()
}

func (q Qemu) Boot() error {
	if err := q.m.Cont(); err != nil {
		return err
	}

	return q.m.SystemWakeup()
}

type Status int

const (
	StatusDebug         Status = Status(raw.RunStateDebug)
	StatusFinishMigrate Status = Status(raw.RunStateFinishMigrate)
	StatusGuestPanicked Status = Status(raw.RunStateGuestPanicked)
	StatusIOError       Status = Status(raw.RunStateIOError)
	StatusInMigrate     Status = Status(raw.RunStateInmigrate)
	StatusInternalError Status = Status(raw.RunStateInternalError)
	StatusPaused        Status = Status(raw.RunStatePaused)
	StatusPostMigrate   Status = Status(raw.RunStatePostmigrate)
	StatusPreLaunch     Status = Status(raw.RunStatePrelaunch)
	StatusRestoreVM     Status = Status(raw.RunStateRestoreVM)
	StatusRunning       Status = Status(raw.RunStateRunning)
	StatusSaveVM        Status = Status(raw.RunStateSaveVM)
	StatusShutdown      Status = Status(raw.RunStateShutdown)
	StatusSuspended     Status = Status(raw.RunStateSuspended)
	StatusWatchdog      Status = Status(raw.RunStateWatchdog)
)

func (q Qemu) Status() (Status, error) {
	s, err := q.m.QueryStatus()
	if err != nil {
		if strings.Contains(err.Error(), "not running") {
			return StatusShutdown, nil
		}

		return 0, err
	}

	return Status(s.Status), nil
}

func (q Qemu) IsRunning() bool {
	if q.proc == nil {
		return false
	}

	return true
}

func (q *Qemu) Delete() error {
	if q.proc != nil {
		if err := q.proc.Kill(); err != nil {
			return fmt.Errorf("Failed to kill process: err='%s'", err.Error())
		}

		q.proc = nil
	}

	if err := os.Remove(q.qmpPath); err != nil {
		return fmt.Errorf("Failed to delete QMP socket: err='%s'", err.Error())
	}

	return nil
}

func (q Qemu) getVNCPort() uint16 {
	for p := 5900; p < 65536; p++ {
		l, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", p))
		if err == nil {
			defer l.Close()

			return uint16(p)
		}
	}

	return 0
}

func (q *Qemu) Start(name, qmpPath string, vncWebsocketPort, vcpus uint32, memory uint64) error {
	qmpPath, err := filepath.Abs(qmpPath)
	if err != nil {
		return fmt.Errorf("Failed to get absolute path of qmpPath: err='%s'", err.Error())
	}

	args := []string{
		"qemu-system-x86_64",

		// -- QEMU metadata --
		"-uuid",
		q.id.String(),
		"-name",
		fmt.Sprintf("guest=%s,debug-threads=on", name),
		"-msg",
		"timestamp=on",

		// Config
		"-daemonize",
		"-nodefaults",     // Don't create default devices
		"-no-user-config", // The "-no-user-config" option makes QEMU not load any of the user-provided config files on sysconfdir
		"-S",              // Do not start CPU at startup
		"-no-shutdown",    // Don't exit QEMU on guest shutdown

		// QMP
		"-chardev",
		fmt.Sprintf("socket,id=charmonitor,path=%s,server,nowait", qmpPath),
		"-mon",
		"chardev=charmonitor,id=monitor,mode=control",

		// -- BIOS --
		// boot priority
		"-boot",
		"menu=on,strict=on",

		// keyboard
		"-k",
		"en-us",

		// VNC
		"-vnc",
		fmt.Sprintf("127.0.0.1:%d,websocket=%d", q.getVNCPort()-5900, vncWebsocketPort), // TODO: ぶつからないようにポートを設定する必要がある、現状一台しか立たない

		// clock
		"-rtc",
		"base=utc,driftfix=slew",
		"-global",
		"kvm-pit.lost_tick_policy=delay",
		"-no-hpet",

		// CPU
		// TODO: 必要があればmonitorを操作してhotaddできるようにする
		// TODO: スケジューリングが可能かどうか調べる
		"-smp",
		fmt.Sprintf("%d,sockets=1,cores=%d,threads=1", vcpus, vcpus),
		"-cpu",
		"host",
		"-enable-kvm",

		// Memory
		"-m",
		fmt.Sprintf("%s", bytefmt.ByteSize(memory)),
		// "-device",
		// "virtio-balloon-pci,id=balloon0,bus=pci.0", // dynamic configurations
		"-realtime",
		"mlock=off",

		// VGA controller
		"-device",
		"VGA,id=video0,bus=pci.0",

		// SCSI controller
		"-device",
		"lsi53c895a,bus=pci.0,id=scsi0",
	}

	if !q.isKVM {
		// remove "-cpu", "host" and "-enable-kvm", because kvm is disable
		args = append(args[:29], args[32:]...)
	}

	cmd := exec.Command(args[0], args[1:]...)
	out, err := cmd.CombinedOutput()
	if err != nil { // TODO: combine でもいいかもしれない
		return fmt.Errorf("Failed to start process: args='%s', out='%s', err='%s'", args, string(out), err.Error())
	}

	if err := q.init(); err != nil {
		return fmt.Errorf("Failed to initialize: args='%s', err='%s'", args, err.Error())
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2013 The Camlistore Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package search

import (
	"errors"
	"fmt"
	"log"
	"os"
	"strings"
	"sync"
	"time"

	"camlistore.org/pkg/blob"
	"camlistore.org/pkg/syncutil"
)

type SortType int

// TODO: extend/merge/delete this type? probably dups in this package.
type BlobMeta struct {
	Ref      blob.Ref
	Size     int
	MIMEType string
}

const (
	UnspecifiedSort SortType = iota
	LastModifiedDesc
	LastModifiedAsc
	CreatedDesc
	CreatedAsc
)

type SearchQuery struct {
	Constraint *Constraint
	Limit      int      // optional. default is automatic.
	Sort       SortType // optional. default is automatic or unsorted.
}

type SearchResult struct {
	Blobs []*SearchResultBlob
}

type SearchResultBlob struct {
	Blob blob.Ref
	// ... file info, permanode info, blob info ... ?
}

func (r *SearchResultBlob) String() string {
	return fmt.Sprintf("[blob: %s]", r.Blob)
}

// Constraint specifies a blob matching constraint.
// A blob matches if it matches all non-zero fields' predicates.
// A zero constraint matches nothing.
type Constraint struct {
	// If Logical is non-nil, all other fields are ignored.
	Logical *LogicalConstraint

	// Anything, if true, matches all blobs.
	Anything bool

	CamliType     string // camliType of the JSON blob
	AnyCamliType  bool   // if true, any camli JSON blob matches
	BlobRefPrefix string

	// For claims:
	Claim *ClaimConstraint

	BlobSize *BlobSizeConstraint
	Type     *BlobTypeConstraint

	// For permanodes:
	Attribute *AttributeConstraint
}

type ClaimConstraint struct {
	SignedBy     string // identity
	SignedAfter  time.Time
	SignedBefore time.Time
}

type LogicalConstraint struct {
	Op string // "and", "or", "xor", "not"
	A  *Constraint
	B  *Constraint // only valid if Op == "not"
}

type BlobTypeConstraint struct {
	IsJSON  bool
	IsImage bool // chunk header looks like an image. likely just first chunk.
}

type BlobSizeConstraint struct {
	Min int // inclusive
	Max int // inclusive. if zero, ignored.
}

type AttributeConstraint struct {
	// At specifies the time at which to pretend we're resolving attributes.
	// Attribute claims after this point in time are ignored.
	// If zero, the current time is used.
	At time.Time

	// Attr is the attribute to match.
	// e.g. "camliContent", "camliMember", "tag"
	// TODO: field to control whether first vs. all permanode values are considered?
	Attr         string
	Value        string      // if non-zero, absolute match
	ValueAny     []string    // Value is any of these strings
	ValueMatches *Constraint // if non-zero, Attr value is blobref in this set of matches
	ValueSet     bool        // value is set to something non-blank
}

// search is the state of an in-progress search
type search struct {
	h   *Handler
	q   *SearchQuery
	res *SearchResult

	mu      sync.Mutex
	matches map[blob.Ref]bool
}

func (s *search) blobMeta(br blob.Ref) (BlobMeta, error) {
	mime, size, err := s.h.index.GetBlobMIMEType(br)
	return BlobMeta{Ref: br, Size: int(size), MIMEType: mime}, err
}

// optimizePlan returns an optimized version of c which will hopefully
// execute faster than executing c literally.
func optimizePlan(c *Constraint) *Constraint {
	// TODO: what the comment above says.
	return c
}

func (h *Handler) Query(q *SearchQuery) (*SearchResult, error) {
	res := new(SearchResult)
	s := &search{
		h:       h,
		q:       q,
		res:     res,
		matches: make(map[blob.Ref]bool),
	}
	ch := make(chan BlobMeta, buffered)
	errc := make(chan error, 1)
	go func() {
		errc <- h.index.EnumerateBlobMeta(ch)
	}()
	optConstraint := optimizePlan(q.Constraint)

	for meta := range ch {
		match, err := optConstraint.blobMatches(s, meta.Ref, meta)
		if err != nil {
			// drain ch
			go func() {
				for _ = range ch {
				}
			}()
			return nil, err
		}
		if match {
			res.Blobs = append(res.Blobs, &SearchResultBlob{
				Blob: meta.Ref,
			})
		}
	}
	if err := <-errc; err != nil {
		return nil, err
	}
	return s.res, nil
}

const camliTypeMIME = "application/json; camliType="

type blobMatcher interface {
	blobMatches(s *search, br blob.Ref, blobMeta BlobMeta) (bool, error)
}

type matchFn func(*search, blob.Ref, BlobMeta) (bool, error)

func alwaysMatch(*search, blob.Ref, BlobMeta) (bool, error) {
	return true, nil
}

func anyCamliType(s *search, br blob.Ref, bm BlobMeta) (bool, error) {
	return strings.HasPrefix(bm.MIMEType, camliTypeMIME), nil
}

func (c *Constraint) blobMatches(s *search, br blob.Ref, blobMeta BlobMeta) (bool, error) {
	var conds []matchFn
	addCond := func(fn matchFn) {
		conds = append(conds, fn)
	}
	if c.Logical != nil {
		addCond(c.Logical.blobMatches)
	}
	if c.Anything {
		addCond(alwaysMatch)
	}
	if c.CamliType != "" {
		addCond(func(s *search, br blob.Ref, bm BlobMeta) (bool, error) {
			return strings.TrimPrefix(bm.MIMEType, camliTypeMIME) == c.CamliType, nil
		})
	}
	if c.AnyCamliType {
		addCond(anyCamliType)
	}
	if c.Attribute != nil {
		addCond(c.Attribute.blobMatches)
	}
	if bs := c.BlobSize; bs != nil {
		addCond(func(s *search, br blob.Ref, bm BlobMeta) (bool, error) {
			if bm.Size < bs.Min {
				return false, nil
			}
			if bs.Max > 0 && bm.Size > bs.Max {
				return false, nil
			}
			return true, nil
		})

	}
	if pfx := c.BlobRefPrefix; pfx != "" {
		addCond(func(*search, blob.Ref, BlobMeta) (bool, error) {
			return strings.HasPrefix(br.String(), pfx), nil
		})
	}
	switch len(conds) {
	case 0:
		return false, nil
	case 1:
		return conds[0](s, br, blobMeta)
	default:
		panic("TODO")
	}
}

func (c *LogicalConstraint) blobMatches(s *search, br blob.Ref, bm BlobMeta) (bool, error) {
	switch c.Op {
	case "and", "xor":
		if c.A == nil || c.B == nil {
			return false, errors.New("In LogicalConstraint, need both A and B set")
		}
		var g syncutil.Group
		var av, bv bool
		g.Go(func() (err error) {
			av, err = c.A.blobMatches(s, br, bm)
			return
		})
		g.Go(func() (err error) {
			bv, err = c.B.blobMatches(s, br, bm)
			return
		})
		if err := g.Err(); err != nil {
			return false, err
		}
		switch c.Op {
		case "and":
			return av && bv, nil
		case "xor":
			return av != bv, nil
		default:
			panic("unreachable")
		}
	case "or":
		if c.A == nil || c.B == nil {
			return false, errors.New("In LogicalConstraint, need both A and B set")
		}
		av, err := c.A.blobMatches(s, br, bm)
		if err != nil {
			return false, err
		}
		if av {
			// Short-circuit.
			return true, nil
		}
		return c.B.blobMatches(s, br, bm)
	case "not":
		if c.A == nil {
			return false, errors.New("In LogicalConstraint, need to set A")
		}
		if c.B != nil {
			return false, errors.New("In LogicalConstraint, can't specify B with Op \"not\"")
		}
		v, err := c.A.blobMatches(s, br, bm)
		return !v, err
	default:
		return false, fmt.Errorf("In LogicalConstraint, unknown operation %q", c.Op)
	}
}

func (c *AttributeConstraint) blobMatches(s *search, br blob.Ref, bm BlobMeta) (bool, error) {
	if bm.MIMEType != "application/json; camliType=permanode" {
		return false, nil
	}
	dr, err := s.h.Describe(&DescribeRequest{
		BlobRef: br,
	})
	if err != nil {
		return false, err
	}
	db := dr.Meta[br.String()]
	if db == nil || db.Permanode == nil {
		return false, nil
	}
	attrs := db.Permanode.Attr // url.Values: a map[string][]string
	if c.Value != "" {
		got := attrs.Get(c.Attr)
		return got == c.Value, nil
	}
	if len(c.ValueAny) > 0 {
		for _, attr := range attrs[c.Attr] {
			for _, want := range c.ValueAny {
				if want == attr {
					return true, nil
				}
			}
		}
		return false, nil
	}
	if c.ValueSet {
		for _, attr := range attrs[c.Attr] {
			if attr != "" {
				return true, nil
			}
		}
		return false, nil
	}
	if subc := c.ValueMatches; subc != nil {
		for _, attr := range attrs[c.Attr] {
			if attrBr, ok := blob.Parse(attr); ok {
				meta, err := s.blobMeta(attrBr)
				if err == os.ErrNotExist {
					continue
				}
				if err != nil {
					return false, err
				}
				matches, err := subc.blobMatches(s, attrBr, meta)
				if err != nil {
					return false, err
				}
				if matches {
					return true, nil
				}
			}
		}
		return false, nil
	}

	log.Printf("br=%v meta=%+v: %#v", br, bm, dr)
	panic("TODO: not implemented")
	return false, nil
}
<commit_msg>search: delete unimplemented and now-undesired BlobTypeConstraint<commit_after>/*
Copyright 2013 The Camlistore Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package search

import (
	"errors"
	"fmt"
	"log"
	"os"
	"strings"
	"sync"
	"time"

	"camlistore.org/pkg/blob"
	"camlistore.org/pkg/syncutil"
)

type SortType int

// TODO: extend/merge/delete this type? probably dups in this package.
type BlobMeta struct {
	Ref      blob.Ref
	Size     int
	MIMEType string
}

const (
	UnspecifiedSort SortType = iota
	LastModifiedDesc
	LastModifiedAsc
	CreatedDesc
	CreatedAsc
)

type SearchQuery struct {
	Constraint *Constraint
	Limit      int      // optional. default is automatic.
	Sort       SortType // optional. default is automatic or unsorted.
}

type SearchResult struct {
	Blobs []*SearchResultBlob
}

type SearchResultBlob struct {
	Blob blob.Ref
	// ... file info, permanode info, blob info ... ?
}

func (r *SearchResultBlob) String() string {
	return fmt.Sprintf("[blob: %s]", r.Blob)
}

// Constraint specifies a blob matching constraint.
// A blob matches if it matches all non-zero fields' predicates.
// A zero constraint matches nothing.
type Constraint struct {
	// If Logical is non-nil, all other fields are ignored.
	Logical *LogicalConstraint

	// Anything, if true, matches all blobs.
	Anything bool

	CamliType     string // camliType of the JSON blob
	AnyCamliType  bool   // if true, any camli JSON blob matches
	BlobRefPrefix string

	// For claims:
	Claim *ClaimConstraint

	BlobSize *BlobSizeConstraint

	// For permanodes:
	Attribute *AttributeConstraint
}

type ClaimConstraint struct {
	SignedBy     string // identity
	SignedAfter  time.Time
	SignedBefore time.Time
}

type LogicalConstraint struct {
	Op string // "and", "or", "xor", "not"
	A  *Constraint
	B  *Constraint // only valid if Op == "not"
}

type BlobSizeConstraint struct {
	Min int // inclusive
	Max int // inclusive. if zero, ignored.
}

type AttributeConstraint struct {
	// At specifies the time at which to pretend we're resolving attributes.
	// Attribute claims after this point in time are ignored.
	// If zero, the current time is used.
	At time.Time

	// Attr is the attribute to match.
	// e.g. "camliContent", "camliMember", "tag"
	// TODO: field to control whether first vs. all permanode values are considered?
	Attr         string
	Value        string      // if non-zero, absolute match
	ValueAny     []string    // Value is any of these strings
	ValueMatches *Constraint // if non-zero, Attr value is blobref in this set of matches
	ValueSet     bool        // value is set to something non-blank
}

// search is the state of an in-progress search
type search struct {
	h   *Handler
	q   *SearchQuery
	res *SearchResult

	mu      sync.Mutex
	matches map[blob.Ref]bool
}

func (s *search) blobMeta(br blob.Ref) (BlobMeta, error) {
	mime, size, err := s.h.index.GetBlobMIMEType(br)
	return BlobMeta{Ref: br, Size: int(size), MIMEType: mime}, err
}

// optimizePlan returns an optimized version of c which will hopefully
// execute faster than executing c literally.
func optimizePlan(c *Constraint) *Constraint {
	// TODO: what the comment above says.
	return c
}

func (h *Handler) Query(q *SearchQuery) (*SearchResult, error) {
	res := new(SearchResult)
	s := &search{
		h:       h,
		q:       q,
		res:     res,
		matches: make(map[blob.Ref]bool),
	}
	ch := make(chan BlobMeta, buffered)
	errc := make(chan error, 1)
	go func() {
		errc <- h.index.EnumerateBlobMeta(ch)
	}()
	optConstraint := optimizePlan(q.Constraint)

	for meta := range ch {
		match, err := optConstraint.blobMatches(s, meta.Ref, meta)
		if err != nil {
			// drain ch
			go func() {
				for _ = range ch {
				}
			}()
			return nil, err
		}
		if match {
			res.Blobs = append(res.Blobs, &SearchResultBlob{
				Blob: meta.Ref,
			})
		}
	}
	if err := <-errc; err != nil {
		return nil, err
	}
	return s.res, nil
}

const camliTypeMIME = "application/json; camliType="

type blobMatcher interface {
	blobMatches(s *search, br blob.Ref, blobMeta BlobMeta) (bool, error)
}

type matchFn func(*search, blob.Ref, BlobMeta) (bool, error)

func alwaysMatch(*search, blob.Ref, BlobMeta) (bool, error) {
	return true, nil
}

func anyCamliType(s *search, br blob.Ref, bm BlobMeta) (bool, error) {
	return strings.HasPrefix(bm.MIMEType, camliTypeMIME), nil
}

func (c *Constraint) blobMatches(s *search, br blob.Ref, blobMeta BlobMeta) (bool, error) {
	var conds []matchFn
	addCond := func(fn matchFn) {
		conds = append(conds, fn)
	}
	if c.Logical != nil {
		addCond(c.Logical.blobMatches)
	}
	if c.Anything {
		addCond(alwaysMatch)
	}
	if c.CamliType != "" {
		addCond(func(s *search, br blob.Ref, bm BlobMeta) (bool, error) {
			return strings.TrimPrefix(bm.MIMEType, camliTypeMIME) == c.CamliType, nil
		})
	}
	if c.AnyCamliType {
		addCond(anyCamliType)
	}
	if c.Attribute != nil {
		addCond(c.Attribute.blobMatches)
	}
	if bs := c.BlobSize; bs != nil {
		addCond(func(s *search, br blob.Ref, bm BlobMeta) (bool, error) {
			if bm.Size < bs.Min {
				return false, nil
			}
			if bs.Max > 0 && bm.Size > bs.Max {
				return false, nil
			}
			return true, nil
		})

	}
	if pfx := c.BlobRefPrefix; pfx != "" {
		addCond(func(*search, blob.Ref, BlobMeta) (bool, error) {
			return strings.HasPrefix(br.String(), pfx), nil
		})
	}
	switch len(conds) {
	case 0:
		return false, nil
	case 1:
		return conds[0](s, br, blobMeta)
	default:
		panic("TODO")
	}
}

func (c *LogicalConstraint) blobMatches(s *search, br blob.Ref, bm BlobMeta) (bool, error) {
	switch c.Op {
	case "and", "xor":
		if c.A == nil || c.B == nil {
			return false, errors.New("In LogicalConstraint, need both A and B set")
		}
		var g syncutil.Group
		var av, bv bool
		g.Go(func() (err error) {
			av, err = c.A.blobMatches(s, br, bm)
			return
		})
		g.Go(func() (err error) {
			bv, err = c.B.blobMatches(s, br, bm)
			return
		})
		if err := g.Err(); err != nil {
			return false, err
		}
		switch c.Op {
		case "and":
			return av && bv, nil
		case "xor":
			return av != bv, nil
		default:
			panic("unreachable")
		}
	case "or":
		if c.A == nil || c.B == nil {
			return false, errors.New("In LogicalConstraint, need both A and B set")
		}
		av, err := c.A.blobMatches(s, br, bm)
		if err != nil {
			return false, err
		}
		if av {
			// Short-circuit.
			return true, nil
		}
		return c.B.blobMatches(s, br, bm)
	case "not":
		if c.A == nil {
			return false, errors.New("In LogicalConstraint, need to set A")
		}
		if c.B != nil {
			return false, errors.New("In LogicalConstraint, can't specify B with Op \"not\"")
		}
		v, err := c.A.blobMatches(s, br, bm)
		return !v, err
	default:
		return false, fmt.Errorf("In LogicalConstraint, unknown operation %q", c.Op)
	}
}

func (c *AttributeConstraint) blobMatches(s *search, br blob.Ref, bm BlobMeta) (bool, error) {
	if bm.MIMEType != "application/json; camliType=permanode" {
		return false, nil
	}
	dr, err := s.h.Describe(&DescribeRequest{
		BlobRef: br,
	})
	if err != nil {
		return false, err
	}
	db := dr.Meta[br.String()]
	if db == nil || db.Permanode == nil {
		return false, nil
	}
	attrs := db.Permanode.Attr // url.Values: a map[string][]string
	if c.Value != "" {
		got := attrs.Get(c.Attr)
		return got == c.Value, nil
	}
	if len(c.ValueAny) > 0 {
		for _, attr := range attrs[c.Attr] {
			for _, want := range c.ValueAny {
				if want == attr {
					return true, nil
				}
			}
		}
		return false, nil
	}
	if c.ValueSet {
		for _, attr := range attrs[c.Attr] {
			if attr != "" {
				return true, nil
			}
		}
		return false, nil
	}
	if subc := c.ValueMatches; subc != nil {
		for _, attr := range attrs[c.Attr] {
			if attrBr, ok := blob.Parse(attr); ok {
				meta, err := s.blobMeta(attrBr)
				if err == os.ErrNotExist {
					continue
				}
				if err != nil {
					return false, err
				}
				matches, err := subc.blobMatches(s, attrBr, meta)
				if err != nil {
					return false, err
				}
				if matches {
					return true, nil
				}
			}
		}
		return false, nil
	}

	log.Printf("br=%v meta=%+v: %#v", br, bm, dr)
	panic("TODO: not implemented")
	return false, nil
}
<|endoftext|>


<commit_before>package indicators

// Dema(X) = (2 * EMA(X, CLOSE)) - (EMA(X, EMA(X, CLOSE)))

import (
	"errors"
	"github.com/thetruetrade/gotrade"
)

// A Double Exponential Moving Average Indicator (Dema), no storage, for use in other indicators
type DemaWithoutStorage struct {
	*baseIndicator
	*baseFloatBounds

	// private variables
	valueAvailableAction ValueAvailableActionFloat
	ema1                 *EmaWithoutStorage
	ema2                 *EmaWithoutStorage
	currentEMA           float64
}

// NewDemaWithoutStorage creates a Double Exponential Moving Average Indicator (Dema) without storage
func NewDemaWithoutStorage(timePeriod int, valueAvailableAction ValueAvailableActionFloat) (indicator *DemaWithoutStorage, err error) {

	// an indicator without storage MUST have a value available action
	if valueAvailableAction == nil {
		return nil, ErrValueAvailableActionIsNil
	}

	// the minimum timeperiod for a Dema indicator is 2
	if timePeriod < 2 {
		return nil, errors.New("timePeriod is less than the minimum (2)")
	}

	// check the maximum timeperiod
	if timePeriod > MaximumLookbackPeriod {
		return nil, errors.New("timePeriod is greater than the maximum (100000)")
	}

	lookback := 2 * (timePeriod - 1)
	ind := DemaWithoutStorage{
		baseIndicator:        newBaseIndicator(lookback),
		baseFloatBounds:      newBaseFloatBounds(),
		valueAvailableAction: valueAvailableAction,
	}

	ind.ema1, _ = NewEmaWithoutStorage(timePeriod, func(dataItem float64, streamBarIndex int) {
		ind.currentEMA = dataItem
		ind.ema2.ReceiveTick(dataItem, streamBarIndex)
	})

	ind.ema2, _ = NewEmaWithoutStorage(timePeriod, func(dataItem float64, streamBarIndex int) {
		// increment the number of results this indicator can be expected to return
		ind.dataLength += 1
		if ind.validFromBar == -1 {
			// set the streamBarIndex from which this indicator returns valid results
			ind.validFromBar = streamBarIndex
		}

		// Dema(X) = (2 * EMA(X, CLOSE)) - (EMA(X, EMA(X, CLOSE)))
		dema := (2 * ind.currentEMA) - dataItem

		// update the maximum result value
		if dema > ind.maxValue {
			ind.maxValue = dema
		}

		// update the minimum result value
		if dema < ind.minValue {
			ind.minValue = dema
		}

		// notify of a new result value though the value available action
		ind.valueAvailableAction(dema, streamBarIndex)
	})

	return &ind, nil
}

// A Double Exponential Moving Average Indicator (Dema)
type Dema struct {
	*DemaWithoutStorage
	selectData gotrade.DataSelectionFunc

	// public variables
	Data []float64
}

// NewDema creates a Double Exponential Moving Average (Dema) for online usage
func NewDema(timePeriod int, selectData gotrade.DataSelectionFunc) (indicator *Dema, err error) {

	ind := Dema{selectData: selectData}
	ind.DemaWithoutStorage, err = NewDemaWithoutStorage(timePeriod,
		func(dataItem float64, streamBarIndex int) {
			ind.Data = append(ind.Data, dataItem)
		})

	return &ind, err
}

// NewDefaultDema creates a Double Exponential Moving Average (Dema) for online usage with default parameters
//	- timePeriod: 30
//  - selectData: useClosePrice
func NewDefaultDema() (indicator *Dema, err error) {
	timePeriod := 30
	selectData := gotrade.UseClosePrice
	return NewDema(timePeriod, selectData)
}

// NewDemaWithSrcLen creates a Double Exponential Moving Average (Dema) for offline usage
func NewDemaWithSrcLen(sourceLength uint, timePeriod int, selectData gotrade.DataSelectionFunc) (indicator *Dema, err error) {
	ind, err := NewDema(timePeriod, selectData)

	// only initialise the storage if there is enough source data to require it
	if sourceLength-uint(ind.GetLookbackPeriod()) > 1 {
		ind.Data = make([]float64, 0, sourceLength-uint(ind.GetLookbackPeriod()))
	}

	return ind, err
}

// NewDefaultDemaWithSrcLen creates a Double Exponential Moving Average (Dema) for offline usage with default parameters
func NewDefaultDemaWithSrcLen(sourceLength uint) (indicator *Dema, err error) {
	ind, err := NewDefaultDema()

	// only initialise the storage if there is enough source data to require it
	if sourceLength-uint(ind.GetLookbackPeriod()) > 1 {
		ind.Data = make([]float64, 0, sourceLength-uint(ind.GetLookbackPeriod()))
	}

	return ind, err
}

// NewDemaForStream creates a Double Exponential Moving Average (Dema) for online usage with a source data stream
func NewDemaForStream(priceStream gotrade.DOHLCVStreamSubscriber, timePeriod int, selectData gotrade.DataSelectionFunc) (indicator *Dema, err error) {
	newDema, err := NewDema(timePeriod, selectData)
	priceStream.AddTickSubscription(newDema)
	return newDema, err
}

// NewDefaultDemaForStream creates a Double Exponential Moving Average (Dema) for online usage with a source data stream
func NewDefaultDemaForStream(priceStream gotrade.DOHLCVStreamSubscriber) (indicator *Dema, err error) {
	ind, err := NewDefaultDema()
	priceStream.AddTickSubscription(ind)
	return ind, err
}

// NewDemaForStreamWithSrcLen creates a Double Exponential Moving Average (Dema) for offline usage with a source data stream
func NewDemaForStreamWithSrcLen(sourceLength uint, priceStream gotrade.DOHLCVStreamSubscriber, timePeriod int, selectData gotrade.DataSelectionFunc) (indicator *Dema, err error) {
	ind, err := NewDemaWithSrcLen(sourceLength, timePeriod, selectData)
	priceStream.AddTickSubscription(ind)
	return ind, err
}

// NewDefaultDemaForStreamWithSrcLen creates a Double Exponential Moving Average (Dema) for offline usage with a source data stream
func NewDefaultDemaForStreamWithSrcLen(sourceLength uint, priceStream gotrade.DOHLCVStreamSubscriber) (indicator *Dema, err error) {
	ind, err := NewDefaultDemaWithSrcLen(sourceLength)
	priceStream.AddTickSubscription(ind)
	return ind, err
}

// ReceiveDOHLCVTick consumes a source data DOHLCV price tick
func (dema *Dema) ReceiveDOHLCVTick(tickData gotrade.DOHLCV, streamBarIndex int) {
	var selectedData = dema.selectData(tickData)
	dema.ReceiveTick(selectedData, streamBarIndex)
}

func (dema *DemaWithoutStorage) ReceiveTick(tickData float64, streamBarIndex int) {
	dema.ema1.ReceiveTick(tickData, streamBarIndex)
}
<commit_msg>#76 Remove duplication - dema<commit_after>package indicators

// Dema(X) = (2 * EMA(X, CLOSE)) - (EMA(X, EMA(X, CLOSE)))

import (
	"errors"
	"github.com/thetruetrade/gotrade"
)

// A Double Exponential Moving Average Indicator (Dema), no storage, for use in other indicators
type DemaWithoutStorage struct {
	*baseIndicatorWithFloatBounds

	// private variables
	ema1       *EmaWithoutStorage
	ema2       *EmaWithoutStorage
	currentEMA float64
}

// NewDemaWithoutStorage creates a Double Exponential Moving Average Indicator (Dema) without storage
func NewDemaWithoutStorage(timePeriod int, valueAvailableAction ValueAvailableActionFloat) (indicator *DemaWithoutStorage, err error) {

	// an indicator without storage MUST have a value available action
	if valueAvailableAction == nil {
		return nil, ErrValueAvailableActionIsNil
	}

	// the minimum timeperiod for a Dema indicator is 2
	if timePeriod < 2 {
		return nil, errors.New("timePeriod is less than the minimum (2)")
	}

	// check the maximum timeperiod
	if timePeriod > MaximumLookbackPeriod {
		return nil, errors.New("timePeriod is greater than the maximum (100000)")
	}

	lookback := 2 * (timePeriod - 1)
	ind := DemaWithoutStorage{
		baseIndicatorWithFloatBounds: newBaseIndicatorWithFloatBounds(lookback, valueAvailableAction),
	}

	ind.ema1, _ = NewEmaWithoutStorage(timePeriod, func(dataItem float64, streamBarIndex int) {
		ind.currentEMA = dataItem
		ind.ema2.ReceiveTick(dataItem, streamBarIndex)
	})

	ind.ema2, _ = NewEmaWithoutStorage(timePeriod, func(dataItem float64, streamBarIndex int) {

		// Dema(X) = (2 * EMA(X, CLOSE)) - (EMA(X, EMA(X, CLOSE)))
		result := (2 * ind.currentEMA) - dataItem

		ind.UpdateIndicatorWithNewValue(result, streamBarIndex)
	})

	return &ind, nil
}

// A Double Exponential Moving Average Indicator (Dema)
type Dema struct {
	*DemaWithoutStorage
	selectData gotrade.DataSelectionFunc

	// public variables
	Data []float64
}

// NewDema creates a Double Exponential Moving Average (Dema) for online usage
func NewDema(timePeriod int, selectData gotrade.DataSelectionFunc) (indicator *Dema, err error) {

	ind := Dema{selectData: selectData}
	ind.DemaWithoutStorage, err = NewDemaWithoutStorage(timePeriod,
		func(dataItem float64, streamBarIndex int) {
			ind.Data = append(ind.Data, dataItem)
		})

	return &ind, err
}

// NewDefaultDema creates a Double Exponential Moving Average (Dema) for online usage with default parameters
//	- timePeriod: 30
//  - selectData: useClosePrice
func NewDefaultDema() (indicator *Dema, err error) {
	timePeriod := 30
	selectData := gotrade.UseClosePrice
	return NewDema(timePeriod, selectData)
}

// NewDemaWithSrcLen creates a Double Exponential Moving Average (Dema) for offline usage
func NewDemaWithSrcLen(sourceLength uint, timePeriod int, selectData gotrade.DataSelectionFunc) (indicator *Dema, err error) {
	ind, err := NewDema(timePeriod, selectData)

	// only initialise the storage if there is enough source data to require it
	if sourceLength-uint(ind.GetLookbackPeriod()) > 1 {
		ind.Data = make([]float64, 0, sourceLength-uint(ind.GetLookbackPeriod()))
	}

	return ind, err
}

// NewDefaultDemaWithSrcLen creates a Double Exponential Moving Average (Dema) for offline usage with default parameters
func NewDefaultDemaWithSrcLen(sourceLength uint) (indicator *Dema, err error) {
	ind, err := NewDefaultDema()

	// only initialise the storage if there is enough source data to require it
	if sourceLength-uint(ind.GetLookbackPeriod()) > 1 {
		ind.Data = make([]float64, 0, sourceLength-uint(ind.GetLookbackPeriod()))
	}

	return ind, err
}

// NewDemaForStream creates a Double Exponential Moving Average (Dema) for online usage with a source data stream
func NewDemaForStream(priceStream gotrade.DOHLCVStreamSubscriber, timePeriod int, selectData gotrade.DataSelectionFunc) (indicator *Dema, err error) {
	newDema, err := NewDema(timePeriod, selectData)
	priceStream.AddTickSubscription(newDema)
	return newDema, err
}

// NewDefaultDemaForStream creates a Double Exponential Moving Average (Dema) for online usage with a source data stream
func NewDefaultDemaForStream(priceStream gotrade.DOHLCVStreamSubscriber) (indicator *Dema, err error) {
	ind, err := NewDefaultDema()
	priceStream.AddTickSubscription(ind)
	return ind, err
}

// NewDemaForStreamWithSrcLen creates a Double Exponential Moving Average (Dema) for offline usage with a source data stream
func NewDemaForStreamWithSrcLen(sourceLength uint, priceStream gotrade.DOHLCVStreamSubscriber, timePeriod int, selectData gotrade.DataSelectionFunc) (indicator *Dema, err error) {
	ind, err := NewDemaWithSrcLen(sourceLength, timePeriod, selectData)
	priceStream.AddTickSubscription(ind)
	return ind, err
}

// NewDefaultDemaForStreamWithSrcLen creates a Double Exponential Moving Average (Dema) for offline usage with a source data stream
func NewDefaultDemaForStreamWithSrcLen(sourceLength uint, priceStream gotrade.DOHLCVStreamSubscriber) (indicator *Dema, err error) {
	ind, err := NewDefaultDemaWithSrcLen(sourceLength)
	priceStream.AddTickSubscription(ind)
	return ind, err
}

// ReceiveDOHLCVTick consumes a source data DOHLCV price tick
func (dema *Dema) ReceiveDOHLCVTick(tickData gotrade.DOHLCV, streamBarIndex int) {
	var selectedData = dema.selectData(tickData)
	dema.ReceiveTick(selectedData, streamBarIndex)
}

func (dema *DemaWithoutStorage) ReceiveTick(tickData float64, streamBarIndex int) {
	dema.ema1.ReceiveTick(tickData, streamBarIndex)
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 Conformal Systems LLC.
// Copyright (c) 2015-2020 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package dcrjson

// Standard JSON-RPC 2.0 errors
var (
	ErrInvalidRequest = RPCError{
		Code:    -32600,
		Message: "Invalid request",
	}
	ErrMethodNotFound = RPCError{
		Code:    -32601,
		Message: "Method not found",
	}
	ErrInvalidParams = RPCError{
		Code:    -32602,
		Message: "Invalid parameters",
	}
	ErrInternal = RPCError{
		Code:    -32603,
		Message: "Internal error",
	}
	ErrParse = RPCError{
		Code:    -32700,
		Message: "Parse error",
	}
)

// General application defined JSON errors
var (
	ErrMisc = RPCError{
		Code:    -1,
		Message: "Miscellaneous error",
	}
	ErrForbiddenBySafeMode = RPCError{
		Code:    -2,
		Message: "Server is in safe mode, and command is not allowed in safe mode",
	}
	ErrType = RPCError{
		Code:    -3,
		Message: "Unexpected type was passed as parameter",
	}
	ErrInvalidAddressOrKey = RPCError{
		Code:    -5,
		Message: "Invalid address or key",
	}
	ErrOutOfMemory = RPCError{
		Code:    -7,
		Message: "Ran out of memory during operation",
	}
	ErrInvalidParameter = RPCError{
		Code:    -8,
		Message: "Invalid, missing or duplicate parameter",
	}
	ErrDatabase = RPCError{
		Code:    -20,
		Message: "Database error",
	}
	ErrDeserialization = RPCError{
		Code:    -22,
		Message: "Error parsing or validating structure in raw format",
	}
)

// Peer-to-peer client errors
var (
	ErrClientNotConnected = RPCError{
		Code:    -9,
		Message: "dcrd is not connected",
	}
	ErrClientInInitialDownload = RPCError{
		Code:    -10,
		Message: "dcrd is downloading blocks...",
	}
)

// Wallet JSON errors
var (
	ErrWallet = RPCError{
		Code:    -4,
		Message: "Unspecified problem with wallet",
	}
	ErrWalletInsufficientFunds = RPCError{
		Code:    -6,
		Message: "Not enough funds in wallet or account",
	}
	ErrWalletInvalidAccountName = RPCError{
		Code:    -11,
		Message: "Invalid account name",
	}
	ErrWalletKeypoolRanOut = RPCError{
		Code:    -12,
		Message: "Keypool ran out, call keypoolrefill first",
	}
	ErrWalletUnlockNeeded = RPCError{
		Code:    -13,
		Message: "Enter the wallet passphrase with walletpassphrase first",
	}
	ErrWalletPassphraseIncorrect = RPCError{
		Code:    -14,
		Message: "The wallet passphrase entered was incorrect",
	}
	ErrWalletWrongEncState = RPCError{
		Code:    -15,
		Message: "Command given in wrong wallet encryption state",
	}
	ErrWalletEncryptionFailed = RPCError{
		Code:    -16,
		Message: "Failed to encrypt the wallet",
	}
	ErrWalletAlreadyUnlocked = RPCError{
		Code:    -17,
		Message: "Wallet is already unlocked",
	}
)

// Specific Errors related to commands.  These are the ones a user of the rpc
// server are most likely to see.  Generally, the codes should match one of the
// more general errors above.
var (
	ErrBlockNotFound = RPCError{
		Code:    -5,
		Message: "Block not found",
	}
	ErrBlockCount = RPCError{
		Code:    -5,
		Message: "Error getting block count",
	}
	ErrBestBlockHash = RPCError{
		Code:    -5,
		Message: "Error getting best block hash",
	}
	ErrDifficulty = RPCError{
		Code:    -5,
		Message: "Error getting difficulty",
	}
	ErrOutOfRange = RPCError{
		Code:    -1,
		Message: "Block number out of range",
	}
	ErrNoTxInfo = RPCError{
		Code:    -5,
		Message: "No information available about transaction",
	}
	ErrNoNewestBlockInfo = RPCError{
		Code:    -5,
		Message: "No information about newest block",
	}
	ErrInvalidTxVout = RPCError{
		Code:    -5,
		Message: "Output index number (vout) does not exist for transaction.",
	}
	ErrRawTxString = RPCError{
		Code:    -32602,
		Message: "Raw tx is not a string",
	}
	ErrDecodeHexString = RPCError{
		Code:    -22,
		Message: "Unable to decode hex string",
	}
)

// Errors that are specific to dcrd.
var (
	ErrNoWallet = RPCError{
		Code:    -1,
		Message: "This implementation does not implement wallet commands",
	}
	ErrUnimplemented = RPCError{
		Code:    -1,
		Message: "Command unimplemented",
	}
)
<commit_msg>dcrjson: Minor jsonerr.go update.<commit_after>// Copyright (c) 2013-2014 Conformal Systems LLC.
// Copyright (c) 2015-2020 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package dcrjson

// Standard JSON-RPC 2.0 errors
var (
	ErrInvalidRequest = RPCError{
		Code:    -32600,
		Message: "Invalid request",
	}
	ErrMethodNotFound = RPCError{
		Code:    -32601,
		Message: "Method not found",
	}
	ErrInvalidParams = RPCError{
		Code:    -32602,
		Message: "Invalid parameters",
	}
	ErrInternal = RPCError{
		Code:    -32603,
		Message: "Internal error",
	}
	ErrParse = RPCError{
		Code:    -32700,
		Message: "Parse error",
	}
)

// General application defined JSON errors
var (
	ErrMisc = RPCError{
		Code:    -1,
		Message: "Miscellaneous error",
	}
	ErrForbiddenBySafeMode = RPCError{
		Code:    -2,
		Message: "Server is in safe mode, and command is not allowed in safe mode",
	}
	ErrType = RPCError{
		Code:    -3,
		Message: "Unexpected type was passed as parameter",
	}
	ErrInvalidAddressOrKey = RPCError{
		Code:    -5,
		Message: "Invalid address or key",
	}
	ErrOutOfMemory = RPCError{
		Code:    -7,
		Message: "Ran out of memory during operation",
	}
	ErrInvalidParameter = RPCError{
		Code:    -8,
		Message: "Invalid, missing or duplicate parameter",
	}
	ErrDatabase = RPCError{
		Code:    -20,
		Message: "Database error",
	}
	ErrDeserialization = RPCError{
		Code:    -22,
		Message: "Error parsing or validating structure in raw format",
	}
)

// Peer-to-peer client errors
var (
	ErrClientNotConnected = RPCError{
		Code:    -9,
		Message: "node is not connected",
	}
	ErrClientInInitialDownload = RPCError{
		Code:    -10,
		Message: "node is downloading blocks...",
	}
)

// Wallet JSON errors
var (
	ErrWallet = RPCError{
		Code:    -4,
		Message: "Unspecified problem with wallet",
	}
	ErrWalletInsufficientFunds = RPCError{
		Code:    -6,
		Message: "Not enough funds in wallet or account",
	}
	ErrWalletInvalidAccountName = RPCError{
		Code:    -11,
		Message: "Invalid account name",
	}
	ErrWalletKeypoolRanOut = RPCError{
		Code:    -12,
		Message: "Keypool ran out, call keypoolrefill first",
	}
	ErrWalletUnlockNeeded = RPCError{
		Code:    -13,
		Message: "Enter the wallet passphrase with walletpassphrase first",
	}
	ErrWalletPassphraseIncorrect = RPCError{
		Code:    -14,
		Message: "The wallet passphrase entered was incorrect",
	}
	ErrWalletWrongEncState = RPCError{
		Code:    -15,
		Message: "Command given in wrong wallet encryption state",
	}
	ErrWalletEncryptionFailed = RPCError{
		Code:    -16,
		Message: "Failed to encrypt the wallet",
	}
	ErrWalletAlreadyUnlocked = RPCError{
		Code:    -17,
		Message: "Wallet is already unlocked",
	}
)

// Specific Errors related to commands.  These are the ones a user of the rpc
// server are most likely to see.  Generally, the codes should match one of the
// more general errors above.
var (
	ErrBlockNotFound = RPCError{
		Code:    -5,
		Message: "Block not found",
	}
	ErrBlockCount = RPCError{
		Code:    -5,
		Message: "Error getting block count",
	}
	ErrBestBlockHash = RPCError{
		Code:    -5,
		Message: "Error getting best block hash",
	}
	ErrDifficulty = RPCError{
		Code:    -5,
		Message: "Error getting difficulty",
	}
	ErrOutOfRange = RPCError{
		Code:    -1,
		Message: "Block number out of range",
	}
	ErrNoTxInfo = RPCError{
		Code:    -5,
		Message: "No information available about transaction",
	}
	ErrNoNewestBlockInfo = RPCError{
		Code:    -5,
		Message: "No information about newest block",
	}
	ErrInvalidTxVout = RPCError{
		Code:    -5,
		Message: "Output index number (vout) does not exist for transaction.",
	}
	ErrRawTxString = RPCError{
		Code:    -32602,
		Message: "Raw tx is not a string",
	}
	ErrDecodeHexString = RPCError{
		Code:    -22,
		Message: "Unable to decode hex string",
	}
)

// Errors that are specific to dcrd.
var (
	ErrNoWallet = RPCError{
		Code:    -1,
		Message: "This implementation does not implement wallet commands",
	}
	ErrUnimplemented = RPCError{
		Code:    -1,
		Message: "Command unimplemented",
	}
)
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package version

// Base version information.
//
// This is the fallback data used when version information from git is not
// provided via go ldflags. It provides an approximation of the Kubernetes
// version for ad-hoc builds (e.g. `go build`) that cannot get the version
// information from git.
//
// If you are looking at these fields in the git tree, they look
// strange. They are modified on the fly by the build process. The
// in-tree values are dummy values used for "git archive", which also
// works for GitHub tar downloads.
//
// When releasing a new Kubernetes version, this file is updated by
// build/mark_new_version.sh to reflect the new version, and then a
// git annotated tag (using format vX.Y where X == Major version and Y
// == Minor version) is created to point to the commit that updates
// pkg/version/base.go
var (
	// TODO: Deprecate gitMajor and gitMinor, use only gitVersion
	// instead. First step in deprecation, keep the fields but make
	// them irrelevant. (Next we'll take it out, which may muck with
	// scripts consuming the kubectl version output - but most of
	// these should be looking at gitVersion already anyways.)
	gitMajor string = "1" // major version, always numeric
	gitMinor string = "5" // minor version, numeric possibly followed by "+"

	// semantic version, derived by build scripts (see
	// https://github.com/kubernetes/kubernetes/blob/master/docs/design/versioning.md
	// for a detailed discussion of this field)
	//
	// TODO: This field is still called "gitVersion" for legacy
	// reasons. For prerelease versions, the build metadata on the
	// semantic version is a git hash, but the version itself is no
	// longer the direct output of "git describe", but a slight
	// translation to be semver compliant.
	gitVersion   string = "v1.5.2+$Format:%h$"
	gitCommit    string = "$Format:%H$"    // sha1 from git, output of $(git rev-parse HEAD)
	gitTreeState string = "not a git tree" // state of git tree, either "clean" or "dirty"

	buildDate string = "1970-01-01T00:00:00Z" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ')
)
<commit_msg>Kubernetes version v1.5.3-beta.0<commit_after>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package version

// Base version information.
//
// This is the fallback data used when version information from git is not
// provided via go ldflags. It provides an approximation of the Kubernetes
// version for ad-hoc builds (e.g. `go build`) that cannot get the version
// information from git.
//
// If you are looking at these fields in the git tree, they look
// strange. They are modified on the fly by the build process. The
// in-tree values are dummy values used for "git archive", which also
// works for GitHub tar downloads.
//
// When releasing a new Kubernetes version, this file is updated by
// build/mark_new_version.sh to reflect the new version, and then a
// git annotated tag (using format vX.Y where X == Major version and Y
// == Minor version) is created to point to the commit that updates
// pkg/version/base.go
var (
	// TODO: Deprecate gitMajor and gitMinor, use only gitVersion
	// instead. First step in deprecation, keep the fields but make
	// them irrelevant. (Next we'll take it out, which may muck with
	// scripts consuming the kubectl version output - but most of
	// these should be looking at gitVersion already anyways.)
	gitMajor string = "1"  // major version, always numeric
	gitMinor string = "5+" // minor version, numeric possibly followed by "+"

	// semantic version, derived by build scripts (see
	// https://github.com/kubernetes/kubernetes/blob/master/docs/design/versioning.md
	// for a detailed discussion of this field)
	//
	// TODO: This field is still called "gitVersion" for legacy
	// reasons. For prerelease versions, the build metadata on the
	// semantic version is a git hash, but the version itself is no
	// longer the direct output of "git describe", but a slight
	// translation to be semver compliant.
	gitVersion   string = "v1.5.3-beta.0+$Format:%h$"
	gitCommit    string = "$Format:%H$"    // sha1 from git, output of $(git rev-parse HEAD)
	gitTreeState string = "not a git tree" // state of git tree, either "clean" or "dirty"

	buildDate string = "1970-01-01T00:00:00Z" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ')
)
<|endoftext|>


<commit_before>package view

import (
	"log"
	"sync"
)

type CurrentView struct {
	viewRef ViewRef
	view    *View
	mu      *sync.RWMutex
}

func NewCurrentView() CurrentView {
	newCurrentView := CurrentView{}
	newCurrentView.view = newView()
	newCurrentView.mu = &sync.RWMutex{}
	return newCurrentView
}

func (currentView CurrentView) String() string {
	currentView.mu.RLock()
	defer currentView.mu.RUnlock()

	return currentView.view.String()
}

func (currentView *CurrentView) Update(newView *View) {
	currentView.mu.Lock()
	defer currentView.mu.Unlock()

	if newView.LessUpdatedThan(currentView.view) || newView.Equal(currentView.view) {
		if newView.LessUpdatedThan(currentView.view) {
			log.Println("Tried to Update current view with a less updated view")
		} else {
			log.Println("Tried to Update current view with the same view")
		}
		return
	}

	currentView.view = newView
	currentView.viewRef = ViewToViewRef(newView)
	log.Println("CurrentView updated to:", currentView.view)
}

func (currentView *CurrentView) View() *View {
	currentView.mu.RLock()
	defer currentView.mu.RUnlock()

	return currentView.view
}

func (currentView *CurrentView) ViewRef() ViewRef {
	currentView.mu.RLock()
	defer currentView.mu.RUnlock()

	return currentView.viewRef
}

func (currentView *CurrentView) ViewAndViewRef() (*View, ViewRef) {
	currentView.mu.RLock()
	defer currentView.mu.RUnlock()

	return currentView.view, currentView.viewRef
}
<commit_msg>Debugging current view<commit_after>package view

import (
	"log"
	"sync"
)

type CurrentView struct {
	viewRef ViewRef
	view    *View
	mu      *sync.RWMutex
}

func NewCurrentView() CurrentView {
	newCurrentView := CurrentView{}
	newCurrentView.view = newView()
	newCurrentView.mu = &sync.RWMutex{}
	return newCurrentView
}

func (currentView CurrentView) String() string {
	currentView.mu.RLock()
	defer currentView.mu.RUnlock()

	return currentView.view.String()
}

func (currentView *CurrentView) Update(newView *View) {
	currentView.mu.Lock()
	defer currentView.mu.Unlock()

	if newView.LessUpdatedThan(currentView.view) || newView.Equal(currentView.view) {
		if newView.LessUpdatedThan(currentView.view) {
			log.Println("Tried to Update current view with a less updated view")
		} else {
			log.Fatalln("Tried to Update current view with the same view")
		}
		return
	}

	currentView.view = newView
	currentView.viewRef = ViewToViewRef(newView)
	log.Println("CurrentView updated to:", currentView.view)
}

func (currentView *CurrentView) View() *View {
	currentView.mu.RLock()
	defer currentView.mu.RUnlock()

	return currentView.view
}

func (currentView *CurrentView) ViewRef() ViewRef {
	currentView.mu.RLock()
	defer currentView.mu.RUnlock()

	return currentView.viewRef
}

func (currentView *CurrentView) ViewAndViewRef() (*View, ViewRef) {
	currentView.mu.RLock()
	defer currentView.mu.RUnlock()

	return currentView.view, currentView.viewRef
}
<|endoftext|>


<commit_before>package inject

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/printer"
	"go/token"
	"os"
	"path/filepath"
	"strings"
)

type parsedFile struct {
	filePath string
	pkgName  string
	fset     *token.FileSet
	astFile  *ast.File
	touched  bool
}

type Injector struct {
	pkgRootPath         string
	pkgRootPathOverride string

	hookedFuncs map[string]*Target

	parsedFiles []*parsedFile
}

func NewInjector(pkgRootPath, pkgRootPathOverride string) *Injector {
	return &Injector{
		parsedFiles:         make([]*parsedFile, 0),
		pkgRootPath:         pkgRootPath,
		pkgRootPathOverride: pkgRootPathOverride,
	}
}

// Apply profiler hooks to the supplied targets. Returns the number of modified files.
func (in *Injector) Hook(targets []*Target) (int, error) {
	// Parse package files
	err := filepath.Walk(in.pkgRootPath, in.buildAST)
	if err != nil {
		return 0, err
	}

	in.initCache(targets)

	touchedFiles := 0
	for _, parsedFile := range in.parsedFiles {
		injectProfiler(parsedFile, in)

		// Write modified ASTs to disk
		if parsedFile.touched {
			f, err := os.Create(parsedFile.filePath)
			if err != nil {
				return 0, err
			}
			printer.Fprint(f, parsedFile.fset, parsedFile.astFile)
			f.Close()
			touchedFiles++
		}
	}

	return touchedFiles, nil
}

// Initialize cache for accelerating lookups and ensuring that shared targets
// are only hooked once.
func (in *Injector) initCache(targets []*Target) {
	in.hookedFuncs = make(map[string]*Target, len(targets))

	for _, target := range targets {
		in.hookedFuncs[target.Name] = target
	}
}

// Parse a go file and store its AST representation.
func (in *Injector) buildAST(path string, info os.FileInfo, err error) error {
	if err != nil {
		return err
	}

	// Skip dirs and non-go files
	if info.IsDir() || !strings.HasSuffix(path, ".go") {
		return nil
	}

	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
	if err != nil {
		return fmt.Errorf("in: could not parse %s; %v", path, err)
	}

	pkgName, err := qualifiedPkgName(
		strings.Replace(path, in.pkgRootPath, in.pkgRootPathOverride, -1),
	)
	if err != nil {
		return err
	}

	in.parsedFiles = append(in.parsedFiles, &parsedFile{
		pkgName:  pkgName,
		filePath: path,
		fset:     fset,
		astFile:  f,
	})

	return nil
}
<commit_msg>Change AST parser to ignore go test files<commit_after>package inject

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/printer"
	"go/token"
	"os"
	"path/filepath"
	"strings"
)

type parsedFile struct {
	filePath string
	pkgName  string
	fset     *token.FileSet
	astFile  *ast.File
	touched  bool
}

type Injector struct {
	pkgRootPath         string
	pkgRootPathOverride string

	hookedFuncs map[string]*Target

	parsedFiles []*parsedFile
}

func NewInjector(pkgRootPath, pkgRootPathOverride string) *Injector {
	return &Injector{
		parsedFiles:         make([]*parsedFile, 0),
		pkgRootPath:         pkgRootPath,
		pkgRootPathOverride: pkgRootPathOverride,
	}
}

// Apply profiler hooks to the supplied targets. Returns the number of modified files.
func (in *Injector) Hook(targets []*Target) (int, error) {
	// Parse package files
	err := filepath.Walk(in.pkgRootPath, in.buildAST)
	if err != nil {
		return 0, err
	}

	in.initCache(targets)

	touchedFiles := 0
	for _, parsedFile := range in.parsedFiles {
		injectProfiler(parsedFile, in)

		// Write modified ASTs to disk
		if parsedFile.touched {
			f, err := os.Create(parsedFile.filePath)
			if err != nil {
				return 0, err
			}
			printer.Fprint(f, parsedFile.fset, parsedFile.astFile)
			f.Close()
			touchedFiles++
		}
	}

	return touchedFiles, nil
}

// Initialize cache for accelerating lookups and ensuring that shared targets
// are only hooked once.
func (in *Injector) initCache(targets []*Target) {
	in.hookedFuncs = make(map[string]*Target, len(targets))

	for _, target := range targets {
		in.hookedFuncs[target.Name] = target
	}
}

// Parse a go file and store its AST representation.
func (in *Injector) buildAST(path string, info os.FileInfo, err error) error {
	if err != nil {
		return err
	}

	// Skip dirs, non-go and go test files
	if info.IsDir() || !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") {
		return nil
	}

	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
	if err != nil {
		return fmt.Errorf("in: could not parse %s; %v", path, err)
	}

	pkgName, err := qualifiedPkgName(
		strings.Replace(path, in.pkgRootPath, in.pkgRootPathOverride, -1),
	)
	if err != nil {
		return err
	}

	in.parsedFiles = append(in.parsedFiles, &parsedFile{
		pkgName:  pkgName,
		filePath: path,
		fset:     fset,
		astFile:  f,
	})

	return nil
}
<|endoftext|>


<commit_before>package main

import (
    "net/http"
    "fmt"
    "io/ioutil"
    "os"
    "os/exec"
    "log"
    "path/filepath"
    "io"
    "archive/zip"
    "container/list"
    "strings"
)

type Unzip struct {
    zipfile string
    prefix string
    artifacts *list.List
}

type ZipArtifact struct {
  name string
  path string
  file *zip.File
}

func (u *Unzip) PrintListing() error {
    file_handler := func (meta *ZipMeta) error {
        log.Println(meta.current_file.path + meta.current_file.name)
        return nil
    }

    folder_handler := func(meta *ZipMeta) error {
        return nil
    }
    meta := ZipMeta { file_handler, folder_handler, make(map[string]bool), "", nil, nil} //list.New(), 1,true}
    if err := meta.walk(u.zipfile); err != nil {
        return err
    } else {
        u.artifacts = meta.artifacts
    }
    return nil
}

func (u *Unzip) GenerateListing() error {
    file_handler := func (meta *ZipMeta) error {
        if meta.artifacts == nil {
            meta.artifacts = list.New()
        }
        meta.artifacts.PushBack(meta.current_file)
        return nil
    }

    folder_handler := func(meta *ZipMeta) error {
        return nil
    }
    meta := ZipMeta { file_handler, folder_handler, make(map[string]bool), "", nil, nil}
    if err := meta.walk(u.zipfile); err != nil {
        return err
    } else {
        u.artifacts = meta.artifacts
    }
    return nil
}

func (u *Unzip) Expand() error {
    file_handler := func (meta *ZipMeta) error {

        if meta.artifacts == nil {
            meta.artifacts = list.New()
        }
        zipfile := meta.current_file.file
        meta.artifacts.PushBack(meta.current_file)

        var (
            err error
            file *os.File
            rc io.ReadCloser
        )
        if rc, err = zipfile.Open(); err != nil {
            return err
        }

        if file, err = os.Create(zipfile.Name); err != nil {
            return err
        }

        if _, err = io.Copy(file, rc); err != nil {
            return err
        }

        if err = file.Chmod(zipfile.Mode().Perm()); err != nil {
            //log.Println(err) // Windows WTF?
        }
        file.Close()
        rc.Close()
        return nil
    }

    folder_handler := func(meta *ZipMeta) error {
        return os.MkdirAll(meta.last_folder, 0755)
    }
    meta := ZipMeta {file_handler, folder_handler, make(map[string]bool), "", nil, nil}
    if err := meta.walk(u.zipfile); err != nil {
        return err
    } else {
        u.artifacts = meta.artifacts
    }
    return nil
}

type ZipMeta struct {
    file_handler func(*ZipMeta) error
    folder_handler func(*ZipMeta) error
    folders map[string]bool
    last_folder string
    current_file *ZipArtifact
    artifacts *list.List
}

func (w *ZipMeta) isNewFolder(folder_name string) bool {
    _, found := w.folders[folder_name]
    return !found
}

func (w *ZipMeta) handleFile(path string, file_name string, file *zip.File) error {
    w.current_file = &ZipArtifact{file_name, path, file}
    return w.file_handler(w)
}

func (w *ZipMeta) handleFolder(folder_name string) error {
    w.folders[folder_name] = true
    w.last_folder = folder_name
    return w.folder_handler(w)
}

func (w *ZipMeta) walk(zipfile string) error {
    var err error
    var r *zip.ReadCloser
    if r, err = zip.OpenReader(zipfile); err != nil {
      log.Fatalln(err)
      return err
    }

    for _, f := range r.File {

      if f.Mode().IsDir() {
        w.handleFolder(f.Name)
        continue
      }

      file_name := f.Name
      folder_name := ""
      folder_index := strings.LastIndex(f.Name, "/")
      if folder_index != -1 {
        folder_name += file_name[:folder_index] // 4.4.17
        if w.isNewFolder(folder_name) {
            if err = w.handleFolder(folder_name); err != nil {
                return err
            }
        }
        file_name = file_name[folder_index+1:] // strings.Join(dirlist[:len(dirlist)-1]
      }

      if err = w.handleFile(folder_name, file_name, f); err != nil {
          return err
      }
    }
    r.Close()
    return nil
}

/////////////////////////////////////////////////////////////////////////

var base_url = "http://jpercent.org/"

func downloadAndWrite(url_name string, file_name string) {
    resp, err := http.Get(url_name)
    if err != nil {
        log.Fatalln(err)
    }
    defer resp.Body.Close()
    body, err1 := ioutil.ReadAll(resp.Body)
    if err1 = ioutil.WriteFile(file_name, body, 0744); err1 != nil {
        log.Fatalln(err1)
    }
}

func cwdAbs() string {
    cwd, err := filepath.Abs("")
    log.Println("Current working directory = ", cwd)
    if err != nil {
        log.Fatalln(err)
    }
    return cwd
}

func checkForPython() error {
    log.Println("Checking for Python...")
    cmd := exec.Command("cmd", "/C C:\\Python27\\python.exe")
    return cmd.Run()
}

func installExe(exe_name string, package_name string, post_fn func()) {
    log.Println("Downloading "+package_name+"...")
    downloadAndWrite(base_url+exe_name, exe_name)
    log.Println("Installing "+package_name+"...")
    cwd := cwdAbs()
    cmd3 := exec.Command("cmd", "/C "+cwd+"\\"+exe_name)
    if err := cmd3.Run(); err != nil {
        log.Fatalln(err)
    }
    fmt.Println("Successfully installed "+package_name)
}

func installZippedPythonPackage(file_name string, package_name string, local_dir string, post_fn func()) {
    log.Println("Downloading "+package_name+"... ")
    downloadAndWrite(base_url+file_name, file_name)
    log.Println("Installing "+package_name+"... ")
    u := &Unzip{file_name, "", nil}
    if err := u.Expand(); err != nil {
        log.Fatalln("Failed to expand "+file_name, err)
    }

    if err := os.Chdir(local_dir); err != nil {
        log.Fatalln("Downloading and/or installing "+package_name+" failed ", err)
    }
    cmd2 := exec.Command("cmd", "/C C:\\Python27\\python.exe setup.py install")
    if err := cmd2.Run(); err != nil {
        log.Fatalln("Failed to install "+package_name, err)
    }
    post_fn()
    if err := os.Chdir(".."); err != nil {
        // ...
    }
    log.Println("Successfully installed "+package_name)
}

func installPython27() {
    if err := checkForPython(); err != nil {
        post_fn := func() {}
        installExe("python-2.7.5.msi", "Python-2.7.5", post_fn)
    } else {
        log.Println("Python is already installed")
    }
}

func installPyglet12alpha() {
    post_fn := func() {}
    installZippedPythonPackage("pyglet-1.2alpha.zip", "pyglet-1.2alpha", "pyglet-1.2alpha1", post_fn)
}

func installNumPy171() {
    post_fn := func() {}
    installExe("numpy-MKL-1.7.1.win32-py2.7.exe", "NumPy-1.7.1", post_fn)
}

func installPySerial26() {
    post_fn := func() {}
    installZippedPythonPackage("pyserial-2.6.zip", "pyserial-2.6", "pyserial-2.6", post_fn)
}

func installUnlock() {
    post_processing_fn := func() {
        if err := os.MkdirAll("C:\\Unlock", 0755); err != nil {
            log.Fatalln("Failed to make unlock directory", err)
        }
        if err := os.Rename("collector.py", "C:\\Unlock\\collector.py"); err != nil {
            log.Fatalln("Failed to install unlock collector", err)
        }

        if err := os.Rename("collector.bat", "C:\\Unlock\\collector.bat"); err != nil {
            log.Fatalln("Failed to install unlock collector", err)
        }

        if err := os.Rename("pygtec.py", "C:\\Unlock\\pygtec.py"); err != nil {
            log.Fatalln("Failed to install unlock collector", err)
        }

        if err := os.Rename("targets.png", "C:\\Unlock\\targets.png"); err != nil {
            log.Fatalln("Failed to install unlock collector", err)
        }
    }
    installZippedPythonPackage("unlock.zip", "unlock", "unlock-npl", post_processing_fn)
}

func main() {
    logf, err := os.OpenFile("unlock-install.log", os.O_WRONLY|os.O_CREATE,0640)
    if err != nil {
        log.Fatalln(err)
    }
    log.SetOutput(io.MultiWriter(logf, os.Stdout))
    installPython27()
    installPyglet12alpha()
    installNumPy171()
    installPySerial26()
    installUnlock()
}
<commit_msg>Added SQLAlchemy<commit_after><|endoftext|>


<commit_before>package install

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

const (
	edgeBinaryServer    = "https://edge-binaries.cockroachdb.com"
	releaseBinaryServer = "https://s3.amazonaws.com/binaries.cockroachdb.com/"
)

func getEdgeBinaryURL(binaryName string, SHA string) (*url.URL, error) {
	edgeBinaryLocation, err := url.Parse(edgeBinaryServer)
	if err != nil {
		return nil, err
	}
	edgeBinaryLocation.Path = binaryName
	// If a specific SHA is provided, just attach that.
	if len(SHA) > 0 {
		edgeBinaryLocation.Path += "." + SHA
	} else {
		edgeBinaryLocation.Path += ".LATEST"
		// Otherwise, find the latest SHA binary available. This works because
		// "[executable].LATEST" redirects to the latest SHA.
		resp, err := http.Head(edgeBinaryLocation.String())
		if err != nil {
			return nil, err
		}
		edgeBinaryLocation = resp.Request.URL
	}

	return edgeBinaryLocation, nil
}

// StageRemoteBinary downloads a cockroach edge binary with the provided
// application path to each specified by the cluster. If no SHA is specified,
// the latest build of the binary is used instead.
func StageRemoteBinary(c *SyncedCluster, applicationName, binaryPath, SHA string) error {
	binURL, err := getEdgeBinaryURL(binaryPath, SHA)
	if err != nil {
		return err
	}
	fmt.Println("Resolved binary url for %s: %s", applicationName, binURL)
	cmdStr := fmt.Sprintf(
		`curl -sfSL -o %s "%s" && chmod 755 ./%s`, applicationName, binURL, applicationName,
	)
	return c.Run(
		os.Stdout, os.Stderr, c.Nodes, fmt.Sprintf("staging binary (%s)", applicationName), cmdStr,
	)
}

// StageCockroachRelease downloads an official CockroachDB release binary with
// the specified version.
func StageCockroachRelease(c *SyncedCluster, version string) error {
	if len(version) == 0 {
		return fmt.Errorf(
			"release application cannot be staged without specifying a specific version",
		)
	}
	binURL, err := url.Parse(releaseBinaryServer)
	if err != nil {
		return err
	}
	binURL.Path += fmt.Sprintf("cockroach-%s.linux-amd64.tgz", version)
	fmt.Println("Resolved release url for cockroach version %s: %s", version, binURL)

	// This command incantation:
	// - Creates a temporary directory on the remote machine
	// - Downloads and unpacks the cockroach release into the temp directory
	// - Moves the cockroach executable from the binary to '/.' and gives it
	// the correct permissions.
	cmdStr := fmt.Sprintf(`
tmpdir="$(mktemp -d /tmp/cockroach-release.XXX)" && \
curl -f -s -S -o- %s | tar xfz - -C "${tmpdir}" --strip-components 1 && \
mv ${tmpdir}/cockroach ./cockroach && \
chmod 755 ./cockroach
`, binURL)
	return c.Run(
		os.Stdout, os.Stderr, c.Nodes, "staging cockroach release binary", cmdStr,
	)
}
<commit_msg>fix bug in `stage` in which println was used when printf was intendend<commit_after>package install

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

const (
	edgeBinaryServer    = "https://edge-binaries.cockroachdb.com"
	releaseBinaryServer = "https://s3.amazonaws.com/binaries.cockroachdb.com/"
)

func getEdgeBinaryURL(binaryName string, SHA string) (*url.URL, error) {
	edgeBinaryLocation, err := url.Parse(edgeBinaryServer)
	if err != nil {
		return nil, err
	}
	edgeBinaryLocation.Path = binaryName
	// If a specific SHA is provided, just attach that.
	if len(SHA) > 0 {
		edgeBinaryLocation.Path += "." + SHA
	} else {
		edgeBinaryLocation.Path += ".LATEST"
		// Otherwise, find the latest SHA binary available. This works because
		// "[executable].LATEST" redirects to the latest SHA.
		resp, err := http.Head(edgeBinaryLocation.String())
		if err != nil {
			return nil, err
		}
		edgeBinaryLocation = resp.Request.URL
	}

	return edgeBinaryLocation, nil
}

// StageRemoteBinary downloads a cockroach edge binary with the provided
// application path to each specified by the cluster. If no SHA is specified,
// the latest build of the binary is used instead.
func StageRemoteBinary(c *SyncedCluster, applicationName, binaryPath, SHA string) error {
	binURL, err := getEdgeBinaryURL(binaryPath, SHA)
	if err != nil {
		return err
	}
	fmt.Printf("Resolved binary url for %s: %s\n", applicationName, binURL)
	cmdStr := fmt.Sprintf(
		`curl -sfSL -o %s "%s" && chmod 755 ./%s`, applicationName, binURL, applicationName,
	)
	return c.Run(
		os.Stdout, os.Stderr, c.Nodes, fmt.Sprintf("staging binary (%s)", applicationName), cmdStr,
	)
}

// StageCockroachRelease downloads an official CockroachDB release binary with
// the specified version.
func StageCockroachRelease(c *SyncedCluster, version string) error {
	if len(version) == 0 {
		return fmt.Errorf(
			"release application cannot be staged without specifying a specific version",
		)
	}
	binURL, err := url.Parse(releaseBinaryServer)
	if err != nil {
		return err
	}
	binURL.Path += fmt.Sprintf("cockroach-%s.linux-amd64.tgz", version)
	fmt.Println("Resolved release url for cockroach version %s: %s", version, binURL)

	// This command incantation:
	// - Creates a temporary directory on the remote machine
	// - Downloads and unpacks the cockroach release into the temp directory
	// - Moves the cockroach executable from the binary to '/.' and gives it
	// the correct permissions.
	cmdStr := fmt.Sprintf(`
tmpdir="$(mktemp -d /tmp/cockroach-release.XXX)" && \
curl -f -s -S -o- %s | tar xfz - -C "${tmpdir}" --strip-components 1 && \
mv ${tmpdir}/cockroach ./cockroach && \
chmod 755 ./cockroach
`, binURL)
	return c.Run(
		os.Stdout, os.Stderr, c.Nodes, "staging cockroach release binary", cmdStr,
	)
}
<|endoftext|>


<commit_before>package internal

import (
	"../agent"
	"golang.org/x/net/context"
	"code.google.com/p/go-uuid/uuid"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"time"
	"sync"
	"net"
	"log"
	"fmt"
)

type Session struct {
	lastKeep time.Time

	proxies  map[string]net.Conn

	done     chan struct{}
}

func NewSessionInfo() *Session {
	return &Session{
		lastKeep: time.Now(),
		proxies: make(map[string]net.Conn),
		done: make(chan struct{}),
	}
}

type AgentServer struct {
	guard      Guard //auth

	slocker    sync.Mutex
	sessions   map[string]*Session

	pingTicker *time.Ticker
}

func NewAgentServer(guard Guard) *AgentServer {
	srv := &AgentServer{
		guard: guard,
		sessions: make(map[string]*Session, 10),
		pingTicker: time.NewTicker(defaultPingCheckDelay),
	}

	go srv.checkLoop()

	return srv
}

func (srv *AgentServer) ListenAndServe(network, address string, opts ...grpc.ServerOption) (err error) {
	listener, err := net.Listen(network, address)
	if err != nil {
		return err
	}

	grpcServer := grpc.NewServer(opts...)
	agent.RegisterAgentServer(grpcServer, srv)

	err = grpcServer.Serve(listener)
	if err != nil {
		return err
	}
	return nil
}

func (srv *AgentServer) Hello(ctx context.Context, req *agent.HelloRequest) (reply *agent.HelloReply, err error) {
	if req.Major != versionMajor && req.Minor != versionMinor {
		return nil, gerrVersionNotSupported
	}

	reply = &agent.HelloReply{
		Major: versionMajor,
		Minor: versionMinor,
	}

	if srv.guard == nil {
		session := uuid.New()
		sInfo := NewSessionInfo()

		srv.slocker.Lock()
		srv.sessions[session] = sInfo
		srv.slocker.Unlock()

		reply.AuthMethod = agent.AuthMethod_NoAuth
		reply.Session = session

		log.Println("New session:", session)
	} else {
		reply.AuthMethod = srv.guard.Type()
	}

	return reply, nil
}

func (srv *AgentServer) Auth(ctx context.Context, req *agent.AuthRequest) (reply *agent.AuthReply, err error) {
	if srv.guard == nil {
		return nil, gerrOther
	}

	ok := false
	ok, err = srv.guard.AuthFromProto(req)
	if err != nil {
		return nil, err
	}
	if !ok {
		return nil, gerrUnauthenticated
	}

	session := uuid.New()
	sInfo := NewSessionInfo()

	srv.slocker.Lock()
	srv.sessions[session] = sInfo
	srv.slocker.Unlock()

	reply = &agent.AuthReply{
		Session: session,
	}

	log.Println("New session:", session)

	return reply, nil
}

func (srv *AgentServer) Bind(ctx context.Context, req *agent.BindRequest) (reply *agent.BindReply, err error) {
	var parent string
	if md, ok := metadata.FromContext(ctx); ok {
		ss := md["session"]
		if len(ss) >= 1 {
			parent = ss[0]
		}
	}
	if parent == "" {
		return nil, gerrSessionLoss
	}

	var session string
	var sInfo *Session

	srv.slocker.Lock()
	defer srv.slocker.Unlock()

	if _, ok := srv.sessions[parent]; !ok {
		return nil, gerrSessionInvaild
	}

	session = uuid.New()
	sInfo = NewSessionInfo()
	srv.sessions[session] = sInfo

	reply = &agent.BindReply{
		Session: session,
	}

	return reply, nil
}

func (srv *AgentServer) Connect(ctx context.Context, req *agent.ConnectRequest) (reply *agent.ConnectReply, err error) {
	var session string
	if md, ok := metadata.FromContext(ctx); ok {
		ss := md["session"]
		if len(ss) >= 1 {
			session = ss[0]
		}
	}
	if session == "" {
		return nil, gerrSessionLoss
	}

	srv.slocker.Lock()
	if _, ok := srv.sessions[session]; !ok {
		err = gerrSessionInvaild
	}
	srv.slocker.Unlock()
	if err != nil {
		return nil, err
	}

	var conn net.Conn
	if conn, err = net.Dial(req.Remote.Network, req.Remote.Address); err != nil {
		return nil, err
	}

	srv.slocker.Lock()
	defer srv.slocker.Unlock()

	channel := uuid.New()
	if sInfo, ok := srv.sessions[session]; ok {
		sInfo.proxies[channel] = conn
	} else {
		return nil, gerrSessionInvaild
	}

	reply = &agent.ConnectReply{
		Channel: channel,
		Bound: &agent.Address{
			Network: conn.LocalAddr().Network(),
			Address: conn.LocalAddr().String(),
		},
	}

	log.Println("New channel:", fmt.Sprintf("%s@%s", channel, session))

	return reply, nil
}

//bidirection stream procedure
//client must ack
func (srv *AgentServer) Exchange(stream agent.Agent_ExchangeServer) (err error) {
	var session string
	var channel string
	if md, ok := metadata.FromContext(stream.Context()); ok {
		ss := md["session"]
		if len(ss) >= 1 {
			session = ss[0]
		}
		cs := md["channel"]
		if len(cs) >= 1 {
			channel = cs[0]
		}
	}
	if session == "" {
		return gerrSessionLoss
	}
	if channel == "" {
		return gerrChannelLoss
	}

	var done chan struct{}
	var proxy net.Conn

	//get proxy connection
	srv.slocker.Lock()
	if sInfo, ok := srv.sessions[session]; ok {
		done = sInfo.done

		if proxy, ok = sInfo.proxies[channel]; ok {
			delete(sInfo.proxies, channel)
		} else {
			err = gerrChannelInvaild
		}
	} else {
		err = gerrSessionInvaild
	}
	srv.slocker.Unlock()

	if err != nil {
		return err
	}

	pipe := NewStreamPipe(context.Background(), stream)

	log.Println("New proxy:", fmt.Sprintf("%s@%s", channel, session))

	//proxy
	//until error(contain eof)
	return IoExchange(pipe, proxy, done)
}

//call procedure
func (srv *AgentServer) Heartbeat(ctx context.Context, ping *agent.Ping) (pong *agent.Pong, err error) {
	var session string
	if md, ok := metadata.FromContext(ctx); ok {
		ss := md["session"]
		if len(ss) >= 1 {
			session = ss[0]
		}
	}
	if session == "" {
		return nil, gerrSessionLoss
	}

	srv.slocker.Lock()
	defer srv.slocker.Unlock()

	if sInfo, ok := srv.sessions[session]; !ok {
		return nil, gerrSessionInvaild
	} else {
		sInfo.lastKeep = time.Now()
	}

	pong = &agent.Pong{
		AppData: ping.AppData,
	}
	return pong, err
}

func (srv *AgentServer) Bye(ctx context.Context, req *agent.Empty) (reply *agent.Empty, err error) {
	var session string
	if md, ok := metadata.FromContext(ctx); ok {
		ss := md["session"]
		if len(ss) >= 1 {
			session = ss[0]
		}
	}
	if session == "" {
		return nil, gerrSessionLoss
	}

	srv.slocker.Lock()
	defer srv.slocker.Unlock()

	if sInfo, ok := srv.sessions[session]; !ok {
		return nil, gerrSessionInvaild
	} else {
		close(sInfo.done)
		delete(srv.sessions, session)

		log.Println("Byte:", session)
	}

	return &agent.Empty{}, err
}

func (srv *AgentServer) checkAndRemove() {
	srv.slocker.Lock()
	defer srv.slocker.Unlock()

	now := time.Now()

	for session, sInfo := range srv.sessions {
		if now.Sub(sInfo.lastKeep) > defaultPingMaxDelay {
			//kick
			close(sInfo.done)
			delete(srv.sessions, session)
			for _, proxy := range sInfo.proxies {
				proxy.Close()
			}

			log.Println("Kick invaild session:", session)
		}
	}
}

func (srv *AgentServer) checkLoop() {
	for _ = range srv.pingTicker.C {
		srv.checkAndRemove()
	}
}

<commit_msg>回话say byte时，也移除从属连接<commit_after>package internal

import (
	"../agent"
	"golang.org/x/net/context"
	"code.google.com/p/go-uuid/uuid"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"time"
	"sync"
	"net"
	"log"
	"fmt"
)

type Session struct {
	lastKeep time.Time

	proxies  map[string]net.Conn

	done     chan struct{}
}

func NewSessionInfo() *Session {
	return &Session{
		lastKeep: time.Now(),
		proxies: make(map[string]net.Conn),
		done: make(chan struct{}),
	}
}

type AgentServer struct {
	guard      Guard //auth

	slocker    sync.Mutex
	sessions   map[string]*Session

	pingTicker *time.Ticker
}

func NewAgentServer(guard Guard) *AgentServer {
	srv := &AgentServer{
		guard: guard,
		sessions: make(map[string]*Session, 10),
		pingTicker: time.NewTicker(defaultPingCheckDelay),
	}

	go srv.checkLoop()

	return srv
}

func (srv *AgentServer) ListenAndServe(network, address string, opts ...grpc.ServerOption) (err error) {
	listener, err := net.Listen(network, address)
	if err != nil {
		return err
	}

	grpcServer := grpc.NewServer(opts...)
	agent.RegisterAgentServer(grpcServer, srv)

	err = grpcServer.Serve(listener)
	if err != nil {
		return err
	}
	return nil
}

func (srv *AgentServer) Hello(ctx context.Context, req *agent.HelloRequest) (reply *agent.HelloReply, err error) {
	if req.Major != versionMajor && req.Minor != versionMinor {
		return nil, gerrVersionNotSupported
	}

	reply = &agent.HelloReply{
		Major: versionMajor,
		Minor: versionMinor,
	}

	if srv.guard == nil {
		session := uuid.New()
		sInfo := NewSessionInfo()

		srv.slocker.Lock()
		srv.sessions[session] = sInfo
		srv.slocker.Unlock()

		reply.AuthMethod = agent.AuthMethod_NoAuth
		reply.Session = session

		log.Println("New session:", session)
	} else {
		reply.AuthMethod = srv.guard.Type()
	}

	return reply, nil
}

func (srv *AgentServer) Auth(ctx context.Context, req *agent.AuthRequest) (reply *agent.AuthReply, err error) {
	if srv.guard == nil {
		return nil, gerrOther
	}

	ok := false
	ok, err = srv.guard.AuthFromProto(req)
	if err != nil {
		return nil, err
	}
	if !ok {
		return nil, gerrUnauthenticated
	}

	session := uuid.New()
	sInfo := NewSessionInfo()

	srv.slocker.Lock()
	srv.sessions[session] = sInfo
	srv.slocker.Unlock()

	reply = &agent.AuthReply{
		Session: session,
	}

	log.Println("New session:", session)

	return reply, nil
}

func (srv *AgentServer) Bind(ctx context.Context, req *agent.BindRequest) (reply *agent.BindReply, err error) {
	var parent string
	if md, ok := metadata.FromContext(ctx); ok {
		ss := md["session"]
		if len(ss) >= 1 {
			parent = ss[0]
		}
	}
	if parent == "" {
		return nil, gerrSessionLoss
	}

	var session string
	var sInfo *Session

	srv.slocker.Lock()
	defer srv.slocker.Unlock()

	if _, ok := srv.sessions[parent]; !ok {
		return nil, gerrSessionInvaild
	}

	session = uuid.New()
	sInfo = NewSessionInfo()
	srv.sessions[session] = sInfo

	reply = &agent.BindReply{
		Session: session,
	}

	return reply, nil
}

func (srv *AgentServer) Connect(ctx context.Context, req *agent.ConnectRequest) (reply *agent.ConnectReply, err error) {
	var session string
	if md, ok := metadata.FromContext(ctx); ok {
		ss := md["session"]
		if len(ss) >= 1 {
			session = ss[0]
		}
	}
	if session == "" {
		return nil, gerrSessionLoss
	}

	srv.slocker.Lock()
	if _, ok := srv.sessions[session]; !ok {
		err = gerrSessionInvaild
	}
	srv.slocker.Unlock()
	if err != nil {
		return nil, err
	}

	log.Println("Connecting:", req.Remote.Address)

	var conn net.Conn
	if conn, err = net.Dial(req.Remote.Network, req.Remote.Address); err != nil {
		return nil, err
	}

	srv.slocker.Lock()
	defer srv.slocker.Unlock()

	channel := uuid.New()
	if sInfo, ok := srv.sessions[session]; ok {
		sInfo.proxies[channel] = conn
	} else {
		return nil, gerrSessionInvaild
	}

	reply = &agent.ConnectReply{
		Channel: channel,
		Bound: &agent.Address{
			Network: conn.LocalAddr().Network(),
			Address: conn.LocalAddr().String(),
		},
	}

	return reply, nil
}

//bidirection stream procedure
//client must ack
func (srv *AgentServer) Exchange(stream agent.Agent_ExchangeServer) (err error) {
	var session string
	var channel string
	if md, ok := metadata.FromContext(stream.Context()); ok {
		ss := md["session"]
		if len(ss) >= 1 {
			session = ss[0]
		}
		cs := md["channel"]
		if len(cs) >= 1 {
			channel = cs[0]
		}
	}
	if session == "" {
		return gerrSessionLoss
	}
	if channel == "" {
		return gerrChannelLoss
	}

	var done chan struct{}
	var proxy net.Conn

	//get proxy connection
	srv.slocker.Lock()
	if sInfo, ok := srv.sessions[session]; ok {
		done = sInfo.done

		if proxy, ok = sInfo.proxies[channel]; ok {
			delete(sInfo.proxies, channel)
		} else {
			err = gerrChannelInvaild
		}
	} else {
		err = gerrSessionInvaild
	}
	srv.slocker.Unlock()

	if err != nil {
		return err
	}

	pipe := NewStreamPipe(context.Background(), stream)

	//proxy
	//until error(contain eof)
	return IoExchange(pipe, proxy, done)
}

//call procedure
func (srv *AgentServer) Heartbeat(ctx context.Context, ping *agent.Ping) (pong *agent.Pong, err error) {
	var session string
	if md, ok := metadata.FromContext(ctx); ok {
		ss := md["session"]
		if len(ss) >= 1 {
			session = ss[0]
		}
	}
	if session == "" {
		return nil, gerrSessionLoss
	}

	srv.slocker.Lock()
	defer srv.slocker.Unlock()

	if sInfo, ok := srv.sessions[session]; !ok {
		return nil, gerrSessionInvaild
	} else {
		sInfo.lastKeep = time.Now()
	}

	pong = &agent.Pong{
		AppData: ping.AppData,
	}
	return pong, err
}

func (srv *AgentServer) Bye(ctx context.Context, req *agent.Empty) (reply *agent.Empty, err error) {
	var session string
	if md, ok := metadata.FromContext(ctx); ok {
		ss := md["session"]
		if len(ss) >= 1 {
			session = ss[0]
		}
	}
	if session == "" {
		return nil, gerrSessionLoss
	}

	srv.slocker.Lock()
	defer srv.slocker.Unlock()

	if sInfo, ok := srv.sessions[session]; !ok {
		return nil, gerrSessionInvaild
	} else {
		close(sInfo.done)
		delete(srv.sessions, session)
		for _, proxy := range sInfo.proxies {
			proxy.Close()
		}

		log.Println("Byte:", session)
	}

	return &agent.Empty{}, err
}

func (srv *AgentServer) checkAndRemove() {
	srv.slocker.Lock()
	defer srv.slocker.Unlock()

	now := time.Now()

	for session, sInfo := range srv.sessions {
		if now.Sub(sInfo.lastKeep) > defaultPingMaxDelay {
			//kick
			close(sInfo.done)
			delete(srv.sessions, session)
			for _, proxy := range sInfo.proxies {
				proxy.Close()
			}

			log.Println("Kick invaild session:", session)
		}
	}
}

func (srv *AgentServer) checkLoop() {
	for _ = range srv.pingTicker.C {
		srv.checkAndRemove()
	}
}

<|endoftext|>


<commit_before>package main

import "github.com/hprose/hprose-go"

var Session []map[string]interface{}

func GetSession(context hprose.Context) map[string]interface{} {
	sessionid, _ := context.GetInt("sessionid")
	return Session[sessionid]
}

type MyServerFilter struct{}

func (MyServerFilter) InputFilter(data []byte, context hprose.Context) []byte {
	if len(data) > 7 && data[0] == 's' && data[1] == 'i' && data[2] == 'd' {
		context.SetInt("sessionid", int(data[3])<<24|int(data[4])<<16|int(data[5])<<8|int(data[6]))
		data = data[7:]
	} else {
		context.SetInt("sessionid", len(Session))
		Session = append(Session, make(map[string]interface{}))
	}
	return data
}

func (MyServerFilter) OutputFilter(data []byte, context hprose.Context) []byte {
	sessionid, _ := context.GetInt("sessionid")
	buf := make([]byte, 7+len(data))
	buf[0] = 's'
	buf[1] = 'i'
	buf[2] = 'd'
	buf[3] = byte(sessionid >> 24 & 0xff)
	buf[4] = byte(sessionid >> 16 & 0xff)
	buf[5] = byte(sessionid >> 8 & 0xff)
	buf[6] = byte(sessionid & 0xff)
	copy(buf[7:], data)
	return buf
}

func inc(context hprose.Context) int {
	session := GetSession(context)
	n, ok := session["n"]
	if !ok {
		session["n"] = 0
		return 0
	}
	i := n.(int) + 1
	session["n"] = i
	return i
}

func main() {
	server := hprose.NewTcpServer("tcp4://:4321/")
	server.AddFilter(MyServerFilter{})
	server.AddFunction("inc", inc)
	server.ThreadCount = 16
	server.Start()
}
<commit_msg>Fixed example tcpsessionserver.<commit_after>package main

import "github.com/hprose/hprose-go"

var Session []map[string]interface{}

func GetSession(context hprose.Context) map[string]interface{} {
	sessionid, _ := context.GetInt("sessionid")
	return Session[sessionid]
}

type MyServerFilter struct{}

func (MyServerFilter) InputFilter(data []byte, context hprose.Context) []byte {
	if len(data) > 7 && data[0] == 's' && data[1] == 'i' && data[2] == 'd' {
		context.SetInt("sessionid", int(data[3])<<24|int(data[4])<<16|int(data[5])<<8|int(data[6]))
		data = data[7:]
	} else {
		context.SetInt("sessionid", len(Session))
		Session = append(Session, make(map[string]interface{}))
	}
	return data
}

func (MyServerFilter) OutputFilter(data []byte, context hprose.Context) []byte {
	sessionid, _ := context.GetInt("sessionid")
	buf := make([]byte, 7+len(data))
	buf[0] = 's'
	buf[1] = 'i'
	buf[2] = 'd'
	buf[3] = byte(sessionid >> 24 & 0xff)
	buf[4] = byte(sessionid >> 16 & 0xff)
	buf[5] = byte(sessionid >> 8 & 0xff)
	buf[6] = byte(sessionid & 0xff)
	copy(buf[7:], data)
	return buf
}

func inc(context hprose.Context) int {
	session := GetSession(context)
	n, ok := session["n"]
	if !ok {
		session["n"] = 0
		return 0
	}
	i := n.(int) + 1
	session["n"] = i
	return i
}

func main() {
	server := hprose.NewTcpServer("tcp4://:4321/")
	server.AddFilter(MyServerFilter{})
	server.AddFunction("inc", inc)
	server.Start()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"github.com/goerlang/etf"
	"github.com/goerlang/node"
	"log"
)

var enableNode bool
var nodeName string
var nodeCookie string
var nodePort int

func init() {
	flag.BoolVar(&enableNode, "node", false, "start erlang node")
	flag.StringVar(&nodeName, "node-name", "", "name of erlang node")
	flag.StringVar(&nodeCookie, "node-cookie", "", "cookie of erlang node")
	flag.IntVar(&nodePort, "node-port", 5858, "port of erlang node")
}

func nodeEnabled() bool {
	return enableNode
}

func runNode() (enode *node.Node) {
	enode = node.NewNode(nodeName, nodeCookie)
	err := enode.Publish(nodePort)
	if err != nil {
		log.Printf("Cannot publish: %s", err)
		enode = nil
	}
	eSrv := new(eclusSrv)
	enode.Spawn(eSrv)

	eClos := func(terms etf.List) (r etf.Term) {
		r = etf.Term(etf.Tuple{etf.Atom("enode"), len(terms)})
		return
	}

	err = enode.RpcProvide("enode", "lambda", eClos)
	if err != nil {
		log.Printf("Cannot provide function to RPC: %s", err)
	}

	return
}
<commit_msg>go fmt<commit_after>package main

import (
	"flag"
	"github.com/goerlang/etf"
	"github.com/goerlang/node"
	"log"
)

var enableNode bool
var nodeName string
var nodeCookie string
var nodePort int

func init() {
	flag.BoolVar(&enableNode, "node", false, "start erlang node")
	flag.StringVar(&nodeName, "node-name", "", "name of erlang node")
	flag.StringVar(&nodeCookie, "node-cookie", "", "cookie of erlang node")
	flag.IntVar(&nodePort, "node-port", 5858, "port of erlang node")
}

func nodeEnabled() bool {
	return enableNode
}

func runNode() (enode *node.Node) {
	enode = node.NewNode(nodeName, nodeCookie)
	err := enode.Publish(nodePort)
	if err != nil {
		log.Printf("Cannot publish: %s", err)
		enode = nil
	}
	eSrv := new(eclusSrv)
	enode.Spawn(eSrv)

	eClos := func(terms etf.List) (r etf.Term) {
		r = etf.Term(etf.Tuple{etf.Atom("enode"), len(terms)})
		return
	}

	err = enode.RpcProvide("enode", "lambda", eClos)
	if err != nil {
		log.Printf("Cannot provide function to RPC: %s", err)
	}

	return
}
<|endoftext|>