<commit_before>package aero

import (
	"context"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"sort"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/aerogo/csp"

	"crypto/sha256"

	"encoding/base64"

	"github.com/aerogo/http/client"
	performance "github.com/aerogo/linter-performance"
	"github.com/aerogo/session"
	memstore "github.com/aerogo/session-store-memory"
	"github.com/fatih/color"
	"github.com/julienschmidt/httprouter"
	cache "github.com/patrickmn/go-cache"
)

const (
	gzipCacheDuration = 5 * time.Minute
	gzipCacheCleanup  = 1 * time.Minute
)

// Application represents a single web service.
type Application struct {
	Config                *Configuration
	Sessions              session.Manager
	Security              ApplicationSecurity
	Servers               [2]*http.Server
	Linters               []Linter
	Router                *httprouter.Router
	ContentSecurityPolicy *csp.ContentSecurityPolicy

	root       string
	routeTests map[string][]string
	gzipCache  *cache.Cache
	start      time.Time
	rewrite    func(*RewriteContext)
	middleware []Middleware
	onShutdown []func()

	routes struct {
		GET  []string
		POST []string
	}

	css            string
	cssHash        string
	cssReplacement string
}

// New creates a new application.
func New() *Application {
	app := new(Application)
	app.start = time.Now()
	app.routeTests = make(map[string][]string)
	app.gzipCache = cache.New(gzipCacheDuration, gzipCacheCleanup)
	app.Router = httprouter.New()

	// Default linters
	app.Linters = []Linter{
		performance.New(),
	}

	// Default CSP
	app.ContentSecurityPolicy = csp.New()
	app.ContentSecurityPolicy.SetMap(map[string]string{
		"default-src":  "'none'",
		"img-src":      "https:",
		"media-src":    "https:",
		"script-src":   "'self'",
		"style-src":    "'self'",
		"font-src":     "https:",
		"manifest-src": "'self'",
		"child-src":    "https:",
		"connect-src":  "https: wss:",
	})

	// Configuration
	app.Config = new(Configuration)
	app.Config.Reset()
	app.Load()

	// Default session store: Memory
	app.Sessions.Store = memstore.New()

	return app
}

// Get registers your function to be called when a certain GET path has been requested.
func (app *Application) Get(path string, handle Handle) {
	app.routes.GET = append(app.routes.GET, path)
	app.Router.GET(path, app.createRouteHandler(path, handle))
}

// Post registers your function to be called when a certain POST path has been requested.
func (app *Application) Post(path string, handle Handle) {
	app.routes.POST = append(app.routes.POST, path)
	app.Router.POST(path, app.createRouteHandler(path, handle))
}

// createRouteHandler creates a handler function for httprouter.
func (app *Application) createRouteHandler(path string, handle Handle) httprouter.Handle {
	return func(response http.ResponseWriter, request *http.Request, params httprouter.Params) {
		// Create context.
		ctx := Context{
			App:        app,
			StatusCode: http.StatusOK,
			request:    request,
			response:   response,
			params:     params,
		}

		// The last part of the call chain will send the actual response.
		lastPartOfCallChain := func() {
			data := handle(&ctx)
			ctx.respond(data)
		}

		// Declare the type of generateNext so that we can define it recursively in the next part.
		var generateNext func(index int) func()

		// Create a function that returns a bound function next()
		// which can be used as the 2nd parameter in the call chain.
		generateNext = func(index int) func() {
			if index == len(app.middleware) {
				return lastPartOfCallChain
			}

			return func() {
				app.middleware[index](&ctx, generateNext(index+1))
			}
		}

		generateNext(0)()
	}
}

// Run starts your application.
func (app *Application) Run() {
	app.TestManifest()
	app.TestRoutes()
	app.Listen()
	app.Wait()
	app.Shutdown()
}

// Use adds middleware to your middleware chain.
func (app *Application) Use(middlewares ...Middleware) {
	app.middleware = append(app.middleware, middlewares...)
}

// Load loads the application configuration from config.json.
func (app *Application) Load() {
	var err error
	app.Config, err = LoadConfig("config.json")

	if err != nil {
		color.Red(err.Error())
	}
}

// Listen starts the server.
func (app *Application) Listen() {
	if app.Security.Key != "" && app.Security.Certificate != "" {
		go func() {
			app.serveHTTPS(":" + strconv.Itoa(app.Config.Ports.HTTPS))
		}()

		fmt.Println("Server running on:", color.GreenString("https://localhost:"+strconv.Itoa(app.Config.Ports.HTTPS)))
	} else {
		fmt.Println("Server running on:", color.GreenString("http://localhost:"+strconv.Itoa(app.Config.Ports.HTTP)))
	}

	go func() {
		app.serveHTTP(":" + strconv.Itoa(app.Config.Ports.HTTP))
	}()
}

// Wait will make the process wait until it is killed.
func (app *Application) Wait() {
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
	<-stop
}

// Shutdown will gracefully shut down the server.
func (app *Application) Shutdown() {
	for _, server := range app.Servers {
		if server == nil {
			continue
		}

		server.Shutdown(context.Background())
	}

	for _, callback := range app.onShutdown {
		callback()
	}
}

// OnShutdown registers a callback to be executed on server shutdown.
func (app *Application) OnShutdown(callback func()) {
	app.onShutdown = append(app.onShutdown, callback)
}

// Rewrite sets the URL rewrite function.
func (app *Application) Rewrite(rewrite func(*RewriteContext)) {
	app.rewrite = rewrite
}

// SetStyle applies the given CSS code to the application.
func (app *Application) SetStyle(css string) {
	app.css = css

	// Generate a hash
	hash := sha256.Sum256([]byte(css))
	app.cssHash = base64.StdEncoding.EncodeToString(hash[:])

	// Content security policy
	app.ContentSecurityPolicy.Set("style-src", "'sha256-"+app.cssHash+"'")

	// This will be used in the final response later on to inject the CSS code
	app.cssReplacement = "<body"
}

// StartTime returns the time the application started.
func (app *Application) StartTime() time.Time {
	return app.start
}

// Handler returns the request handler used by the application.
func (app *Application) Handler() http.Handler {
	router := app.Router
	rewrite := app.rewrite

	if rewrite != nil {
		return &rewriteHandler{
			rewrite: rewrite,
			router:  router,
		}
	}

	return router
}

// createServer creates an http server instance.
func (app *Application) createServer(address string) *http.Server {
	return &http.Server{
		Addr:              address,
		Handler:           app.Handler(),
		ReadHeaderTimeout: 5 * time.Second,
		WriteTimeout:      15 * time.Second,
		IdleTimeout:       120 * time.Second,
		TLSConfig:         createTLSConfig(),
	}
}

// serveHTTP serves requests from the given listener.
func (app *Application) serveHTTP(address string) {
	server := app.createServer(address)
	app.Servers[0] = server

	// This will block the calling goroutine until the server shuts down.
	serveError := server.ListenAndServe()

	if serveError != nil && strings.Index(serveError.Error(), "closed") == -1 {
		panic(serveError)
	}
}

// serveHTTPS serves requests from the given listener.
func (app *Application) serveHTTPS(address string) {
	server := app.createServer(address)
	app.Servers[1] = server

	// This will block the calling goroutine until the server shuts down.
	serveError := server.ListenAndServeTLS(app.Security.Certificate, app.Security.Key)

	if serveError != nil && strings.Index(serveError.Error(), "closed") == -1 {
		panic(serveError)
	}
}

// Test tests the given URI paths when the application starts.
func (app *Application) Test(route string, paths []string) {
	app.routeTests[route] = paths
}

// TestManifest tests your application's manifest.
func (app *Application) TestManifest() {
	manifest := app.Config.Manifest

	// Warn about short name length (Google Lighthouse)
	// https://developer.chrome.com/apps/manifest/name#short_name
	if len(manifest.ShortName) >= 12 {
		color.Yellow("The short name of your application should have less than 12 characters")
	}
}

// TestRoutes tests your application's routes.
func (app *Application) TestRoutes() {
	fmt.Println(strings.Repeat("-", 80))

	go func() {
		sort.Strings(app.routes.GET)

		for _, route := range app.routes.GET {
			// Skip ajax routes
			if strings.HasPrefix(route, "/_") {
				continue
			}

			// Check if the user defined test routes for the given route
			testRoutes, exists := app.routeTests[route]

			if exists {
				for _, testRoute := range testRoutes {
					app.TestRoute(route, testRoute)
				}

				continue
			}

			// Skip routes with parameters and display a warning to indicate it needs a test route
			if strings.Contains(route, ":") {
				color.Yellow(route)
				continue
			}

			// Test the static route without parameters
			app.TestRoute(route, route)
		}

		// json, _ := Post("https://html5.validator.nu/?out=json").Header("Content-Type", "text/html; charset=utf-8").Header("Content-Encoding", "gzip").Body(body).Send()
		// fmt.Println(json)
	}()
}

// TestRoute tests the given route.
func (app *Application) TestRoute(route string, uri string) {
	for _, linter := range app.Linters {
		linter.Begin(route, uri)
	}

	response, _ := client.Get("http://localhost:" + strconv.Itoa(app.Config.Ports.HTTP) + uri).End()

	for _, linter := range app.Linters {
		linter.End(route, uri, response)
	}
}
<commit_msg>Removed deprecated child-src<commit_after>package aero

import (
	"context"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"sort"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/aerogo/csp"

	"crypto/sha256"

	"encoding/base64"

	"github.com/aerogo/http/client"
	performance "github.com/aerogo/linter-performance"
	"github.com/aerogo/session"
	memstore "github.com/aerogo/session-store-memory"
	"github.com/fatih/color"
	"github.com/julienschmidt/httprouter"
	cache "github.com/patrickmn/go-cache"
)

const (
	gzipCacheDuration = 5 * time.Minute
	gzipCacheCleanup  = 1 * time.Minute
)

// Application represents a single web service.
type Application struct {
	Config                *Configuration
	Sessions              session.Manager
	Security              ApplicationSecurity
	Servers               [2]*http.Server
	Linters               []Linter
	Router                *httprouter.Router
	ContentSecurityPolicy *csp.ContentSecurityPolicy

	root       string
	routeTests map[string][]string
	gzipCache  *cache.Cache
	start      time.Time
	rewrite    func(*RewriteContext)
	middleware []Middleware
	onShutdown []func()

	routes struct {
		GET  []string
		POST []string
	}

	css            string
	cssHash        string
	cssReplacement string
}

// New creates a new application.
func New() *Application {
	app := new(Application)
	app.start = time.Now()
	app.routeTests = make(map[string][]string)
	app.gzipCache = cache.New(gzipCacheDuration, gzipCacheCleanup)
	app.Router = httprouter.New()

	// Default linters
	app.Linters = []Linter{
		performance.New(),
	}

	// Default CSP
	app.ContentSecurityPolicy = csp.New()
	app.ContentSecurityPolicy.SetMap(map[string]string{
		"default-src":  "'none'",
		"img-src":      "https:",
		"media-src":    "https:",
		"script-src":   "'self'",
		"style-src":    "'self'",
		"font-src":     "https:",
		"manifest-src": "'self'",
		"frame-src":    "https:",
		"connect-src":  "https: wss:",
	})

	// Configuration
	app.Config = new(Configuration)
	app.Config.Reset()
	app.Load()

	// Default session store: Memory
	app.Sessions.Store = memstore.New()

	return app
}

// Get registers your function to be called when a certain GET path has been requested.
func (app *Application) Get(path string, handle Handle) {
	app.routes.GET = append(app.routes.GET, path)
	app.Router.GET(path, app.createRouteHandler(path, handle))
}

// Post registers your function to be called when a certain POST path has been requested.
func (app *Application) Post(path string, handle Handle) {
	app.routes.POST = append(app.routes.POST, path)
	app.Router.POST(path, app.createRouteHandler(path, handle))
}

// createRouteHandler creates a handler function for httprouter.
func (app *Application) createRouteHandler(path string, handle Handle) httprouter.Handle {
	return func(response http.ResponseWriter, request *http.Request, params httprouter.Params) {
		// Create context.
		ctx := Context{
			App:        app,
			StatusCode: http.StatusOK,
			request:    request,
			response:   response,
			params:     params,
		}

		// The last part of the call chain will send the actual response.
		lastPartOfCallChain := func() {
			data := handle(&ctx)
			ctx.respond(data)
		}

		// Declare the type of generateNext so that we can define it recursively in the next part.
		var generateNext func(index int) func()

		// Create a function that returns a bound function next()
		// which can be used as the 2nd parameter in the call chain.
		generateNext = func(index int) func() {
			if index == len(app.middleware) {
				return lastPartOfCallChain
			}

			return func() {
				app.middleware[index](&ctx, generateNext(index+1))
			}
		}

		generateNext(0)()
	}
}

// Run starts your application.
func (app *Application) Run() {
	app.TestManifest()
	app.TestRoutes()
	app.Listen()
	app.Wait()
	app.Shutdown()
}

// Use adds middleware to your middleware chain.
func (app *Application) Use(middlewares ...Middleware) {
	app.middleware = append(app.middleware, middlewares...)
}

// Load loads the application configuration from config.json.
func (app *Application) Load() {
	var err error
	app.Config, err = LoadConfig("config.json")

	if err != nil {
		color.Red(err.Error())
	}
}

// Listen starts the server.
func (app *Application) Listen() {
	if app.Security.Key != "" && app.Security.Certificate != "" {
		go func() {
			app.serveHTTPS(":" + strconv.Itoa(app.Config.Ports.HTTPS))
		}()

		fmt.Println("Server running on:", color.GreenString("https://localhost:"+strconv.Itoa(app.Config.Ports.HTTPS)))
	} else {
		fmt.Println("Server running on:", color.GreenString("http://localhost:"+strconv.Itoa(app.Config.Ports.HTTP)))
	}

	go func() {
		app.serveHTTP(":" + strconv.Itoa(app.Config.Ports.HTTP))
	}()
}

// Wait will make the process wait until it is killed.
func (app *Application) Wait() {
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
	<-stop
}

// Shutdown will gracefully shut down the server.
func (app *Application) Shutdown() {
	for _, server := range app.Servers {
		if server == nil {
			continue
		}

		server.Shutdown(context.Background())
	}

	for _, callback := range app.onShutdown {
		callback()
	}
}

// OnShutdown registers a callback to be executed on server shutdown.
func (app *Application) OnShutdown(callback func()) {
	app.onShutdown = append(app.onShutdown, callback)
}

// Rewrite sets the URL rewrite function.
func (app *Application) Rewrite(rewrite func(*RewriteContext)) {
	app.rewrite = rewrite
}

// SetStyle applies the given CSS code to the application.
func (app *Application) SetStyle(css string) {
	app.css = css

	// Generate a hash
	hash := sha256.Sum256([]byte(css))
	app.cssHash = base64.StdEncoding.EncodeToString(hash[:])

	// Content security policy
	app.ContentSecurityPolicy.Set("style-src", "'sha256-"+app.cssHash+"'")

	// This will be used in the final response later on to inject the CSS code
	app.cssReplacement = "<body"
}

// StartTime returns the time the application started.
func (app *Application) StartTime() time.Time {
	return app.start
}

// Handler returns the request handler used by the application.
func (app *Application) Handler() http.Handler {
	router := app.Router
	rewrite := app.rewrite

	if rewrite != nil {
		return &rewriteHandler{
			rewrite: rewrite,
			router:  router,
		}
	}

	return router
}

// createServer creates an http server instance.
func (app *Application) createServer(address string) *http.Server {
	return &http.Server{
		Addr:              address,
		Handler:           app.Handler(),
		ReadHeaderTimeout: 5 * time.Second,
		WriteTimeout:      15 * time.Second,
		IdleTimeout:       120 * time.Second,
		TLSConfig:         createTLSConfig(),
	}
}

// serveHTTP serves requests from the given listener.
func (app *Application) serveHTTP(address string) {
	server := app.createServer(address)
	app.Servers[0] = server

	// This will block the calling goroutine until the server shuts down.
	serveError := server.ListenAndServe()

	if serveError != nil && strings.Index(serveError.Error(), "closed") == -1 {
		panic(serveError)
	}
}

// serveHTTPS serves requests from the given listener.
func (app *Application) serveHTTPS(address string) {
	server := app.createServer(address)
	app.Servers[1] = server

	// This will block the calling goroutine until the server shuts down.
	serveError := server.ListenAndServeTLS(app.Security.Certificate, app.Security.Key)

	if serveError != nil && strings.Index(serveError.Error(), "closed") == -1 {
		panic(serveError)
	}
}

// Test tests the given URI paths when the application starts.
func (app *Application) Test(route string, paths []string) {
	app.routeTests[route] = paths
}

// TestManifest tests your application's manifest.
func (app *Application) TestManifest() {
	manifest := app.Config.Manifest

	// Warn about short name length (Google Lighthouse)
	// https://developer.chrome.com/apps/manifest/name#short_name
	if len(manifest.ShortName) >= 12 {
		color.Yellow("The short name of your application should have less than 12 characters")
	}
}

// TestRoutes tests your application's routes.
func (app *Application) TestRoutes() {
	fmt.Println(strings.Repeat("-", 80))

	go func() {
		sort.Strings(app.routes.GET)

		for _, route := range app.routes.GET {
			// Skip ajax routes
			if strings.HasPrefix(route, "/_") {
				continue
			}

			// Check if the user defined test routes for the given route
			testRoutes, exists := app.routeTests[route]

			if exists {
				for _, testRoute := range testRoutes {
					app.TestRoute(route, testRoute)
				}

				continue
			}

			// Skip routes with parameters and display a warning to indicate it needs a test route
			if strings.Contains(route, ":") {
				color.Yellow(route)
				continue
			}

			// Test the static route without parameters
			app.TestRoute(route, route)
		}

		// json, _ := Post("https://html5.validator.nu/?out=json").Header("Content-Type", "text/html; charset=utf-8").Header("Content-Encoding", "gzip").Body(body).Send()
		// fmt.Println(json)
	}()
}

// TestRoute tests the given route.
func (app *Application) TestRoute(route string, uri string) {
	for _, linter := range app.Linters {
		linter.Begin(route, uri)
	}

	response, _ := client.Get("http://localhost:" + strconv.Itoa(app.Config.Ports.HTTP) + uri).End()

	for _, linter := range app.Linters {
		linter.End(route, uri, response)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package codec

import (
	"bytes"
	"time"

	"github.com/juju/errors"
	mysql "github.com/pingcap/tidb/mysqldef"
)

var (
	// InfiniteValue is the greatest than any other encoded value.
	InfiniteValue = []byte{0xFF, 0xFF}
	// NilValue is the smallest than any other encoded value.
	NilValue = []byte{0x00, 0x00}
	// SmallestNoneNilValue is smaller than any other encoded value except nil value.
	SmallestNoneNilValue = []byte{0x00, 0x01}
)

// TODO: use iota + 1 instead?
const (
	formatNilFlag    = 'n'
	formatIntFlag    = 'd'
	formatUintFlag   = 'u'
	formatFloatFlag  = 'f'
	formatStringFlag = 's'
	formatBytesFlag  = 'b'
	formatDuration   = 't'
)

var sepKey = []byte{0x00, 0x00}

// EncodeKey encodes args to a slice which can be sorted lexicographically later.
// EncodeKey guarantees the encoded slice is in ascending order for comparison.
// TODO: we may add more test to check its valiadation, especially for null type and multi indices.
func EncodeKey(args ...interface{}) ([]byte, error) {
	var b []byte
	format := make([]byte, 0, len(args))
	for _, arg := range args {
		switch v := arg.(type) {
		case bool:
			if v {
				b = EncodeInt(b, int64(1))
			} else {
				b = EncodeInt(b, int64(0))
			}
			format = append(format, formatIntFlag)
		case int:
			b = EncodeInt(b, int64(v))
			format = append(format, formatIntFlag)
		case int8:
			b = EncodeInt(b, int64(v))
			format = append(format, formatIntFlag)
		case int16:
			b = EncodeInt(b, int64(v))
			format = append(format, formatIntFlag)
		case int32:
			b = EncodeInt(b, int64(v))
			format = append(format, formatIntFlag)
		case int64:
			b = EncodeInt(b, int64(v))
			format = append(format, formatIntFlag)
		case uint:
			b = EncodeUint(b, uint64(v))
			format = append(format, formatUintFlag)
		case uint8:
			b = EncodeUint(b, uint64(v))
			format = append(format, formatUintFlag)
		case uint16:
			b = EncodeUint(b, uint64(v))
			format = append(format, formatUintFlag)
		case uint32:
			b = EncodeUint(b, uint64(v))
			format = append(format, formatUintFlag)
		case uint64:
			b = EncodeUint(b, uint64(v))
			format = append(format, formatUintFlag)
		case float32:
			b = EncodeFloat(b, float64(v))
			format = append(format, formatFloatFlag)
		case float64:
			b = EncodeFloat(b, float64(v))
			format = append(format, formatFloatFlag)
		case string:
			b = EncodeBytes(b, []byte(v))
			format = append(format, formatStringFlag)
		case []byte:
			b = EncodeBytes(b, v)
			format = append(format, formatBytesFlag)
		case mysql.Time:
			b = EncodeBytes(b, []byte(v.String()))
			format = append(format, formatStringFlag)
		case mysql.Duration:
			// duration may have negative value, so we cannot use String to encode directly.
			b = EncodeInt(b, int64(v.Duration))
			format = append(format, formatDuration)
		case nil:
			// We will 0x00, 0x00 for nil.
			// The []byte{} will be encoded as 0x00, 0x01.
			// The []byte{0x00} will be encode as 0x00, 0xFF, 0x00, 0x01.
			// And any integer and float encoded values are greater than 0x00, 0x01.
			// So maybe the smallest none null value is []byte{} and we can use it to skip null values.
			b = append(b, sepKey...)
			format = append(format, formatNilFlag)
		default:
			return nil, errors.Errorf("unsupport encode type %T", arg)
		}
	}

	// The comma is the seperator,
	// e.g:		0x00, 0x00
	// We need more tests to check its validation.
	b = append(b, sepKey...)
	b = append(b, format...)
	return b, nil
}

// StripEnd splits a slice b into two substrings separated by sepKey
// and returns a slice byte of the previous substrings.
func StripEnd(b []byte) ([]byte, error) {
	n := bytes.LastIndex(b, sepKey)
	if n == -1 || n+2 >= len(b) {
		// No seperator or no proper format.
		return nil, errors.Errorf("invalid encoded key")
	}

	return b[:n], nil
}

// DecodeKey decodes values from a byte slice generated with EncodeKey before.
func DecodeKey(b []byte) ([]interface{}, error) {
	// At first read the format.
	n := bytes.LastIndex(b, sepKey)
	if n == -1 || n+2 >= len(b) {
		// No seperator or no proper format.
		return nil, errors.Errorf("invalid encoded key")
	}

	format := b[n+2:]
	b = b[0:n]

	v := make([]interface{}, len(format))
	var err error
	for i, flag := range format {
		switch flag {
		case formatIntFlag:
			b, v[i], err = DecodeInt(b)
		case formatUintFlag:
			b, v[i], err = DecodeUint(b)
		case formatFloatFlag:
			b, v[i], err = DecodeFloat(b)
		case formatStringFlag:
			var r []byte
			b, r, err = DecodeBytes(b)
			if err == nil {
				v[i] = string(r)
			}
		case formatBytesFlag:
			b, v[i], err = DecodeBytes(b)
		case formatDuration:
			var r int64
			b, r, err = DecodeInt(b)
			if err == nil {
				// use max fsp, let outer to do round manually.
				v[i] = mysql.Duration{Duration: time.Duration(r), Fsp: mysql.MaxFsp}
			}
		case formatNilFlag:
			if len(b) < 2 || (b[0] != 0x00 && b[1] != 0x00) {
				return nil, errors.Errorf("malformed encoded nil")
			}
			b, v[i] = b[2:], nil
		default:
			return nil, errors.Errorf("invalid encoded key format %v in %s", flag, format)
		}
		if err != nil {
			return nil, errors.Trace(err)
		}
	}
	return v, nil
}
<commit_msg>Address comment<commit_after>// Copyright 2015 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package codec

import (
	"bytes"
	"time"

	"github.com/juju/errors"
	mysql "github.com/pingcap/tidb/mysqldef"
)

var (
	// InfiniteValue is the greatest than any other encoded value.
	InfiniteValue = []byte{0xFF, 0xFF}
	// NilValue is the smallest than any other encoded value.
	NilValue = []byte{0x00, 0x00}
	// SmallestNoneNilValue is smaller than any other encoded value except nil value.
	SmallestNoneNilValue = []byte{0x00, 0x01}
)

// TODO: use iota + 1 instead?
const (
	formatNilFlag      = 'n'
	formatIntFlag      = 'd'
	formatUintFlag     = 'u'
	formatFloatFlag    = 'f'
	formatStringFlag   = 's'
	formatBytesFlag    = 'b'
	formatDurationFlag = 't'
)

var sepKey = []byte{0x00, 0x00}

// EncodeKey encodes args to a slice which can be sorted lexicographically later.
// EncodeKey guarantees the encoded slice is in ascending order for comparison.
// TODO: we may add more test to check its valiadation, especially for null type and multi indices.
func EncodeKey(args ...interface{}) ([]byte, error) {
	var b []byte
	format := make([]byte, 0, len(args))
	for _, arg := range args {
		switch v := arg.(type) {
		case bool:
			if v {
				b = EncodeInt(b, int64(1))
			} else {
				b = EncodeInt(b, int64(0))
			}
			format = append(format, formatIntFlag)
		case int:
			b = EncodeInt(b, int64(v))
			format = append(format, formatIntFlag)
		case int8:
			b = EncodeInt(b, int64(v))
			format = append(format, formatIntFlag)
		case int16:
			b = EncodeInt(b, int64(v))
			format = append(format, formatIntFlag)
		case int32:
			b = EncodeInt(b, int64(v))
			format = append(format, formatIntFlag)
		case int64:
			b = EncodeInt(b, int64(v))
			format = append(format, formatIntFlag)
		case uint:
			b = EncodeUint(b, uint64(v))
			format = append(format, formatUintFlag)
		case uint8:
			b = EncodeUint(b, uint64(v))
			format = append(format, formatUintFlag)
		case uint16:
			b = EncodeUint(b, uint64(v))
			format = append(format, formatUintFlag)
		case uint32:
			b = EncodeUint(b, uint64(v))
			format = append(format, formatUintFlag)
		case uint64:
			b = EncodeUint(b, uint64(v))
			format = append(format, formatUintFlag)
		case float32:
			b = EncodeFloat(b, float64(v))
			format = append(format, formatFloatFlag)
		case float64:
			b = EncodeFloat(b, float64(v))
			format = append(format, formatFloatFlag)
		case string:
			b = EncodeBytes(b, []byte(v))
			format = append(format, formatStringFlag)
		case []byte:
			b = EncodeBytes(b, v)
			format = append(format, formatBytesFlag)
		case mysql.Time:
			b = EncodeBytes(b, []byte(v.String()))
			format = append(format, formatStringFlag)
		case mysql.Duration:
			// duration may have negative value, so we cannot use String to encode directly.
			b = EncodeInt(b, int64(v.Duration))
			format = append(format, formatDurationFlag)
		case nil:
			// We will 0x00, 0x00 for nil.
			// The []byte{} will be encoded as 0x00, 0x01.
			// The []byte{0x00} will be encode as 0x00, 0xFF, 0x00, 0x01.
			// And any integer and float encoded values are greater than 0x00, 0x01.
			// So maybe the smallest none null value is []byte{} and we can use it to skip null values.
			b = append(b, sepKey...)
			format = append(format, formatNilFlag)
		default:
			return nil, errors.Errorf("unsupport encode type %T", arg)
		}
	}

	// The comma is the seperator,
	// e.g:		0x00, 0x00
	// We need more tests to check its validation.
	b = append(b, sepKey...)
	b = append(b, format...)
	return b, nil
}

// StripEnd splits a slice b into two substrings separated by sepKey
// and returns a slice byte of the previous substrings.
func StripEnd(b []byte) ([]byte, error) {
	n := bytes.LastIndex(b, sepKey)
	if n == -1 || n+2 >= len(b) {
		// No seperator or no proper format.
		return nil, errors.Errorf("invalid encoded key")
	}

	return b[:n], nil
}

// DecodeKey decodes values from a byte slice generated with EncodeKey before.
func DecodeKey(b []byte) ([]interface{}, error) {
	// At first read the format.
	n := bytes.LastIndex(b, sepKey)
	if n == -1 || n+2 >= len(b) {
		// No seperator or no proper format.
		return nil, errors.Errorf("invalid encoded key")
	}

	format := b[n+2:]
	b = b[0:n]

	v := make([]interface{}, len(format))
	var err error
	for i, flag := range format {
		switch flag {
		case formatIntFlag:
			b, v[i], err = DecodeInt(b)
		case formatUintFlag:
			b, v[i], err = DecodeUint(b)
		case formatFloatFlag:
			b, v[i], err = DecodeFloat(b)
		case formatStringFlag:
			var r []byte
			b, r, err = DecodeBytes(b)
			if err == nil {
				v[i] = string(r)
			}
		case formatBytesFlag:
			b, v[i], err = DecodeBytes(b)
		case formatDurationFlag:
			var r int64
			b, r, err = DecodeInt(b)
			if err == nil {
				// use max fsp, let outer to do round manually.
				v[i] = mysql.Duration{Duration: time.Duration(r), Fsp: mysql.MaxFsp}
			}
		case formatNilFlag:
			if len(b) < 2 || (b[0] != 0x00 && b[1] != 0x00) {
				return nil, errors.Errorf("malformed encoded nil")
			}
			b, v[i] = b[2:], nil
		default:
			return nil, errors.Errorf("invalid encoded key format %v in %s", flag, format)
		}
		if err != nil {
			return nil, errors.Trace(err)
		}
	}
	return v, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package main

import (
	"fmt"
	"log"
	"os"
	"os/signal"
	"runtime"
	"runtime/pprof"
	"syscall"
	"time"

	"golang.org/x/net/context"

	"github.com/keybase/client/go/client"
	"github.com/keybase/client/go/externals"
	"github.com/keybase/client/go/install"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/logger"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/service"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
)

// Keep this around to simplify things
var G = libkb.G

var cmd libcmdline.Command

type Canceler interface {
	Cancel() error
}

type Stopper interface {
	Stop(exitcode keybase1.ExitCode)
}

func main() {
	err := libkb.SaferDLLLoading()

	f, err := os.Create("/tmp/service.profile")
	if err != nil {
		log.Fatal(err)
	}
	pprof.StartCPUProfile(f)
	defer pprof.StopCPUProfile()

	g := G
	g.Init()

	// Set our panel of external services.
	g.SetServices(externals.GetServices())

	// Don't abort here. This should not happen on any known version of Windows, but
	// new MS platforms may create regressions.
	if err != nil {
		g.Log.Errorf("SaferDLLLoading error: %v", err.Error())
	}

	go HandleSignals()
	err = mainInner(g)

	if g.Env.GetDebug() {
		// hack to wait a little bit to receive all the log messages from the
		// service before shutting down in debug mode.
		time.Sleep(100 * time.Millisecond)
	}

	e2 := g.Shutdown()
	if err == nil {
		err = e2
	}
	if err != nil {
		// Note that logger.Error and logger.Errorf are the same, which causes problems
		// trying to print percent signs, which are used in environment variables
		// in Windows.
		// Had to change from Error to Errorf because of go vet because of:
		// https://github.com/golang/go/issues/6407
		g.Log.Errorf("%s", err.Error())
		if g.ExitCode == keybase1.ExitCode_OK {
			g.ExitCode = keybase1.ExitCode_NOTOK
		}
	}
	if g.ExitCode != keybase1.ExitCode_OK {
		os.Exit(int(g.ExitCode))
	}
}

func warnNonProd(log logger.Logger, e *libkb.Env) {
	mode := e.GetRunMode()
	if mode != libkb.ProductionRunMode {
		log.Warning("Running in %s mode", mode)
	}
}

func checkSystemUser(log logger.Logger) {
	if isAdminUser, match, _ := libkb.IsSystemAdminUser(); isAdminUser {
		log.Errorf("Oops, you are trying to run as an admin user (%s). This isn't supported.", match)
		os.Exit(int(keybase1.ExitCode_NOTOK))
	}
}

func mainInner(g *libkb.GlobalContext) error {
	cl := libcmdline.NewCommandLine(true, client.GetExtraFlags())
	cl.AddCommands(client.GetCommands(cl, g))
	cl.AddCommands(service.GetCommands(cl, g))
	cl.AddHelpTopics(client.GetHelpTopics())

	var err error
	cmd, err = cl.Parse(os.Args)
	if err != nil {
		err = fmt.Errorf("Error parsing command line arguments: %s\n", err)
		return err
	}

	if cmd == nil {
		return nil
	}

	checkSystemUser(g.Log)

	if !cl.IsService() {
		if logger.SaveConsoleMode() == nil {
			defer logger.RestoreConsoleMode()
		}
		client.InitUI()
	}

	if err = g.ConfigureCommand(cl, cmd); err != nil {
		return err
	}
	g.StartupMessage()

	warnNonProd(g.Log, g.Env)

	if err = configureProcesses(g, cl, &cmd); err != nil {
		return err
	}

	err = cmd.Run()
	if !cl.IsService() && !cl.SkipOutOfDateCheck() {
		// Errors that come up in printing this warning are logged but ignored.
		client.PrintOutOfDateWarnings(g)
	}
	return err
}

// AutoFork? Standalone? ClientServer? Brew service?  This function deals with the
// various run configurations that we can run in.
func configureProcesses(g *libkb.GlobalContext, cl *libcmdline.CommandLine, cmd *libcmdline.Command) (err error) {

	g.Log.Debug("+ configureProcesses")
	defer func() {
		g.Log.Debug("- configureProcesses -> %v", err)
	}()

	// On Linux, the service configures its own autostart file. Otherwise, no
	// need to configure if we're a service.
	if cl.IsService() {
		g.Log.Debug("| in configureProcesses, is service")
		if runtime.GOOS == "linux" {
			g.Log.Debug("| calling AutoInstall")
			_, err := install.AutoInstall(g, "", false, 10*time.Second, g.Log)
			if err != nil {
				return err
			}
		}
		return nil
	}

	// Start the server on the other end, possibly.
	// There are two cases in which we do this: (1) we want
	// a local loopback server in standalone mode; (2) we
	// need to "autofork" it. Do at most one of these
	// operations.
	if g.Env.GetStandalone() {
		if cl.IsNoStandalone() {
			err = fmt.Errorf("Can't run command in standalone mode")
			return err
		}
		err := service.NewService(g, false /* isDaemon */).StartLoopbackServer()
		if err != nil {
			if pflerr, ok := err.(libkb.PIDFileLockError); ok {
				err = fmt.Errorf("Can't run in standalone mode with a service running (see %q)",
					pflerr.Filename)
				return err
			}
		}
		return err
	}

	// After this point, we need to provide a remote logging story if necessary

	// If this command specifically asks not to be forked, then we are done in this
	// function. This sort of thing is true for the `ctl` commands and also the `version`
	// command.
	fc := cl.GetForkCmd()
	if fc == libcmdline.NoFork {
		return configureLogging(g, cl)
	}

	var newProc bool
	if libkb.IsBrewBuild {
		// If we're running in Brew mode, we might need to install ourselves as a persistent
		// service for future invocations of the command.
		newProc, err = install.AutoInstall(g, "", false, 10*time.Second, g.Log)
		if err != nil {
			return err
		}
	} else {
		// If this command warrants an autofork, do it now.
		if fc == libcmdline.ForceFork || g.Env.GetAutoFork() {
			newProc, err = client.AutoForkServer(g, cl)
			if err != nil {
				return err
			}
		}
	}

	// Restart the service if we see that it's out of date. It's important to do this
	// before we make any RPCs to the service --- for instance, before the logging
	// calls below. See the v1.0.8 update fiasco for more details. Also, only need
	// to do this if we didn't just start a new process.
	if !newProc {
		if err = client.FixVersionClash(g, cl); err != nil {
			return err
		}
	}

	g.Log.Debug("| After forks; newProc=%v", newProc)
	if err = configureLogging(g, cl); err != nil {
		return err
	}

	// This sends the client's PATH to the service so the service can update
	// its PATH if necessary. This is called after FixVersionClash(), which
	// happens above in configureProcesses().
	if err = configurePath(g, cl); err != nil {
		// Further note -- don't die here.  It could be we're calling this method
		// against an earlier version of the service that doesn't support it.
		// It's not critical that it succeed, so continue on.
		g.Log.Debug("Configure path failed: %v", err)
	}

	return nil
}

func configureLogging(g *libkb.GlobalContext, cl *libcmdline.CommandLine) error {

	g.Log.Debug("+ configureLogging")
	defer func() {
		g.Log.Debug("- configureLogging")
	}()
	// Whether or not we autoforked, we're now running in client-server
	// mode (as opposed to standalone). Register a global LogUI so that
	// calls to G.Log() in the daemon can be copied to us. This is
	// something of a hack on the daemon side.
	if !g.Env.GetDoLogForward() || cl.GetLogForward() == libcmdline.LogForwardNone {
		g.Log.Debug("Disabling log forwarding")
		return nil
	}

	protocols := []rpc.Protocol{client.NewLogUIProtocol()}
	if err := client.RegisterProtocolsWithContext(protocols, g); err != nil {
		return err
	}

	logLevel := keybase1.LogLevel_INFO
	if g.Env.GetDebug() {
		logLevel = keybase1.LogLevel_DEBUG
	}
	logClient, err := client.GetLogClient(g)
	if err != nil {
		return err
	}
	arg := keybase1.RegisterLoggerArg{
		Name:  "CLI client",
		Level: logLevel,
	}
	if err := logClient.RegisterLogger(context.TODO(), arg); err != nil {
		g.Log.Warning("Failed to register as a logger: %s", err)
	}

	return nil
}

// configurePath sends the client's PATH to the service.
func configurePath(g *libkb.GlobalContext, cl *libcmdline.CommandLine) error {
	if cl.IsService() {
		// this only runs on the client
		return nil
	}

	return client.SendPath(g)
}

func HandleSignals() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM, os.Kill)
	for {
		s := <-c
		if s != nil {
			G.Log.Debug("trapped signal %v", s)

			// if the current command has a Stop function, then call it.
			// It will do its own stopping of the process and calling
			// shutdown
			if stop, ok := cmd.(Stopper); ok {
				G.Log.Debug("Stopping command cleanly via stopper")
				stop.Stop(keybase1.ExitCode_OK)
				return
			}

			// if the current command has a Cancel function, then call it:
			if canc, ok := cmd.(Canceler); ok {
				G.Log.Debug("canceling running command")
				if err := canc.Cancel(); err != nil {
					G.Log.Warning("error canceling command: %s", err)
				}
			}

			G.Log.Debug("calling shutdown")
			G.Shutdown()
			G.Log.Error("interrupted")
			os.Exit(3)
		}
	}
}
<commit_msg>Remove cpu profile<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package main

import (
	"fmt"
	"os"
	"os/signal"
	"runtime"
	"syscall"
	"time"

	"golang.org/x/net/context"

	"github.com/keybase/client/go/client"
	"github.com/keybase/client/go/externals"
	"github.com/keybase/client/go/install"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/logger"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/service"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
)

// Keep this around to simplify things
var G = libkb.G

var cmd libcmdline.Command

type Canceler interface {
	Cancel() error
}

type Stopper interface {
	Stop(exitcode keybase1.ExitCode)
}

func main() {
	err := libkb.SaferDLLLoading()

	g := G
	g.Init()

	// Set our panel of external services.
	g.SetServices(externals.GetServices())

	// Don't abort here. This should not happen on any known version of Windows, but
	// new MS platforms may create regressions.
	if err != nil {
		g.Log.Errorf("SaferDLLLoading error: %v", err.Error())
	}

	go HandleSignals()
	err = mainInner(g)

	if g.Env.GetDebug() {
		// hack to wait a little bit to receive all the log messages from the
		// service before shutting down in debug mode.
		time.Sleep(100 * time.Millisecond)
	}

	e2 := g.Shutdown()
	if err == nil {
		err = e2
	}
	if err != nil {
		// Note that logger.Error and logger.Errorf are the same, which causes problems
		// trying to print percent signs, which are used in environment variables
		// in Windows.
		// Had to change from Error to Errorf because of go vet because of:
		// https://github.com/golang/go/issues/6407
		g.Log.Errorf("%s", err.Error())
		if g.ExitCode == keybase1.ExitCode_OK {
			g.ExitCode = keybase1.ExitCode_NOTOK
		}
	}
	if g.ExitCode != keybase1.ExitCode_OK {
		os.Exit(int(g.ExitCode))
	}
}

func warnNonProd(log logger.Logger, e *libkb.Env) {
	mode := e.GetRunMode()
	if mode != libkb.ProductionRunMode {
		log.Warning("Running in %s mode", mode)
	}
}

func checkSystemUser(log logger.Logger) {
	if isAdminUser, match, _ := libkb.IsSystemAdminUser(); isAdminUser {
		log.Errorf("Oops, you are trying to run as an admin user (%s). This isn't supported.", match)
		os.Exit(int(keybase1.ExitCode_NOTOK))
	}
}

func mainInner(g *libkb.GlobalContext) error {
	cl := libcmdline.NewCommandLine(true, client.GetExtraFlags())
	cl.AddCommands(client.GetCommands(cl, g))
	cl.AddCommands(service.GetCommands(cl, g))
	cl.AddHelpTopics(client.GetHelpTopics())

	var err error
	cmd, err = cl.Parse(os.Args)
	if err != nil {
		err = fmt.Errorf("Error parsing command line arguments: %s\n", err)
		return err
	}

	if cmd == nil {
		return nil
	}

	checkSystemUser(g.Log)

	if !cl.IsService() {
		if logger.SaveConsoleMode() == nil {
			defer logger.RestoreConsoleMode()
		}
		client.InitUI()
	}

	if err = g.ConfigureCommand(cl, cmd); err != nil {
		return err
	}
	g.StartupMessage()

	warnNonProd(g.Log, g.Env)

	if err = configureProcesses(g, cl, &cmd); err != nil {
		return err
	}

	err = cmd.Run()
	if !cl.IsService() && !cl.SkipOutOfDateCheck() {
		// Errors that come up in printing this warning are logged but ignored.
		client.PrintOutOfDateWarnings(g)
	}
	return err
}

// AutoFork? Standalone? ClientServer? Brew service?  This function deals with the
// various run configurations that we can run in.
func configureProcesses(g *libkb.GlobalContext, cl *libcmdline.CommandLine, cmd *libcmdline.Command) (err error) {

	g.Log.Debug("+ configureProcesses")
	defer func() {
		g.Log.Debug("- configureProcesses -> %v", err)
	}()

	// On Linux, the service configures its own autostart file. Otherwise, no
	// need to configure if we're a service.
	if cl.IsService() {
		g.Log.Debug("| in configureProcesses, is service")
		if runtime.GOOS == "linux" {
			g.Log.Debug("| calling AutoInstall")
			_, err := install.AutoInstall(g, "", false, 10*time.Second, g.Log)
			if err != nil {
				return err
			}
		}
		return nil
	}

	// Start the server on the other end, possibly.
	// There are two cases in which we do this: (1) we want
	// a local loopback server in standalone mode; (2) we
	// need to "autofork" it. Do at most one of these
	// operations.
	if g.Env.GetStandalone() {
		if cl.IsNoStandalone() {
			err = fmt.Errorf("Can't run command in standalone mode")
			return err
		}
		err := service.NewService(g, false /* isDaemon */).StartLoopbackServer()
		if err != nil {
			if pflerr, ok := err.(libkb.PIDFileLockError); ok {
				err = fmt.Errorf("Can't run in standalone mode with a service running (see %q)",
					pflerr.Filename)
				return err
			}
		}
		return err
	}

	// After this point, we need to provide a remote logging story if necessary

	// If this command specifically asks not to be forked, then we are done in this
	// function. This sort of thing is true for the `ctl` commands and also the `version`
	// command.
	fc := cl.GetForkCmd()
	if fc == libcmdline.NoFork {
		return configureLogging(g, cl)
	}

	var newProc bool
	if libkb.IsBrewBuild {
		// If we're running in Brew mode, we might need to install ourselves as a persistent
		// service for future invocations of the command.
		newProc, err = install.AutoInstall(g, "", false, 10*time.Second, g.Log)
		if err != nil {
			return err
		}
	} else {
		// If this command warrants an autofork, do it now.
		if fc == libcmdline.ForceFork || g.Env.GetAutoFork() {
			newProc, err = client.AutoForkServer(g, cl)
			if err != nil {
				return err
			}
		}
	}

	// Restart the service if we see that it's out of date. It's important to do this
	// before we make any RPCs to the service --- for instance, before the logging
	// calls below. See the v1.0.8 update fiasco for more details. Also, only need
	// to do this if we didn't just start a new process.
	if !newProc {
		if err = client.FixVersionClash(g, cl); err != nil {
			return err
		}
	}

	g.Log.Debug("| After forks; newProc=%v", newProc)
	if err = configureLogging(g, cl); err != nil {
		return err
	}

	// This sends the client's PATH to the service so the service can update
	// its PATH if necessary. This is called after FixVersionClash(), which
	// happens above in configureProcesses().
	if err = configurePath(g, cl); err != nil {
		// Further note -- don't die here.  It could be we're calling this method
		// against an earlier version of the service that doesn't support it.
		// It's not critical that it succeed, so continue on.
		g.Log.Debug("Configure path failed: %v", err)
	}

	return nil
}

func configureLogging(g *libkb.GlobalContext, cl *libcmdline.CommandLine) error {

	g.Log.Debug("+ configureLogging")
	defer func() {
		g.Log.Debug("- configureLogging")
	}()
	// Whether or not we autoforked, we're now running in client-server
	// mode (as opposed to standalone). Register a global LogUI so that
	// calls to G.Log() in the daemon can be copied to us. This is
	// something of a hack on the daemon side.
	if !g.Env.GetDoLogForward() || cl.GetLogForward() == libcmdline.LogForwardNone {
		g.Log.Debug("Disabling log forwarding")
		return nil
	}

	protocols := []rpc.Protocol{client.NewLogUIProtocol()}
	if err := client.RegisterProtocolsWithContext(protocols, g); err != nil {
		return err
	}

	logLevel := keybase1.LogLevel_INFO
	if g.Env.GetDebug() {
		logLevel = keybase1.LogLevel_DEBUG
	}
	logClient, err := client.GetLogClient(g)
	if err != nil {
		return err
	}
	arg := keybase1.RegisterLoggerArg{
		Name:  "CLI client",
		Level: logLevel,
	}
	if err := logClient.RegisterLogger(context.TODO(), arg); err != nil {
		g.Log.Warning("Failed to register as a logger: %s", err)
	}

	return nil
}

// configurePath sends the client's PATH to the service.
func configurePath(g *libkb.GlobalContext, cl *libcmdline.CommandLine) error {
	if cl.IsService() {
		// this only runs on the client
		return nil
	}

	return client.SendPath(g)
}

func HandleSignals() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM, os.Kill)
	for {
		s := <-c
		if s != nil {
			G.Log.Debug("trapped signal %v", s)

			// if the current command has a Stop function, then call it.
			// It will do its own stopping of the process and calling
			// shutdown
			if stop, ok := cmd.(Stopper); ok {
				G.Log.Debug("Stopping command cleanly via stopper")
				stop.Stop(keybase1.ExitCode_OK)
				return
			}

			// if the current command has a Cancel function, then call it:
			if canc, ok := cmd.(Canceler); ok {
				G.Log.Debug("canceling running command")
				if err := canc.Cancel(); err != nil {
					G.Log.Warning("error canceling command: %s", err)
				}
			}

			G.Log.Debug("calling shutdown")
			G.Shutdown()
			G.Log.Error("interrupted")
			os.Exit(3)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package tlf

import (
	"fmt"
	"sort"
	"strings"

	kbname "github.com/keybase/client/go/kbun"
	"github.com/keybase/client/go/protocol/keybase1"
)

const (
	// ReaderSep is the string that separates readers from writers in a
	// TLF name.
	ReaderSep = "#"
)

// SplitExtension separates any extension suffix from the assertions.
func SplitExtension(name string) (
	assertions, extensionSuffix string, err error) {
	names := strings.SplitN(name, HandleExtensionSep, 2)
	if len(names) > 2 {
		return "", "", BadNameError{name}
	}
	if len(names) > 1 {
		extensionSuffix = names[1]
	}
	return names[0], extensionSuffix, nil
}

// SplitName splits a TLF name into components.
func SplitName(name string) (writerNames, readerNames []string,
	extensionSuffix string, err error) {
	assertions, extensionSuffix, err := SplitExtension(name)
	if err != nil {
		return nil, nil, "", err
	}

	splitNames := strings.SplitN(assertions, ReaderSep, 3)
	if len(splitNames) > 2 {
		return nil, nil, "", BadNameError{name}
	}
	writerNames = strings.Split(splitNames[0], ",")
	if len(splitNames) > 1 {
		readerNames = strings.Split(splitNames[1], ",")
	}

	return writerNames, readerNames, extensionSuffix, nil
}

// CanonicalName is a string containing the canonical name of a TLF.
type CanonicalName string

func getSortedNames(
	resolved []kbname.NormalizedUsername,
	unresolved []keybase1.SocialAssertion) []string {
	var names []string
	for _, name := range resolved {
		names = append(names, name.String())
	}
	for _, sa := range unresolved {
		names = append(names, sa.String())
	}
	sort.Strings(names)
	return names
}

func makeCanonicalName(resolvedWriters []kbname.NormalizedUsername,
	unresolvedWriters []keybase1.SocialAssertion,
	resolvedReaders []kbname.NormalizedUsername,
	unresolvedReaders []keybase1.SocialAssertion,
	extensions []HandleExtension, isBackedByTeam bool) CanonicalName {
	writerNames := getSortedNames(resolvedWriters, unresolvedWriters)
	canonicalName := strings.Join(writerNames, ",")
	if len(resolvedReaders)+len(unresolvedReaders) > 0 {
		readerNames := getSortedNames(resolvedReaders, unresolvedReaders)
		canonicalName += ReaderSep + strings.Join(readerNames, ",")
	}

	extensionList := make(HandleExtensionList, len(extensions))
	copy(extensionList, extensions)
	sort.Sort(extensionList)
	if isBackedByTeam {
		canonicalName += extensionList.SuffixForTeamHandle()
	} else {
		canonicalName += extensionList.Suffix()
	}
	return CanonicalName(canonicalName)
}

// MakeCanonicalName makes a CanonicalName from components.
func MakeCanonicalName(resolvedWriters []kbname.NormalizedUsername,
	unresolvedWriters []keybase1.SocialAssertion,
	resolvedReaders []kbname.NormalizedUsername,
	unresolvedReaders []keybase1.SocialAssertion,
	extensions []HandleExtension) CanonicalName {
	return makeCanonicalName(
		resolvedWriters, unresolvedWriters, resolvedReaders, unresolvedReaders,
		extensions, false)
}

// MakeCanonicalNameForTeam makes a CanonicalName from components for a team.
func MakeCanonicalNameForTeam(resolvedWriters []kbname.NormalizedUsername,
	unresolvedWriters []keybase1.SocialAssertion,
	resolvedReaders []kbname.NormalizedUsername,
	unresolvedReaders []keybase1.SocialAssertion,
	extensions []HandleExtension) CanonicalName {
	return makeCanonicalName(
		resolvedWriters, unresolvedWriters, resolvedReaders, unresolvedReaders,
		extensions, true)
}

// PreferredName is a preferred TLF name.
type PreferredName string

func putUserFirst(uname string, users []string) []string {
	for i, w := range users {
		if w == uname {
			if i != 0 {
				copy(users[1:i+1], users[0:i])
				users[0] = w
				return users
			}
		}
	}
	return users
}

// CanonicalToPreferredName returns the preferred TLF name, given a
// canonical name and a username. The username may be empty, and
// results in the canonical name being being returned unmodified.
func CanonicalToPreferredName(username kbname.NormalizedUsername,
	canon CanonicalName) (PreferredName, error) {
	tlfname := string(canon)
	if len(username) == 0 {
		return PreferredName(tlfname), nil
	}
	ws, rs, ext, err := SplitName(tlfname)
	if err != nil {
		return "", err
	}
	if len(ws) == 0 {
		return "", fmt.Errorf("TLF name %q with no writers", tlfname)
	}
	uname := username.String()
	ws = putUserFirst(uname, ws)
	rs = putUserFirst(uname, rs)
	tlfname = strings.Join(ws, ",")
	if len(rs) > 0 {
		tlfname += ReaderSep + strings.Join(rs, ",")
	}
	if len(ext) > 0 {
		tlfname += HandleExtensionSep + ext
	}
	return PreferredName(tlfname), nil
}

// UserIsOnlyWriter returns true if and only if username is the only writer in
// a TLF represented by canon. In any error case, false is returned. This
// function only naively looks at the TLF name, so it should only be used on
// non-team TLFs.
func UserIsOnlyWriter(username kbname.NormalizedUsername, canon CanonicalName) bool {
	tlfname := string(canon)
	if len(username) == 0 {
		return false
	}
	ws, _, _, err := SplitName(tlfname)
	if err != nil {
		return false
	}
	fmt.Printf("SONGGAO %s %s %#+v\n", username, canon, ws)
	return len(ws) == 1 && ws[0] == string(username)
}
<commit_msg>review feedback from jzila<commit_after>// Copyright 2017 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package tlf

import (
	"fmt"
	"sort"
	"strings"

	kbname "github.com/keybase/client/go/kbun"
	"github.com/keybase/client/go/protocol/keybase1"
)

const (
	// ReaderSep is the string that separates readers from writers in a
	// TLF name.
	ReaderSep = "#"
)

// SplitExtension separates any extension suffix from the assertions.
func SplitExtension(name string) (
	assertions, extensionSuffix string, err error) {
	names := strings.SplitN(name, HandleExtensionSep, 2)
	if len(names) > 2 {
		return "", "", BadNameError{name}
	}
	if len(names) > 1 {
		extensionSuffix = names[1]
	}
	return names[0], extensionSuffix, nil
}

// SplitName splits a TLF name into components.
func SplitName(name string) (writerNames, readerNames []string,
	extensionSuffix string, err error) {
	assertions, extensionSuffix, err := SplitExtension(name)
	if err != nil {
		return nil, nil, "", err
	}

	splitNames := strings.SplitN(assertions, ReaderSep, 3)
	if len(splitNames) > 2 {
		return nil, nil, "", BadNameError{name}
	}
	writerNames = strings.Split(splitNames[0], ",")
	if len(splitNames) > 1 {
		readerNames = strings.Split(splitNames[1], ",")
	}

	return writerNames, readerNames, extensionSuffix, nil
}

// CanonicalName is a string containing the canonical name of a TLF.
type CanonicalName string

func getSortedNames(
	resolved []kbname.NormalizedUsername,
	unresolved []keybase1.SocialAssertion) []string {
	var names []string
	for _, name := range resolved {
		names = append(names, name.String())
	}
	for _, sa := range unresolved {
		names = append(names, sa.String())
	}
	sort.Strings(names)
	return names
}

func makeCanonicalName(resolvedWriters []kbname.NormalizedUsername,
	unresolvedWriters []keybase1.SocialAssertion,
	resolvedReaders []kbname.NormalizedUsername,
	unresolvedReaders []keybase1.SocialAssertion,
	extensions []HandleExtension, isBackedByTeam bool) CanonicalName {
	writerNames := getSortedNames(resolvedWriters, unresolvedWriters)
	canonicalName := strings.Join(writerNames, ",")
	if len(resolvedReaders)+len(unresolvedReaders) > 0 {
		readerNames := getSortedNames(resolvedReaders, unresolvedReaders)
		canonicalName += ReaderSep + strings.Join(readerNames, ",")
	}

	extensionList := make(HandleExtensionList, len(extensions))
	copy(extensionList, extensions)
	sort.Sort(extensionList)
	if isBackedByTeam {
		canonicalName += extensionList.SuffixForTeamHandle()
	} else {
		canonicalName += extensionList.Suffix()
	}
	return CanonicalName(canonicalName)
}

// MakeCanonicalName makes a CanonicalName from components.
func MakeCanonicalName(resolvedWriters []kbname.NormalizedUsername,
	unresolvedWriters []keybase1.SocialAssertion,
	resolvedReaders []kbname.NormalizedUsername,
	unresolvedReaders []keybase1.SocialAssertion,
	extensions []HandleExtension) CanonicalName {
	return makeCanonicalName(
		resolvedWriters, unresolvedWriters, resolvedReaders, unresolvedReaders,
		extensions, false)
}

// MakeCanonicalNameForTeam makes a CanonicalName from components for a team.
func MakeCanonicalNameForTeam(resolvedWriters []kbname.NormalizedUsername,
	unresolvedWriters []keybase1.SocialAssertion,
	resolvedReaders []kbname.NormalizedUsername,
	unresolvedReaders []keybase1.SocialAssertion,
	extensions []HandleExtension) CanonicalName {
	return makeCanonicalName(
		resolvedWriters, unresolvedWriters, resolvedReaders, unresolvedReaders,
		extensions, true)
}

// PreferredName is a preferred TLF name.
type PreferredName string

func putUserFirst(uname string, users []string) []string {
	for i, w := range users {
		if w == uname {
			if i != 0 {
				copy(users[1:i+1], users[0:i])
				users[0] = w
				return users
			}
		}
	}
	return users
}

// CanonicalToPreferredName returns the preferred TLF name, given a
// canonical name and a username. The username may be empty, and
// results in the canonical name being being returned unmodified.
func CanonicalToPreferredName(username kbname.NormalizedUsername,
	canon CanonicalName) (PreferredName, error) {
	tlfname := string(canon)
	if len(username) == 0 {
		return PreferredName(tlfname), nil
	}
	ws, rs, ext, err := SplitName(tlfname)
	if err != nil {
		return "", err
	}
	if len(ws) == 0 {
		return "", fmt.Errorf("TLF name %q with no writers", tlfname)
	}
	uname := username.String()
	ws = putUserFirst(uname, ws)
	rs = putUserFirst(uname, rs)
	tlfname = strings.Join(ws, ",")
	if len(rs) > 0 {
		tlfname += ReaderSep + strings.Join(rs, ",")
	}
	if len(ext) > 0 {
		tlfname += HandleExtensionSep + ext
	}
	return PreferredName(tlfname), nil
}

// UserIsOnlyWriter returns true if and only if username is the only writer in
// a TLF represented by canon. In any error case, false is returned. This
// function only naively looks at the TLF name, so it should only be used on
// non-team TLFs.
func UserIsOnlyWriter(username kbname.NormalizedUsername, canon CanonicalName) bool {
	tlfname := string(canon)
	if len(username) == 0 {
		return false
	}
	ws, _, _, err := SplitName(tlfname)
	if err != nil {
		return false
	}
	return len(ws) == 1 && ws[0] == string(username)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/dynport/gocli"
	"fmt"
	"os"
)

func main() {
	fmt.Println(gocli.Green("gocli test script"))
	router := gocli.NewRouter(
		map[string]*gocli.Action{
			"container/start": {
				Description: "Start container",
				Usage:       "<container_id>",
				Handler: func(args *gocli.Args) error {
					fmt.Println("ACTION: start container")
					return nil
				},
			},
			"container/stop": {
				Description: "Stop container",
				Usage:       "<container_id>",
				Handler: func(args *gocli.Args) error {
					fmt.Println("ACTION: stop container", args.Args)
					return nil
				},
			},
			"image/list": {
				Description: "List Images",
				Handler: func(args *gocli.Args) error {
					fmt.Println("ACITON: list images")
					return nil
				},
			},
		},
	)
	router.Separator = " "
	router.Handle(os.Args)
}
<commit_msg>Tiny typo fix<commit_after>package main

import (
	"github.com/dynport/gocli"
	"fmt"
	"os"
)

func main() {
	fmt.Println(gocli.Green("gocli test script"))
	router := gocli.NewRouter(
		map[string]*gocli.Action{
			"container/start": {
				Description: "Start container",
				Usage:       "<container_id>",
				Handler: func(args *gocli.Args) error {
					fmt.Println("ACTION: start container")
					return nil
				},
			},
			"container/stop": {
				Description: "Stop container",
				Usage:       "<container_id>",
				Handler: func(args *gocli.Args) error {
					fmt.Println("ACTION: stop container", args.Args)
					return nil
				},
			},
			"image/list": {
				Description: "List Images",
				Handler: func(args *gocli.Args) error {
					fmt.Println("ACTION: list images")
					return nil
				},
			},
		},
	)
	router.Separator = " "
	router.Handle(os.Args)
}
<|endoftext|>


<commit_before>package goreporter

import (
	gi "github.com/onsi/ginkgo"
	gom "github.com/onsi/gomega"
)

var _ = gi.Describe("Goreporter", func() {
	gi.It("Basic creation with invalid queue name", func() {
		url := "fail"
		r, err := NewReporter(&url)
		r.SetTimeout(1)
		gom.Expect(err).ShouldNot(gom.Equal(gom.BeNil()))
	})

	gi.It("Basic new stat with failed flush", func() {
		url := "ipc:///tmp/test"
		r, err := NewReporter(&url)
		r.SetTimeout(1)
		gom.Expect(err).Should(gom.BeNil())
		key := "key"
		r.AddStat(key, 30)
		err = r.Flush()
		gom.Expect(err).ShouldNot(gom.Equal(gom.BeNil()))
	})
})
<commit_msg>test travis<commit_after>package goreporter

import (
	gi "github.com/onsi/ginkgo"
	gom "github.com/onsi/gomega"
)

var _ = gi.Describe("Goreporter", func() {
	gi.It("Basic creation with invalid queue name", func() {
		url := "fail"
		r, err := NewReporter(&url)
		r.SetTimeout(1)
		gom.Expect(err).ShouldNot(gom.Equal(gom.BeNil()))
	})

	gi.It("Basic new stat with failed flush", func() {
		url := "ipc:///tmp/test"
		r, err := NewReporter(&url)
		r.SetTimeout(1)
		gom.Expect(err).Should(gom.BeNil())
		key := "key"
		r.AddStat(key, 30)
		err = r.Flush()
		gom.Expect(err).ShouldNot(gom.Equal(gom.BeNil()))
	})

})
<|endoftext|>


<commit_before>package embd

import (
	"testing"
	"time"
)

type fakeDigitalPin struct {
	id string
	n  int

	drv GPIODriver
}

func (p *fakeDigitalPin) N() int {
	return p.n
}

func (*fakeDigitalPin) SetDirection(dir Direction) error {
	return nil
}

func (*fakeDigitalPin) Read() (int, error) {
	return 0, nil
}

func (*fakeDigitalPin) Write(val int) error {
	return nil
}

func (*fakeDigitalPin) TimePulse(state int) (time.Duration, error) {
	return 0, nil
}

func (*fakeDigitalPin) ActiveLow(b bool) error {
	return nil
}

func (*fakeDigitalPin) PullUp() error {
	return nil
}

func (*fakeDigitalPin) PullDown() error {
	return nil
}

func (p *fakeDigitalPin) Close() error {
	return p.drv.Unregister(p.id)
}

func newFakeDigitalPin(pd *PinDesc, drv GPIODriver) DigitalPin {
	return &fakeDigitalPin{id: pd.ID, n: pd.DigitalLogical, drv: drv}
}

func TestGpioDriverDigitalPin(t *testing.T) {
	tests := []struct {
		key interface{}
		n   int
	}{
		{1, 1},
	}
	pinMap := PinMap{
		&PinDesc{ID: "P1_1", Aliases: []string{"1"}, Caps: CapDigital, DigitalLogical: 1},
	}
	driver := NewGPIODriver(pinMap, newFakeDigitalPin, nil, nil)
	for _, test := range tests {
		pin, err := driver.DigitalPin(test.key)
		if err != nil {
			t.Errorf("Looking up %v: unexpected error: %v", test.key, err)
			continue
		}
		if pin.N() != test.n {
			t.Errorf("Looking up %v: got %v, want %v", test.key, pin.N(), test.n)
		}
	}
}

type fakeAnalogPin struct {
	id string
	n  int

	drv GPIODriver
}

func (p *fakeAnalogPin) N() int {
	return p.n
}

func (*fakeAnalogPin) Read() (int, error) {
	return 0, nil
}

func (*fakeAnalogPin) Write(val int) error {
	return nil
}

func (*fakeAnalogPin) Close() error {
	return nil
}

func newFakeAnalogPin(pd *PinDesc, drv GPIODriver) AnalogPin {
	return &fakeAnalogPin{id: pd.ID, n: pd.AnalogLogical, drv: drv}
}

func TestGpioDriverAnalogPin(t *testing.T) {
	tests := []struct {
		key interface{}
		n   int
	}{
		{1, 1},
	}
	pinMap := PinMap{
		&PinDesc{ID: "P1_1", Aliases: []string{"1"}, Caps: CapAnalog, AnalogLogical: 1},
	}
	driver := NewGPIODriver(pinMap, nil, newFakeAnalogPin, nil)
	for _, test := range tests {
		pin, err := driver.AnalogPin(test.key)
		if err != nil {
			t.Errorf("Looking up %v: unexpected error: %v", test.key, err)
			continue
		}
		if pin.N() != test.n {
			t.Errorf("Looking up %v: got %v, want %v", test.key, pin.N(), test.n)
		}
	}
}

func TestGpioDriverUnavailablePinType(t *testing.T) {
	pinMap := PinMap{
		&PinDesc{ID: "P1_1", Aliases: []string{"1"}, Caps: CapDigital, DigitalLogical: 1},
		&PinDesc{ID: "P1_2", Aliases: []string{"1"}, Caps: CapAnalog, AnalogLogical: 1},
	}
	driver := NewGPIODriver(pinMap, nil, nil, nil)
	_, err := driver.DigitalPin(1)
	if err == nil {
		t.Fatal("Looking up digital pin 1: did not get error")
	}
	expected := "gpio: digital io not supported on this host"
	if err.Error() != expected {
		t.Fatalf("Looking up digital pin 1: got error %q, expected %q", err, expected)
	}
	_, err = driver.AnalogPin(1)
	if err == nil {
		t.Fatal("Looking up analog pin 1: did not get error")
	}
	expected = "gpio: analog io not supported on this host"
	if err.Error() != expected {
		t.Fatalf("Looking up analog pin 1: got error %q, expected %q", err, expected)
	}
}

func TestGpioPinCaching(t *testing.T) {
	pinMap := PinMap{
		&PinDesc{ID: "P1_1", Aliases: []string{"1"}, Caps: CapDigital},
	}
	driver := NewGPIODriver(pinMap, newFakeDigitalPin, nil, nil)
	pin, err := driver.DigitalPin(1)
	if err != nil {
		t.Fatalf("Looking up digital pin 1: got %v", err)
	}
	// Lookup the same pin again
	pin2, err := driver.DigitalPin(1)
	if err != nil {
		t.Fatalf("Looking up digital pin 1: got %v", err)
	}
	if pin != pin2 {
		t.Fatalf("Looking up digital pin 1 for the second time: got %v, want %v", &pin2, &pin)
	}
	// Looking up a closed pin
	pin.Close()
	pin3, err := driver.DigitalPin(1)
	if err != nil {
		t.Fatalf("Looking up digital pin 1: got %v", err)
		return
	}
	if pin == pin3 {
		t.Fatal("Looking up a closed pin, but got the same old instance")
	}
}
<commit_msg>gpio: added missing function in fakeDigitalPin<commit_after>package embd

import (
	"testing"
	"time"
)

type fakeDigitalPin struct {
	id string
	n  int

	drv GPIODriver
}

func (p *fakeDigitalPin) N() int {
	return p.n
}

func (*fakeDigitalPin) SetDirection(dir Direction) error {
	return nil
}

func (*fakeDigitalPin) Read() (int, error) {
	return 0, nil
}

func (*fakeDigitalPin) Write(val int) error {
	return nil
}

func (*fakeDigitalPin) TimePulse(state int) (time.Duration, error) {
	return 0, nil
}

func (*fakeDigitalPin) ActiveLow(b bool) error {
	return nil
}

func (*fakeDigitalPin) PullUp() error {
	return nil
}

func (*fakeDigitalPin) PullDown() error {
	return nil
}

func (p *fakeDigitalPin) Close() error {
	return p.drv.Unregister(p.id)
}

func (p *fakeDigitalPin) Watch(edge Edge, handler func(DigitalPin)) error {
	return nil
}

func (p *fakeDigitalPin) StopWatching() error {
	return nil
}

func newFakeDigitalPin(pd *PinDesc, drv GPIODriver) DigitalPin {
	return &fakeDigitalPin{id: pd.ID, n: pd.DigitalLogical, drv: drv}
}

func TestGpioDriverDigitalPin(t *testing.T) {
	tests := []struct {
		key interface{}
		n   int
	}{
		{1, 1},
	}
	pinMap := PinMap{
		&PinDesc{ID: "P1_1", Aliases: []string{"1"}, Caps: CapDigital, DigitalLogical: 1},
	}
	driver := NewGPIODriver(pinMap, newFakeDigitalPin, nil, nil)
	for _, test := range tests {
		pin, err := driver.DigitalPin(test.key)
		if err != nil {
			t.Errorf("Looking up %v: unexpected error: %v", test.key, err)
			continue
		}
		if pin.N() != test.n {
			t.Errorf("Looking up %v: got %v, want %v", test.key, pin.N(), test.n)
		}
	}
}

type fakeAnalogPin struct {
	id string
	n  int

	drv GPIODriver
}

func (p *fakeAnalogPin) N() int {
	return p.n
}

func (*fakeAnalogPin) Read() (int, error) {
	return 0, nil
}

func (*fakeAnalogPin) Write(val int) error {
	return nil
}

func (*fakeAnalogPin) Close() error {
	return nil
}

func newFakeAnalogPin(pd *PinDesc, drv GPIODriver) AnalogPin {
	return &fakeAnalogPin{id: pd.ID, n: pd.AnalogLogical, drv: drv}
}

func TestGpioDriverAnalogPin(t *testing.T) {
	tests := []struct {
		key interface{}
		n   int
	}{
		{1, 1},
	}
	pinMap := PinMap{
		&PinDesc{ID: "P1_1", Aliases: []string{"1"}, Caps: CapAnalog, AnalogLogical: 1},
	}
	driver := NewGPIODriver(pinMap, nil, newFakeAnalogPin, nil)
	for _, test := range tests {
		pin, err := driver.AnalogPin(test.key)
		if err != nil {
			t.Errorf("Looking up %v: unexpected error: %v", test.key, err)
			continue
		}
		if pin.N() != test.n {
			t.Errorf("Looking up %v: got %v, want %v", test.key, pin.N(), test.n)
		}
	}
}

func TestGpioDriverUnavailablePinType(t *testing.T) {
	pinMap := PinMap{
		&PinDesc{ID: "P1_1", Aliases: []string{"1"}, Caps: CapDigital, DigitalLogical: 1},
		&PinDesc{ID: "P1_2", Aliases: []string{"1"}, Caps: CapAnalog, AnalogLogical: 1},
	}
	driver := NewGPIODriver(pinMap, nil, nil, nil)
	_, err := driver.DigitalPin(1)
	if err == nil {
		t.Fatal("Looking up digital pin 1: did not get error")
	}
	expected := "gpio: digital io not supported on this host"
	if err.Error() != expected {
		t.Fatalf("Looking up digital pin 1: got error %q, expected %q", err, expected)
	}
	_, err = driver.AnalogPin(1)
	if err == nil {
		t.Fatal("Looking up analog pin 1: did not get error")
	}
	expected = "gpio: analog io not supported on this host"
	if err.Error() != expected {
		t.Fatalf("Looking up analog pin 1: got error %q, expected %q", err, expected)
	}
}

func TestGpioPinCaching(t *testing.T) {
	pinMap := PinMap{
		&PinDesc{ID: "P1_1", Aliases: []string{"1"}, Caps: CapDigital},
	}
	driver := NewGPIODriver(pinMap, newFakeDigitalPin, nil, nil)
	pin, err := driver.DigitalPin(1)
	if err != nil {
		t.Fatalf("Looking up digital pin 1: got %v", err)
	}
	// Lookup the same pin again
	pin2, err := driver.DigitalPin(1)
	if err != nil {
		t.Fatalf("Looking up digital pin 1: got %v", err)
	}
	if pin != pin2 {
		t.Fatalf("Looking up digital pin 1 for the second time: got %v, want %v", &pin2, &pin)
	}
	// Looking up a closed pin
	pin.Close()
	pin3, err := driver.DigitalPin(1)
	if err != nil {
		t.Fatalf("Looking up digital pin 1: got %v", err)
		return
	}
	if pin == pin3 {
		t.Fatal("Looking up a closed pin, but got the same old instance")
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 Hajime Hoshi
//
// 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 ebiten

import (
	"math"

	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/graphics/opengl"
	"github.com/hajimehoshi/ebiten/internal/ui"
)

func newGraphicsContext(f func(*Image) error) *graphicsContext {
	return &graphicsContext{
		f: f,
	}
}

type graphicsContext struct {
	f           func(*Image) error
	offscreen   *Image
	offscreen2  *Image // TODO: better name
	screen      *Image
	screenScale float64
	initialized bool
}

func (c *graphicsContext) SetSize(screenWidth, screenHeight int, screenScale float64) error {
	if c.screen != nil {
		c.screen.Dispose()
	}
	if c.offscreen != nil {
		c.offscreen.Dispose()
	}
	offscreen, err := NewImage(screenWidth, screenHeight, FilterNearest)
	if err != nil {
		return err
	}
	offscreen.impl.noSave = true

	intScreenScale := int(math.Ceil(screenScale))
	w := screenWidth * intScreenScale
	h := screenHeight * intScreenScale
	offscreen2, err := NewImage(w, h, FilterLinear)
	if err != nil {
		return err
	}
	offscreen2.impl.noSave = true

	w = int(float64(screenWidth) * screenScale)
	h = int(float64(screenHeight) * screenScale)
	c.screen, err = newImageWithScreenFramebuffer(w, h)
	if err != nil {
		return err
	}
	c.screen.impl.noSave = true
	c.screen.Clear()

	c.offscreen = offscreen
	c.offscreen2 = offscreen2
	c.screenScale = screenScale
	ui.GLContext().ResetViewportSize()
	return nil
}

func (c *graphicsContext) needsRestoring(context *opengl.Context) (bool, error) {
	imageM.Lock()
	defer imageM.Unlock()
	// FlushCommands is required because c.offscreen.impl might not have an actual texture.
	if err := graphics.FlushCommands(ui.GLContext()); err != nil {
		return false, err
	}
	return c.offscreen.impl.isInvalidated(context), nil
}

func (c *graphicsContext) initializeIfNeeded() error {
	// glViewport must be called at every frame on iOS
	ui.GLContext().ResetViewportSize()
	if !c.initialized {
		if err := graphics.Initialize(ui.GLContext()); err != nil {
			return err
		}
		c.initialized = true
	}
	r, err := c.needsRestoring(ui.GLContext())
	if err != nil {
		return err
	}
	if r {
		if err := c.restore(); err != nil {
			return err
		}
	}
	return nil
}

func drawWithFittingScale(dst *Image, src *Image) error {
	wd, hd := dst.Size()
	ws, hs := src.Size()
	sw := float64(wd) / float64(ws)
	sh := float64(hd) / float64(hs)
	op := &DrawImageOptions{}
	op.GeoM.Scale(sw, sh)
	if err := dst.DrawImage(src, op); err != nil {
		return err
	}
	return nil
}

func (c *graphicsContext) drawToDefaultRenderTarget() error {
	if err := c.screen.Clear(); err != nil {
		return err
	}
	if err := drawWithFittingScale(c.screen, c.offscreen2); err != nil {
		return err
	}
	if err := c.flush(); err != nil {
		return err
	}
	return nil
}

func (c *graphicsContext) UpdateAndDraw() error {
	if err := c.initializeIfNeeded(); err != nil {
		return err
	}
	if err := c.offscreen.Clear(); err != nil {
		return err
	}
	if err := c.f(c.offscreen); err != nil {
		return err
	}
	if IsRunningSlowly() {
		return nil
	}
	if err := c.offscreen2.Clear(); err != nil {
		return err
	}
	if err := drawWithFittingScale(c.offscreen2, c.offscreen); err != nil {
		return err
	}
	if err := c.drawToDefaultRenderTarget(); err != nil {
		return err
	}
	if err := theImages.savePixels(ui.GLContext()); err != nil {
		return err
	}
	return nil
}

func (c *graphicsContext) Draw() error {
	if err := c.initializeIfNeeded(); err != nil {
		return err
	}
	if err := c.drawToDefaultRenderTarget(); err != nil {
		return err
	}
	return nil
}

func (c *graphicsContext) flush() error {
	// TODO: imageM is necessary to call graphics functions. Move this to graphics package.
	imageM.Lock()
	defer imageM.Unlock()
	if err := graphics.FlushCommands(ui.GLContext()); err != nil {
		return err
	}
	// Call glFlush to prevent black flicking (especially on Android (#226) and iOS).
	ui.GLContext().Flush()
	return nil
}

func (c *graphicsContext) restore() error {
	imageM.Lock()
	defer imageM.Unlock()
	ui.GLContext().Resume()
	if err := graphics.Initialize(ui.GLContext()); err != nil {
		return err
	}
	if err := theImages.restorePixels(ui.GLContext()); err != nil {
		return err
	}
	return nil
}
<commit_msg>graphics: Reduce calls of ui.GLContext()<commit_after>// Copyright 2014 Hajime Hoshi
//
// 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 ebiten

import (
	"math"

	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/graphics/opengl"
	"github.com/hajimehoshi/ebiten/internal/ui"
)

func newGraphicsContext(f func(*Image) error) *graphicsContext {
	return &graphicsContext{
		f: f,
	}
}

type graphicsContext struct {
	f           func(*Image) error
	offscreen   *Image
	offscreen2  *Image // TODO: better name
	screen      *Image
	screenScale float64
	initialized bool
}

func (c *graphicsContext) SetSize(screenWidth, screenHeight int, screenScale float64) error {
	if c.screen != nil {
		c.screen.Dispose()
	}
	if c.offscreen != nil {
		c.offscreen.Dispose()
	}
	offscreen, err := NewImage(screenWidth, screenHeight, FilterNearest)
	if err != nil {
		return err
	}
	offscreen.impl.noSave = true

	intScreenScale := int(math.Ceil(screenScale))
	w := screenWidth * intScreenScale
	h := screenHeight * intScreenScale
	offscreen2, err := NewImage(w, h, FilterLinear)
	if err != nil {
		return err
	}
	offscreen2.impl.noSave = true

	w = int(float64(screenWidth) * screenScale)
	h = int(float64(screenHeight) * screenScale)
	c.screen, err = newImageWithScreenFramebuffer(w, h)
	if err != nil {
		return err
	}
	c.screen.impl.noSave = true
	c.screen.Clear()

	c.offscreen = offscreen
	c.offscreen2 = offscreen2
	c.screenScale = screenScale
	ui.GLContext().ResetViewportSize()
	return nil
}

func (c *graphicsContext) needsRestoring(context *opengl.Context) (bool, error) {
	imageM.Lock()
	defer imageM.Unlock()
	// FlushCommands is required because c.offscreen.impl might not have an actual texture.
	if err := graphics.FlushCommands(context); err != nil {
		return false, err
	}
	return c.offscreen.impl.isInvalidated(context), nil
}

func (c *graphicsContext) initializeIfNeeded(context *opengl.Context) error {
	// glViewport must be called at every frame on iOS
	context.ResetViewportSize()
	if !c.initialized {
		if err := graphics.Initialize(context); err != nil {
			return err
		}
		c.initialized = true
	}
	r, err := c.needsRestoring(context)
	if err != nil {
		return err
	}
	if r {
		if err := c.restore(context); err != nil {
			return err
		}
	}
	return nil
}

func drawWithFittingScale(dst *Image, src *Image) error {
	wd, hd := dst.Size()
	ws, hs := src.Size()
	sw := float64(wd) / float64(ws)
	sh := float64(hd) / float64(hs)
	op := &DrawImageOptions{}
	op.GeoM.Scale(sw, sh)
	if err := dst.DrawImage(src, op); err != nil {
		return err
	}
	return nil
}

func (c *graphicsContext) drawToDefaultRenderTarget(context *opengl.Context) error {
	if err := c.screen.Clear(); err != nil {
		return err
	}
	if err := drawWithFittingScale(c.screen, c.offscreen2); err != nil {
		return err
	}
	if err := c.flush(context); err != nil {
		return err
	}
	return nil
}

func (c *graphicsContext) UpdateAndDraw() error {
	if err := c.initializeIfNeeded(ui.GLContext()); err != nil {
		return err
	}
	if err := c.offscreen.Clear(); err != nil {
		return err
	}
	if err := c.f(c.offscreen); err != nil {
		return err
	}
	if IsRunningSlowly() {
		return nil
	}
	if err := c.offscreen2.Clear(); err != nil {
		return err
	}
	if err := drawWithFittingScale(c.offscreen2, c.offscreen); err != nil {
		return err
	}
	if err := c.drawToDefaultRenderTarget(ui.GLContext()); err != nil {
		return err
	}
	if err := theImages.savePixels(ui.GLContext()); err != nil {
		return err
	}
	return nil
}

func (c *graphicsContext) Draw() error {
	if err := c.initializeIfNeeded(ui.GLContext()); err != nil {
		return err
	}
	if err := c.drawToDefaultRenderTarget(ui.GLContext()); err != nil {
		return err
	}
	return nil
}

func (c *graphicsContext) flush(context *opengl.Context) error {
	// TODO: imageM is necessary to call graphics functions. Move this to graphics package.
	imageM.Lock()
	defer imageM.Unlock()
	if err := graphics.FlushCommands(context); err != nil {
		return err
	}
	// Call glFlush to prevent black flicking (especially on Android (#226) and iOS).
	context.Flush()
	return nil
}

func (c *graphicsContext) restore(context *opengl.Context) error {
	imageM.Lock()
	defer imageM.Unlock()
	context.Resume()
	if err := graphics.Initialize(context); err != nil {
		return err
	}
	if err := theImages.restorePixels(context); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


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

package rmeta

import (
	"fmt"
	"reflect"
	"strings"

	"go-hep.org/x/hep/groot/internal/rtype"
)

var GoType2ROOTEnum = map[reflect.Type]Enum{
	reflect.TypeOf(int8(0)):  Char,
	reflect.TypeOf(int16(0)): Short,
	reflect.TypeOf(int32(0)): Int,
	reflect.TypeOf(int64(0)): Long,
	//	reflect.TypeOf(int64(0)): Long64,
	reflect.TypeOf(float32(0)): Float,
	reflect.TypeOf(float64(0)): Double,
	reflect.TypeOf(uint8(0)):   UChar,
	//	reflect.TypeOf(uint8(0)): CharStar,
	reflect.TypeOf(uint16(0)): UShort,
	reflect.TypeOf(uint32(0)): UInt,
	reflect.TypeOf(uint64(0)): ULong,
	//	reflect.TypeOf(uint64(0)): ULong64,
	reflect.TypeOf(false):             Bool,
	reflect.TypeOf(rtype.Double32(0)): Double32,
	reflect.TypeOf(""):                TString,
}

var GoType2Cxx = map[string]string{
	"bool":    "bool",
	"byte":    "unsigned char",
	"uint":    "unsigned int",
	"uint8":   "unsigned char",
	"uint16":  "unsigned short",
	"uint32":  "unsigned int",
	"uint64":  "unsigned long",
	"int8":    "char",
	"int16":   "short",
	"int32":   "int",
	"int64":   "long",
	"float32": "float",
	"float64": "double",
}

var CxxBuiltins = map[string]reflect.Type{
	"bool": reflect.TypeOf(false),

	/*
		"uint":   reflect.TypeOf(uint(0)),
		"uint8":  reflect.TypeOf(uint8(0)),
		"uint16": reflect.TypeOf(uint16(0)),
		"uint32": reflect.TypeOf(uint32(0)),
		"uint64": reflect.TypeOf(uint64(0)),

		"int":   reflect.TypeOf(int(0)),
		"int8":  reflect.TypeOf(int8(0)),
		"int16": reflect.TypeOf(int16(0)),
		"int32": reflect.TypeOf(int32(0)),
		"int64": reflect.TypeOf(int64(0)),

		"float32": reflect.TypeOf(float32(0)),
		"float64": reflect.TypeOf(float64(0)),
	*/

	// C/C++ builtins

	"unsigned":       reflect.TypeOf(uint(0)),
	"unsigned char":  reflect.TypeOf(uint8(0)),
	"unsigned short": reflect.TypeOf(uint16(0)),
	"unsigned int":   reflect.TypeOf(uint32(0)),
	"unsigned long":  reflect.TypeOf(uint64(0)),

	//"int":   reflect.TypeOf(int(0)),
	"char":  reflect.TypeOf(int8(0)),
	"short": reflect.TypeOf(int16(0)),
	"int":   reflect.TypeOf(int32(0)),
	"long":  reflect.TypeOf(int64(0)),

	"float":  reflect.TypeOf(float32(0)),
	"double": reflect.TypeOf(float64(0)),

	"string": reflect.TypeOf(""),

	// ROOT builtins
	"Bool_t": reflect.TypeOf(true),

	"Byte_t": reflect.TypeOf(uint8(0)),

	"Char_t":    reflect.TypeOf(int8(0)),
	"UChar_t":   reflect.TypeOf(uint8(0)),
	"Short_t":   reflect.TypeOf(int16(0)),
	"UShort_t":  reflect.TypeOf(uint16(0)),
	"Int_t":     reflect.TypeOf(int32(0)),
	"UInt_t":    reflect.TypeOf(uint32(0)),
	"Seek_t":    reflect.TypeOf(int64(0)),  // FIXME(sbinet): not portable
	"Long_t":    reflect.TypeOf(int64(0)),  // FIXME(sbinet): not portable
	"ULong_t":   reflect.TypeOf(uint64(0)), // FIXME(sbinet): not portable
	"Long64_t":  reflect.TypeOf(int64(0)),
	"ULong64_t": reflect.TypeOf(uint64(0)),

	"Float_t":    reflect.TypeOf(float32(0)),
	"Float16_t":  reflect.TypeOf(rtype.Float16(0)),
	"Double_t":   reflect.TypeOf(float64(0)),
	"Double32_t": reflect.TypeOf(rtype.Double32(0)),

	"Version_t": reflect.TypeOf(int16(0)),
	"Option_t":  reflect.TypeOf(""),
	"Ssiz_t":    reflect.TypeOf(int(0)),
	"Real_t":    reflect.TypeOf(float32(0)),

	"Axis_t": reflect.TypeOf(float64(0)),
	"Stat_t": reflect.TypeOf(float64(0)),

	"Font_t":   reflect.TypeOf(int16(0)),
	"Style_t":  reflect.TypeOf(int16(0)),
	"Marker_t": reflect.TypeOf(int16(0)),
	"Width_t":  reflect.TypeOf(int16(0)),
	"Color_t":  reflect.TypeOf(int16(0)),
	"SCoord_t": reflect.TypeOf(int16(0)),
	"Coord_t":  reflect.TypeOf(float64(0)),
	"Angle_t":  reflect.TypeOf(float32(0)),
	"Size_t":   reflect.TypeOf(float32(0)),
}

// STLNameFor creates a regular C++ STL container name given a STL enum type
// and a ROOT enum value for the contained element.
func STLNameFor(vtype ESTLType, ctype Enum) string {
	ename := rmeta2Name(ctype)
	if strings.HasSuffix(ename, ">") {
		ename += " "
	}

	typfmt := "%s"
	switch vtype {
	case STLvector:
		typfmt = "std::vector<%s>"
	case STLlist:
		typfmt = "std::list<%s>"
	case STLdeque:
		typfmt = "std::deque<%s>"
	case STLmap:
		typfmt = "std::map<%s>"
	case STLmultimap:
		typfmt = "std::multimap<%s>"
	case STLset:
		typfmt = "std::set<%s>"
	case STLmultiset:
		typfmt = "std::multiset<%s>"
	case STLbitset:
		typfmt = "std::bitset<%s>"
	case STLforwardlist:
		typfmt = "std::forward_list<%s>"
	case STLunorderedset:
		typfmt = "std::unordered_set<%s>"
	case STLunorderedmultiset:
		typfmt = "std::unordered_multiset<%s>"
	case STLunorderedmap:
		typfmt = "std::unordered_map<%s>"
	case STLunorderedmultimap:
		typfmt = "std::unordered_multimap<%s>"
	}

	return fmt.Sprintf(typfmt, ename)
}

func rmeta2Name(t Enum) string {
	switch t {
	case Char:
		return "char"
	case Short:
		return "short"
	case Int:
		return "int"
	case Long:
		return "long"
	case Float:
		return "float"
	case Counter:
		return "int"
	case CharStar:
		return "char*"
	case Double:
		return "double"
	case Double32:
		return "Double32_t"
	case LegacyChar:
		return "char" // FIXME(sbinet)
	case UChar:
		return "unsigned char"
	case UShort:
		return "unsigned short"
	case UInt:
		return "unsigned int"
	case ULong:
		return "unsigned long"
	case Bits:
		return "bits" // FIXME(sbinet)
	case Long64:
		return "Long64_t"
	case ULong64:
		return "ULong64_t"
	case Bool:
		return "bool"
	case Float16:
		return "Float16_t"
	case TString:
		return "TString"
	case TObject:
		return "TObject"
	case TNamed:
		return "TNamed"
	case STLstring:
		return "std::string"
	}
	panic(fmt.Errorf("not implemented: t=%d", t))
}
<commit_msg>groot/rmeta: add int to GoType2Cxx<commit_after>// Copyright 2017 The go-hep Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rmeta

import (
	"fmt"
	"reflect"
	"strings"

	"go-hep.org/x/hep/groot/internal/rtype"
)

var GoType2ROOTEnum = map[reflect.Type]Enum{
	reflect.TypeOf(int8(0)):  Char,
	reflect.TypeOf(int16(0)): Short,
	reflect.TypeOf(int32(0)): Int,
	reflect.TypeOf(int64(0)): Long,
	//	reflect.TypeOf(int64(0)): Long64,
	reflect.TypeOf(float32(0)): Float,
	reflect.TypeOf(float64(0)): Double,
	reflect.TypeOf(uint8(0)):   UChar,
	//	reflect.TypeOf(uint8(0)): CharStar,
	reflect.TypeOf(uint16(0)): UShort,
	reflect.TypeOf(uint32(0)): UInt,
	reflect.TypeOf(uint64(0)): ULong,
	//	reflect.TypeOf(uint64(0)): ULong64,
	reflect.TypeOf(false):             Bool,
	reflect.TypeOf(rtype.Double32(0)): Double32,
	reflect.TypeOf(""):                TString,
}

var GoType2Cxx = map[string]string{
	"bool":    "bool",
	"byte":    "unsigned char",
	"uint":    "unsigned int",
	"uint8":   "unsigned char",
	"uint16":  "unsigned short",
	"uint32":  "unsigned int",
	"uint64":  "unsigned long",
	"int":     "int",
	"int8":    "char",
	"int16":   "short",
	"int32":   "int",
	"int64":   "long",
	"float32": "float",
	"float64": "double",
}

var CxxBuiltins = map[string]reflect.Type{
	"bool": reflect.TypeOf(false),

	/*
		"uint":   reflect.TypeOf(uint(0)),
		"uint8":  reflect.TypeOf(uint8(0)),
		"uint16": reflect.TypeOf(uint16(0)),
		"uint32": reflect.TypeOf(uint32(0)),
		"uint64": reflect.TypeOf(uint64(0)),

		"int":   reflect.TypeOf(int(0)),
		"int8":  reflect.TypeOf(int8(0)),
		"int16": reflect.TypeOf(int16(0)),
		"int32": reflect.TypeOf(int32(0)),
		"int64": reflect.TypeOf(int64(0)),

		"float32": reflect.TypeOf(float32(0)),
		"float64": reflect.TypeOf(float64(0)),
	*/

	// C/C++ builtins

	"unsigned":       reflect.TypeOf(uint(0)),
	"unsigned char":  reflect.TypeOf(uint8(0)),
	"unsigned short": reflect.TypeOf(uint16(0)),
	"unsigned int":   reflect.TypeOf(uint32(0)),
	"unsigned long":  reflect.TypeOf(uint64(0)),

	//"int":   reflect.TypeOf(int(0)),
	"char":  reflect.TypeOf(int8(0)),
	"short": reflect.TypeOf(int16(0)),
	"int":   reflect.TypeOf(int32(0)),
	"long":  reflect.TypeOf(int64(0)),

	"float":  reflect.TypeOf(float32(0)),
	"double": reflect.TypeOf(float64(0)),

	"string": reflect.TypeOf(""),

	// ROOT builtins
	"Bool_t": reflect.TypeOf(true),

	"Byte_t": reflect.TypeOf(uint8(0)),

	"Char_t":    reflect.TypeOf(int8(0)),
	"UChar_t":   reflect.TypeOf(uint8(0)),
	"Short_t":   reflect.TypeOf(int16(0)),
	"UShort_t":  reflect.TypeOf(uint16(0)),
	"Int_t":     reflect.TypeOf(int32(0)),
	"UInt_t":    reflect.TypeOf(uint32(0)),
	"Seek_t":    reflect.TypeOf(int64(0)),  // FIXME(sbinet): not portable
	"Long_t":    reflect.TypeOf(int64(0)),  // FIXME(sbinet): not portable
	"ULong_t":   reflect.TypeOf(uint64(0)), // FIXME(sbinet): not portable
	"Long64_t":  reflect.TypeOf(int64(0)),
	"ULong64_t": reflect.TypeOf(uint64(0)),

	"Float_t":    reflect.TypeOf(float32(0)),
	"Float16_t":  reflect.TypeOf(rtype.Float16(0)),
	"Double_t":   reflect.TypeOf(float64(0)),
	"Double32_t": reflect.TypeOf(rtype.Double32(0)),

	"Version_t": reflect.TypeOf(int16(0)),
	"Option_t":  reflect.TypeOf(""),
	"Ssiz_t":    reflect.TypeOf(int(0)),
	"Real_t":    reflect.TypeOf(float32(0)),

	"Axis_t": reflect.TypeOf(float64(0)),
	"Stat_t": reflect.TypeOf(float64(0)),

	"Font_t":   reflect.TypeOf(int16(0)),
	"Style_t":  reflect.TypeOf(int16(0)),
	"Marker_t": reflect.TypeOf(int16(0)),
	"Width_t":  reflect.TypeOf(int16(0)),
	"Color_t":  reflect.TypeOf(int16(0)),
	"SCoord_t": reflect.TypeOf(int16(0)),
	"Coord_t":  reflect.TypeOf(float64(0)),
	"Angle_t":  reflect.TypeOf(float32(0)),
	"Size_t":   reflect.TypeOf(float32(0)),
}

// STLNameFor creates a regular C++ STL container name given a STL enum type
// and a ROOT enum value for the contained element.
func STLNameFor(vtype ESTLType, ctype Enum) string {
	ename := rmeta2Name(ctype)
	if strings.HasSuffix(ename, ">") {
		ename += " "
	}

	typfmt := "%s"
	switch vtype {
	case STLvector:
		typfmt = "std::vector<%s>"
	case STLlist:
		typfmt = "std::list<%s>"
	case STLdeque:
		typfmt = "std::deque<%s>"
	case STLmap:
		typfmt = "std::map<%s>"
	case STLmultimap:
		typfmt = "std::multimap<%s>"
	case STLset:
		typfmt = "std::set<%s>"
	case STLmultiset:
		typfmt = "std::multiset<%s>"
	case STLbitset:
		typfmt = "std::bitset<%s>"
	case STLforwardlist:
		typfmt = "std::forward_list<%s>"
	case STLunorderedset:
		typfmt = "std::unordered_set<%s>"
	case STLunorderedmultiset:
		typfmt = "std::unordered_multiset<%s>"
	case STLunorderedmap:
		typfmt = "std::unordered_map<%s>"
	case STLunorderedmultimap:
		typfmt = "std::unordered_multimap<%s>"
	}

	return fmt.Sprintf(typfmt, ename)
}

func rmeta2Name(t Enum) string {
	switch t {
	case Char:
		return "char"
	case Short:
		return "short"
	case Int:
		return "int"
	case Long:
		return "long"
	case Float:
		return "float"
	case Counter:
		return "int"
	case CharStar:
		return "char*"
	case Double:
		return "double"
	case Double32:
		return "Double32_t"
	case LegacyChar:
		return "char" // FIXME(sbinet)
	case UChar:
		return "unsigned char"
	case UShort:
		return "unsigned short"
	case UInt:
		return "unsigned int"
	case ULong:
		return "unsigned long"
	case Bits:
		return "bits" // FIXME(sbinet)
	case Long64:
		return "Long64_t"
	case ULong64:
		return "ULong64_t"
	case Bool:
		return "bool"
	case Float16:
		return "Float16_t"
	case TString:
		return "TString"
	case TObject:
		return "TObject"
	case TNamed:
		return "TNamed"
	case STLstring:
		return "std::string"
	}
	panic(fmt.Errorf("not implemented: t=%d", t))
}
<|endoftext|>