<commit_before>/* A relatively simple Chef server implementation in Go, as a learning project
 * to learn more about programming in Go. */

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

package main

import (
	"net/http"
	"path"
	"log"
	"github.com/ctdk/goiardi/config"
	"github.com/ctdk/goiardi/actor"
)

type InterceptHandler struct {} // Doesn't need to do anything, just sit there.

func main(){
	config.ParseConfigOptions()
	log.SetFlags(log.LstdFlags | log.Lshortfile)

	/* Create default clients and users. Currently chef-validator,
	 * chef-webui, and admin. */
	createDefaultActors()

	/* Register the various handlers, found in their own source files. */
	http.HandleFunc("/authenticate_user", authenticate_user_handler)
	http.HandleFunc("/clients", list_handler)
	http.HandleFunc("/clients/", actor_handler)
	http.HandleFunc("/cookbooks", cookbook_handler)
	http.HandleFunc("/cookbooks/", cookbook_handler)
	http.HandleFunc("/data", data_handler)
	http.HandleFunc("/data/", data_handler)
	http.HandleFunc("/environments", environment_handler)
	http.HandleFunc("/environments/", environment_handler)
	http.HandleFunc("/nodes", list_handler)
	http.HandleFunc("/nodes/", node_handler)
	http.HandleFunc("/principals/", principal_handler)
	http.HandleFunc("/roles", list_handler)
	http.HandleFunc("/roles/", role_handler)
	http.HandleFunc("/sandboxes", sandbox_handler)
	http.HandleFunc("/sandboxes/", sandbox_handler)
	http.HandleFunc("/search", search_handler)
	http.HandleFunc("/search/", search_handler)
	http.HandleFunc("/users", list_handler)
	http.HandleFunc("/users/", actor_handler)
	http.HandleFunc("/file_store/", file_store_handler)

	/* TODO: figure out how to handle the root & not found pages */
	http.HandleFunc("/", root_handler)

	listen_addr := config.ListenAddr()
	http.ListenAndServe(listen_addr, &InterceptHandler{})
}

func root_handler(w http.ResponseWriter, r *http.Request){
	// TODO: make root do something useful
	return
}

func (h *InterceptHandler) ServeHTTP(w http.ResponseWriter, r *http.Request){
	/* knife sometimes sends URL paths that start with //. Redirecting
	 * worked for GETs, but since it was breaking POSTs and screwing with
	 * GETs with query params, we just clean up the path and move on. */

	/* log the URL */
	// TODO: set this to verbosity level 4 or so
	//log.Printf("Serving %s\n", r.URL.Path)

	if r.Method != "CONNECT" {
		if p := cleanPath(r.URL.Path); p != r.URL.Path{
			r.URL.Path = p
		}
	}

	/* Make configurable, I guess, but Chef wants it to be 1000000 */
	if r.ContentLength > 1000000 {
		http.Error(w, "Content-length too long!", http.StatusRequestEntityTooLarge)
		return
	}

	w.Header().Set("X-Goiardi", "yes")
	w.Header().Set("X-Goiardi-Version", config.Version)
	w.Header().Set("X-Chef-Version", config.ChefVersion)

	http.DefaultServeMux.ServeHTTP(w, r)
}

func cleanPath(p string) string {
	/* Borrowing cleanPath from net/http */
	if p == "" {
		return "/"
	}
	if p[0] != '/' {
		p = "/" + p
	}
        np := path.Clean(p)
	// path.Clean removes trailing slash except for root;
	// put the trailing slash back if necessary.
	if p[len(p)-1] == '/' && np != "/" {
		np += "/"
	}
	return np
}

func createDefaultActors() {
	if webui, err := actor.New("chef-webui", "client"); err != nil {
		log.Fatalln(err)
	} else {
		webui.Admin = true
		_, err = webui.GenerateKeys()
		if err != nil {
			log.Fatalln(err)
		}
		webui.Save()
	}

	if validator, err := actor.New("chef-validator", "client"); err != nil {
		log.Fatalln(err)
	} else {
		validator.Validator = true
		_, err = validator.GenerateKeys()
		if err != nil {
			log.Fatalln(err)
		}
		validator.Save()
	}

	if admin, err := actor.New("admin", "user"); err != nil {
		log.Fatalln(err)
	} else {
		admin.Admin = true
		_, err = admin.GenerateKeys()
		if err != nil {
			log.Fatalln(err)
		}
		admin.Save()
	}

	return
}
<commit_msg>And a header like open source Chef server sends<commit_after>/* A relatively simple Chef server implementation in Go, as a learning project
 * to learn more about programming in Go. */

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

package main

import (
	"net/http"
	"path"
	"log"
	"github.com/ctdk/goiardi/config"
	"github.com/ctdk/goiardi/actor"
	"fmt"
)

type InterceptHandler struct {} // Doesn't need to do anything, just sit there.

func main(){
	config.ParseConfigOptions()
	log.SetFlags(log.LstdFlags | log.Lshortfile)

	/* Create default clients and users. Currently chef-validator,
	 * chef-webui, and admin. */
	createDefaultActors()

	/* Register the various handlers, found in their own source files. */
	http.HandleFunc("/authenticate_user", authenticate_user_handler)
	http.HandleFunc("/clients", list_handler)
	http.HandleFunc("/clients/", actor_handler)
	http.HandleFunc("/cookbooks", cookbook_handler)
	http.HandleFunc("/cookbooks/", cookbook_handler)
	http.HandleFunc("/data", data_handler)
	http.HandleFunc("/data/", data_handler)
	http.HandleFunc("/environments", environment_handler)
	http.HandleFunc("/environments/", environment_handler)
	http.HandleFunc("/nodes", list_handler)
	http.HandleFunc("/nodes/", node_handler)
	http.HandleFunc("/principals/", principal_handler)
	http.HandleFunc("/roles", list_handler)
	http.HandleFunc("/roles/", role_handler)
	http.HandleFunc("/sandboxes", sandbox_handler)
	http.HandleFunc("/sandboxes/", sandbox_handler)
	http.HandleFunc("/search", search_handler)
	http.HandleFunc("/search/", search_handler)
	http.HandleFunc("/users", list_handler)
	http.HandleFunc("/users/", actor_handler)
	http.HandleFunc("/file_store/", file_store_handler)

	/* TODO: figure out how to handle the root & not found pages */
	http.HandleFunc("/", root_handler)

	listen_addr := config.ListenAddr()
	http.ListenAndServe(listen_addr, &InterceptHandler{})
}

func root_handler(w http.ResponseWriter, r *http.Request){
	// TODO: make root do something useful
	return
}

func (h *InterceptHandler) ServeHTTP(w http.ResponseWriter, r *http.Request){
	/* knife sometimes sends URL paths that start with //. Redirecting
	 * worked for GETs, but since it was breaking POSTs and screwing with
	 * GETs with query params, we just clean up the path and move on. */

	/* log the URL */
	// TODO: set this to verbosity level 4 or so
	//log.Printf("Serving %s\n", r.URL.Path)

	if r.Method != "CONNECT" {
		if p := cleanPath(r.URL.Path); p != r.URL.Path{
			r.URL.Path = p
		}
	}

	/* Make configurable, I guess, but Chef wants it to be 1000000 */
	if r.ContentLength > 1000000 {
		http.Error(w, "Content-length too long!", http.StatusRequestEntityTooLarge)
		return
	}

	w.Header().Set("X-Goiardi", "yes")
	w.Header().Set("X-Goiardi-Version", config.Version)
	w.Header().Set("X-Chef-Version", config.ChefVersion)
	api_info := fmt.Sprintf("flavor=osc;version:%s;goiardi=%s", config.ChefVersion, config.Version)
	w.Header().Set("X-Ops-API-Info", api_info)

	http.DefaultServeMux.ServeHTTP(w, r)
}

func cleanPath(p string) string {
	/* Borrowing cleanPath from net/http */
	if p == "" {
		return "/"
	}
	if p[0] != '/' {
		p = "/" + p
	}
        np := path.Clean(p)
	// path.Clean removes trailing slash except for root;
	// put the trailing slash back if necessary.
	if p[len(p)-1] == '/' && np != "/" {
		np += "/"
	}
	return np
}

func createDefaultActors() {
	if webui, err := actor.New("chef-webui", "client"); err != nil {
		log.Fatalln(err)
	} else {
		webui.Admin = true
		_, err = webui.GenerateKeys()
		if err != nil {
			log.Fatalln(err)
		}
		webui.Save()
	}

	if validator, err := actor.New("chef-validator", "client"); err != nil {
		log.Fatalln(err)
	} else {
		validator.Validator = true
		_, err = validator.GenerateKeys()
		if err != nil {
			log.Fatalln(err)
		}
		validator.Save()
	}

	if admin, err := actor.New("admin", "user"); err != nil {
		log.Fatalln(err)
	} else {
		admin.Admin = true
		_, err = admin.GenerateKeys()
		if err != nil {
			log.Fatalln(err)
		}
		admin.Save()
	}

	return
}
<|endoftext|>


<commit_before>// Godless is a peer-to-peer database running over IPFS.
//
// Godless uses a Consistent Replicated Data Type called a Namespace to share schemaless data with peers.
//
// This package is a facade to Godless internals.
//
// Godless is in alpha, and should be considered experimental software.
package godless

import (
	"fmt"
	"sync"
	"time"

	gohttp "net/http"

	"github.com/johnny-morrice/godless/api"
	"github.com/johnny-morrice/godless/cache"
	"github.com/johnny-morrice/godless/crdt"
	"github.com/johnny-morrice/godless/internal/crypto"
	"github.com/johnny-morrice/godless/internal/http"
	"github.com/johnny-morrice/godless/internal/ipfs"
	"github.com/johnny-morrice/godless/internal/service"
	"github.com/johnny-morrice/godless/log"
	"github.com/johnny-morrice/godless/query"
	"github.com/pkg/errors"
)

// TODO allow single cache option.
// Godless options.
type Options struct {
	// IpfsServiceUrl is required.
	IpfsServiceUrl string
	// KeyStore is required. A private Key store.
	KeyStore api.KeyStore
	// WebServiceAddr is optional.  If not set, the webservice will be disabled.
	WebServiceAddr string
	// IndexHash is optional.  Set to load an existing index from IPFS.
	IndexHash string
	// FailEarly will cause the godless process to crash if it cannot contact IPFS on startup.
	FailEarly bool
	// ReplicateInterval is optional.  The duration between peer-to-peer replications.
	ReplicateInterval time.Duration
	Pulse             time.Duration
	// Topics is optional.  Two godless servers which share a topic will replicate indices. An empty topics slice will disable replication.
	Topics []string
	// IpfsClient is optional.  Specify a HTTP client for IPFS.
	IpfsClient *gohttp.Client
	// IpfsPingTimeout is optional.  Specify a lower timeout for "Am I Connected?" checks.
	IpfsPingTimeout time.Duration
	// Cache is optional. Build a 12-factor app by supplying your own remote cache.
	// HeadCache, IndexCache and NamespaceCache can be used to specify different caches for different data types.
	Cache api.Cache
	// HeadCache is optional.  Build a 12-factor app by supplying your own remote cache.
	HeadCache api.HeadCache
	// IndexCache is optional.  Build a 12-factor app by supplying your own remote cache.
	IndexCache api.IndexCache
	// NamespaceCache is optional. Build a 12-factor app by supplying your own remote cache.
	NamespaceCache api.NamespaceCache
	// PriorityQueue is optional. Build a 12-factor app by supplying your own remote cache.
	PriorityQueue api.RequestPriorityQueue
	// APIQueryLimit is optional.  Tune performance by setting the number of simultaneous queries.
	APIQueryLimit int
	// PublicServer is optional.  If false, the index will only be updated from peers who are in your public key list.
	PublicServer bool
}

// Godless is a peer-to-peer database.  It shares structured data between peers, using IPFS as a backing store.
// The core datastructure is a CRDT namespace which resembles a relational scheme in that it has tables, rows, and entries.
type Godless struct {
	Options
	errch    chan error
	errwg    sync.WaitGroup
	stopch   chan struct{}
	stoppers []chan<- struct{}
	store    api.RemoteStore
	remote   api.RemoteNamespace
	api      api.APIService
}

// New creates a godless instance, connecting to any services, and providing any services, specified in the options.
func New(options Options) (*Godless, error) {
	godless := &Godless{Options: options}

	missing := godless.findMissingParameters()

	if missing != nil {
		return nil, missing
	}

	setupFuncs := []func() error{
		godless.connectIpfs,
		godless.connectCache,
		godless.setupNamespace,
		godless.launchAPI,
		godless.serveWeb,
		godless.replicate,
	}

	err := breakOnError(setupFuncs)

	if err != nil {
		return nil, err
	}

	godless.report()

	return godless, nil
}

func (godless *Godless) report() {
	if godless.PublicServer {
		log.Info("Running public Godless API")
	} else {
		log.Info("Running private Godless API")
	}

	privCount := len(godless.KeyStore.GetAllPrivateKeys())
	pubCount := len(godless.KeyStore.GetAllPublicKeys())

	log.Info("Godless API using %v private and %v public keys", privCount, pubCount)
}

func (godless *Godless) findMissingParameters() error {
	var missing error
	if godless.IpfsServiceUrl == "" {
		msg := godless.missingParameterText("IpfsServiceUrl")
		missing = errors.New(msg)
	}

	if godless.KeyStore == nil {
		msg := godless.missingParameterText("KeyStore")
		if missing == nil {
			missing = errors.New(msg)
		} else {
			missing = errors.Wrap(missing, msg)
		}
	}

	return missing
}

func (godless *Godless) missingParameterText(param string) string {
	return fmt.Sprintf("Missing required parameter '%v'", param)
}

// Errors provides a stream of errors from godless.  Godless will attempt to handle any errors it can.  Any errors received here indicate that bad things have happened.
func (godless *Godless) Errors() <-chan error {
	return godless.errch
}

// Shutdown stops all godless processes.  It does not wait for those goroutines to stop.
func (godless *Godless) Shutdown() {
	godless.stopch <- struct{}{}
}

func (godless *Godless) connectIpfs() error {
	client := godless.IpfsClient
	pingTimeout := godless.IpfsPingTimeout

	peer := &ipfs.IPFSPeer{
		Url:         godless.IpfsServiceUrl,
		Client:      client,
		PingTimeout: pingTimeout,
	}

	if godless.FailEarly {
		err := peer.Connect()

		if err != nil {
			return err
		}
	}

	godless.store = peer

	return nil
}

func (godless *Godless) connectCache() error {
	if godless.Cache != nil {
		godless.HeadCache = godless.Cache
		godless.IndexCache = godless.Cache
		godless.NamespaceCache = godless.Cache
		return nil
	}

	if godless.HeadCache == nil {
		godless.HeadCache = cache.MakeResidentHeadCache()
	}

	if godless.IndexCache == nil {
		godless.IndexCache = cache.MakeResidentIndexCache(__UNKNOWN_BUFFER_SIZE)
	}

	if godless.NamespaceCache == nil {
		godless.NamespaceCache = cache.MakeResidentNamespaceCache(__UNKNOWN_BUFFER_SIZE)
	}

	return nil
}

func (godless *Godless) setupNamespace() error {
	if godless.IndexHash != "" {
		head := crdt.IPFSPath(godless.IndexHash)

		err := godless.HeadCache.SetHead(head)

		if err != nil {
			return err
		}
	}

	namespaceOptions := service.RemoteNamespaceOptions{
		Pulse:          godless.Pulse,
		Store:          godless.store,
		HeadCache:      godless.Cache,
		IndexCache:     godless.Cache,
		NamespaceCache: godless.Cache,
		KeyStore:       godless.KeyStore,
		IsPublicIndex:  godless.PublicServer,
		MemoryImage:    cache.MakeResidentMemoryImage(),
	}

	godless.remote = service.MakeRemoteNamespace(namespaceOptions)
	return nil
}

func (godless *Godless) launchAPI() error {
	limit := godless.APIQueryLimit

	if limit == 0 {
		limit = 1
	}

	queue := godless.PriorityQueue

	if queue == nil {
		queue = cache.MakeResidentBufferQueue(__UNKNOWN_BUFFER_SIZE)
	}

	api, errch := service.LaunchKeyValueStore(godless.remote, queue, limit)

	godless.addErrors(errch)
	godless.api = api

	return nil
}

// Serve serves the Godless webservice.
func (godless *Godless) serveWeb() error {
	addr := godless.WebServiceAddr

	if addr == "" {
		return nil
	}

	webService := &service.WebService{API: godless.api}
	stopch, err := http.Serve(addr, webService.Handler())

	if err != nil {
		return err
	}

	godless.addStopper(stopch)
	return nil
}

// Replicate shares data via the IPFS pubsub mechanism.
func (godless *Godless) replicate() error {
	topics := godless.Topics
	interval := godless.ReplicateInterval

	if len(topics) == 0 {
		return nil
	}

	pubsubTopics := make([]api.PubSubTopic, len(topics))

	for i, t := range topics {
		pubsubTopics[i] = api.PubSubTopic(t)
	}

	options := service.ReplicateOptions{
		API:         godless.api,
		RemoteStore: godless.store,
		Interval:    interval,
		Topics:      pubsubTopics,
		KeyStore:    godless.KeyStore,
	}
	stopch, errch := service.Replicate(options)
	godless.addStopper(stopch)
	godless.addErrors(errch)
	return nil
}

func (godless *Godless) addStopper(stopch chan<- struct{}) {
	if godless.stopch == nil {
		godless.stopch = make(chan struct{})
		go func() {
			godless.handleShutdown()
		}()
	}

	godless.stoppers = append(godless.stoppers, stopch)
}

func (godless *Godless) handleShutdown() {
	<-godless.stopch
	log.Info("Shutting down")
	for _, stopper := range godless.stoppers {
		go close(stopper)
	}

}

func (godless *Godless) addErrors(errch <-chan error) {
	godless.errwg.Add(1)

	if godless.errch == nil {
		godless.errch = make(chan error)
		go func() {
			godless.errwg.Wait()
			close(godless.errch)
		}()
	}

	go func() {
		for err := range errch {
			godless.errch <- err
		}
		godless.errwg.Done()
	}()
}

// Client is a Godless HTTP client.
type Client interface {
	SendQuery(*query.Query) (api.APIResponse, error)
	SendReflection(api.APIReflectionType) (api.APIResponse, error)
}

// MakeClient creates a Godless HTTP Client.
func MakeClient(serviceAddr string) Client {
	return service.MakeClient(serviceAddr)
}

func MakeClientWithHttp(serviceAddr string, webClient *gohttp.Client) Client {
	return service.MakeClientWithHttp(serviceAddr, webClient)
}

func MakeKeyStore() api.KeyStore {
	return &crypto.KeyStore{}
}

func breakOnError(pipeline []func() error) error {
	for _, f := range pipeline {
		err := f()

		if err != nil {
			return err
		}
	}

	return nil
}

// We don't know the right buffer size here, so let the cache package handle it.
const __UNKNOWN_BUFFER_SIZE = -1
<commit_msg>Fix glitch with default caches<commit_after>// Godless is a peer-to-peer database running over IPFS.
//
// Godless uses a Consistent Replicated Data Type called a Namespace to share schemaless data with peers.
//
// This package is a facade to Godless internals.
//
// Godless is in alpha, and should be considered experimental software.
package godless

import (
	"fmt"
	"sync"
	"time"

	gohttp "net/http"

	"github.com/johnny-morrice/godless/api"
	"github.com/johnny-morrice/godless/cache"
	"github.com/johnny-morrice/godless/crdt"
	"github.com/johnny-morrice/godless/internal/crypto"
	"github.com/johnny-morrice/godless/internal/http"
	"github.com/johnny-morrice/godless/internal/ipfs"
	"github.com/johnny-morrice/godless/internal/service"
	"github.com/johnny-morrice/godless/log"
	"github.com/johnny-morrice/godless/query"
	"github.com/pkg/errors"
)

// TODO allow single cache option.
// Godless options.
type Options struct {
	// IpfsServiceUrl is required.
	IpfsServiceUrl string
	// KeyStore is required. A private Key store.
	KeyStore api.KeyStore
	// WebServiceAddr is optional.  If not set, the webservice will be disabled.
	WebServiceAddr string
	// IndexHash is optional.  Set to load an existing index from IPFS.
	IndexHash string
	// FailEarly will cause the godless process to crash if it cannot contact IPFS on startup.
	FailEarly bool
	// ReplicateInterval is optional.  The duration between peer-to-peer replications.
	ReplicateInterval time.Duration
	Pulse             time.Duration
	// Topics is optional.  Two godless servers which share a topic will replicate indices. An empty topics slice will disable replication.
	Topics []string
	// IpfsClient is optional.  Specify a HTTP client for IPFS.
	IpfsClient *gohttp.Client
	// IpfsPingTimeout is optional.  Specify a lower timeout for "Am I Connected?" checks.
	IpfsPingTimeout time.Duration
	// Cache is optional. Build a 12-factor app by supplying your own remote cache.
	// HeadCache, IndexCache and NamespaceCache can be used to specify different caches for different data types.
	Cache api.Cache
	// HeadCache is optional.  Build a 12-factor app by supplying your own remote cache.
	HeadCache api.HeadCache
	// IndexCache is optional.  Build a 12-factor app by supplying your own remote cache.
	IndexCache api.IndexCache
	// NamespaceCache is optional. Build a 12-factor app by supplying your own remote cache.
	NamespaceCache api.NamespaceCache
	// PriorityQueue is optional. Build a 12-factor app by supplying your own remote cache.
	PriorityQueue api.RequestPriorityQueue
	// APIQueryLimit is optional.  Tune performance by setting the number of simultaneous queries.
	APIQueryLimit int
	// PublicServer is optional.  If false, the index will only be updated from peers who are in your public key list.
	PublicServer bool
}

// Godless is a peer-to-peer database.  It shares structured data between peers, using IPFS as a backing store.
// The core datastructure is a CRDT namespace which resembles a relational scheme in that it has tables, rows, and entries.
type Godless struct {
	Options
	errch    chan error
	errwg    sync.WaitGroup
	stopch   chan struct{}
	stoppers []chan<- struct{}
	store    api.RemoteStore
	remote   api.RemoteNamespace
	api      api.APIService
}

// New creates a godless instance, connecting to any services, and providing any services, specified in the options.
func New(options Options) (*Godless, error) {
	godless := &Godless{Options: options}

	missing := godless.findMissingParameters()

	if missing != nil {
		return nil, missing
	}

	setupFuncs := []func() error{
		godless.connectIpfs,
		godless.connectCache,
		godless.setupNamespace,
		godless.launchAPI,
		godless.serveWeb,
		godless.replicate,
	}

	err := breakOnError(setupFuncs)

	if err != nil {
		return nil, err
	}

	godless.report()

	return godless, nil
}

func (godless *Godless) report() {
	if godless.PublicServer {
		log.Info("Running public Godless API")
	} else {
		log.Info("Running private Godless API")
	}

	privCount := len(godless.KeyStore.GetAllPrivateKeys())
	pubCount := len(godless.KeyStore.GetAllPublicKeys())

	log.Info("Godless API using %v private and %v public keys", privCount, pubCount)
}

func (godless *Godless) findMissingParameters() error {
	var missing error
	if godless.IpfsServiceUrl == "" {
		msg := godless.missingParameterText("IpfsServiceUrl")
		missing = errors.New(msg)
	}

	if godless.KeyStore == nil {
		msg := godless.missingParameterText("KeyStore")
		if missing == nil {
			missing = errors.New(msg)
		} else {
			missing = errors.Wrap(missing, msg)
		}
	}

	return missing
}

func (godless *Godless) missingParameterText(param string) string {
	return fmt.Sprintf("Missing required parameter '%v'", param)
}

// Errors provides a stream of errors from godless.  Godless will attempt to handle any errors it can.  Any errors received here indicate that bad things have happened.
func (godless *Godless) Errors() <-chan error {
	return godless.errch
}

// Shutdown stops all godless processes.  It does not wait for those goroutines to stop.
func (godless *Godless) Shutdown() {
	godless.stopch <- struct{}{}
}

func (godless *Godless) connectIpfs() error {
	client := godless.IpfsClient
	pingTimeout := godless.IpfsPingTimeout

	peer := &ipfs.IPFSPeer{
		Url:         godless.IpfsServiceUrl,
		Client:      client,
		PingTimeout: pingTimeout,
	}

	if godless.FailEarly {
		err := peer.Connect()

		if err != nil {
			return err
		}
	}

	godless.store = peer

	return nil
}

func (godless *Godless) connectCache() error {
	if godless.Cache != nil {
		godless.HeadCache = godless.Cache
		godless.IndexCache = godless.Cache
		godless.NamespaceCache = godless.Cache
		return nil
	}

	if godless.HeadCache == nil {
		godless.HeadCache = cache.MakeResidentHeadCache()
	}

	if godless.IndexCache == nil {
		godless.IndexCache = cache.MakeResidentIndexCache(__UNKNOWN_BUFFER_SIZE)
	}

	if godless.NamespaceCache == nil {
		godless.NamespaceCache = cache.MakeResidentNamespaceCache(__UNKNOWN_BUFFER_SIZE)
	}

	return nil
}

func (godless *Godless) setupNamespace() error {
	if godless.IndexHash != "" {
		head := crdt.IPFSPath(godless.IndexHash)

		err := godless.HeadCache.SetHead(head)

		if err != nil {
			return err
		}
	}

	namespaceOptions := service.RemoteNamespaceOptions{
		Pulse:          godless.Pulse,
		Store:          godless.store,
		HeadCache:      godless.HeadCache,
		IndexCache:     godless.IndexCache,
		NamespaceCache: godless.NamespaceCache,
		KeyStore:       godless.KeyStore,
		IsPublicIndex:  godless.PublicServer,
		MemoryImage:    cache.MakeResidentMemoryImage(),
	}

	godless.remote = service.MakeRemoteNamespace(namespaceOptions)
	return nil
}

func (godless *Godless) launchAPI() error {
	limit := godless.APIQueryLimit

	if limit == 0 {
		limit = 1
	}

	queue := godless.PriorityQueue

	if queue == nil {
		queue = cache.MakeResidentBufferQueue(__UNKNOWN_BUFFER_SIZE)
	}

	api, errch := service.LaunchKeyValueStore(godless.remote, queue, limit)

	godless.addErrors(errch)
	godless.api = api

	return nil
}

// Serve serves the Godless webservice.
func (godless *Godless) serveWeb() error {
	addr := godless.WebServiceAddr

	if addr == "" {
		return nil
	}

	webService := &service.WebService{API: godless.api}
	stopch, err := http.Serve(addr, webService.Handler())

	if err != nil {
		return err
	}

	godless.addStopper(stopch)
	return nil
}

// Replicate shares data via the IPFS pubsub mechanism.
func (godless *Godless) replicate() error {
	topics := godless.Topics
	interval := godless.ReplicateInterval

	if len(topics) == 0 {
		return nil
	}

	pubsubTopics := make([]api.PubSubTopic, len(topics))

	for i, t := range topics {
		pubsubTopics[i] = api.PubSubTopic(t)
	}

	options := service.ReplicateOptions{
		API:         godless.api,
		RemoteStore: godless.store,
		Interval:    interval,
		Topics:      pubsubTopics,
		KeyStore:    godless.KeyStore,
	}
	stopch, errch := service.Replicate(options)
	godless.addStopper(stopch)
	godless.addErrors(errch)
	return nil
}

func (godless *Godless) addStopper(stopch chan<- struct{}) {
	if godless.stopch == nil {
		godless.stopch = make(chan struct{})
		go func() {
			godless.handleShutdown()
		}()
	}

	godless.stoppers = append(godless.stoppers, stopch)
}

func (godless *Godless) handleShutdown() {
	<-godless.stopch
	log.Info("Shutting down")
	for _, stopper := range godless.stoppers {
		go close(stopper)
	}

}

func (godless *Godless) addErrors(errch <-chan error) {
	godless.errwg.Add(1)

	if godless.errch == nil {
		godless.errch = make(chan error)
		go func() {
			godless.errwg.Wait()
			close(godless.errch)
		}()
	}

	go func() {
		for err := range errch {
			godless.errch <- err
		}
		godless.errwg.Done()
	}()
}

// Client is a Godless HTTP client.
type Client interface {
	SendQuery(*query.Query) (api.APIResponse, error)
	SendReflection(api.APIReflectionType) (api.APIResponse, error)
}

// MakeClient creates a Godless HTTP Client.
func MakeClient(serviceAddr string) Client {
	return service.MakeClient(serviceAddr)
}

func MakeClientWithHttp(serviceAddr string, webClient *gohttp.Client) Client {
	return service.MakeClientWithHttp(serviceAddr, webClient)
}

func MakeKeyStore() api.KeyStore {
	return &crypto.KeyStore{}
}

func breakOnError(pipeline []func() error) error {
	for _, f := range pipeline {
		err := f()

		if err != nil {
			return err
		}
	}

	return nil
}

// We don't know the right buffer size here, so let the cache package handle it.
const __UNKNOWN_BUFFER_SIZE = -1
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package gofetch

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
)

// ProgressReport represents the current download progress of a given file
type ProgressReport struct {
	URL string
	// Total length in bytes of the file being downloaded
	Total int64
	// Written bytes to disk on a write by write basis. It does not accumulate.
	WrittenBytes int64
}

// goFetch represents an instance of gofetch, holding global configuration options.
type goFetch struct {
	destDir     string
	etag        bool
	concurrency int
}

// Option as explained in http://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html
type Option func(*goFetch)

// DestDir allows you to set the destination directory for the downloaded files.
func DestDir(dir string) Option {
	return func(f *goFetch) {
		f.destDir = dir
	}
}

// Concurrency allows you to set the number of goroutines used to download a specific
// file.
func Concurrency(c int) Option {
	return func(f *goFetch) {
		f.concurrency = c
	}
}

// ETag allows you to disable or enable ETag support, meaning that if an already
// downloaded file is currently on disk and matches the ETag returned by the server,
// it will not be downloaded again.
func ETag(enable bool) Option {
	return func(f *goFetch) {
		f.etag = enable
	}
}

// New creates a new instance of goFetch with the given options.
func New(opts ...Option) *goFetch {
	// Creates instance and assigns defaults.
	gofetch := &goFetch{
		concurrency: 1,
		destDir:     "./",
		etag:        true,
	}

	for _, opt := range opts {
		opt(gofetch)
	}
	return gofetch
}

// Fetch downloads content from the provided URL. It supports resuming and
// parallelizing downloads while being very memory efficient.
func (gf *goFetch) Fetch(url string, progressCh chan<- ProgressReport) (*os.File, error) {
	if url == "" {
		return nil, errors.New("URL is required")
	}

	// We need to make a preflight request to get the size of the content.
	res, err := http.Head(url)
	if err != nil {
		return nil, err
	}

	if !strings.HasPrefix(res.Status, "2") {
		return nil, errors.New("HTTP requests returned a non 2xx status code")
	}

	fileName := path.Base(url)

	var etag string
	if gf.etag {
		etag = res.Header.Get("ETag")
		fileName += etag
	}

	destFilePath := filepath.Join(gf.destDir, fileName)

	fi, err := os.Stat(destFilePath)
	if err == nil && fi.Size() == res.ContentLength {
		if progressCh != nil {
			close(progressCh)
		}
		return os.Open(destFilePath)
	}

	return gf.parallelFetch(url, destFilePath, res.ContentLength, progressCh)
}

// parallelFetch fetches using multiple goroutines, each piece is streamed down
// to disk which makes it very efficient in terms of memory usage.
func (gf *goFetch) parallelFetch(url, destFilePath string, length int64, progressCh chan<- ProgressReport) (*os.File, error) {
	if progressCh != nil {
		defer close(progressCh)
	}

	var wg sync.WaitGroup

	report := ProgressReport{Total: length}
	concurrency := int64(gf.concurrency)
	chunkSize := length / concurrency
	remainingSize := length % concurrency
	chunksDir := filepath.Join(gf.destDir, path.Base(url)+".chunks")

	if err := os.MkdirAll(chunksDir, 0760); err != nil {
		return nil, err
	}

	var errs []error
	for i := int64(0); i < concurrency; i++ {
		min := chunkSize * i
		max := chunkSize * (i + 1)

		if i == (concurrency - 1) {
			// Add the remaining bytes in the last request
			max += remainingSize
		}

		wg.Add(1)
		go func(min, max int64, chunkNumber int) {
			defer wg.Done()
			chunkFile := filepath.Join(chunksDir, strconv.Itoa(chunkNumber))

			err := gf.fetch(url, chunkFile, min, max, report, progressCh)
			if err != nil {
				errs = append(errs, err)
			}
		}(min, max, int(i))
	}
	wg.Wait()

	if len(errs) > 0 {
		return nil, fmt.Errorf("Errors: \n %s", errs)
	}

	file, err := gf.assembleChunks(destFilePath, chunksDir)
	if err != nil {
		return nil, err
	}

	os.RemoveAll(chunksDir)

	// Makes sure to return the file on the correct offset so it can be
	// consumed by users.
	_, err = file.Seek(0, 0)
	if err != nil {
		return nil, err
	}

	return file, err
}

// assembleChunks join all the data pieces together
func (gf *goFetch) assembleChunks(destFile, chunksDir string) (*os.File, error) {
	file, err := os.Create(destFile)
	if err != nil {
		return nil, err
	}

	for i := 0; i < gf.concurrency; i++ {
		chunkFile, err := os.Open(filepath.Join(chunksDir, strconv.Itoa(i)))
		if err != nil {
			return nil, err
		}

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

// fetch downloads files using one unbuffered HTTP connection and supports
// resuming downloads if interrupted.
func (gf *goFetch) fetch(url, destFile string, min, max int64,
	report ProgressReport, progressCh chan<- ProgressReport) error {
	client := new(http.Client)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}

	// In order to resume previous interrupted downloads we need to open the file
	// in append mode.
	file, err := os.OpenFile(destFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0660)
	if err != nil {
		return err
	}
	defer file.Close()

	fi, err := file.Stat()
	if err != nil {
		return err
	}
	currSize := fi.Size()

	// There is nothing to do if file exists and was fully downloaded.
	// We do substraction between max and min to account for the last chunk
	// size, which may be of different size if division between res.ContentLength and config.SizeLimit
	// is not exact.
	if currSize == (max - min) {
		return nil
	}

	// Adjusts min to resume file download from where it was left off.
	if currSize > 0 {
		min = min + currSize
	}

	// Prepares writer to report download progress.
	writer := fetchWriter{
		Writer:         file,
		progressCh:     progressCh,
		progressReport: report,
	}

	brange := fmt.Sprintf("bytes=%d-%d", min, max-1)
	if max == -1 {
		brange = fmt.Sprintf("bytes=%d-", min)
	}

	req.Header.Add("Range", brange)
	res, err := client.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()

	if !strings.HasPrefix(res.Status, "2") {
		return errors.New("HTTP requests returned a non 2xx status code")
	}

	_, err = io.Copy(&writer, res.Body)
	return err
}

// fetchWriter implements a custom io.Writer so we can send granular
// progress reports when streaming down content.
type fetchWriter struct {
	io.Writer
	//progressCh is the channel sent by the user to get download updates.
	progressCh chan<- ProgressReport
	// report is the structure sent through the progress channel.
	progressReport ProgressReport
}

func (fw *fetchWriter) Write(b []byte) (int, error) {
	n, err := fw.Writer.Write(b)

	if fw.progressCh != nil {
		fw.progressReport.WrittenBytes = int64(n)
		fw.progressCh <- fw.progressReport
	}

	return n, err
}
<commit_msg>Reuses HTTP Client instead of creating each time.<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package gofetch

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
)

// ProgressReport represents the current download progress of a given file
type ProgressReport struct {
	URL string
	// Total length in bytes of the file being downloaded
	Total int64
	// Written bytes to disk on a write by write basis. It does not accumulate.
	WrittenBytes int64
}

// goFetch represents an instance of gofetch, holding global configuration options.
type goFetch struct {
	destDir     string
	etag        bool
	concurrency int
	httpClient  *http.Client
}

// Option as explained in http://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html
type Option func(*goFetch)

// DestDir allows you to set the destination directory for the downloaded files.
func DestDir(dir string) Option {
	return func(f *goFetch) {
		f.destDir = dir
	}
}

// Concurrency allows you to set the number of goroutines used to download a specific
// file.
func Concurrency(c int) Option {
	return func(f *goFetch) {
		f.concurrency = c
	}
}

// ETag allows you to disable or enable ETag support, meaning that if an already
// downloaded file is currently on disk and matches the ETag returned by the server,
// it will not be downloaded again.
func ETag(enable bool) Option {
	return func(f *goFetch) {
		f.etag = enable
	}
}

// New creates a new instance of goFetch with the given options.
func New(opts ...Option) *goFetch {
	// Creates instance and assigns defaults.
	gofetch := &goFetch{
		concurrency: 1,
		destDir:     "./",
		etag:        true,
		httpClient:  new(http.Client),
	}

	for _, opt := range opts {
		opt(gofetch)
	}
	return gofetch
}

// Fetch downloads content from the provided URL. It supports resuming and
// parallelizing downloads while being very memory efficient.
func (gf *goFetch) Fetch(url string, progressCh chan<- ProgressReport) (*os.File, error) {
	if url == "" {
		return nil, errors.New("URL is required")
	}

	// We need to make a preflight request to get the size of the content.
	res, err := http.Head(url)
	if err != nil {
		return nil, err
	}

	if !strings.HasPrefix(res.Status, "2") {
		return nil, errors.New("HTTP requests returned a non 2xx status code")
	}

	fileName := path.Base(url)

	var etag string
	if gf.etag {
		etag = res.Header.Get("ETag")
		fileName += strings.Trim(etag, `"`)
	}

	destFilePath := filepath.Join(gf.destDir, fileName)

	fi, err := os.Stat(destFilePath)
	if err == nil && fi.Size() == res.ContentLength {
		if progressCh != nil {
			close(progressCh)
		}
		return os.Open(destFilePath)
	}

	return gf.parallelFetch(url, destFilePath, res.ContentLength, progressCh)
}

// parallelFetch fetches using multiple goroutines, each piece is streamed down
// to disk which makes it very efficient in terms of memory usage.
func (gf *goFetch) parallelFetch(url, destFilePath string, length int64, progressCh chan<- ProgressReport) (*os.File, error) {
	if progressCh != nil {
		defer close(progressCh)
	}

	var wg sync.WaitGroup

	report := ProgressReport{Total: length}
	concurrency := int64(gf.concurrency)
	chunkSize := length / concurrency
	remainingSize := length % concurrency
	chunksDir := filepath.Join(gf.destDir, path.Base(url)+".chunks")

	if err := os.MkdirAll(chunksDir, 0760); err != nil {
		return nil, err
	}

	var errs []error
	for i := int64(0); i < concurrency; i++ {
		min := chunkSize * i
		max := chunkSize * (i + 1)

		if i == (concurrency - 1) {
			// Add the remaining bytes in the last request
			max += remainingSize
		}

		wg.Add(1)
		go func(min, max int64, chunkNumber int) {
			defer wg.Done()
			chunkFile := filepath.Join(chunksDir, strconv.Itoa(chunkNumber))

			err := gf.fetch(url, chunkFile, min, max, report, progressCh)
			if err != nil {
				errs = append(errs, err)
			}
		}(min, max, int(i))
	}
	wg.Wait()

	if len(errs) > 0 {
		return nil, fmt.Errorf("Errors: \n %s", errs)
	}

	file, err := gf.assembleChunks(destFilePath, chunksDir)
	if err != nil {
		return nil, err
	}

	os.RemoveAll(chunksDir)

	// Makes sure to return the file on the correct offset so it can be
	// consumed by users.
	_, err = file.Seek(0, 0)
	if err != nil {
		return nil, err
	}

	return file, err
}

// assembleChunks join all the data pieces together
func (gf *goFetch) assembleChunks(destFile, chunksDir string) (*os.File, error) {
	file, err := os.Create(destFile)
	if err != nil {
		return nil, err
	}

	for i := 0; i < gf.concurrency; i++ {
		chunkFile, err := os.Open(filepath.Join(chunksDir, strconv.Itoa(i)))
		if err != nil {
			return nil, err
		}

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

// fetch downloads files using one unbuffered HTTP connection and supports
// resuming downloads if interrupted.
func (gf *goFetch) fetch(url, destFile string, min, max int64,
	report ProgressReport, progressCh chan<- ProgressReport) error {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}

	// In order to resume previous interrupted downloads we need to open the file
	// in append mode.
	file, err := os.OpenFile(destFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0660)
	if err != nil {
		return err
	}
	defer file.Close()

	fi, err := file.Stat()
	if err != nil {
		return err
	}
	currSize := fi.Size()

	// There is nothing to do if file exists and was fully downloaded.
	// We do substraction between max and min to account for the last chunk
	// size, which may be of different size if division between res.ContentLength and config.SizeLimit
	// is not exact.
	if currSize == (max - min) {
		return nil
	}

	// Adjusts min to resume file download from where it was left off.
	if currSize > 0 {
		min = min + currSize
	}

	// Prepares writer to report download progress.
	writer := fetchWriter{
		Writer:         file,
		progressCh:     progressCh,
		progressReport: report,
	}

	brange := fmt.Sprintf("bytes=%d-%d", min, max-1)
	if max == -1 {
		brange = fmt.Sprintf("bytes=%d-", min)
	}

	req.Header.Add("Range", brange)
	res, err := gf.httpClient.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()

	if !strings.HasPrefix(res.Status, "2") {
		return errors.New("HTTP requests returned a non 2xx status code")
	}

	_, err = io.Copy(&writer, res.Body)
	return err
}

// fetchWriter implements a custom io.Writer so we can send granular
// progress reports when streaming down content.
type fetchWriter struct {
	io.Writer
	//progressCh is the channel sent by the user to get download updates.
	progressCh chan<- ProgressReport
	// report is the structure sent through the progress channel.
	progressReport ProgressReport
}

func (fw *fetchWriter) Write(b []byte) (int, error) {
	n, err := fw.Writer.Write(b)

	if fw.progressCh != nil {
		fw.progressReport.WrittenBytes = int64(n)
		fw.progressCh <- fw.progressReport
	}

	return n, err
}
<|endoftext|>


<commit_before>package main

import (
  "net"
  "strconv"
  "fmt"
//  collectd "github.com/paulhammond/gocollectd"
  goopt "github.com/droundy/goopt"
)

var port = goopt.Int([]string{"-p", "--port"}, 8126, "UDP Port to use")

func main() {
  goopt.Description = func() string {
		return "Metric Wrapper for (at first) graphite & elasticsearch."
  }
  goopt.Version = "1.0"
  goopt.Summary = "gostats"
  goopt.Parse(nil)

  addr, _ := net.ResolveUDPAddr("udp", ":" + strconv.Itoa(*port))
  sock, _ := net.ListenUDP("udp", addr)

  i := 0
  for {
    i++
    buf := make([]byte, 1024)
    rlen, _, err := sock.ReadFromUDP(buf)
    if err != nil {
      fmt.Println(err)
    }
    fmt.Println(string(buf[0:rlen]))
    fmt.Println(i)
    //go handlePacket(buf, rlen)
  }
}
<commit_msg>formatted<commit_after>package main

import (
	"fmt"
	"net"
	"strconv"
	//  collectd "github.com/paulhammond/gocollectd"
	goopt "github.com/droundy/goopt"
)

var port = goopt.Int([]string{"-p", "--port"}, 8126, "UDP Port to use")

func main() {
	goopt.Description = func() string {
		return "Metric Wrapper for (at first) graphite & elasticsearch."
	}
	goopt.Version = "1.0"
	goopt.Summary = "gostats"
	goopt.Parse(nil)

	addr, _ := net.ResolveUDPAddr("udp", ":"+strconv.Itoa(*port))
	sock, _ := net.ListenUDP("udp", addr)

	i := 0
	for {
		i++
		buf := make([]byte, 1024)
		rlen, _, err := sock.ReadFromUDP(buf)
		if err != nil {
			fmt.Println(err)
		}
		fmt.Println(string(buf[0:rlen]))
		fmt.Println(i)
		//go handlePacket(buf, rlen)
	}
}
<|endoftext|>


<commit_before>package gpsutil

import (
	"math"
)

type LatLng struct {
	lat float64
	lng float64
}

type GeohashDecoded struct {
	lat float64
	lng float64
	err struct {
		lat float64
		lgn float64
	}
}

type BBox struct {
	Southwest *LatLng
	Northeast *LatLng
	Center    *LatLng
}

func toRad(decDegrees float64) float64 {
	return decDegrees * math.Pi / 180.0
}

func toDegrees(radians float64) float64 {
	return 180.0 * radians / math.Pi
}
<commit_msg>Provide public access for lat and lng<commit_after>package gpsutil

import (
	"math"
)

type LatLng struct {
	lat float64
	lng float64
}

func (latlng *LatLng) Lat() float64 {
	return latlng.lat
}

func (latlng *LatLng) Lng() float64 {
	return latlng.lng
}

type GeohashDecoded struct {
	lat float64
	lng float64
	err struct {
		lat float64
		lgn float64
	}
}

type BBox struct {
	Southwest *LatLng
	Northeast *LatLng
	Center    *LatLng
}

func toRad(decDegrees float64) float64 {
	return decDegrees * math.Pi / 180.0
}

func toDegrees(radians float64) float64 {
	return 180.0 * radians / math.Pi
}
<|endoftext|>


<commit_before>package bytetree

import (
	"testing"
	"time"

	"github.com/getlantern/zenodb/encoding"
	. "github.com/getlantern/zenodb/expr"
	"github.com/stretchr/testify/assert"
)

const ctx = 56

var (
	epoch = time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)
)

func TestByteTree(t *testing.T) {
	resolutionOut := 10 * time.Second
	resolutionIn := 1 * time.Second

	asOf := epoch.Add(-1 * resolutionOut)
	until := epoch

	eOut := ADD(SUM(FIELD("a")), SUM(FIELD("b")))
	eA := SUM(FIELD("a"))
	eB := SUM(FIELD("b"))

	bt := New([]Expr{eOut}, []Expr{eA, eB}, resolutionOut, resolutionIn, asOf, until)
	bt.Update([]byte("test"), []encoding.Sequence{encoding.NewValue(eA, epoch, 1), encoding.NewValue(eB, epoch, 1)}, nil)
	assert.Equal(t, 1, bt.Length())
	bt.Update([]byte("slow"), []encoding.Sequence{encoding.NewValue(eA, epoch, 2), encoding.NewValue(eB, epoch, 2)}, nil)
	assert.Equal(t, 2, bt.Length())
	bt.Update([]byte("water"), []encoding.Sequence{encoding.NewValue(eA, epoch, 3), encoding.NewValue(eB, epoch, 3)}, nil)
	assert.Equal(t, 3, bt.Length())
	bt.Update([]byte("slower"), []encoding.Sequence{encoding.NewValue(eA, epoch, 4), encoding.NewValue(eB, epoch, 4)}, nil)
	assert.Equal(t, 4, bt.Length())
	bt.Update([]byte("team"), []encoding.Sequence{encoding.NewValue(eA, epoch, 5), encoding.NewValue(eB, epoch, 5)}, nil)
	assert.Equal(t, 5, bt.Length())
	bt.Update([]byte("toast"), []encoding.Sequence{encoding.NewValue(eA, epoch, 6), encoding.NewValue(eB, epoch, 6)}, nil)
	assert.Equal(t, 6, bt.Length())

	bt.Update([]byte("test"), []encoding.Sequence{encoding.NewValue(eA, epoch, 10), encoding.NewValue(eB, epoch, 10)}, nil)
	assert.Equal(t, 6, bt.Length())
	bt.Update([]byte("slow"), []encoding.Sequence{encoding.NewValue(eA, epoch, 10), encoding.NewValue(eB, epoch, 10)}, nil)
	assert.Equal(t, 6, bt.Length())
	bt.Update([]byte("water"), []encoding.Sequence{encoding.NewValue(eA, epoch, 10), encoding.NewValue(eB, epoch, 10)}, nil)
	assert.Equal(t, 6, bt.Length())
	bt.Update([]byte("slower"), []encoding.Sequence{encoding.NewValue(eA, epoch, 10), encoding.NewValue(eB, epoch, 10)}, nil)
	assert.Equal(t, 6, bt.Length())
	bt.Update([]byte("team"), []encoding.Sequence{encoding.NewValue(eA, epoch, 10), encoding.NewValue(eB, epoch, 10)}, nil)
	assert.Equal(t, 6, bt.Length())
	bt.Update([]byte("toast"), []encoding.Sequence{encoding.NewValue(eA, epoch, 10), encoding.NewValue(eB, epoch, 10)}, nil)
	assert.Equal(t, 6, bt.Length())

	// This should be ignored because it's outside of the time range
	bt.Update([]byte("test"), []encoding.Sequence{encoding.NewValue(eA, epoch.Add(-1*resolutionOut), 50), encoding.NewValue(eB, epoch.Add(1*resolutionOut), 10)}, nil)

	// Check tree twice with different contexts to make sure removals don't affect
	// other contexts.
	checkTree(ctx, t, bt, eOut)
	checkTree(98, t, bt, eOut)

	// Copy tree and check again
	checkTree(99, t, bt.Copy(), eOut)
}

func checkTree(ctx int64, t *testing.T, bt *Tree, e Expr) {
	walkedValues := 0
	bt.Walk(ctx, func(key []byte, data []encoding.Sequence) bool {
		if assert.Len(t, data, 1) {
			walkedValues++
			val, _ := data[0].ValueAt(0, e)
			switch string(key) {
			case "test":
				assert.EqualValues(t, 22, val, "test")
			case "slow":
				assert.EqualValues(t, 24, val, "slow")
			case "water":
				assert.EqualValues(t, 26, val, "water")
			case "slower":
				assert.EqualValues(t, 28, val, "slower")
			case "team":
				assert.EqualValues(t, 30, val, "team")
			case "toast":
				assert.EqualValues(t, 32, val, "toast")
			default:
				assert.Fail(t, "Unknown key", string(key))
			}
		}
		return true
	})
	assert.Equal(t, 6, walkedValues)

	val, _ := bt.Remove(ctx, []byte("test"))[0].ValueAt(0, e)
	assert.EqualValues(t, 22, val)
	val, _ = bt.Remove(ctx, []byte("slow"))[0].ValueAt(0, e)
	assert.EqualValues(t, 24, val)
	val, _ = bt.Remove(ctx, []byte("water"))[0].ValueAt(0, e)
	assert.EqualValues(t, 26, val)
	val, _ = bt.Remove(ctx, []byte("slower"))[0].ValueAt(0, e)
	assert.EqualValues(t, 28, val)
	val, _ = bt.Remove(ctx, []byte("team"))[0].ValueAt(0, e)
	assert.EqualValues(t, 30, val)
	val, _ = bt.Remove(ctx, []byte("toast"))[0].ValueAt(0, e)
	assert.EqualValues(t, 32, val)
	assert.Nil(t, bt.Remove(ctx, []byte("unknown")))
}
<commit_msg>Fixed bytetree tests<commit_after>package bytetree

import (
	"testing"
	"time"

	"github.com/getlantern/zenodb/encoding"
	. "github.com/getlantern/zenodb/expr"
	"github.com/stretchr/testify/assert"
)

const ctx = 56

var (
	epoch = time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)
)

func TestByteTree(t *testing.T) {
	resolutionOut := 10 * time.Second
	resolutionIn := 1 * time.Second

	asOf := epoch.Add(-1 * resolutionOut)
	until := epoch

	eOut := ADD(SUM(FIELD("a")), SUM(FIELD("b")))
	eA := SUM(FIELD("a"))
	eB := SUM(FIELD("b"))

	bt := New([]Expr{eOut}, []Expr{eA, eB}, resolutionOut, resolutionIn, asOf, until)
	bt.Update([]byte("test"), []encoding.Sequence{encoding.NewFloatValue(eA, epoch, 1), encoding.NewFloatValue(eB, epoch, 1)}, nil)
	assert.Equal(t, 1, bt.Length())
	bt.Update([]byte("slow"), []encoding.Sequence{encoding.NewFloatValue(eA, epoch, 2), encoding.NewFloatValue(eB, epoch, 2)}, nil)
	assert.Equal(t, 2, bt.Length())
	bt.Update([]byte("water"), []encoding.Sequence{encoding.NewFloatValue(eA, epoch, 3), encoding.NewFloatValue(eB, epoch, 3)}, nil)
	assert.Equal(t, 3, bt.Length())
	bt.Update([]byte("slower"), []encoding.Sequence{encoding.NewFloatValue(eA, epoch, 4), encoding.NewFloatValue(eB, epoch, 4)}, nil)
	assert.Equal(t, 4, bt.Length())
	bt.Update([]byte("team"), []encoding.Sequence{encoding.NewFloatValue(eA, epoch, 5), encoding.NewFloatValue(eB, epoch, 5)}, nil)
	assert.Equal(t, 5, bt.Length())
	bt.Update([]byte("toast"), []encoding.Sequence{encoding.NewFloatValue(eA, epoch, 6), encoding.NewFloatValue(eB, epoch, 6)}, nil)
	assert.Equal(t, 6, bt.Length())

	bt.Update([]byte("test"), []encoding.Sequence{encoding.NewFloatValue(eA, epoch, 10), encoding.NewFloatValue(eB, epoch, 10)}, nil)
	assert.Equal(t, 6, bt.Length())
	bt.Update([]byte("slow"), []encoding.Sequence{encoding.NewFloatValue(eA, epoch, 10), encoding.NewFloatValue(eB, epoch, 10)}, nil)
	assert.Equal(t, 6, bt.Length())
	bt.Update([]byte("water"), []encoding.Sequence{encoding.NewFloatValue(eA, epoch, 10), encoding.NewFloatValue(eB, epoch, 10)}, nil)
	assert.Equal(t, 6, bt.Length())
	bt.Update([]byte("slower"), []encoding.Sequence{encoding.NewFloatValue(eA, epoch, 10), encoding.NewFloatValue(eB, epoch, 10)}, nil)
	assert.Equal(t, 6, bt.Length())
	bt.Update([]byte("team"), []encoding.Sequence{encoding.NewFloatValue(eA, epoch, 10), encoding.NewFloatValue(eB, epoch, 10)}, nil)
	assert.Equal(t, 6, bt.Length())
	bt.Update([]byte("toast"), []encoding.Sequence{encoding.NewFloatValue(eA, epoch, 10), encoding.NewFloatValue(eB, epoch, 10)}, nil)
	assert.Equal(t, 6, bt.Length())

	// This should be ignored because it's outside of the time range
	bt.Update([]byte("test"), []encoding.Sequence{encoding.NewFloatValue(eA, epoch.Add(-1*resolutionOut), 50), encoding.NewFloatValue(eB, epoch.Add(1*resolutionOut), 10)}, nil)

	// Check tree twice with different contexts to make sure removals don't affect
	// other contexts.
	checkTree(ctx, t, bt, eOut)
	checkTree(98, t, bt, eOut)

	// Copy tree and check again
	checkTree(99, t, bt.Copy(), eOut)
}

func checkTree(ctx int64, t *testing.T, bt *Tree, e Expr) {
	walkedValues := 0
	bt.Walk(ctx, func(key []byte, data []encoding.Sequence) (bool, bool, error) {
		if assert.Len(t, data, 1) {
			walkedValues++
			val, _ := data[0].ValueAt(0, e)
			switch string(key) {
			case "test":
				assert.EqualValues(t, 22, val, "test")
			case "slow":
				assert.EqualValues(t, 24, val, "slow")
			case "water":
				assert.EqualValues(t, 26, val, "water")
			case "slower":
				assert.EqualValues(t, 28, val, "slower")
			case "team":
				assert.EqualValues(t, 30, val, "team")
			case "toast":
				assert.EqualValues(t, 32, val, "toast")
			default:
				assert.Fail(t, "Unknown key", string(key))
			}
		}
		return true, true, nil
	})
	assert.Equal(t, 6, walkedValues)

	val, _ := bt.Remove(ctx, []byte("test"))[0].ValueAt(0, e)
	assert.EqualValues(t, 22, val)
	val, _ = bt.Remove(ctx, []byte("slow"))[0].ValueAt(0, e)
	assert.EqualValues(t, 24, val)
	val, _ = bt.Remove(ctx, []byte("water"))[0].ValueAt(0, e)
	assert.EqualValues(t, 26, val)
	val, _ = bt.Remove(ctx, []byte("slower"))[0].ValueAt(0, e)
	assert.EqualValues(t, 28, val)
	val, _ = bt.Remove(ctx, []byte("team"))[0].ValueAt(0, e)
	assert.EqualValues(t, 30, val)
	val, _ = bt.Remove(ctx, []byte("toast"))[0].ValueAt(0, e)
	assert.EqualValues(t, 32, val)
	assert.Nil(t, bt.Remove(ctx, []byte("unknown")))
}
<|endoftext|>


<commit_before>package plugin

import (
	"bufio"
	"bytes"
	"errors"
	"io"
	"log"
	"os/exec"
	"strings"
	"time"
)

type client struct {
	cmd *exec.Cmd
	exited bool
}

func NewClient(cmd *exec.Cmd) *client {
	return &client{
		cmd,
		false,
	}
}

func (c *client) Exited() bool {
	return c.exited
}

func (c *client) Start() (address string, err error) {
	env := []string{
		"PACKER_PLUGIN_MIN_PORT=10000",
		"PACKER_PLUGIN_MAX_PORT=25000",
	}

	stdout := new(bytes.Buffer)
	stderr := new(bytes.Buffer)
	c.cmd.Env = append(c.cmd.Env, env...)
	c.cmd.Stderr = stderr
	c.cmd.Stdout = stdout
	err = c.cmd.Start()
	if err != nil {
		return
	}

	// Make sure the command is properly cleaned up if there is an error
	defer func() {
		r := recover()

		if err != nil || r != nil {
			c.cmd.Process.Kill()
		}

		if r != nil {
			panic(r)
		}
	}()

	// Start goroutine to wait for process to exit
	go func() {
		c.cmd.Wait()
		c.exited = true
	}()

	// Start goroutine that logs the stderr
	go c.logStderr(stderr)

	// Some channels for the next step
	timeout := time.After(1 * time.Minute)

	// Start looking for the address
	for done := false; !done; {
		select {
		case <-timeout:
			err = errors.New("timeout while waiting for plugin to start")
			done = true
		default:
		}

		if err == nil && c.Exited() {
			err = errors.New("plugin exited before we could connect")
			done = true
		}

		if line, lerr := stdout.ReadBytes('\n'); lerr == nil {
			// Trim the address and reset the err since we were able
			// to read some sort of address.
			address = strings.TrimSpace(string(line))
			err = nil
			break
		}

		// If error is nil from previously, return now
		if err != nil {
			return
		}

		// Wait a bit
		time.Sleep(10 * time.Millisecond)
	}

	return
}

func (c *client) Kill() {
	c.cmd.Process.Kill()
}

func (c *client) logStderr(r io.Reader) {
	buf := bufio.NewReader(r)

	for done := false; !done; {
		if c.Exited() {
			done = true
		}

		var err error
		for err == nil {
			var line string
			line, err = buf.ReadString('\n')
			if line != "" {
				log.Print(line)
			}
		}

		time.Sleep(10 * time.Millisecond)
	}
}
<commit_msg>packer/plugin: client kill waits for logging to complete<commit_after>package plugin

import (
	"bufio"
	"bytes"
	"errors"
	"io"
	"log"
	"os/exec"
	"strings"
	"time"
)

type client struct {
	cmd *exec.Cmd
	exited bool
	doneLogging bool
}

func NewClient(cmd *exec.Cmd) *client {
	return &client{
		cmd,
		false,
		false,
	}
}

func (c *client) Exited() bool {
	return c.exited
}

func (c *client) Start() (address string, err error) {
	env := []string{
		"PACKER_PLUGIN_MIN_PORT=10000",
		"PACKER_PLUGIN_MAX_PORT=25000",
	}

	stdout := new(bytes.Buffer)
	stderr := new(bytes.Buffer)
	c.cmd.Env = append(c.cmd.Env, env...)
	c.cmd.Stderr = stderr
	c.cmd.Stdout = stdout
	err = c.cmd.Start()
	if err != nil {
		return
	}

	// Make sure the command is properly cleaned up if there is an error
	defer func() {
		r := recover()

		if err != nil || r != nil {
			c.cmd.Process.Kill()
		}

		if r != nil {
			panic(r)
		}
	}()

	// Start goroutine to wait for process to exit
	go func() {
		c.cmd.Wait()
		c.exited = true
	}()

	// Start goroutine that logs the stderr
	go c.logStderr(stderr)

	// Some channels for the next step
	timeout := time.After(1 * time.Minute)

	// Start looking for the address
	for done := false; !done; {
		select {
		case <-timeout:
			err = errors.New("timeout while waiting for plugin to start")
			done = true
		default:
		}

		if err == nil && c.Exited() {
			err = errors.New("plugin exited before we could connect")
			done = true
		}

		if line, lerr := stdout.ReadBytes('\n'); lerr == nil {
			// Trim the address and reset the err since we were able
			// to read some sort of address.
			address = strings.TrimSpace(string(line))
			err = nil
			break
		}

		// If error is nil from previously, return now
		if err != nil {
			return
		}

		// Wait a bit
		time.Sleep(10 * time.Millisecond)
	}

	return
}

func (c *client) Kill() {
	c.cmd.Process.Kill()

	// Wait for the client to finish logging so we have a complete log
	done := make(chan bool)
	go func() {
		for !c.doneLogging {
			time.Sleep(10 * time.Millisecond)
		}

		done <- true
	}()

	<-done
}

func (c *client) logStderr(r io.Reader) {
	buf := bufio.NewReader(r)

	for done := false; !done; {
		if c.Exited() {
			done = true
		}

		var err error
		for err == nil {
			var line string
			line, err = buf.ReadString('\n')
			if line != "" {
				log.Print(line)
			}
		}

		time.Sleep(10 * time.Millisecond)
	}

	// Flag that we've completed logging for others
	c.doneLogging = true
}
<|endoftext|>


<commit_before>package rocserv

import (
	"sync"
	"sync/atomic"
	"time"

	"github.com/shawnfeng/sutil/slog"
)

type ClientPool struct {
	poolClient sync.Map
	poolLen    int
	count      int32
	Factory    func(addr string) rpcClient
}

func NewClientPool(poolLen int, factory func(addr string) rpcClient) *ClientPool {
	return &ClientPool{poolLen: poolLen, Factory: factory, count: 0}
}

func (m *ClientPool) Get(addr string) rpcClient {
	fun := "ClientPool.Get -->"

	po := m.getPool(addr)
	var c rpcClient
	// if pool full, retry get 3 times, each time sleep 500ms
	i := 0
	for i < 3 {
		select {
		case c = <-po:
			slog.Tracef("%s get:%s len:%d", fun, addr, len(po))
			return c
		default:
			if atomic.LoadInt32(&m.count) > int32(m.poolLen) {
				slog.Errorf("get client from addr: %s reach max: %d, retry: %d", addr, m.count, i)
				i++
				time.Sleep(time.Millisecond * 500)
			} else {
				c = m.Factory(addr)
				if c != nil {
					atomic.AddInt32(&m.count, 1)
				}
				return c
			}
		}
	}
	slog.Errorf("get client from addr: %s reach max: %d after retry 3 times", addr, m.count)
	return nil
}

func (m *ClientPool) getPool(addr string) chan rpcClient {
	fun := "ClientPool.getPool -->"

	var tmp chan rpcClient
	value, ok := m.poolClient.Load(addr)
	if ok == true {
		tmp = value.(chan rpcClient)
	} else {
		slog.Infof("%s not found addr:%s", fun, addr)
		tmp = make(chan rpcClient, m.poolLen)
		m.poolClient.Store(addr, tmp)
	}
	return tmp
}

// 连接池链接回收
func (m *ClientPool) Put(addr string, client rpcClient) {
	fun := "ClientPool.Put -->"
	// do nothing
	if client == nil {
		return
	}

	// po 链接池
	po := m.getPool(addr)
	select {

	// 回收连接 client
	case po <- client:
		slog.Tracef("%s payback:%s len:%d", fun, addr, len(po))

	//不能回收了，关闭链接(满了)
	default:
		slog.Infof("%s full not payback:%s len:%d", fun, addr, len(po))
		atomic.AddInt32(&m.count, -1)
		client.Close()
	}
}
<commit_msg>log count<commit_after>package rocserv

import (
	"sync"
	"sync/atomic"
	"time"

	"github.com/shawnfeng/sutil/slog"
)

type ClientPool struct {
	poolClient sync.Map
	poolLen    int
	count      int32
	Factory    func(addr string) rpcClient
}

func NewClientPool(poolLen int, factory func(addr string) rpcClient) *ClientPool {
	return &ClientPool{poolLen: poolLen, Factory: factory, count: 0}
}

func (m *ClientPool) Get(addr string) rpcClient {
	fun := "ClientPool.Get -->"

	po := m.getPool(addr)
	var c rpcClient
	// if pool full, retry get 3 times, each time sleep 500ms
	i := 0
	for i < 3 {
		select {
		case c = <-po:
			slog.Tracef("%s get:%s len:%d, count:%d", fun, addr, len(po), atomic.LoadInt32(&m.count))
			return c
		default:
			if atomic.LoadInt32(&m.count) > int32(m.poolLen) {
				slog.Errorf("get client from addr: %s reach max: %d, retry: %d", addr, m.count, i)
				i++
				time.Sleep(time.Millisecond * 500)
			} else {
				c = m.Factory(addr)
				if c != nil {
					atomic.AddInt32(&m.count, 1)
				}
				return c
			}
		}
	}
	slog.Errorf("get client from addr: %s reach max: %d after retry 3 times", addr, m.count)
	return nil
}

func (m *ClientPool) getPool(addr string) chan rpcClient {
	fun := "ClientPool.getPool -->"

	var tmp chan rpcClient
	value, ok := m.poolClient.Load(addr)
	if ok == true {
		tmp = value.(chan rpcClient)
	} else {
		slog.Infof("%s not found addr:%s", fun, addr)
		tmp = make(chan rpcClient, m.poolLen)
		m.poolClient.Store(addr, tmp)
	}
	return tmp
}

// 连接池链接回收
func (m *ClientPool) Put(addr string, client rpcClient) {
	fun := "ClientPool.Put -->"
	// do nothing
	if client == nil {
		return
	}

	// po 链接池
	po := m.getPool(addr)
	select {

	// 回收连接 client
	case po <- client:
		slog.Tracef("%s payback:%s len:%d, count:%d", fun, addr, len(po), atomic.LoadInt32(&m.count))

	//不能回收了，关闭链接(满了)
	default:
		slog.Infof("%s full not payback:%s len:%d, count:%d", fun, addr, len(po), atomic.LoadInt32(&m.count))
		atomic.AddInt32(&m.count, -1)
		client.Close()
	}
}
<|endoftext|>


<commit_before>//  Copyright 2016 The goscope 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 gui

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

	"github.com/zagrodzki/goscope/scope"
)

type aggrPoint struct {
	sumY  int
	sizeY int
}

func (p *aggrPoint) add(y int) {
	p.sumY += y
	p.sizeY++
}

func (p *aggrPoint) toPoint(x int) image.Point {
	return image.Point{x, p.sumY / p.sizeY}
}

// ZeroAndScale represents the position of zero and the scale of the plot
type ZeroAndScale struct {
	// the position of Y=0 (0 <= Zero <= 1) given as
	// the fraction of the window height counting from the top
	Zero float64
	// scale of the plot in sample units per pixel
	Scale float64
}

func samplesToPoints(samples []scope.Sample, zeroAndScale ZeroAndScale, start, end image.Point) []image.Point {
	if len(samples) == 0 {
		return nil
	}

	sampleMaxY := zeroAndScale.Zero * zeroAndScale.Scale
	sampleMinY := (zeroAndScale.Zero - 1) * zeroAndScale.Scale
	sampleWidthX := float64(len(samples) - 1)
	sampleWidthY := sampleMaxY - sampleMinY

	pixelStartX := float64(start.X)
	pixelEndY := float64(end.Y - 1)
	pixelWidthX := float64(end.X - start.X - 1)
	pixelWidthY := float64(end.Y - start.Y - 1)
	ratioX := pixelWidthX / sampleWidthX
	ratioY := pixelWidthY / sampleWidthY

	points := make([]image.Point, end.Y-start.Y+1)
	lastAggr := aggrPoint{}
	lastX := start.X
	for i, y := range samples {
		mapX := int(pixelStartX + float64(i)*ratioX)
		mapY := int(pixelEndY - float64(y-scope.Sample(sampleMinY))*ratioY)
		if lastX != mapX {
			points = append(points, lastAggr.toPoint(lastX))
			lastX = mapX
			lastAggr = aggrPoint{}
		}
		lastAggr.add(mapY)
	}
	points = append(points, lastAggr.toPoint(lastX))

	return points
}

// Plot represents the entire plotting area.
type Plot struct {
	*image.RGBA
}

var (
	bgCache *image.RGBA
	bgColor color.RGBA
)

func background(r image.Rectangle, col color.RGBA) *image.RGBA {
	img := image.NewRGBA(r)
	pix := img.Pix
	for i := 0; i < len(pix); i = i + 4 {
		pix[i] = col.R
		pix[i+1] = col.G
		pix[i+2] = col.B
		pix[i+3] = col.A
	}
	return img
}

// Fill fills the plot with a background image of the same size.
func (plot Plot) Fill(col color.RGBA) {
	if bgCache == nil || bgCache.Bounds() != plot.Bounds() || bgColor != col {
		bgCache = background(plot.Bounds(), col)
		bgColor = col
	}
	copy(plot.Pix, bgCache.Pix)
}

func isInside(x, y int, start, end image.Point) bool {
	return x >= start.X && x <= end.X && y >= start.Y && y <= end.Y
}

// DrawLine draws a straight line from pixel p1 to p2.
// Only the line fragment inside the image rectangle defined by
// starting (upper left) and ending (lower right) pixel is drawn.
func (plot Plot) DrawLine(p1, p2 image.Point, start, end image.Point, col color.RGBA) {
	if p1.X == p2.X { // vertical line
		for i := min(p1.Y, p2.Y); i <= max(p1.Y, p2.Y); i++ {
			plot.SetRGBA(p1.X, i, col)
		}
		return
	}

	// Calculating the parameters of the equation
	// of the straight line (in the form y=a*x+b)
	// passing through p1 and p2.

	// slope of the line
	a := float64(p1.Y-p2.Y) / float64(p1.X-p2.X)
	// intercept of the line
	b := float64(p1.Y) - float64(p1.X)*a

	// To avoid visual "gaps" between the pixels we switch on,
	// we draw the line in one of two ways.
	if abs(p1.X-p2.X) >= abs(p1.Y-p2.Y) {
		// If the line is more horizontal than vertical,
		// for every pixel column between p1 and p2
		// we find and switch on the pixel closest to y=a*x+b
		for i := min(p1.X, p2.X); i <= max(p1.X, p2.X); i++ {
			y := int(a*float64(i) + b)
			if isInside(i, y, start, end) {
				plot.SetRGBA(i, y, col)
			}
		}
	} else {
		// If the line is more vertical than horizontal,
		// for every pixel row between p1 and p2
		// we find and switch on the pixel closest to y=a*x+b
		for i := min(p1.Y, p2.Y); i <= max(p1.Y, p2.Y); i++ {
			x := int((float64(i) - b) / a)
			if isInside(x, i, start, end) {
				plot.SetRGBA(x, i, col)
			}
		}
	}
}

// DrawSamples draws samples in the image rectangle defined by
// starting (upper left) and ending (lower right) pixel.
func (plot Plot) DrawSamples(samples []scope.Sample, zeroAndScale ZeroAndScale, start, end image.Point, col color.RGBA) {
	points := samplesToPoints(samples, zeroAndScale, start, end)
	for i := 1; i < len(points); i++ {
		plot.DrawLine(points[i-1], points[i], start, end, col)
	}
}

// DrawAll draws samples from all the channels in the plot.
func (plot Plot) DrawAll(samples map[scope.ChanID][]scope.Sample, zas map[scope.ChanID]ZeroAndScale, cols map[scope.ChanID]color.RGBA) {
	plot.Fill(color.RGBA{255, 255, 255, 255})
	b := plot.Bounds()
	for id, v := range samples {
		par, exists := zas[id]
		if !exists {
			par = ZeroAndScale{0.5, 2}
		}
		col, exists := cols[id]
		if !exists {
			col = color.RGBA{0, 0, 0, 255}
		}
		plot.DrawSamples(v, par, b.Min, b.Max, col)
	}
}

// DrawFromDevice draws samples from the device in the plot.
func (plot Plot) DrawFromDevice(dev scope.Device, zas map[scope.ChanID]ZeroAndScale, cols map[scope.ChanID]color.RGBA) error {
	data, stop, err := dev.StartSampling()
	defer stop()
	if err != nil {
		return err
	}
	samples := (<-data).Samples
	plot.DrawAll(samples, zas, cols)
	return nil
}

// CreatePlot plots samples from the device.
func CreatePlot(dev scope.Device, width, height int, zas map[scope.ChanID]ZeroAndScale, cols map[scope.ChanID]color.RGBA) (Plot, error) {
	plot := Plot{image.NewRGBA(image.Rect(0, 0, width, height))}
	err := plot.DrawFromDevice(dev, zas, cols)
	return plot, err
}

// PlotToPng creates a plot of the samples from the device
// and saves it as PNG.
func PlotToPng(dev scope.Device, width, height int, zas map[scope.ChanID]ZeroAndScale, cols map[scope.ChanID]color.RGBA, outputFile string) error {
	plot, err := CreatePlot(dev, width, height, zas, cols)
	if err != nil {
		return err
	}

	f, err := os.Create(outputFile)
	if err != nil {
		return err
	}
	defer f.Close()
	png.Encode(f, plot)
	return nil
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func abs(a int) int {
	if a < 0 {
		return -a
	}
	return a
}
<commit_msg>bugfix: points slice filled from index 0<commit_after>//  Copyright 2016 The goscope 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 gui

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

	"github.com/zagrodzki/goscope/scope"
)

type aggrPoint struct {
	sumY  int
	sizeY int
}

func (p *aggrPoint) add(y int) {
	p.sumY += y
	p.sizeY++
}

func (p *aggrPoint) toPoint(x int) image.Point {
	return image.Point{x, p.sumY / p.sizeY}
}

// ZeroAndScale represents the position of zero and the scale of the plot
type ZeroAndScale struct {
	// the position of Y=0 (0 <= Zero <= 1) given as
	// the fraction of the window height counting from the top
	Zero float64
	// scale of the plot in sample units per pixel
	Scale float64
}

func samplesToPoints(samples []scope.Sample, zeroAndScale ZeroAndScale, start, end image.Point) []image.Point {
	if len(samples) == 0 {
		return nil
	}

	sampleMaxY := zeroAndScale.Zero * zeroAndScale.Scale
	sampleMinY := (zeroAndScale.Zero - 1) * zeroAndScale.Scale
	sampleWidthX := float64(len(samples) - 1)
	sampleWidthY := sampleMaxY - sampleMinY

	pixelStartX := float64(start.X)
	pixelEndY := float64(end.Y - 1)
	pixelWidthX := float64(end.X - start.X - 1)
	pixelWidthY := float64(end.Y - start.Y - 1)
	ratioX := pixelWidthX / sampleWidthX
	ratioY := pixelWidthY / sampleWidthY

	points := make([]image.Point, end.X-start.X)
	lastAggr := aggrPoint{}
	lastX := start.X
	pi := 0
	for i, y := range samples {
		mapX := int(pixelStartX + float64(i)*ratioX)
		mapY := int(pixelEndY - float64(y-scope.Sample(sampleMinY))*ratioY)
		if lastX != mapX {
			points[pi] = lastAggr.toPoint(lastX)
			pi++
			lastX = mapX
			lastAggr = aggrPoint{}
		}
		lastAggr.add(mapY)
	}
	points[pi] = lastAggr.toPoint(lastX)
	pi++

	return points[:pi]
}

// Plot represents the entire plotting area.
type Plot struct {
	*image.RGBA
}

var (
	bgCache *image.RGBA
	bgColor color.RGBA
)

func background(r image.Rectangle, col color.RGBA) *image.RGBA {
	img := image.NewRGBA(r)
	pix := img.Pix
	for i := 0; i < len(pix); i = i + 4 {
		pix[i] = col.R
		pix[i+1] = col.G
		pix[i+2] = col.B
		pix[i+3] = col.A
	}
	return img
}

// Fill fills the plot with a background image of the same size.
func (plot Plot) Fill(col color.RGBA) {
	if bgCache == nil || bgCache.Bounds() != plot.Bounds() || bgColor != col {
		bgCache = background(plot.Bounds(), col)
		bgColor = col
	}
	copy(plot.Pix, bgCache.Pix)
}

func isInside(x, y int, start, end image.Point) bool {
	return x >= start.X && x <= end.X && y >= start.Y && y <= end.Y
}

// DrawLine draws a straight line from pixel p1 to p2.
// Only the line fragment inside the image rectangle defined by
// starting (upper left) and ending (lower right) pixel is drawn.
func (plot Plot) DrawLine(p1, p2 image.Point, start, end image.Point, col color.RGBA) {
	if p1.X == p2.X { // vertical line
		for i := min(p1.Y, p2.Y); i <= max(p1.Y, p2.Y); i++ {
			plot.SetRGBA(p1.X, i, col)
		}
		return
	}

	// Calculating the parameters of the equation
	// of the straight line (in the form y=a*x+b)
	// passing through p1 and p2.

	// slope of the line
	a := float64(p1.Y-p2.Y) / float64(p1.X-p2.X)
	// intercept of the line
	b := float64(p1.Y) - float64(p1.X)*a

	// To avoid visual "gaps" between the pixels we switch on,
	// we draw the line in one of two ways.
	if abs(p1.X-p2.X) >= abs(p1.Y-p2.Y) {
		// If the line is more horizontal than vertical,
		// for every pixel column between p1 and p2
		// we find and switch on the pixel closest to y=a*x+b
		for i := min(p1.X, p2.X); i <= max(p1.X, p2.X); i++ {
			y := int(a*float64(i) + b)
			if isInside(i, y, start, end) {
				plot.SetRGBA(i, y, col)
			}
		}
	} else {
		// If the line is more vertical than horizontal,
		// for every pixel row between p1 and p2
		// we find and switch on the pixel closest to y=a*x+b
		for i := min(p1.Y, p2.Y); i <= max(p1.Y, p2.Y); i++ {
			x := int((float64(i) - b) / a)
			if isInside(x, i, start, end) {
				plot.SetRGBA(x, i, col)
			}
		}
	}
}

// DrawSamples draws samples in the image rectangle defined by
// starting (upper left) and ending (lower right) pixel.
func (plot Plot) DrawSamples(samples []scope.Sample, zeroAndScale ZeroAndScale, start, end image.Point, col color.RGBA) {
	points := samplesToPoints(samples, zeroAndScale, start, end)
	for i := 1; i < len(points); i++ {
		plot.DrawLine(points[i-1], points[i], start, end, col)
	}
}

// DrawAll draws samples from all the channels in the plot.
func (plot Plot) DrawAll(samples map[scope.ChanID][]scope.Sample, zas map[scope.ChanID]ZeroAndScale, cols map[scope.ChanID]color.RGBA) {
	plot.Fill(color.RGBA{255, 255, 255, 255})
	b := plot.Bounds()
	for id, v := range samples {
		par, exists := zas[id]
		if !exists {
			par = ZeroAndScale{0.5, 2}
		}
		col, exists := cols[id]
		if !exists {
			col = color.RGBA{0, 0, 0, 255}
		}
		plot.DrawSamples(v, par, b.Min, b.Max, col)
	}
}

// DrawFromDevice draws samples from the device in the plot.
func (plot Plot) DrawFromDevice(dev scope.Device, zas map[scope.ChanID]ZeroAndScale, cols map[scope.ChanID]color.RGBA) error {
	data, stop, err := dev.StartSampling()
	defer stop()
	if err != nil {
		return err
	}
	samples := (<-data).Samples
	plot.DrawAll(samples, zas, cols)
	return nil
}

// CreatePlot plots samples from the device.
func CreatePlot(dev scope.Device, width, height int, zas map[scope.ChanID]ZeroAndScale, cols map[scope.ChanID]color.RGBA) (Plot, error) {
	plot := Plot{image.NewRGBA(image.Rect(0, 0, width, height))}
	err := plot.DrawFromDevice(dev, zas, cols)
	return plot, err
}

// PlotToPng creates a plot of the samples from the device
// and saves it as PNG.
func PlotToPng(dev scope.Device, width, height int, zas map[scope.ChanID]ZeroAndScale, cols map[scope.ChanID]color.RGBA, outputFile string) error {
	plot, err := CreatePlot(dev, width, height, zas, cols)
	if err != nil {
		return err
	}

	f, err := os.Create(outputFile)
	if err != nil {
		return err
	}
	defer f.Close()
	png.Encode(f, plot)
	return nil
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func abs(a int) int {
	if a < 0 {
		return -a
	}
	return a
}
<|endoftext|>


<commit_before>package gui

import(
  "glop/gin"
  "gl"
)

type Point struct {
  X,Y int
}
func (p Point) Add(q Point) Point {
  return Point{
    X : p.X + q.X,
    Y : p.Y + q.Y,
  }
}
func (p Point) Inside(r Region) bool {
  if p.X < r.X { return false }
  if p.Y < r.Y { return false }
  if p.X > r.X + r.Dx { return false }
  if p.Y > r.Y + r.Dy { return false }
  return true
}
type Dims struct {
  Dx,Dy int
}
type Region struct {
  Point
  Dims
}
func (r Region) Add(p Point) Region {
  return Region{
    r.Point.Add(p),
    r.Dims,
  }
}

// Need a global stack of regions because opengl only handles pushing/popping
// the state of the enable bits for each clip plane, not the planes themselves
var clippers []Region
func (r Region) setClipPlanes() {
  var eqs [][4]float64
  eqs = append(eqs, [4]float64{ 1, 0, 0, -float64(r.X)})
  eqs = append(eqs, [4]float64{-1, 0, 0, float64(r.X + r.Dx)})
  eqs = append(eqs, [4]float64{ 0, 1, 0, -float64(r.Y)})
  eqs = append(eqs, [4]float64{ 0,-1, 0, float64(r.Y + r.Dy)})
  gl.ClipPlane(gl.CLIP_PLANE0, &eqs[0][0])
  gl.ClipPlane(gl.CLIP_PLANE1, &eqs[1][0])
  gl.ClipPlane(gl.CLIP_PLANE2, &eqs[2][0])
  gl.ClipPlane(gl.CLIP_PLANE3, &eqs[3][0])
}
func (r Region) PushClipPlanes() {
  if len(clippers) == 0 {
    gl.Enable(gl.CLIP_PLANE0)
    gl.Enable(gl.CLIP_PLANE1)
    gl.Enable(gl.CLIP_PLANE2)
    gl.Enable(gl.CLIP_PLANE3)
  }
  r.setClipPlanes()
  clippers = append(clippers, r)
}
func (r Region) PopClipPlanes() {
  clippers = clippers[0 : len(clippers) - 1]
  if len(clippers) == 0 {
    gl.Disable(gl.CLIP_PLANE0)
    gl.Disable(gl.CLIP_PLANE1)
    gl.Disable(gl.CLIP_PLANE2)
    gl.Disable(gl.CLIP_PLANE3)
  } else {
    clippers[len(clippers) - 1].setClipPlanes()
  }
}

//func (r Region) setViewport() {
//  gl.Viewport(r.Point.X, r.Point.Y, r.Dims.Dx, r.Dims.Dy)
//}

type Zone interface {
  // Returns the dimensions that this Widget would like available to
  // render itself.  A Widget should only update the value it returns from
  // this method when its Think() method is called.
  Requested() Dims

  // Returns ex,ey, where ex and ey indicate whether this Widget is
  // capable of expanding along the X and Y axes, respectively.
  Expandable() (bool,bool)

  // Returns the region that this Widget used to render itself the last
  // time it was rendered.  Should be completely contained within the
  // region that was passed to it on its last call to Render.
  Rendered() Region
}

type EventGroup struct {
  gin.EventGroup
  Focus bool
}

type Widget interface {
  Zone
  Think(int64)

  // Returns true if this widget or any of its children consumed the
  // event group
  Respond(*Gui,EventGroup) bool

  Draw(Region)
}
type CoreWidget interface {
  DoThink(int64)

  // If take_focus is true, then the EventGroup will be consumed,
  // regardless of the value of consume
  DoRespond(EventGroup) (consume,take_focus bool)
  Zone

  Draw(Region)
  GetChildren() []Widget
}
type EmbeddedWidget interface {
  Think(int64)
  Respond(*Gui, EventGroup) (consume bool)
}
type BasicWidget struct {
  CoreWidget
}
func (w *BasicWidget) Think(t int64) {
  kids := w.GetChildren()
  for i := range kids {
    kids[i].Think(t)
  }
  w.DoThink(t)
}
func (w *BasicWidget) Respond(gui *Gui, event_group EventGroup) bool {
  cursor := event_group.Events[0].Key.Cursor()
  if cursor != nil {
    var p Point
    p.X, p.Y = cursor.Point()
    if !p.Inside(w.Rendered()) {
      return false
    }
  }
  consume,take_focus := w.DoRespond(event_group)
  if take_focus {
    gui.TakeFocus(w)
  }
  if take_focus || consume { return true }
  kids := w.GetChildren()
  for i := range kids {
    if kids[i].Respond(gui, event_group) { return true }
  }
  return false
}

type BasicZone struct {
  Request_dims  Dims
  Render_region Region
  Ex,Ey         bool
}

func (bz *BasicZone) Requested() Dims {
  return bz.Request_dims
}
func (bz *BasicZone) Rendered() Region {
  return bz.Render_region
}
func (bz *BasicZone) Expandable() (bool,bool) {
  return bz.Ex, bz.Ey
}

type NonThinker struct {}
func (n NonThinker) DoThink(int64) {}

type NonResponder struct {}
func (n NonResponder) DoRespond(EventGroup) (bool,bool) {
  return false,false
}

type Childless struct {}
func (c Childless) GetChildren() []Widget { return nil }

type StandardParent struct {
  Children []Widget
}
func (s *StandardParent) GetChildren() []Widget {
  return s.Children
}
func (s *StandardParent) AddChild(w Widget) {
  s.Children = append(s.Children, w)
}
func (s *StandardParent) RemoveChild(w Widget) {
  for i := range s.Children {
    if s.Children[i] == w {
      s.Children[i] = s.Children[len(s.Children)-1]
      s.Children = s.Children[0 : len(s.Children)-1]
      return
    }
  }
}

type rootWidget struct {
  EmbeddedWidget
  StandardParent
  BasicZone
  NonResponder
  NonThinker
}

func (r *rootWidget) Draw(region Region) {
  r.Render_region = region
  for i := range r.Children {
    r.Children[i].Draw(region)
  }
}

type Gui struct {
  root  rootWidget

  // Stack of widgets that have focus
  focus []Widget
}

func Make(dispatcher gin.EventDispatcher, dims Dims) *Gui {
  var g Gui
  g.root.EmbeddedWidget = &BasicWidget{ CoreWidget : &g.root }
  g.root.Request_dims = dims
  g.root.Render_region.Dims = dims
  dispatcher.RegisterEventListener(&g)
  return &g
}

func (g *Gui) Draw() {
  gl.MatrixMode(gl.PROJECTION)
  gl.LoadIdentity();
  region := g.root.Render_region
  gl.Ortho(float64(region.X), float64(region.X + region.Dx), float64(region.Y), float64(region.Y + region.Dy), 1000, -1000)
  gl.ClearColor(0, 0, 0, 1)
  gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
  gl.MatrixMode(gl.MODELVIEW)
  gl.LoadIdentity();
  g.root.Draw(region)
}

// TODO: Shouldn't be exposing this
func (g *Gui) Think(t int64) {
  g.root.Think(t)
}

// TODO: Shouldn't be exposing this
func (g *Gui) HandleEventGroup(gin_group gin.EventGroup) {
  event_group := EventGroup{gin_group, false}
  if len(g.focus) > 0 {
    event_group.Focus = true
    consume := g.focus[len(g.focus)-1].Respond(g, event_group)
    if consume { return }
    event_group.Focus = false
  }
  g.root.Respond(g, event_group)
}

func (g *Gui) AddChild(w Widget) {
  g.root.AddChild(w)
}

func (g *Gui) RemoveChild(w Widget) {
  g.root.RemoveChild(w)
}

func (g *Gui) TakeFocus(w Widget) {
  if len(g.focus) == 0 {
    g.focus = append(g.focus, nil)
  }
  g.focus[len(g.focus)-1] = w
}
<commit_msg>Created a CollapsableZone<commit_after>package gui

import(
  "glop/gin"
  "gl"
)

type Point struct {
  X,Y int
}
func (p Point) Add(q Point) Point {
  return Point{
    X : p.X + q.X,
    Y : p.Y + q.Y,
  }
}
func (p Point) Inside(r Region) bool {
  if p.X < r.X { return false }
  if p.Y < r.Y { return false }
  if p.X > r.X + r.Dx { return false }
  if p.Y > r.Y + r.Dy { return false }
  return true
}
type Dims struct {
  Dx,Dy int
}
type Region struct {
  Point
  Dims
}
func (r Region) Add(p Point) Region {
  return Region{
    r.Point.Add(p),
    r.Dims,
  }
}

// Need a global stack of regions because opengl only handles pushing/popping
// the state of the enable bits for each clip plane, not the planes themselves
var clippers []Region
func (r Region) setClipPlanes() {
  var eqs [][4]float64
  eqs = append(eqs, [4]float64{ 1, 0, 0, -float64(r.X)})
  eqs = append(eqs, [4]float64{-1, 0, 0, float64(r.X + r.Dx)})
  eqs = append(eqs, [4]float64{ 0, 1, 0, -float64(r.Y)})
  eqs = append(eqs, [4]float64{ 0,-1, 0, float64(r.Y + r.Dy)})
  gl.ClipPlane(gl.CLIP_PLANE0, &eqs[0][0])
  gl.ClipPlane(gl.CLIP_PLANE1, &eqs[1][0])
  gl.ClipPlane(gl.CLIP_PLANE2, &eqs[2][0])
  gl.ClipPlane(gl.CLIP_PLANE3, &eqs[3][0])
}
func (r Region) PushClipPlanes() {
  if len(clippers) == 0 {
    gl.Enable(gl.CLIP_PLANE0)
    gl.Enable(gl.CLIP_PLANE1)
    gl.Enable(gl.CLIP_PLANE2)
    gl.Enable(gl.CLIP_PLANE3)
  }
  r.setClipPlanes()
  clippers = append(clippers, r)
}
func (r Region) PopClipPlanes() {
  clippers = clippers[0 : len(clippers) - 1]
  if len(clippers) == 0 {
    gl.Disable(gl.CLIP_PLANE0)
    gl.Disable(gl.CLIP_PLANE1)
    gl.Disable(gl.CLIP_PLANE2)
    gl.Disable(gl.CLIP_PLANE3)
  } else {
    clippers[len(clippers) - 1].setClipPlanes()
  }
}

//func (r Region) setViewport() {
//  gl.Viewport(r.Point.X, r.Point.Y, r.Dims.Dx, r.Dims.Dy)
//}

type Zone interface {
  // Returns the dimensions that this Widget would like available to
  // render itself.  A Widget should only update the value it returns from
  // this method when its Think() method is called.
  Requested() Dims

  // Returns ex,ey, where ex and ey indicate whether this Widget is
  // capable of expanding along the X and Y axes, respectively.
  Expandable() (bool,bool)

  // Returns the region that this Widget used to render itself the last
  // time it was rendered.  Should be completely contained within the
  // region that was passed to it on its last call to Render.
  Rendered() Region
}

type EventGroup struct {
  gin.EventGroup
  Focus bool
}

type Widget interface {
  Zone
  Think(int64)

  // Returns true if this widget or any of its children consumed the
  // event group
  Respond(*Gui,EventGroup) bool

  Draw(Region)
}
type CoreWidget interface {
  DoThink(int64)

  // If take_focus is true, then the EventGroup will be consumed,
  // regardless of the value of consume
  DoRespond(EventGroup) (consume,take_focus bool)
  Zone

  Draw(Region)
  GetChildren() []Widget
}
type EmbeddedWidget interface {
  Think(int64)
  Respond(*Gui, EventGroup) (consume bool)
}
type BasicWidget struct {
  CoreWidget
}
func (w *BasicWidget) Think(t int64) {
  kids := w.GetChildren()
  for i := range kids {
    kids[i].Think(t)
  }
  w.DoThink(t)
}
func (w *BasicWidget) Respond(gui *Gui, event_group EventGroup) bool {
  cursor := event_group.Events[0].Key.Cursor()
  if cursor != nil {
    var p Point
    p.X, p.Y = cursor.Point()
    if !p.Inside(w.Rendered()) {
      return false
    }
  }
  consume,take_focus := w.DoRespond(event_group)
  if take_focus {
    gui.TakeFocus(w)
  }
  if take_focus || consume { return true }
  kids := w.GetChildren()
  for i := range kids {
    if kids[i].Respond(gui, event_group) { return true }
  }
  return false
}

type BasicZone struct {
  Request_dims  Dims
  Render_region Region
  Ex,Ey         bool
}

func (bz BasicZone) Requested() Dims {
  return bz.Request_dims
}
func (bz BasicZone) Rendered() Region {
  return bz.Render_region
}
func (bz BasicZone) Expandable() (bool,bool) {
  return bz.Ex, bz.Ey
}

type CollapsableZone struct {
  Collapsed     bool
  Request_dims  Dims
  Render_region Region
  Ex,Ey         bool
}
func (cz CollapsableZone) Requested() Dims {
  if cz.Collapsed {
    return Dims{}
  }
  return cz.Request_dims
}
func (cz CollapsableZone) Rendered() Region {
  if cz.Collapsed {
    return Region{ Point : cz.Render_region.Point }
  }
  return cz.Render_region
}
func (cz *CollapsableZone) Expandable() (bool,bool) {
  if cz.Collapsed {
    return false, false
  }
  return cz.Ex, cz.Ey
}

type NonThinker struct {}
func (n NonThinker) DoThink(int64) {}

type NonResponder struct {}
func (n NonResponder) DoRespond(EventGroup) (bool,bool) {
  return false,false
}

type Childless struct {}
func (c Childless) GetChildren() []Widget { return nil }

type StandardParent struct {
  Children []Widget
}
func (s *StandardParent) GetChildren() []Widget {
  return s.Children
}
func (s *StandardParent) AddChild(w Widget) {
  s.Children = append(s.Children, w)
}
func (s *StandardParent) RemoveChild(w Widget) {
  for i := range s.Children {
    if s.Children[i] == w {
      s.Children[i] = s.Children[len(s.Children)-1]
      s.Children = s.Children[0 : len(s.Children)-1]
      return
    }
  }
}

type rootWidget struct {
  EmbeddedWidget
  StandardParent
  BasicZone
  NonResponder
  NonThinker
}

func (r *rootWidget) Draw(region Region) {
  r.Render_region = region
  for i := range r.Children {
    r.Children[i].Draw(region)
  }
}

type Gui struct {
  root  rootWidget

  // Stack of widgets that have focus
  focus []Widget
}

func Make(dispatcher gin.EventDispatcher, dims Dims) *Gui {
  var g Gui
  g.root.EmbeddedWidget = &BasicWidget{ CoreWidget : &g.root }
  g.root.Request_dims = dims
  g.root.Render_region.Dims = dims
  dispatcher.RegisterEventListener(&g)
  return &g
}

func (g *Gui) Draw() {
  gl.MatrixMode(gl.PROJECTION)
  gl.LoadIdentity();
  region := g.root.Render_region
  gl.Ortho(float64(region.X), float64(region.X + region.Dx), float64(region.Y), float64(region.Y + region.Dy), 1000, -1000)
  gl.ClearColor(0, 0, 0, 1)
  gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
  gl.MatrixMode(gl.MODELVIEW)
  gl.LoadIdentity();
  g.root.Draw(region)
}

// TODO: Shouldn't be exposing this
func (g *Gui) Think(t int64) {
  g.root.Think(t)
}

// TODO: Shouldn't be exposing this
func (g *Gui) HandleEventGroup(gin_group gin.EventGroup) {
  event_group := EventGroup{gin_group, false}
  if len(g.focus) > 0 {
    event_group.Focus = true
    consume := g.focus[len(g.focus)-1].Respond(g, event_group)
    if consume { return }
    event_group.Focus = false
  }
  g.root.Respond(g, event_group)
}

func (g *Gui) AddChild(w Widget) {
  g.root.AddChild(w)
}

func (g *Gui) RemoveChild(w Widget) {
  g.root.RemoveChild(w)
}

func (g *Gui) TakeFocus(w Widget) {
  if len(g.focus) == 0 {
    g.focus = append(g.focus, nil)
  }
  g.focus[len(g.focus)-1] = w
}
<|endoftext|>


<commit_before>package logging

import (
	"bytes"
	"io"
	"sync"
	"text/template"
	"time"
)

const (
	DefaultTimeLayout = "2006-01-02 15:04:05"
	DefaultFormat     = "[{{.TimeString}}] {{.Level}} {{.Message}}\n"
	DefaultBufSize    = 1024
)

type Handler interface {
	SetBufSize(int)
	SetLevel(LogLevel)
	SetLevelString(string)
	SetLevelRange(LogLevel, LogLevel)
	SetLevelRangeString(string, string)
	SetTimeLayout(string)
	SetFormat(string) error
	SetFilter(func(*Record) bool)
	Emit(Record)
	Panic(bool)
}

type Record struct {
	Time       time.Time
	TimeString string
	Level      LogLevel
	Message    string
}

type BaseHandler struct {
	Mutex      sync.Mutex
	Writer     io.WriteCloser
	Level      LogLevel
	LRange     *LevelRange
	TimeLayout string
	Tmpl       *template.Template
	Buffer     chan *Record
	BufSize    int
	Filter     func(*Record) bool
	Before     func(io.ReadWriter)
	After      func(int64)
	GotError   func(error)
}

func NewBaseHandler(out io.WriteCloser, level LogLevel, layout, format string) (*BaseHandler, error) {
	h := &BaseHandler{
		Writer:     out,
		Level:      level,
		TimeLayout: layout,
	}
	if err := h.SetFormat(format); err != nil {
		return nil, err
	}
	h.Panic(false)
	h.BufSize = DefaultBufSize
	h.Buffer = make(chan *Record, h.BufSize)
	go h.WriteRecord()
	return h, nil
}

func (h *BaseHandler) SetBufSize(size int) {
	h.BufSize = size
	close(h.Buffer)
}

func (h *BaseHandler) SetLevel(level LogLevel) {
	h.Mutex.Lock()
	defer h.Mutex.Unlock()
	h.Level = level
}

func (h *BaseHandler) SetLevelString(s string) {
	h.SetLevel(StringToLogLevel(s))
}

func (h *BaseHandler) SetLevelRange(min_level, max_level LogLevel) {
	h.Mutex.Lock()
	defer h.Mutex.Unlock()
	h.LRange = &LevelRange{min_level, max_level}
}

func (h *BaseHandler) SetLevelRangeString(smin, smax string) {
	h.SetLevelRange(StringToLogLevel(smin), StringToLogLevel(smax))
}

func (h *BaseHandler) SetTimeLayout(layout string) {
	h.Mutex.Lock()
	defer h.Mutex.Unlock()
	h.TimeLayout = layout
}

func (h *BaseHandler) SetFormat(format string) error {
	h.Mutex.Lock()
	defer h.Mutex.Unlock()
	tmpl, err := template.New("tmpl").Parse(format)
	if err != nil {
		return err
	}
	h.Tmpl = tmpl
	return nil
}

func (h *BaseHandler) SetFilter(f func(*Record) bool) {
	h.Mutex.Lock()
	defer h.Mutex.Unlock()
	h.Filter = f
}

func (h *BaseHandler) Emit(rd Record) {
	if h.LRange != nil {
		if !h.LRange.Contain(rd.Level) {
			return
		}
	} else if h.Level > rd.Level {
		return
	}
	h.Buffer <- &rd
}

func (h *BaseHandler) PanicError(err error) {
	if err != nil {
		panic(err)
	}
}

func (h *BaseHandler) IgnoreError(error) {
}

func (h *BaseHandler) Panic(b bool) {
	if b {
		h.GotError = h.PanicError
	} else {
		h.GotError = h.IgnoreError
	}
}

func (h *BaseHandler) WriteRecord() {
	rd := &Record{}
	buf := bytes.NewBuffer(nil)
	for {
		rd = <-h.Buffer
		if rd == nil {
			h.Buffer = make(chan *Record, h.BufSize)
			go h.WriteRecord()
			break
		}
		if h.Filter != nil && h.Filter(rd) {
			continue
		}
		if h.Writer == nil {
			continue
		}
		buf.Reset()
		rd.TimeString = rd.Time.Format(h.TimeLayout)
		if err := h.Tmpl.Execute(buf, rd); err != nil {
			h.GotError(err)
			continue
		}
		if h.Before != nil {
			h.Before(buf)
		}
		n, err := io.Copy(h.Writer, buf)
		if err != nil {
			h.GotError(err)
		}
		if h.After != nil {
			h.After(int64(n))
		}
	}
}
<commit_msg>update handler set buffer size<commit_after>package logging

import (
	"bytes"
	"io"
	"sync"
	"text/template"
	"time"
)

const (
	DefaultTimeLayout = "2006-01-02 15:04:05"
	DefaultFormat     = "[{{.TimeString}}] {{.Level}} {{.Message}}\n"
	DefaultBufSize    = 1024
)

type Handler interface {
	SetBufSize(int)
	SetLevel(LogLevel)
	SetLevelString(string)
	SetLevelRange(LogLevel, LogLevel)
	SetLevelRangeString(string, string)
	SetTimeLayout(string)
	SetFormat(string) error
	SetFilter(func(*Record) bool)
	Emit(Record)
	Panic(bool)
}

type Record struct {
	Time       time.Time
	TimeString string
	Level      LogLevel
	Message    string
}

type BaseHandler struct {
	Mutex      sync.Mutex
	Writer     io.WriteCloser
	Level      LogLevel
	LRange     *LevelRange
	TimeLayout string
	Tmpl       *template.Template
	Buffer     chan *Record
	BufSize    int
	Filter     func(*Record) bool
	Before     func(io.ReadWriter)
	After      func(int64)
	GotError   func(error)
}

func NewBaseHandler(out io.WriteCloser, level LogLevel, layout, format string) (*BaseHandler, error) {
	h := &BaseHandler{
		Writer:     out,
		Level:      level,
		TimeLayout: layout,
	}
	if err := h.SetFormat(format); err != nil {
		return nil, err
	}
	h.Panic(false)
	h.BufSize = DefaultBufSize
	h.Buffer = make(chan *Record, h.BufSize)
	go h.WriteRecord()
	return h, nil
}

func (h *BaseHandler) SetBufSize(size int) {
	h.Mutex.Lock()
	defer h.Mutex.Unlock()
	h.BufSize = size
	h.Buffer <- nil
}

func (h *BaseHandler) SetLevel(level LogLevel) {
	h.Mutex.Lock()
	defer h.Mutex.Unlock()
	h.Level = level
}

func (h *BaseHandler) SetLevelString(s string) {
	h.SetLevel(StringToLogLevel(s))
}

func (h *BaseHandler) SetLevelRange(min_level, max_level LogLevel) {
	h.Mutex.Lock()
	defer h.Mutex.Unlock()
	h.LRange = &LevelRange{min_level, max_level}
}

func (h *BaseHandler) SetLevelRangeString(smin, smax string) {
	h.SetLevelRange(StringToLogLevel(smin), StringToLogLevel(smax))
}

func (h *BaseHandler) SetTimeLayout(layout string) {
	h.Mutex.Lock()
	defer h.Mutex.Unlock()
	h.TimeLayout = layout
}

func (h *BaseHandler) SetFormat(format string) error {
	h.Mutex.Lock()
	defer h.Mutex.Unlock()
	tmpl, err := template.New("tmpl").Parse(format)
	if err != nil {
		return err
	}
	h.Tmpl = tmpl
	return nil
}

func (h *BaseHandler) SetFilter(f func(*Record) bool) {
	h.Mutex.Lock()
	defer h.Mutex.Unlock()
	h.Filter = f
}

func (h *BaseHandler) Emit(rd Record) {
	if h.LRange != nil {
		if !h.LRange.Contain(rd.Level) {
			return
		}
	} else if h.Level > rd.Level {
		return
	}
	h.Mutex.Lock()
	defer h.Mutex.Unlock()
	h.Buffer <- &rd
}

func (h *BaseHandler) PanicError(err error) {
	if err != nil {
		panic(err)
	}
}

func (h *BaseHandler) IgnoreError(error) {
}

func (h *BaseHandler) Panic(b bool) {
	if b {
		h.GotError = h.PanicError
	} else {
		h.GotError = h.IgnoreError
	}
}

func (h *BaseHandler) upgrade_buffer() {
	h.Mutex.Lock()
	defer h.Mutex.Unlock()
	buffer := make(chan *Record, h.BufSize)
	for {
		remain, ok := <-h.Buffer
		if !ok {
			break
		}
		buffer <- remain
	}
	close(h.Buffer)
	h.Buffer = buffer
}

func (h *BaseHandler) WriteRecord() {
	rd := &Record{}
	buf := bytes.NewBuffer(nil)
	for {
		rd = <-h.Buffer
		if rd == nil {
			h.upgrade_buffer()
			go h.WriteRecord()
			break
		}
		if h.Filter != nil && h.Filter(rd) {
			continue
		}
		if h.Writer == nil {
			continue
		}
		buf.Reset()
		rd.TimeString = rd.Time.Format(h.TimeLayout)
		if err := h.Tmpl.Execute(buf, rd); err != nil {
			h.GotError(err)
			continue
		}
		if h.Before != nil {
			h.Before(buf)
		}
		n, err := io.Copy(h.Writer, buf)
		if err != nil {
			h.GotError(err)
		}
		if h.After != nil {
			h.After(int64(n))
		}
	}
}
<|endoftext|>


<commit_before>package compress

import (
	"compress/gzip"
	"net/http"
	"strings"

	"github.com/volatile/core"
)

// Use tells the core to use this handler.
func Use() {
	core.Use(func(c *core.Context) {
		if strings.Contains(c.Request.Header.Get("Accept-Encoding"), "gzip") && len(c.Request.Header.Get("Sec-WebSocket-Key")) == 0 {
			c.ResponseWriter.Header().Set("Content-Encoding", "gzip")

			gzw := gzip.NewWriter(c.ResponseWriter)
			defer gzw.Close()

			// Pass a new ResponseWriter
			c.NextWriter(core.ResponseWriterBinder{
				Writer:         gzw,
				ResponseWriter: c.ResponseWriter,
				BeforeWrite: func(b []byte) {
					if len(c.ResponseWriter.Header().Get("Content-Type")) == 0 {
						c.ResponseWriter.Header().Set("Content-Type", http.DetectContentType(b))
					}
				},
			})
		} else {
			c.Next()
		}
	})
}
<commit_msg>Use coreutil.SetContentType function<commit_after>package compress

import (
	"compress/gzip"
	"strings"

	"github.com/volatile/core"
	"github.com/volatile/core/coreutil"
)

// Use tells the core to use this handler.
func Use() {
	core.Use(func(c *core.Context) {
		if strings.Contains(c.Request.Header.Get("Accept-Encoding"), "gzip") && len(c.Request.Header.Get("Sec-WebSocket-Key")) == 0 {
			c.ResponseWriter.Header().Set("Content-Encoding", "gzip")

			gzw := gzip.NewWriter(c.ResponseWriter)
			defer gzw.Close()

			// Pass a new ResponseWriter
			c.NextWriter(core.ResponseWriterBinder{
				Writer:         gzw,
				ResponseWriter: c.ResponseWriter,
				BeforeWrite: func(b []byte) {
					coreutil.SetContentType(c.ResponseWriter, b)
				},
			})
		} else {
			c.Next()
		}
	})
}
<|endoftext|>