<commit_before>package cl11

import (
	"unsafe"

	clw "github.com/rdwilliamson/clw11"
)

// An OpenCL context is created with one or more devices. Contexts are used by
// the OpenCL runtime for managing objects such as command-queues, memory,
// program and kernel objects and for executing kernels on one or more devices
// specified in the context.
type Context struct {
	id clw.Context

	// The devices in the context.
	Devices []*Device

	// The properties used to create the context.
	Properties []ContextProperties
}

type ContextProperties clw.ContextProperties

const (
	ContextPlatform = ContextProperties(clw.ContextPlatform)
)

type ContextCallback func(err string, data []byte, userData interface{})

// Ensure the definitions of context callbacks match. ContextCallback isn't an
// alias for clw.ContextCallbackFunc just so the user doesn't have to look into
// clw.
var _ clw.ContextCallbackFunc = clw.ContextCallbackFunc(ContextCallback(func(err string, data []byte,
	userData interface{}) {
}))

// Creates an OpenCL context.
//
// An OpenCL context is created with one or more devices. Contexts are used by
// the OpenCL runtime for managing objects such as command-queues, memory,
// program and kernel objects and for executing kernels on one or more devices
// specified in the context.
func CreateContext(d []*Device, cp []ContextProperties, cc ContextCallback, userData interface{}) (*Context, error) {

	devices := make([]clw.DeviceID, len(d))
	for i := range d {
		devices[i] = clw.DeviceID(d[i].id)
	}

	// "Convert" type and appending terminating zero.
	var properties []clw.ContextProperties
	if cp != nil {
		properties = make([]clw.ContextProperties, len(cp)+1)
		for i := range cp {
			properties[i] = clw.ContextProperties(cp[i])
		}
		properties[len(cp)] = 0
	}

	context, err := clw.CreateContext(properties, devices, clw.ContextCallbackFunc(cc), userData)
	if err != nil {
		return nil, err
	}

	return &Context{id: context, Devices: d, Properties: cp}, nil
}

// Create an OpenCL context from a device type that identifies the specific
// device(s) to use.
func CreateContextFromType(cp []ContextProperties, dt DeviceType, cc ContextCallback,
	userData interface{}) (*Context, error) {

	// "Convert" type and appending terminating zero.
	var properties []clw.ContextProperties
	if cp != nil {
		properties = make([]clw.ContextProperties, len(cp)+1)
		for i := range cp {
			properties[i] = clw.ContextProperties(cp[i])
		}
		properties[len(cp)] = 0
	}

	context, err := clw.CreateContextFromType(properties, clw.DeviceType(dt), clw.ContextCallbackFunc(cc), userData)
	if err != nil {
		return nil, err
	}

	// Get devices.
	var numDevices clw.Uint
	err = clw.GetContextInfo(context, clw.ContextNumDevices, clw.Size(unsafe.Sizeof(numDevices)),
		unsafe.Pointer(&numDevices), nil)
	if err != nil {
		return nil, err
	}

	var devicePtrs []*Device
	if numDevices > 0 {
		devices := make([]clw.DeviceID, int(numDevices))
		devicePtrs = make([]*Device, int(numDevices))

		err = clw.GetContextInfo(context, clw.ContextDevices, clw.Size(uintptr(numDevices)*unsafe.Sizeof(devices[0])),
			unsafe.Pointer(&devices[0]), nil)
		if err != nil {
			return nil, err
		}

		for i := range devices {
			d := &Device{id: devices[i]}
			err = d.getAllInfo()
			if err != nil {
				return nil, err
			}
			devicePtrs[i] = d
		}
	}

	return &Context{context, devicePtrs, cp}, nil
}

// Increment the context reference count.
func (c *Context) Retain() error {
	return clw.RetainContext(c.id)
}

// Decrement the context reference count.
func (c *Context) Release() error {
	return clw.ReleaseContext(c.id)
}

// The context reference count.
//
// The reference count returned should be considered immediately stale. It is
// unsuitable for general use in applications. This feature is provided for
// identifying memory leaks.
func (c *Context) ReferenceCount() (int, error) {

	var count clw.Uint
	err := clw.GetContextInfo(c.id, clw.ContextReferenceCount, clw.Size(unsafe.Sizeof(count)), unsafe.Pointer(&count),
		nil)
	if err != nil {
		return 0, err
	}

	return int(count), nil
}
<commit_msg>Updated context documentation.<commit_after>package cl11

import (
	"unsafe"

	clw "github.com/rdwilliamson/clw11"
)

// An OpenCL context is created with one or more devices. Contexts are used by
// the OpenCL runtime for managing objects such as command-queues, memory,
// program and kernel objects and for executing kernels on one or more devices
// specified in the context.
type Context struct {
	id clw.Context

	// The devices in the context.
	Devices []*Device

	// The properties used to create the context.
	Properties []ContextProperties
}

type ContextProperties clw.ContextProperties

const (
	ContextPlatform = ContextProperties(clw.ContextPlatform)
)

type ContextCallback func(err string, data []byte, userData interface{})

// Ensure the definitions of context callbacks match. ContextCallback isn't an
// alias for clw.ContextCallbackFunc just so the user doesn't have to look into
// clw.
var _ clw.ContextCallbackFunc = clw.ContextCallbackFunc(ContextCallback(func(err string, data []byte,
	userData interface{}) {
}))

// Creates an OpenCL context.
//
// An OpenCL context is created with one or more devices. Contexts are used by
// the OpenCL runtime for managing objects such as command-queues, memory,
// program and kernel objects and for executing kernels on one or more devices
// specified in the context.
//
// WARNING: The callback and user data will be referenced for the lifetime of
//          the program. Thus any variables captured if callback is a closure or
//          any variables referenced by user data will not be garbage collected.
func CreateContext(d []*Device, cp []ContextProperties, cc ContextCallback, userData interface{}) (*Context, error) {

	devices := make([]clw.DeviceID, len(d))
	for i := range d {
		devices[i] = clw.DeviceID(d[i].id)
	}

	// "Convert" type and appending terminating zero.
	var properties []clw.ContextProperties
	if cp != nil {
		properties = make([]clw.ContextProperties, len(cp)+1)
		for i := range cp {
			properties[i] = clw.ContextProperties(cp[i])
		}
		properties[len(cp)] = 0
	}

	context, err := clw.CreateContext(properties, devices, clw.ContextCallbackFunc(cc), userData)
	if err != nil {
		return nil, err
	}

	return &Context{id: context, Devices: d, Properties: cp}, nil
}

// Create an OpenCL context from a device type that identifies the specific
// device(s) to use.
//
// WARNING: The callback and user data will be referenced for the lifetime of
//          the program. Thus any variables captured if callback is a closure or
//          any variables referenced by user data will not be garbage collected.
func CreateContextFromType(cp []ContextProperties, dt DeviceType, cc ContextCallback,
	userData interface{}) (*Context, error) {

	// "Convert" type and appending terminating zero.
	var properties []clw.ContextProperties
	if cp != nil {
		properties = make([]clw.ContextProperties, len(cp)+1)
		for i := range cp {
			properties[i] = clw.ContextProperties(cp[i])
		}
		properties[len(cp)] = 0
	}

	context, err := clw.CreateContextFromType(properties, clw.DeviceType(dt), clw.ContextCallbackFunc(cc), userData)
	if err != nil {
		return nil, err
	}

	// Get devices.
	var numDevices clw.Uint
	err = clw.GetContextInfo(context, clw.ContextNumDevices, clw.Size(unsafe.Sizeof(numDevices)),
		unsafe.Pointer(&numDevices), nil)
	if err != nil {
		return nil, err
	}

	var devicePtrs []*Device
	if numDevices > 0 {
		devices := make([]clw.DeviceID, int(numDevices))
		devicePtrs = make([]*Device, int(numDevices))

		err = clw.GetContextInfo(context, clw.ContextDevices, clw.Size(uintptr(numDevices)*unsafe.Sizeof(devices[0])),
			unsafe.Pointer(&devices[0]), nil)
		if err != nil {
			return nil, err
		}

		for i := range devices {
			d := &Device{id: devices[i]}
			err = d.getAllInfo()
			if err != nil {
				return nil, err
			}
			devicePtrs[i] = d
		}
	}

	return &Context{context, devicePtrs, cp}, nil
}

// Increment the context reference count.
//
// CreateContext and CreateContextFromType perform an implicit retain. This is
// very helpful for 3rd party libraries, which typically get a context passed to
// them by the application. However, it is possible that the application may
// delete the context without informing the library. Allowing functions to
// attach to (i.e. retain) and release a context solves the problem of a context
// being used by a library no longer being valid.
func (c *Context) Retain() error {
	return clw.RetainContext(c.id)
}

// Decrement the context reference count.
//
// After the context reference count becomes zero and all the objects attached
// to context (such as memory objects, command-queues) are released, the context
// is deleted.
func (c *Context) Release() error {
	return clw.ReleaseContext(c.id)
}

// The context reference count.
//
// The reference count returned should be considered immediately stale. It is
// unsuitable for general use in applications. This feature is provided for
// identifying memory leaks.
func (c *Context) ReferenceCount() (int, error) {

	var count clw.Uint
	err := clw.GetContextInfo(c.id, clw.ContextReferenceCount, clw.Size(unsafe.Sizeof(count)), unsafe.Pointer(&count),
		nil)
	if err != nil {
		return 0, err
	}

	return int(count), nil
}
<|endoftext|>


<commit_before>package gaebridge

// Only creates and returns a single Google App Engine (GAE) context, once per request.

import (
	"google.golang.org/appengine"
	"golang.org/x/net/context"
	"net/http"
)

var (
	ctx *context.Context

)

func Context(r *http.Request) *context.Context {
	if (ctx==nil) {
		ctx = &appengine.NewContext(r);

	}

	return ctx;

}<commit_msg>Whoops!<commit_after>package gaebridge

// Only creates and returns a single Google App Engine (GAE) context, once per request.

import (
	"google.golang.org/appengine"
	"golang.org/x/net/context"
	"net/http"
)

var (
	ctx *context.Context

)

func Context(r *http.Request) *context.Context {
	if (ctx==nil) {
		newContext := appengine.NewContext(r);
		ctx = &newContext;

	}

	return ctx;

}<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/gonuts/logger"
)

type External struct {
	cmd string
	err error
}
type FixFct func(*Context) error

type Context struct {
	msg       *logger.Logger
	cfg       Config
	siteroot  string // where to install software, binaries, ...
	repourl   string
	rpmprefix string
	dbpath    string
	etcdir    string
	yumconf   string
	yumreposd string
	yum       *YumClient
	tmpdir    string
	bindir    string
	libdir    string
	initfile  string

	extstatus map[string]External
	reqext    []string
	extfix    map[string]FixFct
}

func New(cfg Config) (*Context, error) {
	var err error
	siteroot := cfg.Siteroot
	ctx := Context{
		cfg:       cfg,
		msg:       logger.NewLogger("pkr", logger.INFO, bufio.NewWriter(os.Stdout)),
		siteroot:  siteroot,
		repourl:   cfg.RepoUrl,
		rpmprefix: cfg.RpmPrefix(),
		dbpath:    filepath.Join(siteroot, "var", "lib", "rpm"),
		etcdir:    filepath.Join(siteroot, "etc"),
		yumconf:   filepath.Join(siteroot, "etc", "yum.conf"),
		yumreposd: filepath.Join(siteroot, "etc", "yum.repos.d"),
		tmpdir:    filepath.Join(siteroot, "tmp"),
		bindir:    filepath.Join(siteroot, "usr", "bin"),
		libdir:    filepath.Join(siteroot, "lib"),
		initfile:  filepath.Join(siteroot, "etc", "repoinit"),
	}
	if cfg.Debug {
		ctx.msg.SetLevel(logger.DEBUG)
	}
	for _, dir := range []string{
		ctx.tmpdir,
		ctx.bindir,
		ctx.libdir,
	} {
		err = os.MkdirAll(dir, 0644)
		if err != nil {
			ctx.msg.Errorf("could not create directory %q: %v\n", dir, err)
			return nil, err
		}
	}
	os.Setenv("PATH", os.Getenv("PATH")+string(os.PathListSeparator)+ctx.bindir)

	// make sure the db is initialized
	err = ctx.initRpmDb()
	if err != nil {
		return nil, err
	}

	// yum
	err = ctx.initYum()
	if err != nil {
		return nil, err
	}

	ctx.yum = NewClient(ctx.siteroot)
	if ctx.yum == nil {
		return nil, err
	}

	// defining structures and checking if all needed tools are available
	ctx.extstatus = make(map[string]External)
	ctx.reqext = []string{"rpm"}
	ctx.extfix = make(map[string]FixFct)
	err = ctx.checkPreRequisites()
	if err != nil {
		return nil, err
	}

	err = ctx.checkRepository()
	if err != nil {
		return nil, err
	}

	return &ctx, err
}

func (ctx *Context) Exit(rc int) {
	os.Exit(rc)
}

// initRpmDb initializes the RPM database
func (ctx *Context) initRpmDb() error {
	var err error
	msg := ctx.msg
	msg.Infof("RPM DB in %q\n", ctx.dbpath)
	err = os.MkdirAll(ctx.dbpath, 0644)
	if err != nil {
		msg.Errorf(
			"could not create directory %q for RPM DB: %v\n",
			ctx.dbpath,
			err,
		)
		return err
	}

	pkgdir := filepath.Join(ctx.dbpath, "Packages")
	if !path_exists(pkgdir) {
		msg.Infof("Initializing RPM db\n")
		cmd := exec.Command(
			"rpm",
			"--dbpath", ctx.dbpath,
			"--initdb",
		)
		out, err := cmd.CombinedOutput()
		msg.Debugf(string(out))
		if err != nil {
			return fmt.Errorf("error initializing RPM DB: %v", err)
		}
	}
	return err
}

func (ctx *Context) initYum() error {
	var err error
	err = os.MkdirAll(ctx.etcdir, 0644)
	if err != nil {
		return fmt.Errorf("could not create dir %q: %v", ctx.etcdir, err)
	}

	if !path_exists(ctx.yumconf) {
		yum, err := os.Create(ctx.yumconf)
		if err != nil {
			return err
		}
		defer yum.Close()
		err = ctx.writeYumConf(yum)
		if err != nil {
			return err
		}
		err = yum.Sync()
		if err != nil {
			return err
		}
		err = yum.Close()
		if err != nil {
			return err
		}
	}
	err = ctx.cfg.initYum()
	return err
}

// checkPreRequisites makes sure that all external tools required by
// this tool to perform the installation are present
func (ctx *Context) checkPreRequisites() error {
	var err error
	extmissing := false
	missing := make([]string, 0)

	for _, ext := range ctx.reqext {
		cmd, err := exec.LookPath(ext)
		ctx.extstatus[ext] = External{
			cmd: cmd,
			err: err,
		}
	}

	for k, ext := range ctx.extstatus {
		if ext.err == nil {
			ctx.msg.Infof("%s: Found %q\n", k, ext.cmd)
			continue
		}
		ctx.msg.Infof("%s: Missing - trying compensatory measure\n", k)
		fix, ok := ctx.extfix[k]
		if !ok {
			extmissing = true
			missing = append(missing, k)
			continue
		}

		err = fix(ctx)
		if err != nil {
			return err
		}

		cmd, err := exec.LookPath(k)
		ctx.extstatus[k] = External{
			cmd: cmd,
			err: err,
		}
		if err == nil {
			ctx.msg.Infof("%s: Found %q\n", k, cmd)
			continue
		}
		ctx.msg.Infof("%s: Missing\n", k)
		extmissing = true
		missing = append(missing, k)
	}

	if extmissing {
		err = fmt.Errorf("missing external(s): %v", missing)
	}
	return err
}

func (ctx *Context) checkRepository() error {
	var err error
	if !path_exists(ctx.initfile) {
		fini, err := os.Create(ctx.initfile)
		if err != nil {
			return err
		}
		defer fini.Close()
		_, err = fini.WriteString(time.Now().Format(time.RFC3339) + "\n")
		if err != nil {
			return err
		}
		err = fini.Sync()
		if err != nil {
			return err
		}
		return fini.Close()
	}
	err = ctx.checkUpdates()
	return err
}

func (ctx *Context) writeYumConf(w io.Writer) error {
	var err error
	const tmpl = `
[main]
#CONFVERSION 0001
cachedir=/var/cache/yum
debuglevel=2
logfile=/var/log/yum.log
pkgpolicy=newest
distroverpkg=redhat-release
tolerant=1
exactarch=1
obsoletes=1
plugins=1
gpgcheck=0
installroot=%s
reposdir=/etc/yum.repos.d
`
	_, err = fmt.Fprintf(w, tmpl, ctx.siteroot)
	return err
}

// checkUpdates checks whether packages could be updated in the repository
func (ctx *Context) checkUpdates() error {
	var err error
	return err
}

// install performs the whole download/install procedure (eq. yum install)
func (ctx *Context) install(project, version, cmtconfig string) error {
	var err error
	ctx.msg.Infof("Installing %s/%s/%s\n", project, version, cmtconfig)
	return err
}

// EOF
<commit_msg>context: dont buffer logger i/o<commit_after>package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/gonuts/logger"
)

type External struct {
	cmd string
	err error
}
type FixFct func(*Context) error

type Context struct {
	msg       *logger.Logger
	cfg       Config
	siteroot  string // where to install software, binaries, ...
	repourl   string
	rpmprefix string
	dbpath    string
	etcdir    string
	yumconf   string
	yumreposd string
	yum       *YumClient
	tmpdir    string
	bindir    string
	libdir    string
	initfile  string

	extstatus map[string]External
	reqext    []string
	extfix    map[string]FixFct
}

func New(cfg Config) (*Context, error) {
	var err error
	siteroot := cfg.Siteroot
	ctx := Context{
		cfg:       cfg,
		msg:       logger.NewLogger("pkr", logger.INFO, os.Stdout),
		siteroot:  siteroot,
		repourl:   cfg.RepoUrl,
		rpmprefix: cfg.RpmPrefix(),
		dbpath:    filepath.Join(siteroot, "var", "lib", "rpm"),
		etcdir:    filepath.Join(siteroot, "etc"),
		yumconf:   filepath.Join(siteroot, "etc", "yum.conf"),
		yumreposd: filepath.Join(siteroot, "etc", "yum.repos.d"),
		tmpdir:    filepath.Join(siteroot, "tmp"),
		bindir:    filepath.Join(siteroot, "usr", "bin"),
		libdir:    filepath.Join(siteroot, "lib"),
		initfile:  filepath.Join(siteroot, "etc", "repoinit"),
	}
	if cfg.Debug {
		ctx.msg.SetLevel(logger.DEBUG)
	}
	for _, dir := range []string{
		ctx.tmpdir,
		ctx.bindir,
		ctx.libdir,
	} {
		err = os.MkdirAll(dir, 0644)
		if err != nil {
			ctx.msg.Errorf("could not create directory %q: %v\n", dir, err)
			return nil, err
		}
	}
	os.Setenv("PATH", os.Getenv("PATH")+string(os.PathListSeparator)+ctx.bindir)

	// make sure the db is initialized
	err = ctx.initRpmDb()
	if err != nil {
		return nil, err
	}

	// yum
	err = ctx.initYum()
	if err != nil {
		return nil, err
	}

	ctx.yum = NewClient(ctx.siteroot)
	if ctx.yum == nil {
		return nil, err
	}

	// defining structures and checking if all needed tools are available
	ctx.extstatus = make(map[string]External)
	ctx.reqext = []string{"rpm"}
	ctx.extfix = make(map[string]FixFct)
	err = ctx.checkPreRequisites()
	if err != nil {
		return nil, err
	}

	err = ctx.checkRepository()
	if err != nil {
		return nil, err
	}

	return &ctx, err
}

func (ctx *Context) Exit(rc int) {
	os.Exit(rc)
}

// initRpmDb initializes the RPM database
func (ctx *Context) initRpmDb() error {
	var err error
	msg := ctx.msg
	msg.Infof("RPM DB in %q\n", ctx.dbpath)
	err = os.MkdirAll(ctx.dbpath, 0644)
	if err != nil {
		msg.Errorf(
			"could not create directory %q for RPM DB: %v\n",
			ctx.dbpath,
			err,
		)
		return err
	}

	pkgdir := filepath.Join(ctx.dbpath, "Packages")
	if !path_exists(pkgdir) {
		msg.Infof("Initializing RPM db\n")
		cmd := exec.Command(
			"rpm",
			"--dbpath", ctx.dbpath,
			"--initdb",
		)
		out, err := cmd.CombinedOutput()
		msg.Debugf(string(out))
		if err != nil {
			return fmt.Errorf("error initializing RPM DB: %v", err)
		}
	}
	return err
}

func (ctx *Context) initYum() error {
	var err error
	err = os.MkdirAll(ctx.etcdir, 0644)
	if err != nil {
		return fmt.Errorf("could not create dir %q: %v", ctx.etcdir, err)
	}

	if !path_exists(ctx.yumconf) {
		yum, err := os.Create(ctx.yumconf)
		if err != nil {
			return err
		}
		defer yum.Close()
		err = ctx.writeYumConf(yum)
		if err != nil {
			return err
		}
		err = yum.Sync()
		if err != nil {
			return err
		}
		err = yum.Close()
		if err != nil {
			return err
		}
	}
	err = ctx.cfg.initYum()
	return err
}

// checkPreRequisites makes sure that all external tools required by
// this tool to perform the installation are present
func (ctx *Context) checkPreRequisites() error {
	var err error
	extmissing := false
	missing := make([]string, 0)

	for _, ext := range ctx.reqext {
		cmd, err := exec.LookPath(ext)
		ctx.extstatus[ext] = External{
			cmd: cmd,
			err: err,
		}
	}

	for k, ext := range ctx.extstatus {
		if ext.err == nil {
			ctx.msg.Infof("%s: Found %q\n", k, ext.cmd)
			continue
		}
		ctx.msg.Infof("%s: Missing - trying compensatory measure\n", k)
		fix, ok := ctx.extfix[k]
		if !ok {
			extmissing = true
			missing = append(missing, k)
			continue
		}

		err = fix(ctx)
		if err != nil {
			return err
		}

		cmd, err := exec.LookPath(k)
		ctx.extstatus[k] = External{
			cmd: cmd,
			err: err,
		}
		if err == nil {
			ctx.msg.Infof("%s: Found %q\n", k, cmd)
			continue
		}
		ctx.msg.Infof("%s: Missing\n", k)
		extmissing = true
		missing = append(missing, k)
	}

	if extmissing {
		err = fmt.Errorf("missing external(s): %v", missing)
	}
	return err
}

func (ctx *Context) checkRepository() error {
	var err error
	if !path_exists(ctx.initfile) {
		fini, err := os.Create(ctx.initfile)
		if err != nil {
			return err
		}
		defer fini.Close()
		_, err = fini.WriteString(time.Now().Format(time.RFC3339) + "\n")
		if err != nil {
			return err
		}
		err = fini.Sync()
		if err != nil {
			return err
		}
		return fini.Close()
	}
	err = ctx.checkUpdates()
	return err
}

func (ctx *Context) writeYumConf(w io.Writer) error {
	var err error
	const tmpl = `
[main]
#CONFVERSION 0001
cachedir=/var/cache/yum
debuglevel=2
logfile=/var/log/yum.log
pkgpolicy=newest
distroverpkg=redhat-release
tolerant=1
exactarch=1
obsoletes=1
plugins=1
gpgcheck=0
installroot=%s
reposdir=/etc/yum.repos.d
`
	_, err = fmt.Fprintf(w, tmpl, ctx.siteroot)
	return err
}

// checkUpdates checks whether packages could be updated in the repository
func (ctx *Context) checkUpdates() error {
	var err error
	return err
}

// install performs the whole download/install procedure (eq. yum install)
func (ctx *Context) install(project, version, cmtconfig string) error {
	var err error
	ctx.msg.Infof("Installing %s/%s/%s\n", project, version, cmtconfig)
	return err
}

// EOF
<|endoftext|>


<commit_before>package app

import (
	"fmt"

	"github.com/murlokswarm/log"
	"github.com/murlokswarm/markup"
	"github.com/murlokswarm/uid"
)

var (
	contexts = map[uid.ID]Contexter{}
)

// Contexter represents the support where a component can be mounted.
// eg a window.
type Contexter interface {
	// The ID of the context.
	ID() uid.ID

	// Mounts the component and renders it in the context.
	Mount(c markup.Componer)

	// Renders an element.
	Render(elem *markup.Element)

	// If applicable, returns the position of the context.
	Position() (x float64, y float64)

	// If applicable, moves the context.
	Move(x float64, y float64)

	// If applicable, returns the size of the context.
	Size() (width float64, height float64)

	// If applicable, resizes the context.
	Resize(width float64, height float64)

	// If applicable, set the icon targeted by path.
	SetIcon(path string)

	// If applicable, set the badge with v.
	SetBadge(v string)

	// If applicablex, closes the context.
	Close()
}

// Context returns the context of c.
// c must be mounted.
func Context(c markup.Componer) (ctx Contexter, err error) {
	var root *markup.Element

	if root, err = markup.ComponentRoot(c); err != nil {
		return
	}

	ctx, err = ContextByID(root.ContextID)
	return
}

// ContextByID returns the context registered under id.
func ContextByID(id uid.ID) (ctx Contexter, err error) {
	var registered bool

	if ctx, registered = contexts[id]; !registered {
		err = fmt.Errorf("context %v is not registered or has been closed", id)
	}

	return
}

// RegisterContext registers c.
// Should be used only in a driver implementation.
func RegisterContext(c Contexter) {
	if len(c.ID()) == 0 {
		log.Panicf("context %T is invalid. ID must be set", c)
	}

	if _, registered := contexts[c.ID()]; registered {
		log.Panicf("context %T with id %v is already registered", c, c.ID())
	}

	contexts[c.ID()] = c
}

// UnregisterContext unregisters c.
// Should be used only in a driver implementation.
func UnregisterContext(c Contexter) {
	delete(contexts, c.ID())
}

// ZeroContext is a placeholder context.
// It's used as a replacement for non available or non implemented features.
//
// Use of methods from a ZeroContext doesn't do anything.
type ZeroContext struct {
	id          uid.ID
	placeholder string
	root        markup.Componer
}

// NewZeroContext creates a ZeroContext.
func NewZeroContext(placeholder string) (ctx *ZeroContext) {
	ctx = &ZeroContext{
		id:          uid.Context(),
		placeholder: placeholder,
	}

	RegisterContext(ctx)
	return
}

// ID returns the ID of the context.
func (c *ZeroContext) ID() uid.ID {
	return c.id
}

// Mount is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) Mount(component markup.Componer) {
	markup.Mount(component, c.ID())
	log.Infof("%T is mounted into %v (%v)", component, c.placeholder, c.ID())
}

// Render is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) Render(elem *markup.Element) {
	log.Infof("rendering:\n\033[32m%v\033[00m", elem.HTML())
}

// Size is a placeholder method to satisfy the Contexter interface.
func (c *ZeroContext) Size() (width float64, height float64) {
	return
}

// Resize is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) Resize(width float64, height float64) {
	log.Infof("%v (%v) simulates a resize of %v x %v", c.placeholder, c.ID(), width, height)
}

// Position is a placeholder method to satisfy the Contexter interface.
func (c *ZeroContext) Position() (x float64, y float64) {
	return
}

// Move is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) Move(x float64, y float64) {
	log.Infof("%v (%v) simulates a move to (%v, %v)", c.placeholder, c.ID(), x, y)
}

// SetIcon is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) SetIcon(path string) {
	log.Infof("%v (%v) simulates set icon with %v", c.placeholder, c.ID(), path)
}

// SetBadge is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) SetBadge(v string) {
	log.Infof("%v (%v) simulates set badge with %v", c.placeholder, c.ID(), v)

}

// Close is a closes the context.
func (c *ZeroContext) Close() {
	markup.Dismount(c.root)
	UnregisterContext(c)
	log.Infof("%v (%v) is closed", c.placeholder, c.ID())
}
<commit_msg>set badge arg<commit_after>package app

import (
	"fmt"

	"github.com/murlokswarm/log"
	"github.com/murlokswarm/markup"
	"github.com/murlokswarm/uid"
)

var (
	contexts = map[uid.ID]Contexter{}
)

// Contexter represents the support where a component can be mounted.
// eg a window.
type Contexter interface {
	// The ID of the context.
	ID() uid.ID

	// Mounts the component and renders it in the context.
	Mount(c markup.Componer)

	// Renders an element.
	Render(elem *markup.Element)

	// If applicable, returns the position of the context.
	Position() (x float64, y float64)

	// If applicable, moves the context.
	Move(x float64, y float64)

	// If applicable, returns the size of the context.
	Size() (width float64, height float64)

	// If applicable, resizes the context.
	Resize(width float64, height float64)

	// If applicable, set the icon targeted by path.
	SetIcon(path string)

	// If applicable, set the badge with v.
	// Uses fmt.Sprint under the hood.
	SetBadge(v interface{})

	// If applicablex, closes the context.
	Close()
}

// Context returns the context of c.
// c must be mounted.
func Context(c markup.Componer) (ctx Contexter, err error) {
	var root *markup.Element

	if root, err = markup.ComponentRoot(c); err != nil {
		return
	}

	ctx, err = ContextByID(root.ContextID)
	return
}

// ContextByID returns the context registered under id.
func ContextByID(id uid.ID) (ctx Contexter, err error) {
	var registered bool

	if ctx, registered = contexts[id]; !registered {
		err = fmt.Errorf("context %v is not registered or has been closed", id)
	}

	return
}

// RegisterContext registers c.
// Should be used only in a driver implementation.
func RegisterContext(c Contexter) {
	if len(c.ID()) == 0 {
		log.Panicf("context %T is invalid. ID must be set", c)
	}

	if _, registered := contexts[c.ID()]; registered {
		log.Panicf("context %T with id %v is already registered", c, c.ID())
	}

	contexts[c.ID()] = c
}

// UnregisterContext unregisters c.
// Should be used only in a driver implementation.
func UnregisterContext(c Contexter) {
	delete(contexts, c.ID())
}

// ZeroContext is a placeholder context.
// It's used as a replacement for non available or non implemented features.
//
// Use of methods from a ZeroContext doesn't do anything.
type ZeroContext struct {
	id          uid.ID
	placeholder string
	root        markup.Componer
}

// NewZeroContext creates a ZeroContext.
func NewZeroContext(placeholder string) (ctx *ZeroContext) {
	ctx = &ZeroContext{
		id:          uid.Context(),
		placeholder: placeholder,
	}

	RegisterContext(ctx)
	return
}

// ID returns the ID of the context.
func (c *ZeroContext) ID() uid.ID {
	return c.id
}

// Mount is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) Mount(component markup.Componer) {
	markup.Mount(component, c.ID())
	log.Infof("%T is mounted into %v (%v)", component, c.placeholder, c.ID())
}

// Render is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) Render(elem *markup.Element) {
	log.Infof("rendering:\n\033[32m%v\033[00m", elem.HTML())
}

// Size is a placeholder method to satisfy the Contexter interface.
func (c *ZeroContext) Size() (width float64, height float64) {
	return
}

// Resize is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) Resize(width float64, height float64) {
	log.Infof("%v (%v) simulates a resize of %v x %v", c.placeholder, c.ID(), width, height)
}

// Position is a placeholder method to satisfy the Contexter interface.
func (c *ZeroContext) Position() (x float64, y float64) {
	return
}

// Move is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) Move(x float64, y float64) {
	log.Infof("%v (%v) simulates a move to (%v, %v)", c.placeholder, c.ID(), x, y)
}

// SetIcon is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) SetIcon(path string) {
	log.Infof("%v (%v) simulates set icon with %v", c.placeholder, c.ID(), path)
}

// SetBadge is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) SetBadge(v interface{}) {
	log.Infof("%v (%v) simulates set badge with %v", c.placeholder, c.ID(), v)

}

// Close is a closes the context.
func (c *ZeroContext) Close() {
	markup.Dismount(c.root)
	UnregisterContext(c)
	log.Infof("%v (%v) is closed", c.placeholder, c.ID())
}
<|endoftext|>


<commit_before>// Copyright 2014 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package context

import (
	"net/http"
	"sync"
)

var (
	ctxs    = make(map[*http.Request]*Context)
	ctxsMux sync.Mutex
)

// 获取或是新建Context。
// 当传递nil值时，会触发panic。
func Get(r *http.Request) *Context {
	if r == nil {
		panic("参数r不能为空")
	}

	ctxsMux.Lock()
	defer ctxsMux.Unlock()

	ctx, found := ctxs[r]
	if !found {
		ctx = &Context{items: make(map[interface{}]interface{})}
		ctxs[r] = ctx
	}

	return ctx
}

// 释放Context。
func Free(r *http.Request) {
	if r == nil {
		return
	}

	ctxsMux.Lock()
	defer ctxsMux.Unlock()

	delete(ctxs, r)
}

// 与http.Request相关联的上下文环境
type Context struct {
	sync.Mutex
	items map[interface{}]interface{}
}

// 查找key对应的值。在没有查到的情况下，found返回false
func (ctx *Context) Get(key interface{}) (val interface{}, found bool) {
	ctx.Lock()
	defer ctx.Unlock()

	val, found = ctx.items[key]
	return
}

// 功能与Get()相同，在没有找到相关值的情况下，会返回def，
// 但该值不会写入到Context中，下次用Get()依然会返回false
func (ctx *Context) MustGet(key, def interface{}) interface{} {
	ctx.Lock()
	defer ctx.Unlock()

	val, found := ctx.items[key]
	if !found {
		return def
	} else {
		return val
	}
}

// 设置或是添加一个键值对。
func (ctx *Context) Set(key, val interface{}) {
	ctx.Lock()
	defer ctx.Unlock()

	ctx.items[key] = val
}

// 添加一个键值对，若该键名已经存在，则不作任何操作，
// 并且ok返回false，表示操作没有成功
func (ctx *Context) Add(key, val interface{}) (ok bool) {
	ctx.Lock()
	defer ctx.Unlock()

	_, found := ctx.items[key]
	if found {
		return false
	}

	ctx.items[key] = val
	return true
}
<commit_msg>去掉一些简单函数的defer语句，为一个锁而defer，开销有点大<commit_after>// Copyright 2014 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package context

import (
	"net/http"
	"sync"
)

var (
	ctxs    = make(map[*http.Request]*Context)
	ctxsMux sync.Mutex
)

// 获取或是新建Context。
// 当传递nil值时，会触发panic。
func Get(r *http.Request) *Context {
	if r == nil {
		panic("参数r不能为空")
	}

	ctxsMux.Lock()

	ctx, found := ctxs[r]
	if !found {
		ctx = &Context{items: make(map[interface{}]interface{})}
		ctxs[r] = ctx
	}

	ctxsMux.Unlock() // Unlock
	return ctx
}

// 释放Context。
func Free(r *http.Request) {
	if r == nil {
		return
	}

	ctxsMux.Lock()
	delete(ctxs, r)
	ctxsMux.Unlock()
}

// 与http.Request相关联的上下文环境
type Context struct {
	sync.Mutex
	items map[interface{}]interface{}
}

// 查找key对应的值。在没有查到的情况下，found返回false
func (ctx *Context) Get(key interface{}) (val interface{}, found bool) {
	ctx.Lock()
	val, found = ctx.items[key]
	ctx.Unlock()
	return
}

// 功能与Get()相同，在没有找到相关值的情况下，会返回def，
// 但该值不会写入到Context中，下次用Get()依然会返回false
func (ctx *Context) MustGet(key, def interface{}) interface{} {
	ctx.Lock()
	val, found := ctx.items[key]
	ctx.Unlock()

	if !found {
		return def
	} else {
		return val
	}
}

// 设置或是添加一个键值对。
func (ctx *Context) Set(key, val interface{}) {
	ctx.Lock()
	ctx.items[key] = val
	ctx.Unlock()
}

// 添加一个键值对，若该键名已经存在，则不作任何操作，
// 并且ok返回false，表示操作没有成功
func (ctx *Context) Add(key, val interface{}) (ok bool) {
	ctx.Lock()

	_, found := ctx.items[key]
	if !found {
		ctx.items[key] = val
	}

	ctx.Unlock()
	return !found
}
<|endoftext|>


<commit_before>package script

import (
	"os"

	"github.com/spf13/afero"
)

// Context for script operations. A Context includes the working directory and provides
// access the buffers and results of commands run in the Context.
// Using different Contexts it is possible to handle multiple separate environments.
type Context struct {
	workingDir string
	env        map[string]string
	fs         afero.Fs
	stdout     *os.File
	stderr     *os.File
}

// NewContext returns a pointer to a new Context.
func NewContext() (context *Context) {
	// initialize Context
	context = &Context{}
	context.env = make(map[string]string, 0)
	context.fs = afero.NewOsFs()
	context.stdout = os.Stdout
	context.stderr = os.Stderr

	cwd, err := os.Getwd()
	if err == nil {
		context.SetWorkingDir(cwd)
	}
	return
}

// SetWorkingDir changes the current working dir
func (c *Context) SetWorkingDir(workingDir string) {
	c.workingDir = workingDir
}

// WorkingDir retrieves the current working dir
func (c *Context) WorkingDir() string {
	return c.workingDir
}

// IsUserRoot checks if a user is root priviledged (Linux only? Mac? Windows?)
func (c *Context) IsUserRoot() bool {
	return os.Geteuid() == 0
}
<commit_msg>Improve documentation<commit_after>package script

import (
	"os"

	"github.com/spf13/afero"
)

// Context for script operations. A Context includes the working directory and provides
// access the buffers and results of commands run in the Context.
// Using different Contexts it is possible to handle multiple separate environments.
type Context struct {
	workingDir string
	env        map[string]string
	fs         afero.Fs
	stdout     *os.File
	stderr     *os.File
}

// NewContext returns a pointer to a new Context.
func NewContext() (context *Context) {
	// initialize Context
	context = &Context{}
	context.env = make(map[string]string, 0)
	context.fs = afero.NewOsFs()
	context.stdout = os.Stdout
	context.stderr = os.Stderr

	cwd, err := os.Getwd()
	if err == nil {
		context.SetWorkingDir(cwd)
	}
	return
}

// SetWorkingDir changes the current working dir
func (c *Context) SetWorkingDir(workingDir string) {
	c.workingDir = workingDir
}

// WorkingDir retrieves the current working dir
func (c *Context) WorkingDir() string {
	return c.workingDir
}

// IsUserRoot checks if a user is root priviledged (Linux and Mac only? Windows?)
func (c *Context) IsUserRoot() bool {
	return os.Geteuid() == 0
}
<|endoftext|>


<commit_before>package gocql

import (
	"context"
	crand "crypto/rand"
	"errors"
	"fmt"
	"math/rand"
	"net"
	"regexp"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
)

var (
	randr    *rand.Rand
	mutRandr sync.Mutex
)

func init() {
	b := make([]byte, 4)
	if _, err := crand.Read(b); err != nil {
		panic(fmt.Sprintf("unable to seed random number generator: %v", err))
	}

	randr = rand.New(rand.NewSource(int64(readInt(b))))
}

// Ensure that the atomic variable is aligned to a 64bit boundary
// so that atomic operations can be applied on 32bit architectures.
type controlConn struct {
	session *Session
	conn    atomic.Value

	retry RetryPolicy

	started int32
	quit    chan struct{}
}

func createControlConn(session *Session) *controlConn {
	control := &controlConn{
		session: session,
		quit:    make(chan struct{}),
		retry:   &SimpleRetryPolicy{NumRetries: 3},
	}

	control.conn.Store((*connHost)(nil))

	return control
}

func (c *controlConn) heartBeat() {
	if !atomic.CompareAndSwapInt32(&c.started, 0, 1) {
		return
	}

	sleepTime := 1 * time.Second

	for {
		select {
		case <-c.quit:
			return
		case <-time.After(sleepTime):
		}

		resp, err := c.writeFrame(&writeOptionsFrame{})
		if err != nil {
			goto reconn
		}

		switch resp.(type) {
		case *supportedFrame:
			// Everything ok
			sleepTime = 5 * time.Second
			continue
		case error:
			goto reconn
		default:
			panic(fmt.Sprintf("gocql: unknown frame in response to options: %T", resp))
		}

	reconn:
		// try to connect a bit faster
		sleepTime = 1 * time.Second
		c.reconnect(true)
		// time.Sleep(5 * time.Second)
		continue
	}
}

var hostLookupPreferV4 = false

func hostInfo(addr string, defaultPort int) (*HostInfo, error) {
	var port int
	host, portStr, err := net.SplitHostPort(addr)
	if err != nil {
		host = addr
		port = defaultPort
	} else {
		port, err = strconv.Atoi(portStr)
		if err != nil {
			return nil, err
		}
	}

	ip := net.ParseIP(host)
	if ip == nil {
		ips, err := net.LookupIP(host)
		if err != nil {
			return nil, err
		} else if len(ips) == 0 {
			return nil, fmt.Errorf("No IP's returned from DNS lookup for %q", addr)
		}

		if hostLookupPreferV4 {
			for _, v := range ips {
				if v4 := v.To4(); v4 != nil {
					ip = v4
					break
				}
			}
			if ip == nil {
				ip = ips[0]
			}
		} else {
			// TODO(zariel): should we check that we can connect to any of the ips?
			ip = ips[0]
		}

	}

	return &HostInfo{connectAddress: ip, port: port}, nil
}

func shuffleHosts(hosts []*HostInfo) []*HostInfo {
	mutRandr.Lock()
	perm := randr.Perm(len(hosts))
	mutRandr.Unlock()
	shuffled := make([]*HostInfo, len(hosts))

	for i, host := range hosts {
		shuffled[perm[i]] = host
	}

	return shuffled
}

func (c *controlConn) shuffleDial(endpoints []*HostInfo) (*Conn, error) {
	// shuffle endpoints so not all drivers will connect to the same initial
	// node.
	shuffled := shuffleHosts(endpoints)

	var err error
	for _, host := range shuffled {
		var conn *Conn
		conn, err = c.session.connect(host, c)
		if err == nil {
			return conn, nil
		}

		Logger.Printf("gocql: unable to dial control conn %v: %v\n", host.ConnectAddress(), err)
	}

	return nil, err
}

// this is going to be version dependant and a nightmare to maintain :(
var protocolSupportRe = regexp.MustCompile(`the lowest supported version is \d+ and the greatest is (\d+)$`)

func parseProtocolFromError(err error) int {
	// I really wish this had the actual info in the error frame...
	matches := protocolSupportRe.FindAllStringSubmatch(err.Error(), -1)
	if len(matches) != 1 || len(matches[0]) != 2 {
		if verr, ok := err.(*protocolError); ok {
			return int(verr.frame.Header().version.version())
		}
		return 0
	}

	max, err := strconv.Atoi(matches[0][1])
	if err != nil {
		return 0
	}

	return max
}

func (c *controlConn) discoverProtocol(hosts []*HostInfo) (int, error) {
	hosts = shuffleHosts(hosts)

	connCfg := *c.session.connCfg
	connCfg.ProtoVersion = 4 // TODO: define maxProtocol

	handler := connErrorHandlerFn(func(c *Conn, err error, closed bool) {
		// we should never get here, but if we do it means we connected to a
		// host successfully which means our attempted protocol version worked
		if !closed {
			c.Close()
		}
	})

	var err error
	for _, host := range hosts {
		var conn *Conn
		conn, err = c.session.dial(host.ConnectAddress(), host.Port(), &connCfg, handler)
		if conn != nil {
			conn.Close()
		}

		if err == nil {
			return connCfg.ProtoVersion, nil
		}

		if proto := parseProtocolFromError(err); proto > 0 {
			return proto, nil
		}
	}

	return 0, err
}

func (c *controlConn) connect(hosts []*HostInfo) error {
	if len(hosts) == 0 {
		return errors.New("control: no endpoints specified")
	}

	conn, err := c.shuffleDial(hosts)
	if err != nil {
		return fmt.Errorf("control: unable to connect to initial hosts: %v", err)
	}

	if err := c.setupConn(conn); err != nil {
		conn.Close()
		return fmt.Errorf("control: unable to setup connection: %v", err)
	}

	// we could fetch the initial ring here and update initial host data. So that
	// when we return from here we have a ring topology ready to go.

	go c.heartBeat()

	return nil
}

type connHost struct {
	conn *Conn
	host *HostInfo
}

func (c *controlConn) setupConn(conn *Conn) error {
	if err := c.registerEvents(conn); err != nil {
		conn.Close()
		return err
	}

	// TODO(zariel): do we need to fetch host info everytime
	// the control conn connects? Surely we have it cached?
	host, err := conn.localHostInfo()
	if err != nil {
		return err
	}

	ch := &connHost{
		conn: conn,
		host: host,
	}

	c.conn.Store(ch)
	// c.session.handleNodeUp(host.ConnectAddress(), host.Port(), false)

	return nil
}

func (c *controlConn) registerEvents(conn *Conn) error {
	var events []string

	if !c.session.cfg.Events.DisableTopologyEvents {
		events = append(events, "TOPOLOGY_CHANGE")
	}
	if !c.session.cfg.Events.DisableNodeStatusEvents {
		events = append(events, "STATUS_CHANGE")
	}
	if !c.session.cfg.Events.DisableSchemaEvents {
		events = append(events, "SCHEMA_CHANGE")
	}

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

	framer, err := conn.exec(context.Background(),
		&writeRegisterFrame{
			events: events,
		}, nil)
	if err != nil {
		return err
	}

	frame, err := framer.parseFrame()
	if err != nil {
		return err
	} else if _, ok := frame.(*readyFrame); !ok {
		return fmt.Errorf("unexpected frame in response to register: got %T: %v\n", frame, frame)
	}

	return nil
}

func (c *controlConn) reconnect(refreshring bool) {
	// TODO: simplify this function, use session.ring to get hosts instead of the
	// connection pool

	var host *HostInfo
	ch := c.getConn()
	if ch != nil {
		host = ch.host
		ch.conn.Close()
	}

	var newConn *Conn
	if host != nil {
		// try to connect to the old host
		conn, err := c.session.connect(host, c)
		if err != nil {
			// host is dead
			// TODO: this is replicated in a few places
			c.session.handleNodeDown(host.ConnectAddress(), host.Port())
		} else {
			newConn = conn
		}
	}

	// TODO: should have our own round-robin for hosts so that we can try each
	// in succession and guarantee that we get a different host each time.
	if newConn == nil {
		host := c.session.ring.rrHost()
		if host == nil {
			c.connect(c.session.ring.endpoints)
			return
		}

		var err error
		newConn, err = c.session.connect(host, c)
		if err != nil {
			// TODO: add log handler for things like this
			return
		}
	}

	if err := c.setupConn(newConn); err != nil {
		newConn.Close()
		Logger.Printf("gocql: control unable to register events: %v\n", err)
		return
	}

	if refreshring {
		c.session.hostSource.refreshRing()
	}
}

func (c *controlConn) HandleError(conn *Conn, err error, closed bool) {
	if !closed {
		return
	}

	oldConn := c.getConn()
	if oldConn.conn != conn {
		return
	}

	c.reconnect(true)
}

func (c *controlConn) getConn() *connHost {
	return c.conn.Load().(*connHost)
}

func (c *controlConn) writeFrame(w frameWriter) (frame, error) {
	ch := c.getConn()
	if ch == nil {
		return nil, errNoControl
	}

	framer, err := ch.conn.exec(context.Background(), w, nil)
	if err != nil {
		return nil, err
	}

	return framer.parseFrame()
}

func (c *controlConn) withConnHost(fn func(*connHost) *Iter) *Iter {
	const maxConnectAttempts = 5
	connectAttempts := 0

	for i := 0; i < maxConnectAttempts; i++ {
		ch := c.getConn()
		if ch == nil {
			if connectAttempts > maxConnectAttempts {
				break
			}

			connectAttempts++

			c.reconnect(false)
			continue
		}

		return fn(ch)
	}

	return &Iter{err: errNoControl}
}

func (c *controlConn) withConn(fn func(*Conn) *Iter) *Iter {
	return c.withConnHost(func(ch *connHost) *Iter {
		return fn(ch.conn)
	})
}

// query will return nil if the connection is closed or nil
func (c *controlConn) query(statement string, values ...interface{}) (iter *Iter) {
	q := c.session.Query(statement, values...).Consistency(One).RoutingKey([]byte{}).Trace(nil)

	for {
		iter = c.withConn(func(conn *Conn) *Iter {
			return conn.executeQuery(q)
		})

		if gocqlDebug && iter.err != nil {
			Logger.Printf("control: error executing %q: %v\n", statement, iter.err)
		}

		q.attempts++
		if iter.err == nil || !c.retry.Attempt(q) {
			break
		}
	}

	return
}

func (c *controlConn) awaitSchemaAgreement() error {
	return c.withConn(func(conn *Conn) *Iter {
		return &Iter{err: conn.awaitSchemaAgreement()}
	}).err
}

func (c *controlConn) close() {
	if atomic.CompareAndSwapInt32(&c.started, 1, -1) {
		c.quit <- struct{}{}
	}

	ch := c.getConn()
	if ch != nil {
		ch.conn.Close()
	}
}

var errNoControl = errors.New("gocql: no control connection available")
<commit_msg>enable ipv4 preferred lookup using an environment variable<commit_after>package gocql

import (
	"context"
	crand "crypto/rand"
	"errors"
	"fmt"
	"math/rand"
	"net"
	"os"
	"regexp"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
)

var (
	randr    *rand.Rand
	mutRandr sync.Mutex
)

func init() {
	b := make([]byte, 4)
	if _, err := crand.Read(b); err != nil {
		panic(fmt.Sprintf("unable to seed random number generator: %v", err))
	}

	randr = rand.New(rand.NewSource(int64(readInt(b))))
}

// Ensure that the atomic variable is aligned to a 64bit boundary
// so that atomic operations can be applied on 32bit architectures.
type controlConn struct {
	session *Session
	conn    atomic.Value

	retry RetryPolicy

	started int32
	quit    chan struct{}
}

func createControlConn(session *Session) *controlConn {
	control := &controlConn{
		session: session,
		quit:    make(chan struct{}),
		retry:   &SimpleRetryPolicy{NumRetries: 3},
	}

	control.conn.Store((*connHost)(nil))

	return control
}

func (c *controlConn) heartBeat() {
	if !atomic.CompareAndSwapInt32(&c.started, 0, 1) {
		return
	}

	sleepTime := 1 * time.Second

	for {
		select {
		case <-c.quit:
			return
		case <-time.After(sleepTime):
		}

		resp, err := c.writeFrame(&writeOptionsFrame{})
		if err != nil {
			goto reconn
		}

		switch resp.(type) {
		case *supportedFrame:
			// Everything ok
			sleepTime = 5 * time.Second
			continue
		case error:
			goto reconn
		default:
			panic(fmt.Sprintf("gocql: unknown frame in response to options: %T", resp))
		}

	reconn:
		// try to connect a bit faster
		sleepTime = 1 * time.Second
		c.reconnect(true)
		// time.Sleep(5 * time.Second)
		continue
	}
}

var hostLookupPreferV4 = os.Getenv("GOCQL_HOST_LOOKUP_PREFER_V4") == "true"

func hostInfo(addr string, defaultPort int) (*HostInfo, error) {
	var port int
	host, portStr, err := net.SplitHostPort(addr)
	if err != nil {
		host = addr
		port = defaultPort
	} else {
		port, err = strconv.Atoi(portStr)
		if err != nil {
			return nil, err
		}
	}

	ip := net.ParseIP(host)
	if ip == nil {
		ips, err := net.LookupIP(host)
		if err != nil {
			return nil, err
		} else if len(ips) == 0 {
			return nil, fmt.Errorf("No IP's returned from DNS lookup for %q", addr)
		}

		if hostLookupPreferV4 {
			for _, v := range ips {
				if v4 := v.To4(); v4 != nil {
					ip = v4
					break
				}
			}
			if ip == nil {
				ip = ips[0]
			}
		} else {
			// TODO(zariel): should we check that we can connect to any of the ips?
			ip = ips[0]
		}

	}

	return &HostInfo{connectAddress: ip, port: port}, nil
}

func shuffleHosts(hosts []*HostInfo) []*HostInfo {
	mutRandr.Lock()
	perm := randr.Perm(len(hosts))
	mutRandr.Unlock()
	shuffled := make([]*HostInfo, len(hosts))

	for i, host := range hosts {
		shuffled[perm[i]] = host
	}

	return shuffled
}

func (c *controlConn) shuffleDial(endpoints []*HostInfo) (*Conn, error) {
	// shuffle endpoints so not all drivers will connect to the same initial
	// node.
	shuffled := shuffleHosts(endpoints)

	var err error
	for _, host := range shuffled {
		var conn *Conn
		conn, err = c.session.connect(host, c)
		if err == nil {
			return conn, nil
		}

		Logger.Printf("gocql: unable to dial control conn %v: %v\n", host.ConnectAddress(), err)
	}

	return nil, err
}

// this is going to be version dependant and a nightmare to maintain :(
var protocolSupportRe = regexp.MustCompile(`the lowest supported version is \d+ and the greatest is (\d+)$`)

func parseProtocolFromError(err error) int {
	// I really wish this had the actual info in the error frame...
	matches := protocolSupportRe.FindAllStringSubmatch(err.Error(), -1)
	if len(matches) != 1 || len(matches[0]) != 2 {
		if verr, ok := err.(*protocolError); ok {
			return int(verr.frame.Header().version.version())
		}
		return 0
	}

	max, err := strconv.Atoi(matches[0][1])
	if err != nil {
		return 0
	}

	return max
}

func (c *controlConn) discoverProtocol(hosts []*HostInfo) (int, error) {
	hosts = shuffleHosts(hosts)

	connCfg := *c.session.connCfg
	connCfg.ProtoVersion = 4 // TODO: define maxProtocol

	handler := connErrorHandlerFn(func(c *Conn, err error, closed bool) {
		// we should never get here, but if we do it means we connected to a
		// host successfully which means our attempted protocol version worked
		if !closed {
			c.Close()
		}
	})

	var err error
	for _, host := range hosts {
		var conn *Conn
		conn, err = c.session.dial(host.ConnectAddress(), host.Port(), &connCfg, handler)
		if conn != nil {
			conn.Close()
		}

		if err == nil {
			return connCfg.ProtoVersion, nil
		}

		if proto := parseProtocolFromError(err); proto > 0 {
			return proto, nil
		}
	}

	return 0, err
}

func (c *controlConn) connect(hosts []*HostInfo) error {
	if len(hosts) == 0 {
		return errors.New("control: no endpoints specified")
	}

	conn, err := c.shuffleDial(hosts)
	if err != nil {
		return fmt.Errorf("control: unable to connect to initial hosts: %v", err)
	}

	if err := c.setupConn(conn); err != nil {
		conn.Close()
		return fmt.Errorf("control: unable to setup connection: %v", err)
	}

	// we could fetch the initial ring here and update initial host data. So that
	// when we return from here we have a ring topology ready to go.

	go c.heartBeat()

	return nil
}

type connHost struct {
	conn *Conn
	host *HostInfo
}

func (c *controlConn) setupConn(conn *Conn) error {
	if err := c.registerEvents(conn); err != nil {
		conn.Close()
		return err
	}

	// TODO(zariel): do we need to fetch host info everytime
	// the control conn connects? Surely we have it cached?
	host, err := conn.localHostInfo()
	if err != nil {
		return err
	}

	ch := &connHost{
		conn: conn,
		host: host,
	}

	c.conn.Store(ch)
	// c.session.handleNodeUp(host.ConnectAddress(), host.Port(), false)

	return nil
}

func (c *controlConn) registerEvents(conn *Conn) error {
	var events []string

	if !c.session.cfg.Events.DisableTopologyEvents {
		events = append(events, "TOPOLOGY_CHANGE")
	}
	if !c.session.cfg.Events.DisableNodeStatusEvents {
		events = append(events, "STATUS_CHANGE")
	}
	if !c.session.cfg.Events.DisableSchemaEvents {
		events = append(events, "SCHEMA_CHANGE")
	}

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

	framer, err := conn.exec(context.Background(),
		&writeRegisterFrame{
			events: events,
		}, nil)
	if err != nil {
		return err
	}

	frame, err := framer.parseFrame()
	if err != nil {
		return err
	} else if _, ok := frame.(*readyFrame); !ok {
		return fmt.Errorf("unexpected frame in response to register: got %T: %v\n", frame, frame)
	}

	return nil
}

func (c *controlConn) reconnect(refreshring bool) {
	// TODO: simplify this function, use session.ring to get hosts instead of the
	// connection pool

	var host *HostInfo
	ch := c.getConn()
	if ch != nil {
		host = ch.host
		ch.conn.Close()
	}

	var newConn *Conn
	if host != nil {
		// try to connect to the old host
		conn, err := c.session.connect(host, c)
		if err != nil {
			// host is dead
			// TODO: this is replicated in a few places
			c.session.handleNodeDown(host.ConnectAddress(), host.Port())
		} else {
			newConn = conn
		}
	}

	// TODO: should have our own round-robin for hosts so that we can try each
	// in succession and guarantee that we get a different host each time.
	if newConn == nil {
		host := c.session.ring.rrHost()
		if host == nil {
			c.connect(c.session.ring.endpoints)
			return
		}

		var err error
		newConn, err = c.session.connect(host, c)
		if err != nil {
			// TODO: add log handler for things like this
			return
		}
	}

	if err := c.setupConn(newConn); err != nil {
		newConn.Close()
		Logger.Printf("gocql: control unable to register events: %v\n", err)
		return
	}

	if refreshring {
		c.session.hostSource.refreshRing()
	}
}

func (c *controlConn) HandleError(conn *Conn, err error, closed bool) {
	if !closed {
		return
	}

	oldConn := c.getConn()
	if oldConn.conn != conn {
		return
	}

	c.reconnect(true)
}

func (c *controlConn) getConn() *connHost {
	return c.conn.Load().(*connHost)
}

func (c *controlConn) writeFrame(w frameWriter) (frame, error) {
	ch := c.getConn()
	if ch == nil {
		return nil, errNoControl
	}

	framer, err := ch.conn.exec(context.Background(), w, nil)
	if err != nil {
		return nil, err
	}

	return framer.parseFrame()
}

func (c *controlConn) withConnHost(fn func(*connHost) *Iter) *Iter {
	const maxConnectAttempts = 5
	connectAttempts := 0

	for i := 0; i < maxConnectAttempts; i++ {
		ch := c.getConn()
		if ch == nil {
			if connectAttempts > maxConnectAttempts {
				break
			}

			connectAttempts++

			c.reconnect(false)
			continue
		}

		return fn(ch)
	}

	return &Iter{err: errNoControl}
}

func (c *controlConn) withConn(fn func(*Conn) *Iter) *Iter {
	return c.withConnHost(func(ch *connHost) *Iter {
		return fn(ch.conn)
	})
}

// query will return nil if the connection is closed or nil
func (c *controlConn) query(statement string, values ...interface{}) (iter *Iter) {
	q := c.session.Query(statement, values...).Consistency(One).RoutingKey([]byte{}).Trace(nil)

	for {
		iter = c.withConn(func(conn *Conn) *Iter {
			return conn.executeQuery(q)
		})

		if gocqlDebug && iter.err != nil {
			Logger.Printf("control: error executing %q: %v\n", statement, iter.err)
		}

		q.attempts++
		if iter.err == nil || !c.retry.Attempt(q) {
			break
		}
	}

	return
}

func (c *controlConn) awaitSchemaAgreement() error {
	return c.withConn(func(conn *Conn) *Iter {
		return &Iter{err: conn.awaitSchemaAgreement()}
	}).err
}

func (c *controlConn) close() {
	if atomic.CompareAndSwapInt32(&c.started, 1, -1) {
		c.quit <- struct{}{}
	}

	ch := c.getConn()
	if ch != nil {
		ch.conn.Close()
	}
}

var errNoControl = errors.New("gocql: no control connection available")
<|endoftext|>


<commit_before>// Copyright (c) 2012 The gocql 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 gocql

import (
	"database/sql/driver"
	"encoding/binary"
	"fmt"
	"github.com/tux21b/gocql/uuid"
	"math"
	"reflect"
	"strconv"
	"time"
)

const (
	typeCustom    uint16 = 0x0000
	typeAscii     uint16 = 0x0001
	typeBigInt    uint16 = 0x0002
	typeBlob      uint16 = 0x0003
	typeBool      uint16 = 0x0004
	typeCounter   uint16 = 0x0005
	typeDecimal   uint16 = 0x0006
	typeDouble    uint16 = 0x0007
	typeFloat     uint16 = 0x0008
	typeInt       uint16 = 0x0009
	typeText      uint16 = 0x000A
	typeTimestamp uint16 = 0x000B
	typeUUID      uint16 = 0x000C
	typeVarchar   uint16 = 0x000D
	typeVarint    uint16 = 0x000E
	typeTimeUUID  uint16 = 0x000F
	typeList      uint16 = 0x0020
	typeMap       uint16 = 0x0021
	typeSet       uint16 = 0x0022
)

func decode(b []byte, t uint16) driver.Value {
	switch t {
	case typeBool:
		if len(b) >= 1 && b[0] != 0 {
			return true
		}
		return false
	case typeBlob:
		return b
	case typeVarchar, typeText, typeAscii:
		return b
	case typeInt:
		return int64(int32(binary.BigEndian.Uint32(b)))
	case typeBigInt:
		return int64(binary.BigEndian.Uint64(b))
	case typeFloat:
		return float64(math.Float32frombits(binary.BigEndian.Uint32(b)))
	case typeDouble:
		return math.Float64frombits(binary.BigEndian.Uint64(b))
	case typeTimestamp:
		t := int64(binary.BigEndian.Uint64(b))
		sec := t / 1000
		nsec := (t - sec*1000) * 1000000
		return time.Unix(sec, nsec)
	case typeUUID, typeTimeUUID:
		return uuid.FromBytes(b)
	default:
		panic("unsupported type")
	}
	return b
}

type columnEncoder struct {
	columnTypes []uint16
}

func (e *columnEncoder) ColumnConverter(idx int) ValueConverter {
	switch e.columnTypes[idx] {
	case typeInt:
		return ValueConverter(encInt)
	case typeBigInt:
		return ValueConverter(encBigInt)
	case typeFloat:
		return ValueConverter(encFloat)
	case typeDouble:
		return ValueConverter(encDouble)
	case typeBool:
		return ValueConverter(encBool)
	case typeVarchar, typeText, typeAscii:
		return ValueConverter(encVarchar)
	case typeBlob:
		return ValueConverter(encBlob)
	case typeTimestamp:
		return ValueConverter(encTimestamp)
	case typeUUID, typeTimeUUID:
		return ValueConverter(encUUID)
	}
	panic("not implemented")
}

type ValueConverter func(v interface{}) (driver.Value, error)

func (vc ValueConverter) ConvertValue(v interface{}) (driver.Value, error) {
	return vc(v)
}

func encBool(v interface{}) (driver.Value, error) {
	b, err := driver.Bool.ConvertValue(v)
	if err != nil {
		return nil, err
	}
	if b.(bool) {
		return []byte{1}, nil
	}
	return []byte{0}, nil
}

func encInt(v interface{}) (driver.Value, error) {
	x, err := driver.Int32.ConvertValue(v)
	if err != nil {
		return nil, err
	}
	b := make([]byte, 4)
	binary.BigEndian.PutUint32(b, uint32(x.(int64)))
	return b, nil
}

func encBigInt(v interface{}) (driver.Value, error) {
	x := reflect.Indirect(reflect.ValueOf(v)).Interface()
	b := make([]byte, 8)
	binary.BigEndian.PutUint64(b, uint64(x.(int64)))
	return b, nil
}

func encVarchar(v interface{}) (driver.Value, error) {
	x, err := driver.String.ConvertValue(v)
	if err != nil {
		return nil, err
	}
	return []byte(x.(string)), nil
}

func encFloat(v interface{}) (driver.Value, error) {
	x, err := driver.DefaultParameterConverter.ConvertValue(v)
	if err != nil {
		return nil, err
	}
	var f float64
	switch x := x.(type) {
	case float64:
		f = x
	case int64:
		f = float64(x)
	case []byte:
		if f, err = strconv.ParseFloat(string(x), 64); err != nil {
			return nil, err
		}
	default:
		return nil, fmt.Errorf("can not convert %T to float64", x)
	}
	b := make([]byte, 4)
	binary.BigEndian.PutUint32(b, math.Float32bits(float32(f)))
	return b, nil
}

func encDouble(v interface{}) (driver.Value, error) {
	x, err := driver.DefaultParameterConverter.ConvertValue(v)
	if err != nil {
		return nil, err
	}
	var f float64
	switch x := x.(type) {
	case float64:
		f = x
	case int64:
		f = float64(x)
	case []byte:
		if f, err = strconv.ParseFloat(string(x), 64); err != nil {
			return nil, err
		}
	default:
		return nil, fmt.Errorf("can not convert %T to float64", x)
	}
	b := make([]byte, 8)
	binary.BigEndian.PutUint64(b, math.Float64bits(f))
	return b, nil
}

func encTimestamp(v interface{}) (driver.Value, error) {
	x, err := driver.DefaultParameterConverter.ConvertValue(v)
	if err != nil {
		return nil, err
	}
	var millis int64
	switch x := x.(type) {
	case time.Time:
		x = x.In(time.UTC)
		millis = x.UnixNano() / 1000000
	default:
		return nil, fmt.Errorf("can not convert %T to a timestamp", x)
	}
	b := make([]byte, 8)
	binary.BigEndian.PutUint64(b, uint64(millis))
	return b, nil
}

func encBlob(v interface{}) (driver.Value, error) {
	x, err := driver.DefaultParameterConverter.ConvertValue(v)
	if err != nil {
		return nil, err
	}
	var b []byte
	switch x := x.(type) {
	case string:
		b = []byte(x)
	case []byte:
		b = x
	default:
		return nil, fmt.Errorf("can not convert %T to a []byte", x)
	}
	return b, nil
}

func encUUID(v interface{}) (driver.Value, error) {
	var u uuid.UUID
	switch v := v.(type) {
	case string:
		var err error
		u, err = uuid.ParseUUID(v)
		if err != nil {
			return nil, err
		}
	case []byte:
		u = uuid.FromBytes(v)
	case uuid.UUID:
		u = v
	default:
		return nil, fmt.Errorf("can not convert %T to a UUID", v)
	}
	return u.Bytes(), nil
}
<commit_msg>Adding support for counter columns while reading<commit_after>// Copyright (c) 2012 The gocql 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 gocql

import (
	"database/sql/driver"
	"encoding/binary"
	"fmt"
	"github.com/tux21b/gocql/uuid"
	"math"
	"reflect"
	"strconv"
	"time"
)

const (
	typeCustom    uint16 = 0x0000
	typeAscii     uint16 = 0x0001
	typeBigInt    uint16 = 0x0002
	typeBlob      uint16 = 0x0003
	typeBool      uint16 = 0x0004
	typeCounter   uint16 = 0x0005
	typeDecimal   uint16 = 0x0006
	typeDouble    uint16 = 0x0007
	typeFloat     uint16 = 0x0008
	typeInt       uint16 = 0x0009
	typeText      uint16 = 0x000A
	typeTimestamp uint16 = 0x000B
	typeUUID      uint16 = 0x000C
	typeVarchar   uint16 = 0x000D
	typeVarint    uint16 = 0x000E
	typeTimeUUID  uint16 = 0x000F
	typeList      uint16 = 0x0020
	typeMap       uint16 = 0x0021
	typeSet       uint16 = 0x0022
)

func decode(b []byte, t uint16) driver.Value {
	switch t {
	case typeBool:
		if len(b) >= 1 && b[0] != 0 {
			return true
		}
		return false
	case typeBlob:
		return b
	case typeVarchar, typeText, typeAscii:
		return b
	case typeInt:
		return int64(int32(binary.BigEndian.Uint32(b)))
	case typeBigInt,typeCounter :
		return int64(binary.BigEndian.Uint64(b))
	case typeFloat:
		return float64(math.Float32frombits(binary.BigEndian.Uint32(b)))
	case typeDouble:
		return math.Float64frombits(binary.BigEndian.Uint64(b))
	case typeTimestamp:
		t := int64(binary.BigEndian.Uint64(b))
		sec := t / 1000
		nsec := (t - sec*1000) * 1000000
		return time.Unix(sec, nsec)
	case typeUUID, typeTimeUUID:
		return uuid.FromBytes(b)
	default:
		panic("unsupported type")
	}
	return b
}

type columnEncoder struct {
	columnTypes []uint16
}

func (e *columnEncoder) ColumnConverter(idx int) ValueConverter {
	switch e.columnTypes[idx] {
	case typeInt:
		return ValueConverter(encInt)
	case typeBigInt:
		return ValueConverter(encBigInt)
	case typeFloat:
		return ValueConverter(encFloat)
	case typeDouble:
		return ValueConverter(encDouble)
	case typeBool:
		return ValueConverter(encBool)
	case typeVarchar, typeText, typeAscii:
		return ValueConverter(encVarchar)
	case typeBlob:
		return ValueConverter(encBlob)
	case typeTimestamp:
		return ValueConverter(encTimestamp)
	case typeUUID, typeTimeUUID:
		return ValueConverter(encUUID)
	}
	panic("not implemented")
}

type ValueConverter func(v interface{}) (driver.Value, error)

func (vc ValueConverter) ConvertValue(v interface{}) (driver.Value, error) {
	return vc(v)
}

func encBool(v interface{}) (driver.Value, error) {
	b, err := driver.Bool.ConvertValue(v)
	if err != nil {
		return nil, err
	}
	if b.(bool) {
		return []byte{1}, nil
	}
	return []byte{0}, nil
}

func encInt(v interface{}) (driver.Value, error) {
	x, err := driver.Int32.ConvertValue(v)
	if err != nil {
		return nil, err
	}
	b := make([]byte, 4)
	binary.BigEndian.PutUint32(b, uint32(x.(int64)))
	return b, nil
}

func encBigInt(v interface{}) (driver.Value, error) {
	x := reflect.Indirect(reflect.ValueOf(v)).Interface()
	b := make([]byte, 8)
	binary.BigEndian.PutUint64(b, uint64(x.(int64)))
	return b, nil
}

func encVarchar(v interface{}) (driver.Value, error) {
	x, err := driver.String.ConvertValue(v)
	if err != nil {
		return nil, err
	}
	return []byte(x.(string)), nil
}

func encFloat(v interface{}) (driver.Value, error) {
	x, err := driver.DefaultParameterConverter.ConvertValue(v)
	if err != nil {
		return nil, err
	}
	var f float64
	switch x := x.(type) {
	case float64:
		f = x
	case int64:
		f = float64(x)
	case []byte:
		if f, err = strconv.ParseFloat(string(x), 64); err != nil {
			return nil, err
		}
	default:
		return nil, fmt.Errorf("can not convert %T to float64", x)
	}
	b := make([]byte, 4)
	binary.BigEndian.PutUint32(b, math.Float32bits(float32(f)))
	return b, nil
}

func encDouble(v interface{}) (driver.Value, error) {
	x, err := driver.DefaultParameterConverter.ConvertValue(v)
	if err != nil {
		return nil, err
	}
	var f float64
	switch x := x.(type) {
	case float64:
		f = x
	case int64:
		f = float64(x)
	case []byte:
		if f, err = strconv.ParseFloat(string(x), 64); err != nil {
			return nil, err
		}
	default:
		return nil, fmt.Errorf("can not convert %T to float64", x)
	}
	b := make([]byte, 8)
	binary.BigEndian.PutUint64(b, math.Float64bits(f))
	return b, nil
}

func encTimestamp(v interface{}) (driver.Value, error) {
	x, err := driver.DefaultParameterConverter.ConvertValue(v)
	if err != nil {
		return nil, err
	}
	var millis int64
	switch x := x.(type) {
	case time.Time:
		x = x.In(time.UTC)
		millis = x.UnixNano() / 1000000
	default:
		return nil, fmt.Errorf("can not convert %T to a timestamp", x)
	}
	b := make([]byte, 8)
	binary.BigEndian.PutUint64(b, uint64(millis))
	return b, nil
}

func encBlob(v interface{}) (driver.Value, error) {
	x, err := driver.DefaultParameterConverter.ConvertValue(v)
	if err != nil {
		return nil, err
	}
	var b []byte
	switch x := x.(type) {
	case string:
		b = []byte(x)
	case []byte:
		b = x
	default:
		return nil, fmt.Errorf("can not convert %T to a []byte", x)
	}
	return b, nil
}

func encUUID(v interface{}) (driver.Value, error) {
	var u uuid.UUID
	switch v := v.(type) {
	case string:
		var err error
		u, err = uuid.ParseUUID(v)
		if err != nil {
			return nil, err
		}
	case []byte:
		u = uuid.FromBytes(v)
	case uuid.UUID:
		u = v
	default:
		return nil, fmt.Errorf("can not convert %T to a UUID", v)
	}
	return u.Bytes(), nil
}
<|endoftext|>


<commit_before>/*
CPF validator and generator
*/

package cpf

import (
	"errors"
	"math"
	"math/rand"
	"strconv"
	"strings"
	"time"
)

const (
	cpfValidLength = 11
	invalidLength  = "CPF deve ter 11 dígitos."
	repeatedDigits = "CPF não pode ser composto por números repetidos."
)

// Valid validates the CPF returning a boolean
func Valid(cpf string) bool {
	isValid, err := AssertValid(cpf)
	if err != nil {
		return false
	}
	return isValid
}

// AssertValid validates the CPF returning a boolean and the error if any
func AssertValid(cpf string) (bool, error) {
	cpf = sanitize(cpf)
	if len(cpf) != cpfValidLength {
		return false, errors.New(invalidLength)
	}

	for i := 0; i <= 9; i++ {
		if cpf == strings.Repeat(string(i), 11) {
			return false, errors.New(repeatedDigits)
		}
	}

	return checkDigits(cpf), nil
}

// Generate returns a random valid CPF
func Generate() string {
	rand.Seed(time.Now().UTC().UnixNano())

	data := make([]int, 9)
	for i := 0; i < 9; i++ {
		data[i] = rand.Intn(9)
	}
	checkDigit1 := computeCheckDigit(data)
	data = append(data, checkDigit1)
	checkDigit2 := computeCheckDigit(data)
	data = append(data, checkDigit2)

	var cpf string
	for _, value := range data {
		cpf += strconv.Itoa(value)
	}
	return cpf
}

func sanitize(data string) string {
	data = strings.Replace(data, ".", "", -1)
	data = strings.Replace(data, "-", "", -1)
	return data
}

func checkDigits(cpf string) bool {
	data := strings.Split(cpf, "")

	doc := make([]int, 9)
	for i := 0; i <= 8; i++ {
		digit, err := strconv.Atoi(data[i])
		if err != nil {
			return false
		}
		doc[i] = digit
	}

	checkDigit1 := computeCheckDigit(doc)
	doc = append(doc, checkDigit1)
	checkDigit2 := computeCheckDigit(doc)

	checkDigit1IsValid := strconv.Itoa(checkDigit1) == string(data[9])
	checkDigit2IsValid := strconv.Itoa(checkDigit2) == string(data[10])
	return checkDigit1IsValid && checkDigit2IsValid
}

func computeCheckDigit(data []int) int {
	var calc float64
	for i, j := 2, len(data)-1; j >= 0; i, j = i+1, j-1 {
		calc += float64(i * data[j])
	}
	mod := int(math.Mod(calc*10, 11))
	if mod == 10 {
		return 0
	}
	return mod
}
<commit_msg>Added doc.go<commit_after>package cpf

import (
	"errors"
	"math"
	"math/rand"
	"strconv"
	"strings"
	"time"
)

const (
	cpfValidLength = 11
	invalidLength  = "CPF deve ter 11 dígitos."
	repeatedDigits = "CPF não pode ser composto por números repetidos."
)

// Valid validates the CPF returning a boolean
func Valid(cpf string) bool {
	isValid, err := AssertValid(cpf)
	if err != nil {
		return false
	}
	return isValid
}

// AssertValid validates the CPF returning a boolean and the error if any
func AssertValid(cpf string) (bool, error) {
	cpf = sanitize(cpf)
	if len(cpf) != cpfValidLength {
		return false, errors.New(invalidLength)
	}

	for i := 0; i <= 9; i++ {
		if cpf == strings.Repeat(string(i), 11) {
			return false, errors.New(repeatedDigits)
		}
	}

	return checkDigits(cpf), nil
}

// Generate returns a random valid CPF
func Generate() string {
	rand.Seed(time.Now().UTC().UnixNano())

	data := make([]int, 9)
	for i := 0; i < 9; i++ {
		data[i] = rand.Intn(9)
	}
	checkDigit1 := computeCheckDigit(data)
	data = append(data, checkDigit1)
	checkDigit2 := computeCheckDigit(data)
	data = append(data, checkDigit2)

	var cpf string
	for _, value := range data {
		cpf += strconv.Itoa(value)
	}
	return cpf
}

func sanitize(data string) string {
	data = strings.Replace(data, ".", "", -1)
	data = strings.Replace(data, "-", "", -1)
	return data
}

func checkDigits(cpf string) bool {
	data := strings.Split(cpf, "")

	doc := make([]int, 9)
	for i := 0; i <= 8; i++ {
		digit, err := strconv.Atoi(data[i])
		if err != nil {
			return false
		}
		doc[i] = digit
	}

	checkDigit1 := computeCheckDigit(doc)
	doc = append(doc, checkDigit1)
	checkDigit2 := computeCheckDigit(doc)

	checkDigit1IsValid := strconv.Itoa(checkDigit1) == string(data[9])
	checkDigit2IsValid := strconv.Itoa(checkDigit2) == string(data[10])
	return checkDigit1IsValid && checkDigit2IsValid
}

func computeCheckDigit(data []int) int {
	var calc float64
	for i, j := 2, len(data)-1; j >= 0; i, j = i+1, j-1 {
		calc += float64(i * data[j])
	}
	mod := int(math.Mod(calc*10, 11))
	if mod == 10 {
		return 0
	}
	return mod
}
<|endoftext|>


<commit_before>// +build linux

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

Copyright 2016 Intel Corporation

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 cpu provides implementation of snap-plugin-collector-cpu plugin
package cpu

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/intelsdi-x/snap/control/plugin"
	"github.com/intelsdi-x/snap/control/plugin/cpolicy"
	"github.com/intelsdi-x/snap/core"
	"github.com/intelsdi-x/snap/core/ctypes"

	"github.com/intelsdi-x/snap-plugin-utilities/ns"
)

const (
	//vendor namespace part
	vendor = "intel"

	//fs namespace part
	fs = "procfs"

	//pluginName namespace part
	pluginName = "cpu"

	// version of cpu plugin
	version = 2

	//pluginType type of plugin
	pluginType = plugin.CollectorPluginType

	//userProcStat "user" metric from /proc/stat
	userProcStat = "user"

	//userProcStat "nice" metric from /proc/stat
	niceProcStat = "nice"

	//systemProcStat "system" metric from /proc/stat
	systemProcStat = "system"

	//idleProcStat "idle" metric from /proc/stat
	idleProcStat = "idle"

	//iowaitProcStat "iowait" metric from /proc/stat
	iowaitProcStat = "iowait"

	//irqProcStat "irq" metric from /proc/stat
	irqProcStat = "irq"

	//softirqProcStat "softirq" metric from /proc/stat
	softirqProcStat = "softirq"

	//stealProcStat "steal" metric from /proc/stat
	stealProcStat = "steal"

	//guestProcStat "guest" metric from /proc/stat
	guestProcStat = "guest"

	//guestNiceProcStat "guest_nice" metric from /proc/stat
	guestNiceProcStat = "guest_nice"

	//activeProcStat "active" snap metric
	activeProcStat = "active"

	//utilizationProcStat "utilization" snap metric
	utilizationProcStat = "utilization"

	//jiffiesRepresentationType jiffies representation type
	jiffiesRepresentationType = "jiffies"

	//percentageRepresentationType percentage representation type
	percentageRepresentationType = "percentage"

	//maxNamespaceSize max size of namespace for metrics
	maxNamespaceSize = 5

	//allCPU string indentifier for aggregation metrics (for all CPUs)
	allCPU = "all"

	//firstCPU string indentifier for the first CPU
	firstCPU = "0"

	//secondCPU string indentifier for the second CPU
	secondCPU = "1"

	//cpuStr string indentifier for /proc/stat line which have desired CPU metrics
	cpuStr = "cpu"
)

//cpuInfo source of data for metrics
var cpuInfo = "/proc/stat"

// GetMetricTypes returns list of available metric types
// It returns error in case retrieval was not successful
func (p *Plugin) GetMetricTypes(cfg plugin.ConfigType) ([]plugin.MetricType, error) {
	if !p.initialized {
		if err := p.init(cfg.Table()); err != nil {
			return nil, err
		}
	}
	metricTypes := []plugin.MetricType{}

	namespaces := []string{}

	err := ns.FromMap(p.stats, filepath.Join(vendor, fs, pluginName), &namespaces)

	if err != nil {
		return nil, err
	}

	for _, namespace := range namespaces {
		namespace = strings.TrimRight(namespace, string(os.PathSeparator))
		metricType := plugin.MetricType{
			Namespace_: core.NewNamespace(strings.Split(namespace, string(os.PathSeparator))...)}
		metricTypes = append(metricTypes, metricType)
	}
	return metricTypes, nil
}

// CollectMetrics returns list of requested metric values
// It returns error in case retrieval was not successful
func (p *Plugin) CollectMetrics(metricTypes []plugin.MetricType) ([]plugin.MetricType, error) {
	metrics := []plugin.MetricType{}
	if len(metricTypes) == 0 {
		return nil, nil
	}
	if !p.initialized {
		if err := p.init(metricTypes[0].Config().Table()); err != nil {
			return nil, err
		}
	} else {
		if err := getStats(p.stats, p.prevMetricsSum, p.cpuMetricsNumber,
			p.snapMetricsNames, p.procStatMetricsNames); err != nil {
			return nil, err
		}
	}
	for _, metricType := range metricTypes {
		ns := metricType.Namespace()
		if len(ns) != maxNamespaceSize {
			return nil, fmt.Errorf("Incorrect namespace length (len = %d)", len(ns))
		}

		val, err := getMapValueByNamespace(p.stats, ns[3:].Strings())

		if err != nil {
			return metrics, err
		}

		metric := plugin.MetricType{
			Namespace_: ns,
			Data_:      val,
			Timestamp_: time.Now(),
		}
		metrics = append(metrics, metric)
	}
	return metrics, nil
}

// GetConfigPolicy returns config policy
// It returns error in case retrieval was not successful
func (p *Plugin) GetConfigPolicy() (*cpolicy.ConfigPolicy, error) {
	cp := cpolicy.New()
	rule, _ := cpolicy.NewStringRule("proc_path", false, "/proc")
	node := cpolicy.NewPolicyNode()
	node.Add(rule)
	cp.Add([]string{vendor, fs, pluginName}, node)
	return cp, nil
}

//Meta returns meta data for plugin
func Meta() *plugin.PluginMeta {
	return plugin.NewPluginMeta(
		pluginName,
		version,
		pluginType,
		[]string{},
		[]string{plugin.SnapGOBContentType},
		plugin.ConcurrencyCount(1))
}

func (p *Plugin) init(cfg map[string]ctypes.ConfigValue) error {
	if procPath, ok := cfg["proc_path"]; ok {
		p.proc_path = procPath.(ctypes.ConfigValueStr).Value + "/stat"
	}
	fh, err := os.Open(p.proc_path)
	if err != nil {
		return err
	}
	defer fh.Close()

	var procStatMetricsNumber int
	p.cpuMetricsNumber, procStatMetricsNumber, err = getInitialProcStatData(p.proc_path)
	if err != nil {
		return err
	}

	//initialize metric names arrays
	p.procStatMetricsNames = []string{userProcStat, niceProcStat, systemProcStat, idleProcStat,
		iowaitProcStat, irqProcStat, softirqProcStat, stealProcStat, guestProcStat, guestNiceProcStat}
	snapSpecificMetricsNames := []string{activeProcStat, utilizationProcStat}

	//build snapMetricsNames to support different kernels
	//var snapMetricsNames []string
	p.snapMetricsNames = append(p.snapMetricsNames, p.procStatMetricsNames[0:procStatMetricsNumber]...)
	p.snapMetricsNames = append(p.snapMetricsNames, snapSpecificMetricsNames...)
	p.stats = make(map[string]interface{})
	p.prevMetricsSum = make(map[string]float64)
	if err := getStats(p.proc_path, p.stats, p.prevMetricsSum, p.cpuMetricsNumber,
		p.snapMetricsNames, p.procStatMetricsNames); err != nil {
		return err
	}
	p.initialized = true
	return nil
}

// New creates instance of interface info plugin
func New() *Plugin {
	host, err := os.Hostname()
	if err != nil {
		host = "localhost"
	}
	p := &Plugin{
		host:      host,
		proc_path: cpuInfo,
	}
	return p
}

//Plugin cpu plugin struct which gathers plugin specific data
type Plugin struct {
	initialized          bool
	proc_path            string
	host                 string
	cpuMetricsNumber     int // number of cpu + "all" metric
	stats                map[string]interface{}
	prevMetricsSum       map[string]float64
	procStatMetricsNames []string
	snapMetricsNames     []string
}

//getStats gets metrics from /proc/stat output and calculates snap specific metrics
func getStats(path string, stats map[string]interface{}, prevMetricsSum map[string]float64, cpuMetricsNumber int,
	snapMetricsNames []string, procStatMetricsNames []string) (err error) {
	fh, err := os.Open(path)
	if err != nil {
		return err
	}
	defer fh.Close()

	scanner := bufio.NewScanner(fh)
	for i := 0; i < cpuMetricsNumber; i++ {
		scanErr := scanner.Scan()
		if !scanErr {
			return fmt.Errorf("Wrong %s format", path)
		}
		fields := strings.Fields(scanner.Text())

		if len(fields) < 2 {
			return fmt.Errorf("Wrong %s format", path)
		}

		cpuID := strings.TrimSpace(fields[0])
		if cpuID == cpuStr {
			if err != nil {
				return err
			}
			cpuID = allCPU //change CPU identifier for aggregation metrics
		} else {
			cpuID = cpuID[len(cpuID)-1:] //get the last character
		}
		metrics := fields[1:]

		if len(metrics) != len(procStatMetricsNames) {
			return fmt.Errorf("Wrong data length. Expected {%d} is {%d}",
				len(procStatMetricsNames), len(metrics))
		}

		//sum of new data in line
		currDataSum, err := strTabSum(metrics)
		if err != nil {
			return err
		}

		metricStats := make(map[string]interface{})
		for j := range snapMetricsNames {

			metricName := snapMetricsNames[j]
			var currVal float64
			//data collecting, there is an assumption that firstly metrics from /proc/stat/
			//are gathered then snap specific metrics (e.g. active and utilization are calculated)
			if metricName == activeProcStat {
				idleVal, err := getMapFloatValueByNamespace(metricStats,
					[]string{getNamespaceMetricPart(idleProcStat, jiffiesRepresentationType)})
				if err != nil {
					return err
				}
				currVal = currDataSum - idleVal
			} else if metricName == utilizationProcStat {
				nonActiveVal, err := getMapFloatValueByNamespace(metricStats,
					[]string{getNamespaceMetricPart(idleProcStat, jiffiesRepresentationType)})
				if err != nil {
					return err
				}

				currVal = currDataSum - nonActiveVal

				nonActiveVal, err = getMapFloatValueByNamespace(metricStats,
					[]string{getNamespaceMetricPart(iowaitProcStat, jiffiesRepresentationType)})
				if err != nil {
					return err
				}

				currVal = currVal - nonActiveVal
			} else {
				currVal, err = strconv.ParseFloat(metrics[j], 64)
				if err != nil {
					return err
				}
			}

			metricStats[getNamespaceMetricPart(metricName, percentageRepresentationType)] = float64(0)

			if mapKeyExists(cpuID, prevMetricsSum) {
				diffSum := currDataSum - prevMetricsSum[cpuID]
				if diffSum != 0 {
					prevVal, err := getMapFloatValueByNamespace(stats,
						[]string{cpuID, getNamespaceMetricPart(metricName, jiffiesRepresentationType)})
					if err != nil {
						return err
					}
					metricStats[getNamespaceMetricPart(metricName, percentageRepresentationType)] =
						float64(100 * (currVal - prevVal) / diffSum)
				}
			}
			metricStats[getNamespaceMetricPart(metricName, jiffiesRepresentationType)] = currVal
		}
		stats[cpuID] = metricStats
		prevMetricsSum[cpuID] = currDataSum
	}
	return nil
}

//getNamespaceMetricPart builds part of namespace specific for metric and representation type
func getNamespaceMetricPart(metricName string, representationType string) (s string) {
	s = metricName + "_" + representationType
	return s
}

//mapKeyExists checks if element with given key exists in map
func mapKeyExists(key string, m map[string]float64) bool {
	var ret = false
	if _, ok := m[key]; ok {
		ret = true
	}
	return ret
}

//strTabSum adds string data as float
func strTabSum(metrics []string) (sum float64, err error) {
	sum = 0
	for i := range metrics {
		val, err := strconv.ParseFloat(metrics[i], 64)
		if err != nil {
			return sum, err
		}
		sum += val
	}
	return sum, err
}

//getMapFloatValueByNamespace gets value as float from map by namespace given in array of strings
func getMapFloatValueByNamespace(m map[string]interface{}, ns []string) (val float64, err error) {
	var interfaceVal interface{}
	interfaceVal, err = getMapValueByNamespace(m, ns)
	if err != nil {
		return val, err
	}
	var errBool bool
	val, errBool = interfaceVal.(float64)
	if !errBool {
		return val, fmt.Errorf("Parsing error")
	}
	return val, err
}

//getMapValueByNamespace gets value from map by namespace given in array of strings
func getMapValueByNamespace(m map[string]interface{}, ns []string) (val interface{}, err error) {
	if len(ns) == 0 {
		return val, fmt.Errorf("Namespace length equal to zero")
	}

	current := ns[0]

	if len(ns) == 1 {
		if val, ok := m[current]; ok {
			return val, err
		}
		return val, fmt.Errorf("Key does not exist in map {key %s}", current)
	}

	if v, ok := m[current].(map[string]interface{}); ok {
		val, err = getMapValueByNamespace(v, ns[1:])
		return val, err
	}
	return val, err
}

//getInitialProcStatData gets number of CPUs and number of metrics available in /proc/stat output
func getInitialProcStatData(path string) (cpuMetricsNumber int, procStatMetricNumber int, err error) {
	fh, err := os.Open(path)
	if err != nil {
		return cpuMetricsNumber, procStatMetricNumber, err
	}
	defer fh.Close()

	scanner := bufio.NewScanner(fh)
	//read the first line to start the loop
	scanErr := scanner.Scan()
	if scanErr != true {
		return cpuMetricsNumber, procStatMetricNumber, fmt.Errorf("Cannot read from %s", path)
	}

	procStatLine := strings.Fields(scanner.Text())
	procStatMetricNumber = len(procStatLine) - 1 //without cpu identifier (e.g. cpu or cpu0)

	cpuMetricsNumber = 0
	for strings.Contains(procStatLine[0], cpuStr) {
		//check line length, compare current length with length of the first line
		//length of the first line = len(procStatMetricNumber) + CPU identifier
		if len(procStatLine) != (procStatMetricNumber + 1) {
			return cpuMetricsNumber, procStatMetricNumber, fmt.Errorf("Incorrect %s output", path)
		}
		cpuMetricsNumber++
		//read the next line to be able to check loop condition
		scanErr = scanner.Scan()
		if scanErr != true {
			break //no more lines to read
		}
		procStatLine = strings.Fields(scanner.Text())
	}
	return cpuMetricsNumber, procStatMetricNumber, err
}
<commit_msg>Removing getStats call from init<commit_after>// +build linux

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

Copyright 2016 Intel Corporation

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 cpu provides implementation of snap-plugin-collector-cpu plugin
package cpu

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/intelsdi-x/snap/control/plugin"
	"github.com/intelsdi-x/snap/control/plugin/cpolicy"
	"github.com/intelsdi-x/snap/core"
	"github.com/intelsdi-x/snap/core/ctypes"

	"github.com/intelsdi-x/snap-plugin-utilities/ns"
)

const (
	//vendor namespace part
	vendor = "intel"

	//fs namespace part
	fs = "procfs"

	//pluginName namespace part
	pluginName = "cpu"

	// version of cpu plugin
	version = 2

	//pluginType type of plugin
	pluginType = plugin.CollectorPluginType

	//userProcStat "user" metric from /proc/stat
	userProcStat = "user"

	//userProcStat "nice" metric from /proc/stat
	niceProcStat = "nice"

	//systemProcStat "system" metric from /proc/stat
	systemProcStat = "system"

	//idleProcStat "idle" metric from /proc/stat
	idleProcStat = "idle"

	//iowaitProcStat "iowait" metric from /proc/stat
	iowaitProcStat = "iowait"

	//irqProcStat "irq" metric from /proc/stat
	irqProcStat = "irq"

	//softirqProcStat "softirq" metric from /proc/stat
	softirqProcStat = "softirq"

	//stealProcStat "steal" metric from /proc/stat
	stealProcStat = "steal"

	//guestProcStat "guest" metric from /proc/stat
	guestProcStat = "guest"

	//guestNiceProcStat "guest_nice" metric from /proc/stat
	guestNiceProcStat = "guest_nice"

	//activeProcStat "active" snap metric
	activeProcStat = "active"

	//utilizationProcStat "utilization" snap metric
	utilizationProcStat = "utilization"

	//jiffiesRepresentationType jiffies representation type
	jiffiesRepresentationType = "jiffies"

	//percentageRepresentationType percentage representation type
	percentageRepresentationType = "percentage"

	//maxNamespaceSize max size of namespace for metrics
	maxNamespaceSize = 5

	//allCPU string indentifier for aggregation metrics (for all CPUs)
	allCPU = "all"

	//firstCPU string indentifier for the first CPU
	firstCPU = "0"

	//secondCPU string indentifier for the second CPU
	secondCPU = "1"

	//cpuStr string indentifier for /proc/stat line which have desired CPU metrics
	cpuStr = "cpu"
)

//cpuInfo source of data for metrics
var cpuInfo = "/proc/stat"

// GetMetricTypes returns list of available metric types
// It returns error in case retrieval was not successful
func (p *Plugin) GetMetricTypes(cfg plugin.ConfigType) ([]plugin.MetricType, error) {
	if !p.initialized {
		if err := p.init(cfg.Table()); err != nil {
			return nil, err
		}
	}
	if err := getStats(p.stats, p.prevMetricsSum, p.cpuMetricsNumber,
		p.snapMetricsNames, p.procStatMetricsNames); err != nil {
		return nil, err
	}
	metricTypes := []plugin.MetricType{}

	namespaces := []string{}

	err := ns.FromMap(p.stats, filepath.Join(vendor, fs, pluginName), &namespaces)

	if err != nil {
		return nil, err
	}

	for _, namespace := range namespaces {
		namespace = strings.TrimRight(namespace, string(os.PathSeparator))
		metricType := plugin.MetricType{
			Namespace_: core.NewNamespace(strings.Split(namespace, string(os.PathSeparator))...)}
		metricTypes = append(metricTypes, metricType)
	}
	return metricTypes, nil
}

// CollectMetrics returns list of requested metric values
// It returns error in case retrieval was not successful
func (p *Plugin) CollectMetrics(metricTypes []plugin.MetricType) ([]plugin.MetricType, error) {
	metrics := []plugin.MetricType{}
	if len(metricTypes) == 0 {
		return nil, nil
	}
	if !p.initialized {
		if err := p.init(metricTypes[0].Config().Table()); err != nil {
			return nil, err
		}
	}
	if err := getStats(p.stats, p.prevMetricsSum, p.cpuMetricsNumber,
		p.snapMetricsNames, p.procStatMetricsNames); err != nil {
		return nil, err
	}
	for _, metricType := range metricTypes {
		ns := metricType.Namespace()
		if len(ns) != maxNamespaceSize {
			return nil, fmt.Errorf("Incorrect namespace length (len = %d)", len(ns))
		}

		val, err := getMapValueByNamespace(p.stats, ns[3:].Strings())

		if err != nil {
			return metrics, err
		}

		metric := plugin.MetricType{
			Namespace_: ns,
			Data_:      val,
			Timestamp_: time.Now(),
		}
		metrics = append(metrics, metric)
	}
	return metrics, nil
}

// GetConfigPolicy returns config policy
// It returns error in case retrieval was not successful
func (p *Plugin) GetConfigPolicy() (*cpolicy.ConfigPolicy, error) {
	cp := cpolicy.New()
	rule, _ := cpolicy.NewStringRule("proc_path", false, "/proc")
	node := cpolicy.NewPolicyNode()
	node.Add(rule)
	cp.Add([]string{vendor, fs, pluginName}, node)
	return cp, nil
}

//Meta returns meta data for plugin
func Meta() *plugin.PluginMeta {
	return plugin.NewPluginMeta(
		pluginName,
		version,
		pluginType,
		[]string{},
		[]string{plugin.SnapGOBContentType},
		plugin.ConcurrencyCount(1))
}

func (p *Plugin) init(cfg map[string]ctypes.ConfigValue) error {
	if procPath, ok := cfg["proc_path"]; ok {
		p.proc_path = procPath.(ctypes.ConfigValueStr).Value + "/stat"
	}
	fh, err := os.Open(p.proc_path)
	if err != nil {
		return err
	}
	defer fh.Close()

	var procStatMetricsNumber int
	p.cpuMetricsNumber, procStatMetricsNumber, err = getInitialProcStatData(p.proc_path)
	if err != nil {
		return err
	}

	//initialize metric names arrays
	p.procStatMetricsNames = []string{userProcStat, niceProcStat, systemProcStat, idleProcStat,
		iowaitProcStat, irqProcStat, softirqProcStat, stealProcStat, guestProcStat, guestNiceProcStat}
	snapSpecificMetricsNames := []string{activeProcStat, utilizationProcStat}

	//build snapMetricsNames to support different kernels
	//var snapMetricsNames []string
	p.snapMetricsNames = append(p.snapMetricsNames, p.procStatMetricsNames[0:procStatMetricsNumber]...)
	p.snapMetricsNames = append(p.snapMetricsNames, snapSpecificMetricsNames...)
	p.stats = make(map[string]interface{})
	p.prevMetricsSum = make(map[string]float64)
	p.initialized = true
	return nil
}

// New creates instance of interface info plugin
func New() *Plugin {
	host, err := os.Hostname()
	if err != nil {
		host = "localhost"
	}
	p := &Plugin{
		host:      host,
		proc_path: cpuInfo,
	}
	return p
}

//Plugin cpu plugin struct which gathers plugin specific data
type Plugin struct {
	initialized          bool
	proc_path            string
	host                 string
	cpuMetricsNumber     int // number of cpu + "all" metric
	stats                map[string]interface{}
	prevMetricsSum       map[string]float64
	procStatMetricsNames []string
	snapMetricsNames     []string
}

//getStats gets metrics from /proc/stat output and calculates snap specific metrics
func getStats(path string, stats map[string]interface{}, prevMetricsSum map[string]float64, cpuMetricsNumber int,
	snapMetricsNames []string, procStatMetricsNames []string) (err error) {
	fh, err := os.Open(path)
	if err != nil {
		return err
	}
	defer fh.Close()

	scanner := bufio.NewScanner(fh)
	for i := 0; i < cpuMetricsNumber; i++ {
		scanErr := scanner.Scan()
		if !scanErr {
			return fmt.Errorf("Wrong %s format", path)
		}
		fields := strings.Fields(scanner.Text())

		if len(fields) < 2 {
			return fmt.Errorf("Wrong %s format", path)
		}

		cpuID := strings.TrimSpace(fields[0])
		if cpuID == cpuStr {
			if err != nil {
				return err
			}
			cpuID = allCPU //change CPU identifier for aggregation metrics
		} else {
			cpuID = cpuID[len(cpuID)-1:] //get the last character
		}
		metrics := fields[1:]

		if len(metrics) != len(procStatMetricsNames) {
			return fmt.Errorf("Wrong data length. Expected {%d} is {%d}",
				len(procStatMetricsNames), len(metrics))
		}

		//sum of new data in line
		currDataSum, err := strTabSum(metrics)
		if err != nil {
			return err
		}

		metricStats := make(map[string]interface{})
		for j := range snapMetricsNames {

			metricName := snapMetricsNames[j]
			var currVal float64
			//data collecting, there is an assumption that firstly metrics from /proc/stat/
			//are gathered then snap specific metrics (e.g. active and utilization are calculated)
			if metricName == activeProcStat {
				idleVal, err := getMapFloatValueByNamespace(metricStats,
					[]string{getNamespaceMetricPart(idleProcStat, jiffiesRepresentationType)})
				if err != nil {
					return err
				}
				currVal = currDataSum - idleVal
			} else if metricName == utilizationProcStat {
				nonActiveVal, err := getMapFloatValueByNamespace(metricStats,
					[]string{getNamespaceMetricPart(idleProcStat, jiffiesRepresentationType)})
				if err != nil {
					return err
				}

				currVal = currDataSum - nonActiveVal

				nonActiveVal, err = getMapFloatValueByNamespace(metricStats,
					[]string{getNamespaceMetricPart(iowaitProcStat, jiffiesRepresentationType)})
				if err != nil {
					return err
				}

				currVal = currVal - nonActiveVal
			} else {
				currVal, err = strconv.ParseFloat(metrics[j], 64)
				if err != nil {
					return err
				}
			}

			metricStats[getNamespaceMetricPart(metricName, percentageRepresentationType)] = float64(0)

			if mapKeyExists(cpuID, prevMetricsSum) {
				diffSum := currDataSum - prevMetricsSum[cpuID]
				if diffSum != 0 {
					prevVal, err := getMapFloatValueByNamespace(stats,
						[]string{cpuID, getNamespaceMetricPart(metricName, jiffiesRepresentationType)})
					if err != nil {
						return err
					}
					metricStats[getNamespaceMetricPart(metricName, percentageRepresentationType)] =
						float64(100 * (currVal - prevVal) / diffSum)
				}
			}
			metricStats[getNamespaceMetricPart(metricName, jiffiesRepresentationType)] = currVal
		}
		stats[cpuID] = metricStats
		prevMetricsSum[cpuID] = currDataSum
	}
	return nil
}

//getNamespaceMetricPart builds part of namespace specific for metric and representation type
func getNamespaceMetricPart(metricName string, representationType string) (s string) {
	s = metricName + "_" + representationType
	return s
}

//mapKeyExists checks if element with given key exists in map
func mapKeyExists(key string, m map[string]float64) bool {
	var ret = false
	if _, ok := m[key]; ok {
		ret = true
	}
	return ret
}

//strTabSum adds string data as float
func strTabSum(metrics []string) (sum float64, err error) {
	sum = 0
	for i := range metrics {
		val, err := strconv.ParseFloat(metrics[i], 64)
		if err != nil {
			return sum, err
		}
		sum += val
	}
	return sum, err
}

//getMapFloatValueByNamespace gets value as float from map by namespace given in array of strings
func getMapFloatValueByNamespace(m map[string]interface{}, ns []string) (val float64, err error) {
	var interfaceVal interface{}
	interfaceVal, err = getMapValueByNamespace(m, ns)
	if err != nil {
		return val, err
	}
	var errBool bool
	val, errBool = interfaceVal.(float64)
	if !errBool {
		return val, fmt.Errorf("Parsing error")
	}
	return val, err
}

//getMapValueByNamespace gets value from map by namespace given in array of strings
func getMapValueByNamespace(m map[string]interface{}, ns []string) (val interface{}, err error) {
	if len(ns) == 0 {
		return val, fmt.Errorf("Namespace length equal to zero")
	}

	current := ns[0]

	if len(ns) == 1 {
		if val, ok := m[current]; ok {
			return val, err
		}
		return val, fmt.Errorf("Key does not exist in map {key %s}", current)
	}

	if v, ok := m[current].(map[string]interface{}); ok {
		val, err = getMapValueByNamespace(v, ns[1:])
		return val, err
	}
	return val, err
}

//getInitialProcStatData gets number of CPUs and number of metrics available in /proc/stat output
func getInitialProcStatData(path string) (cpuMetricsNumber int, procStatMetricNumber int, err error) {
	fh, err := os.Open(path)
	if err != nil {
		return cpuMetricsNumber, procStatMetricNumber, err
	}
	defer fh.Close()

	scanner := bufio.NewScanner(fh)
	//read the first line to start the loop
	scanErr := scanner.Scan()
	if scanErr != true {
		return cpuMetricsNumber, procStatMetricNumber, fmt.Errorf("Cannot read from %s", path)
	}

	procStatLine := strings.Fields(scanner.Text())
	procStatMetricNumber = len(procStatLine) - 1 //without cpu identifier (e.g. cpu or cpu0)

	cpuMetricsNumber = 0
	for strings.Contains(procStatLine[0], cpuStr) {
		//check line length, compare current length with length of the first line
		//length of the first line = len(procStatMetricNumber) + CPU identifier
		if len(procStatLine) != (procStatMetricNumber + 1) {
			return cpuMetricsNumber, procStatMetricNumber, fmt.Errorf("Incorrect %s output", path)
		}
		cpuMetricsNumber++
		//read the next line to be able to check loop condition
		scanErr = scanner.Scan()
		if scanErr != true {
			break //no more lines to read
		}
		procStatLine = strings.Fields(scanner.Text())
	}
	return cpuMetricsNumber, procStatMetricNumber, err
}
<|endoftext|>