<commit_before>/*
 * Copyright (c) 2013, 2014 Conformal Systems LLC <info@conformal.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package chain

import (
	"errors"
	"sync"
	"time"

	"github.com/conformal/btcnet"
	"github.com/conformal/btcrpcclient"
	"github.com/conformal/btcutil"
	"github.com/conformal/btcwallet/keystore"
	"github.com/conformal/btcwallet/txstore"
	"github.com/conformal/btcwire"
	"github.com/conformal/btcws"
)

type Client struct {
	*btcrpcclient.Client
	netParams *btcnet.Params

	enqueueNotification chan interface{}
	dequeueNotification chan interface{}
	currentBlock        chan *keystore.BlockStamp

	// Notification channels regarding the state of the client.  These exist
	// so other components can listen in on chain activity.  These are
	// initialized as nil, and must be created by calling one of the Listen*
	// methods.
	connected        chan bool
	notificationLock sync.Locker

	quit    chan struct{}
	wg      sync.WaitGroup
	started bool
	quitMtx sync.Mutex
}

func NewClient(net *btcnet.Params, connect, user, pass string, certs []byte) (*Client, error) {
	client := Client{
		netParams:           net,
		enqueueNotification: make(chan interface{}),
		dequeueNotification: make(chan interface{}),
		currentBlock:        make(chan *keystore.BlockStamp),
		notificationLock:    new(sync.Mutex),
		quit:                make(chan struct{}),
	}
	ntfnCallbacks := btcrpcclient.NotificationHandlers{
		OnClientConnected:   client.onClientConnect,
		OnBlockConnected:    client.onBlockConnected,
		OnBlockDisconnected: client.onBlockDisconnected,
		OnRecvTx:            client.onRecvTx,
		OnRedeemingTx:       client.onRedeemingTx,
		OnRescanFinished:    client.onRescanFinished,
		OnRescanProgress:    client.onRescanProgress,
	}
	conf := btcrpcclient.ConnConfig{
		Host:                connect,
		Endpoint:            "ws",
		User:                user,
		Pass:                pass,
		Certificates:        certs,
		DisableConnectOnNew: true,
	}
	c, err := btcrpcclient.New(&conf, &ntfnCallbacks)
	if err != nil {
		return nil, err
	}
	client.Client = c
	return &client, nil
}

func (c *Client) Start() error {
	err := c.Connect(5) // attempt connection 5 tries at most
	if err != nil {
		return err
	}

	// Verify that the server is running on the expected network.
	net, err := c.GetCurrentNet()
	if err != nil {
		c.Disconnect()
		return err
	}
	if net != c.netParams.Net {
		c.Disconnect()
		return errors.New("mismatched networks")
	}

	c.quitMtx.Lock()
	c.started = true
	c.quitMtx.Unlock()

	c.wg.Add(1)
	go c.handler()
	return nil
}

func (c *Client) Stop() {
	c.quitMtx.Lock()
	defer c.quitMtx.Unlock()

	select {
	case <-c.quit:
	default:
		close(c.quit)
		c.Client.Shutdown()

		if !c.started {
			close(c.dequeueNotification)
		}
	}
}

func (c *Client) WaitForShutdown() {
	c.Client.WaitForShutdown()
	c.wg.Wait()
}

func (c *Client) Notifications() <-chan interface{} {
	return c.dequeueNotification
}

func (c *Client) BlockStamp() (*keystore.BlockStamp, error) {
	select {
	case bs := <-c.currentBlock:
		return bs, nil
	case <-c.quit:
		return nil, errors.New("disconnected")
	}
}

// Notification types.  These are defined here and processed from from reading
// a notificationChan to avoid handling these notifications directly in
// btcrpcclient callbacks, which isn't very Go-like and doesn't allow
// blocking client calls.
type (
	BlockConnected    keystore.BlockStamp
	BlockDisconnected keystore.BlockStamp
	RecvTx            struct {
		Tx    *btcutil.Tx    // Index is guaranteed to be set.
		Block *txstore.Block // nil if unmined
	}
	RedeemingTx struct {
		Tx    *btcutil.Tx    // Index is guaranteed to be set.
		Block *txstore.Block // nil if unmined
	}
	RescanProgress struct {
		Hash   *btcwire.ShaHash
		Height int32
		Time   time.Time
	}
	RescanFinished struct {
		Hash   *btcwire.ShaHash
		Height int32
		Time   time.Time
	}
)

// parseBlock parses a btcws definition of the block a tx is mined it to the
// Block structure of the txstore package, and the block index.  This is done
// here since btcrpcclient doesn't parse this nicely for us.
func parseBlock(block *btcws.BlockDetails) (blk *txstore.Block, idx int, err error) {
	if block == nil {
		return nil, btcutil.TxIndexUnknown, nil
	}
	blksha, err := btcwire.NewShaHashFromStr(block.Hash)
	if err != nil {
		return nil, btcutil.TxIndexUnknown, err
	}
	blk = &txstore.Block{
		Height: block.Height,
		Hash:   *blksha,
		Time:   time.Unix(block.Time, 0),
	}
	return blk, block.Index, nil
}

func (c *Client) onClientConnect() {
	log.Info("Established websocket RPC connection to btcd")
	c.notifyConnected(true)
}

func (c *Client) onBlockConnected(hash *btcwire.ShaHash, height int32) {
	c.enqueueNotification <- BlockConnected{Hash: hash, Height: height}
}

func (c *Client) onBlockDisconnected(hash *btcwire.ShaHash, height int32) {
	c.enqueueNotification <- BlockDisconnected{Hash: hash, Height: height}
}

func (c *Client) onRecvTx(tx *btcutil.Tx, block *btcws.BlockDetails) {
	var blk *txstore.Block
	index := btcutil.TxIndexUnknown
	if block != nil {
		var err error
		blk, index, err = parseBlock(block)
		if err != nil {
			// Log and drop improper notification.
			log.Errorf("recvtx notification bad block: %v", err)
			return
		}
	}
	tx.SetIndex(index)
	c.enqueueNotification <- RecvTx{tx, blk}
}

func (c *Client) onRedeemingTx(tx *btcutil.Tx, block *btcws.BlockDetails) {
	var blk *txstore.Block
	index := btcutil.TxIndexUnknown
	if block != nil {
		var err error
		blk, index, err = parseBlock(block)
		if err != nil {
			// Log and drop improper notification.
			log.Errorf("recvtx notification bad block: %v", err)
			return
		}
	}
	tx.SetIndex(index)
	c.enqueueNotification <- RedeemingTx{tx, blk}
}

func (c *Client) onRescanProgress(hash *btcwire.ShaHash, height int32, blkTime time.Time) {
	c.enqueueNotification <- &RescanProgress{hash, height, blkTime}
}

func (c *Client) onRescanFinished(hash *btcwire.ShaHash, height int32, blkTime time.Time) {
	c.enqueueNotification <- &RescanFinished{hash, height, blkTime}
}

// handler maintains a queue of notifications and the current state (best
// block) of the chain.
func (c *Client) handler() {
	hash, height, err := c.GetBestBlock()
	if err != nil {
		close(c.quit)
		c.wg.Done()
	}

	bs := &keystore.BlockStamp{Hash: hash, Height: height}

	// TODO: Rather than leaving this as an unbounded queue for all types of
	// notifications, try dropping ones where a later enqueued notification
	// can fully invalidate one waiting to be processed.  For example,
	// blockconnected notifications for greater block heights can remove the
	// need to process earlier blockconnected notifications still waiting
	// here.

	var notifications []interface{}
	enqueue := c.enqueueNotification
	var dequeue chan interface{}
	var next interface{}
out:
	for {
		select {
		case n, ok := <-enqueue:
			if !ok {
				// If no notifications are queued for handling,
				// the queue is finished.
				if len(notifications) == 0 {
					break out
				}
				// nil channel so no more reads can occur.
				enqueue = nil
				continue
			}
			if len(notifications) == 0 {
				next = n
				dequeue = c.dequeueNotification
			}
			notifications = append(notifications, n)

		case dequeue <- next:
			if n, ok := next.(BlockConnected); ok {
				bs = (*keystore.BlockStamp)(&n)
			}

			notifications[0] = nil
			notifications = notifications[1:]
			if len(notifications) != 0 {
				next = notifications[0]
			} else {
				// If no more notifications can be enqueued, the
				// queue is finished.
				if enqueue == nil {
					break out
				}
				dequeue = nil
			}

		case c.currentBlock <- bs:

		case <-c.quit:
			break out
		}
	}
	close(c.dequeueNotification)
	c.wg.Done()
}

// ErrDuplicateListen is returned for any attempts to listen for the same
// notification more than once.  If callers must pass along a notifiation to
// multiple places, they must broadcast it themself.
var ErrDuplicateListen = errors.New("duplicate listen")

type noopLocker struct{}

func (noopLocker) Lock()   {}
func (noopLocker) Unlock() {}

// ListenConnected returns a channel that passes the current connection state
// of the client.  This will be automatically sent to when the client is first
// connected, as well as the current state whenever NotifyConnected is
// forcibly called.
//
// If this is called twice, ErrDuplicateListen is returned.
func (c *Client) ListenConnected() (<-chan bool, error) {
	c.notificationLock.Lock()
	defer c.notificationLock.Unlock()

	if c.connected != nil {
		return nil, ErrDuplicateListen
	}
	c.connected = make(chan bool)
	c.notificationLock = noopLocker{}
	return c.connected, nil
}

func (c *Client) notifyConnected(connected bool) {
	c.notificationLock.Lock()
	if c.connected != nil {
		c.connected <- connected
	}
	c.notificationLock.Unlock()
}

// NotifyConnected sends the channel notification for a connected or
// disconnected client.  This is exported so it can be called by other
// packages which require notifying the current connection state.
//
// TODO: This shouldn't exist, but the current notification API requires it.
func (c *Client) NotifyConnected() {
	connected := !c.Client.Disconnected()
	c.notifyConnected(connected)
}
<commit_msg>Fix logging for improper redeemingtx notifications.<commit_after>/*
 * Copyright (c) 2013, 2014 Conformal Systems LLC <info@conformal.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package chain

import (
	"errors"
	"sync"
	"time"

	"github.com/conformal/btcnet"
	"github.com/conformal/btcrpcclient"
	"github.com/conformal/btcutil"
	"github.com/conformal/btcwallet/keystore"
	"github.com/conformal/btcwallet/txstore"
	"github.com/conformal/btcwire"
	"github.com/conformal/btcws"
)

type Client struct {
	*btcrpcclient.Client
	netParams *btcnet.Params

	enqueueNotification chan interface{}
	dequeueNotification chan interface{}
	currentBlock        chan *keystore.BlockStamp

	// Notification channels regarding the state of the client.  These exist
	// so other components can listen in on chain activity.  These are
	// initialized as nil, and must be created by calling one of the Listen*
	// methods.
	connected        chan bool
	notificationLock sync.Locker

	quit    chan struct{}
	wg      sync.WaitGroup
	started bool
	quitMtx sync.Mutex
}

func NewClient(net *btcnet.Params, connect, user, pass string, certs []byte) (*Client, error) {
	client := Client{
		netParams:           net,
		enqueueNotification: make(chan interface{}),
		dequeueNotification: make(chan interface{}),
		currentBlock:        make(chan *keystore.BlockStamp),
		notificationLock:    new(sync.Mutex),
		quit:                make(chan struct{}),
	}
	ntfnCallbacks := btcrpcclient.NotificationHandlers{
		OnClientConnected:   client.onClientConnect,
		OnBlockConnected:    client.onBlockConnected,
		OnBlockDisconnected: client.onBlockDisconnected,
		OnRecvTx:            client.onRecvTx,
		OnRedeemingTx:       client.onRedeemingTx,
		OnRescanFinished:    client.onRescanFinished,
		OnRescanProgress:    client.onRescanProgress,
	}
	conf := btcrpcclient.ConnConfig{
		Host:                connect,
		Endpoint:            "ws",
		User:                user,
		Pass:                pass,
		Certificates:        certs,
		DisableConnectOnNew: true,
	}
	c, err := btcrpcclient.New(&conf, &ntfnCallbacks)
	if err != nil {
		return nil, err
	}
	client.Client = c
	return &client, nil
}

func (c *Client) Start() error {
	err := c.Connect(5) // attempt connection 5 tries at most
	if err != nil {
		return err
	}

	// Verify that the server is running on the expected network.
	net, err := c.GetCurrentNet()
	if err != nil {
		c.Disconnect()
		return err
	}
	if net != c.netParams.Net {
		c.Disconnect()
		return errors.New("mismatched networks")
	}

	c.quitMtx.Lock()
	c.started = true
	c.quitMtx.Unlock()

	c.wg.Add(1)
	go c.handler()
	return nil
}

func (c *Client) Stop() {
	c.quitMtx.Lock()
	defer c.quitMtx.Unlock()

	select {
	case <-c.quit:
	default:
		close(c.quit)
		c.Client.Shutdown()

		if !c.started {
			close(c.dequeueNotification)
		}
	}
}

func (c *Client) WaitForShutdown() {
	c.Client.WaitForShutdown()
	c.wg.Wait()
}

func (c *Client) Notifications() <-chan interface{} {
	return c.dequeueNotification
}

func (c *Client) BlockStamp() (*keystore.BlockStamp, error) {
	select {
	case bs := <-c.currentBlock:
		return bs, nil
	case <-c.quit:
		return nil, errors.New("disconnected")
	}
}

// Notification types.  These are defined here and processed from from reading
// a notificationChan to avoid handling these notifications directly in
// btcrpcclient callbacks, which isn't very Go-like and doesn't allow
// blocking client calls.
type (
	BlockConnected    keystore.BlockStamp
	BlockDisconnected keystore.BlockStamp
	RecvTx            struct {
		Tx    *btcutil.Tx    // Index is guaranteed to be set.
		Block *txstore.Block // nil if unmined
	}
	RedeemingTx struct {
		Tx    *btcutil.Tx    // Index is guaranteed to be set.
		Block *txstore.Block // nil if unmined
	}
	RescanProgress struct {
		Hash   *btcwire.ShaHash
		Height int32
		Time   time.Time
	}
	RescanFinished struct {
		Hash   *btcwire.ShaHash
		Height int32
		Time   time.Time
	}
)

// parseBlock parses a btcws definition of the block a tx is mined it to the
// Block structure of the txstore package, and the block index.  This is done
// here since btcrpcclient doesn't parse this nicely for us.
func parseBlock(block *btcws.BlockDetails) (blk *txstore.Block, idx int, err error) {
	if block == nil {
		return nil, btcutil.TxIndexUnknown, nil
	}
	blksha, err := btcwire.NewShaHashFromStr(block.Hash)
	if err != nil {
		return nil, btcutil.TxIndexUnknown, err
	}
	blk = &txstore.Block{
		Height: block.Height,
		Hash:   *blksha,
		Time:   time.Unix(block.Time, 0),
	}
	return blk, block.Index, nil
}

func (c *Client) onClientConnect() {
	log.Info("Established websocket RPC connection to btcd")
	c.notifyConnected(true)
}

func (c *Client) onBlockConnected(hash *btcwire.ShaHash, height int32) {
	c.enqueueNotification <- BlockConnected{Hash: hash, Height: height}
}

func (c *Client) onBlockDisconnected(hash *btcwire.ShaHash, height int32) {
	c.enqueueNotification <- BlockDisconnected{Hash: hash, Height: height}
}

func (c *Client) onRecvTx(tx *btcutil.Tx, block *btcws.BlockDetails) {
	var blk *txstore.Block
	index := btcutil.TxIndexUnknown
	if block != nil {
		var err error
		blk, index, err = parseBlock(block)
		if err != nil {
			// Log and drop improper notification.
			log.Errorf("recvtx notification bad block: %v", err)
			return
		}
	}
	tx.SetIndex(index)
	c.enqueueNotification <- RecvTx{tx, blk}
}

func (c *Client) onRedeemingTx(tx *btcutil.Tx, block *btcws.BlockDetails) {
	var blk *txstore.Block
	index := btcutil.TxIndexUnknown
	if block != nil {
		var err error
		blk, index, err = parseBlock(block)
		if err != nil {
			// Log and drop improper notification.
			log.Errorf("redeemingtx notification bad block: %v", err)
			return
		}
	}
	tx.SetIndex(index)
	c.enqueueNotification <- RedeemingTx{tx, blk}
}

func (c *Client) onRescanProgress(hash *btcwire.ShaHash, height int32, blkTime time.Time) {
	c.enqueueNotification <- &RescanProgress{hash, height, blkTime}
}

func (c *Client) onRescanFinished(hash *btcwire.ShaHash, height int32, blkTime time.Time) {
	c.enqueueNotification <- &RescanFinished{hash, height, blkTime}
}

// handler maintains a queue of notifications and the current state (best
// block) of the chain.
func (c *Client) handler() {
	hash, height, err := c.GetBestBlock()
	if err != nil {
		close(c.quit)
		c.wg.Done()
	}

	bs := &keystore.BlockStamp{Hash: hash, Height: height}

	// TODO: Rather than leaving this as an unbounded queue for all types of
	// notifications, try dropping ones where a later enqueued notification
	// can fully invalidate one waiting to be processed.  For example,
	// blockconnected notifications for greater block heights can remove the
	// need to process earlier blockconnected notifications still waiting
	// here.

	var notifications []interface{}
	enqueue := c.enqueueNotification
	var dequeue chan interface{}
	var next interface{}
out:
	for {
		select {
		case n, ok := <-enqueue:
			if !ok {
				// If no notifications are queued for handling,
				// the queue is finished.
				if len(notifications) == 0 {
					break out
				}
				// nil channel so no more reads can occur.
				enqueue = nil
				continue
			}
			if len(notifications) == 0 {
				next = n
				dequeue = c.dequeueNotification
			}
			notifications = append(notifications, n)

		case dequeue <- next:
			if n, ok := next.(BlockConnected); ok {
				bs = (*keystore.BlockStamp)(&n)
			}

			notifications[0] = nil
			notifications = notifications[1:]
			if len(notifications) != 0 {
				next = notifications[0]
			} else {
				// If no more notifications can be enqueued, the
				// queue is finished.
				if enqueue == nil {
					break out
				}
				dequeue = nil
			}

		case c.currentBlock <- bs:

		case <-c.quit:
			break out
		}
	}
	close(c.dequeueNotification)
	c.wg.Done()
}

// ErrDuplicateListen is returned for any attempts to listen for the same
// notification more than once.  If callers must pass along a notifiation to
// multiple places, they must broadcast it themself.
var ErrDuplicateListen = errors.New("duplicate listen")

type noopLocker struct{}

func (noopLocker) Lock()   {}
func (noopLocker) Unlock() {}

// ListenConnected returns a channel that passes the current connection state
// of the client.  This will be automatically sent to when the client is first
// connected, as well as the current state whenever NotifyConnected is
// forcibly called.
//
// If this is called twice, ErrDuplicateListen is returned.
func (c *Client) ListenConnected() (<-chan bool, error) {
	c.notificationLock.Lock()
	defer c.notificationLock.Unlock()

	if c.connected != nil {
		return nil, ErrDuplicateListen
	}
	c.connected = make(chan bool)
	c.notificationLock = noopLocker{}
	return c.connected, nil
}

func (c *Client) notifyConnected(connected bool) {
	c.notificationLock.Lock()
	if c.connected != nil {
		c.connected <- connected
	}
	c.notificationLock.Unlock()
}

// NotifyConnected sends the channel notification for a connected or
// disconnected client.  This is exported so it can be called by other
// packages which require notifying the current connection state.
//
// TODO: This shouldn't exist, but the current notification API requires it.
func (c *Client) NotifyConnected() {
	connected := !c.Client.Disconnected()
	c.notifyConnected(connected)
}
<|endoftext|>


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

package main

/* TODO

- symlinks.
- expose md5 as xattr.
- head symlink.

*/

import (
	"crypto"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/hanwen/go-fuse/fuse"

	_ "crypto/md5"
	"p4fuse/p4"
)

type P4Fs struct {
	fuse.DefaultNodeFileSystem
	backingDir string
	root       *p4Root
	p4         *p4.Conn
}

// Creates a new P4FS
func NewP4Fs(conn *p4.Conn, backingDir string) *P4Fs {
	fs := &P4Fs{
		p4: conn,
	}

	fs.backingDir = backingDir
	fs.root = &p4Root{fs: fs}
	return fs
}

func (fs *P4Fs) Root() fuse.FsNode {
	return fs.root
}

func (fs *P4Fs) OnMount(conn *fuse.FileSystemConnector) {
	fs.root.Inode().AddChild("head", fs.root.Inode().New(false, &p4Link{fs: fs}))
}

func (fs *P4Fs) newFolder(path string, change int) *p4Folder {
	f := &p4Folder{fs: fs, path: path, change: change}
	return f
}

func (fs *P4Fs) newFile(st *p4.Stat) *p4File {
	f := &p4File{fs: fs, stat: *st}
	return f
}

////////////////
type p4Link struct {
	fuse.DefaultFsNode
	fs *P4Fs
}

func (f *p4Link) Deletable() bool {
	return false
}

func (f *p4Link) GetAttr(file fuse.File, c *fuse.Context) (*fuse.Attr, fuse.Status) {
	return &fuse.Attr{Mode: fuse.S_IFLNK}, fuse.OK
}

func (f *p4Link) Readlink(c *fuse.Context) ([]byte, fuse.Status) {
	r, err := f.fs.p4.Changes([]string{"-s", "submitted", "-m1"})
	if err != nil {
		log.Printf("p4.Changes: %v", err)
		return nil, fuse.EIO
	}

	ch := r[0].(*p4.Change)
	return []byte(fmt.Sprintf("%d", ch.Change)), fuse.OK
}

type p4Root struct {
	fuse.DefaultFsNode
	fs *P4Fs

	link *p4Link
}

func (f *p4Root) OpenDir(context *fuse.Context) (stream chan fuse.DirEntry, status fuse.Status) {
	stream = make(chan fuse.DirEntry, 1)
	stream <- fuse.DirEntry{Name: "head", Mode: fuse.S_IFLNK}
	close(stream)
	return stream, fuse.OK
}

func (r *p4Root) Lookup(name string, context *fuse.Context) (fi *fuse.Attr, node fuse.FsNode, code fuse.Status) {
	cl, err := strconv.ParseInt(name, 10, 64)
	if err != nil {
		return nil, nil, fuse.ENOENT
	}

	node = r.fs.newFolder("", int(cl))
	r.Inode().AddChild(name, r.Inode().New(true, node))

	a, _ := node.GetAttr(nil, context)
	return a, node, fuse.OK
}

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

type p4Folder struct {
	fuse.DefaultFsNode
	change int
	path   string
	fs     *P4Fs

	// nil means they haven't been fetched yet.
	files   map[string]*p4.Stat
	folders map[string]bool
}

func (f *p4Folder) OpenDir(context *fuse.Context) (stream chan fuse.DirEntry, status fuse.Status) {
	if !f.fetch() {
		return nil, fuse.EIO
	}
	stream = make(chan fuse.DirEntry, len(f.files)+len(f.folders))

	for n, _ := range f.files {
		mode := fuse.S_IFREG | 0644
		stream <- fuse.DirEntry{Name: n, Mode: uint32(mode)}
	}
	for n, _ := range f.folders {
		mode := fuse.S_IFDIR | 0755
		stream <- fuse.DirEntry{Name: n, Mode: uint32(mode)}
	}
	close(stream)
	return stream, fuse.OK
}

func (f *p4Folder) GetAttr(file fuse.File, c *fuse.Context) (*fuse.Attr, fuse.Status) {
	return &fuse.Attr{
		Mode: fuse.S_IFDIR | 0755,
	}, fuse.OK
}

func (f *p4Folder) Deletable() bool {
	return false
}

func (f *p4Folder) fetch() bool {
	if f.files != nil {
		return true
	}

	var err error
	path := "//" + f.path
	if !strings.HasSuffix(path, "/") {
		path += "/"
	}
	path += fmt.Sprintf("*@%d", f.change)

	folders, err := f.fs.p4.Dirs([]string{path})
	if err != nil {
		log.Printf("fetch: %v", err)
		return false
	}
	files, err := f.fs.p4.Fstat([]string{path})
	if err != nil {
		log.Printf("fetch: %v", err)
		return false
	}

	f.files = map[string]*p4.Stat{}
	for _, r := range files {
		if stat, ok := r.(*p4.Stat); ok && stat.HeadAction != "delete" {
			_, base := filepath.Split(stat.DepotFile)
			f.files[base] = stat
		}
	}

	f.folders = map[string]bool{}
	for _, r := range folders {
		if dir, ok := r.(*p4.Dir); ok {
			_, base := filepath.Split(dir.Dir)
			f.folders[base] = true
		}
	}

	return true
}

func (f *p4Folder) Lookup(name string, context *fuse.Context) (fi *fuse.Attr, node fuse.FsNode, code fuse.Status) {
	f.fetch()

	if st := f.files[name]; st != nil {
		node = f.fs.newFile(st)
	} else if f.folders[name] {
		node = f.fs.newFolder(filepath.Join(f.path, name), f.change)
	} else {
		return nil, nil, fuse.ENOENT
	}

	f.Inode().AddChild(name, f.Inode().New(true, node))

	a, _ := node.GetAttr(nil, context)
	return a, node, fuse.OK
}

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

type p4File struct {
	fuse.DefaultFsNode
	stat    p4.Stat
	fs      *P4Fs
	backing string
}

func (f *p4File) GetAttr(file fuse.File, c *fuse.Context) (*fuse.Attr, fuse.Status) {
	return &fuse.Attr{
		Size:  uint64(f.stat.FileSize),
		Mode:  fuse.S_IFREG | 0644,
		Mtime: uint64(f.stat.HeadTime),
	}, fuse.OK
}

func (f *p4File) fetch() bool {
	if f.backing != "" {
		return true
	}
	id := fmt.Sprintf("%s#%d", f.stat.DepotFile, f.stat.HeadRev)
	content, err := f.fs.p4.Print(id)
	if err != nil {
		log.Printf("p4 print error: %v", err)
		return false
	}

	tmp, err := ioutil.TempFile(f.fs.backingDir, "")
	if err != nil {
		log.Printf("TempFile: %v", err)
		return false
	}

	tmp.Write(content)
	tmp.Close()

	h := crypto.MD5.New()
	h.Write([]byte(id))
	dest := fmt.Sprintf("%s/%x", f.fs.backingDir, h.Sum(nil))
	os.Rename(tmp.Name(), dest)
	f.backing = dest
	return true
}

func (f *p4File) Deletable() bool {
	return false
}

func (n *p4File) Open(flags uint32, context *fuse.Context) (file fuse.File, code fuse.Status) {
	if flags&fuse.O_ANYWRITE != 0 {
		return nil, fuse.EROFS
	}

	n.fetch()
	f, err := os.OpenFile(n.backing, int(flags), 0644)
	if err != nil {
		return nil, fuse.ToStatus(err)
	}
	return &fuse.LoopbackFile{File: f}, fuse.OK
}
<commit_msg>Drop TODO.<commit_after>// Copyright 2012 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

/* TODO

- symlinks.
- expose md5 as xattr.

*/

import (
	"crypto"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/hanwen/go-fuse/fuse"

	_ "crypto/md5"
	"p4fuse/p4"
)

type P4Fs struct {
	fuse.DefaultNodeFileSystem
	backingDir string
	root       *p4Root
	p4         *p4.Conn
}

// Creates a new P4FS
func NewP4Fs(conn *p4.Conn, backingDir string) *P4Fs {
	fs := &P4Fs{
		p4: conn,
	}

	fs.backingDir = backingDir
	fs.root = &p4Root{fs: fs}
	return fs
}

func (fs *P4Fs) Root() fuse.FsNode {
	return fs.root
}

func (fs *P4Fs) OnMount(conn *fuse.FileSystemConnector) {
	fs.root.Inode().AddChild("head", fs.root.Inode().New(false, &p4Link{fs: fs}))
}

func (fs *P4Fs) newFolder(path string, change int) *p4Folder {
	f := &p4Folder{fs: fs, path: path, change: change}
	return f
}

func (fs *P4Fs) newFile(st *p4.Stat) *p4File {
	f := &p4File{fs: fs, stat: *st}
	return f
}

////////////////
type p4Link struct {
	fuse.DefaultFsNode
	fs *P4Fs
}

func (f *p4Link) Deletable() bool {
	return false
}

func (f *p4Link) GetAttr(file fuse.File, c *fuse.Context) (*fuse.Attr, fuse.Status) {
	return &fuse.Attr{Mode: fuse.S_IFLNK}, fuse.OK
}

func (f *p4Link) Readlink(c *fuse.Context) ([]byte, fuse.Status) {
	r, err := f.fs.p4.Changes([]string{"-s", "submitted", "-m1"})
	if err != nil {
		log.Printf("p4.Changes: %v", err)
		return nil, fuse.EIO
	}

	ch := r[0].(*p4.Change)
	return []byte(fmt.Sprintf("%d", ch.Change)), fuse.OK
}

type p4Root struct {
	fuse.DefaultFsNode
	fs *P4Fs

	link *p4Link
}

func (f *p4Root) OpenDir(context *fuse.Context) (stream chan fuse.DirEntry, status fuse.Status) {
	stream = make(chan fuse.DirEntry, 1)
	stream <- fuse.DirEntry{Name: "head", Mode: fuse.S_IFLNK}
	close(stream)
	return stream, fuse.OK
}

func (r *p4Root) Lookup(name string, context *fuse.Context) (fi *fuse.Attr, node fuse.FsNode, code fuse.Status) {
	cl, err := strconv.ParseInt(name, 10, 64)
	if err != nil {
		return nil, nil, fuse.ENOENT
	}

	node = r.fs.newFolder("", int(cl))
	r.Inode().AddChild(name, r.Inode().New(true, node))

	a, _ := node.GetAttr(nil, context)
	return a, node, fuse.OK
}

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

type p4Folder struct {
	fuse.DefaultFsNode
	change int
	path   string
	fs     *P4Fs

	// nil means they haven't been fetched yet.
	files   map[string]*p4.Stat
	folders map[string]bool
}

func (f *p4Folder) OpenDir(context *fuse.Context) (stream chan fuse.DirEntry, status fuse.Status) {
	if !f.fetch() {
		return nil, fuse.EIO
	}
	stream = make(chan fuse.DirEntry, len(f.files)+len(f.folders))

	for n, _ := range f.files {
		mode := fuse.S_IFREG | 0644
		stream <- fuse.DirEntry{Name: n, Mode: uint32(mode)}
	}
	for n, _ := range f.folders {
		mode := fuse.S_IFDIR | 0755
		stream <- fuse.DirEntry{Name: n, Mode: uint32(mode)}
	}
	close(stream)
	return stream, fuse.OK
}

func (f *p4Folder) GetAttr(file fuse.File, c *fuse.Context) (*fuse.Attr, fuse.Status) {
	return &fuse.Attr{
		Mode: fuse.S_IFDIR | 0755,
	}, fuse.OK
}

func (f *p4Folder) Deletable() bool {
	return false
}

func (f *p4Folder) fetch() bool {
	if f.files != nil {
		return true
	}

	var err error
	path := "//" + f.path
	if !strings.HasSuffix(path, "/") {
		path += "/"
	}
	path += fmt.Sprintf("*@%d", f.change)

	folders, err := f.fs.p4.Dirs([]string{path})
	if err != nil {
		log.Printf("fetch: %v", err)
		return false
	}
	files, err := f.fs.p4.Fstat([]string{path})
	if err != nil {
		log.Printf("fetch: %v", err)
		return false
	}

	f.files = map[string]*p4.Stat{}
	for _, r := range files {
		if stat, ok := r.(*p4.Stat); ok && stat.HeadAction != "delete" {
			_, base := filepath.Split(stat.DepotFile)
			f.files[base] = stat
		}
	}

	f.folders = map[string]bool{}
	for _, r := range folders {
		if dir, ok := r.(*p4.Dir); ok {
			_, base := filepath.Split(dir.Dir)
			f.folders[base] = true
		}
	}

	return true
}

func (f *p4Folder) Lookup(name string, context *fuse.Context) (fi *fuse.Attr, node fuse.FsNode, code fuse.Status) {
	f.fetch()

	if st := f.files[name]; st != nil {
		node = f.fs.newFile(st)
	} else if f.folders[name] {
		node = f.fs.newFolder(filepath.Join(f.path, name), f.change)
	} else {
		return nil, nil, fuse.ENOENT
	}

	f.Inode().AddChild(name, f.Inode().New(true, node))

	a, _ := node.GetAttr(nil, context)
	return a, node, fuse.OK
}

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

type p4File struct {
	fuse.DefaultFsNode
	stat    p4.Stat
	fs      *P4Fs
	backing string
}

func (f *p4File) GetAttr(file fuse.File, c *fuse.Context) (*fuse.Attr, fuse.Status) {
	return &fuse.Attr{
		Size:  uint64(f.stat.FileSize),
		Mode:  fuse.S_IFREG | 0644,
		Mtime: uint64(f.stat.HeadTime),
	}, fuse.OK
}

func (f *p4File) fetch() bool {
	if f.backing != "" {
		return true
	}
	id := fmt.Sprintf("%s#%d", f.stat.DepotFile, f.stat.HeadRev)
	content, err := f.fs.p4.Print(id)
	if err != nil {
		log.Printf("p4 print error: %v", err)
		return false
	}

	tmp, err := ioutil.TempFile(f.fs.backingDir, "")
	if err != nil {
		log.Printf("TempFile: %v", err)
		return false
	}

	tmp.Write(content)
	tmp.Close()

	h := crypto.MD5.New()
	h.Write([]byte(id))
	dest := fmt.Sprintf("%s/%x", f.fs.backingDir, h.Sum(nil))
	os.Rename(tmp.Name(), dest)
	f.backing = dest
	return true
}

func (f *p4File) Deletable() bool {
	return false
}

func (n *p4File) Open(flags uint32, context *fuse.Context) (file fuse.File, code fuse.Status) {
	if flags&fuse.O_ANYWRITE != 0 {
		return nil, fuse.EROFS
	}

	n.fetch()
	f, err := os.OpenFile(n.backing, int(flags), 0644)
	if err != nil {
		return nil, fuse.ToStatus(err)
	}
	return &fuse.LoopbackFile{File: f}, fuse.OK
}
<|endoftext|>


<commit_before>package runc

import (
	"io"
	"os"
	"os/exec"
)

type IO interface {
	io.Closer
	Stdin() io.WriteCloser
	Stdout() io.ReadCloser
	Stderr() io.ReadCloser
	Set(*exec.Cmd)
}

type StartCloser interface {
	CloseAfterStart() error
}

// NewPipeIO creates pipe pairs to be used with runc
func NewPipeIO() (i IO, err error) {
	var pipes []*pipe
	// cleanup in case of an error
	defer func() {
		if err != nil {
			for _, p := range pipes {
				p.Close()
			}
		}
	}()
	stdin, err := newPipe()
	if err != nil {
		return nil, err
	}
	pipes = append(pipes, stdin)

	stdout, err := newPipe()
	if err != nil {
		return nil, err
	}
	pipes = append(pipes, stdout)

	stderr, err := newPipe()
	if err != nil {
		return nil, err
	}
	pipes = append(pipes, stderr)

	return &pipeIO{
		in:  stdin,
		out: stdout,
		err: stderr,
	}, nil
}

func newPipe() (*pipe, error) {
	r, w, err := os.Pipe()
	if err != nil {
		return nil, err
	}
	return &pipe{
		r: r,
		w: w,
	}, nil
}

type pipe struct {
	r *os.File
	w *os.File
}

func (p *pipe) Close() error {
	err := p.r.Close()
	if werr := p.w.Close(); err == nil {
		err = werr
	}
	return err
}

type pipeIO struct {
	in  *pipe
	out *pipe
	err *pipe
}

func (i *pipeIO) Stdin() io.WriteCloser {
	return i.in.w
}

func (i *pipeIO) Stdout() io.ReadCloser {
	return i.out.r
}

func (i *pipeIO) Stderr() io.ReadCloser {
	return i.err.r
}

func (i *pipeIO) Close() error {
	var err error
	for _, v := range []*pipe{
		i.in,
		i.out,
		i.err,
	} {
		if cerr := v.Close(); err == nil {
			err = cerr
		}
	}
	return err
}

func (i *pipeIO) CloseAfterStart() error {
	for _, f := range []*os.File{
		i.out.w,
		i.err.w,
	} {
		f.Close()
	}
	return nil
}

// Set sets the io to the exec.Cmd
func (i *pipeIO) Set(cmd *exec.Cmd) {
	cmd.Stdin = i.in.r
	cmd.Stdout = i.out.w
	cmd.Stderr = i.err.w
}

func NewSTDIO() (IO, error) {
	return &stdio{}, nil
}

type stdio struct {
}

func (s *stdio) Close() error {
	return nil
}

func (s *stdio) Set(cmd *exec.Cmd) {
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
}

func (s *stdio) Stdin() io.WriteCloser {
	return os.Stdin
}

func (s *stdio) Stdout() io.ReadCloser {
	return os.Stdout
}

func (s *stdio) Stderr() io.ReadCloser {
	return os.Stderr
}

// NewNullIO returns IO setup for /dev/null use with runc
func NewNullIO() (IO, error) {
	f, err := os.Open(os.DevNull)
	if err != nil {
		return nil, err
	}
	return &nullIO{
		devNull: f,
	}, nil
}

type nullIO struct {
	devNull *os.File
}

func (n *nullIO) Close() error {
	// this should be closed after start but if not
	// make sure we close the file but don't return the error
	n.devNull.Close()
	return nil
}

func (n *nullIO) Stdin() io.WriteCloser {
	return nil
}

func (n *nullIO) Stdout() io.ReadCloser {
	return nil
}

func (n *nullIO) Stderr() io.ReadCloser {
	return nil
}

func (n *nullIO) Set(c *exec.Cmd) {
	// don't set STDIN here
	c.Stdout = n.devNull
	c.Stderr = n.devNull
}

func (n *nullIO) CloseAfterStart() error {
	return n.devNull.Close()
}
<commit_msg>Update NewPipeIO to allow setting the uid, gid of the pipes<commit_after>package runc

import (
	"io"
	"os"
	"os/exec"

	"github.com/pkg/errors"
	"golang.org/x/sys/unix"
)

type IO interface {
	io.Closer
	Stdin() io.WriteCloser
	Stdout() io.ReadCloser
	Stderr() io.ReadCloser
	Set(*exec.Cmd)
}

type StartCloser interface {
	CloseAfterStart() error
}

// NewPipeIO creates pipe pairs to be used with runc
func NewPipeIO(uid, gid int) (i IO, err error) {
	var pipes []*pipe
	// cleanup in case of an error
	defer func() {
		if err != nil {
			for _, p := range pipes {
				p.Close()
			}
		}
	}()
	stdin, err := newPipe()
	if err != nil {
		return nil, err
	}
	pipes = append(pipes, stdin)
	if err = unix.Fchown(int(stdin.r.Fd()), uid, gid); err != nil {
		return nil, errors.Wrap(err, "failed to chown stdin")
	}

	stdout, err := newPipe()
	if err != nil {
		return nil, err
	}
	pipes = append(pipes, stdout)
	if err = unix.Fchown(int(stdout.w.Fd()), uid, gid); err != nil {
		return nil, errors.Wrap(err, "failed to chown stdout")
	}

	stderr, err := newPipe()
	if err != nil {
		return nil, err
	}
	pipes = append(pipes, stderr)
	if err = unix.Fchown(int(stderr.w.Fd()), uid, gid); err != nil {
		return nil, errors.Wrap(err, "failed to chown stderr")
	}

	return &pipeIO{
		in:  stdin,
		out: stdout,
		err: stderr,
	}, nil
}

func newPipe() (*pipe, error) {
	r, w, err := os.Pipe()
	if err != nil {
		return nil, err
	}
	return &pipe{
		r: r,
		w: w,
	}, nil
}

type pipe struct {
	r *os.File
	w *os.File
}

func (p *pipe) Close() error {
	err := p.r.Close()
	if werr := p.w.Close(); err == nil {
		err = werr
	}
	return err
}

type pipeIO struct {
	in  *pipe
	out *pipe
	err *pipe
}

func (i *pipeIO) Stdin() io.WriteCloser {
	return i.in.w
}

func (i *pipeIO) Stdout() io.ReadCloser {
	return i.out.r
}

func (i *pipeIO) Stderr() io.ReadCloser {
	return i.err.r
}

func (i *pipeIO) Close() error {
	var err error
	for _, v := range []*pipe{
		i.in,
		i.out,
		i.err,
	} {
		if cerr := v.Close(); err == nil {
			err = cerr
		}
	}
	return err
}

func (i *pipeIO) CloseAfterStart() error {
	for _, f := range []*os.File{
		i.out.w,
		i.err.w,
	} {
		f.Close()
	}
	return nil
}

// Set sets the io to the exec.Cmd
func (i *pipeIO) Set(cmd *exec.Cmd) {
	cmd.Stdin = i.in.r
	cmd.Stdout = i.out.w
	cmd.Stderr = i.err.w
}

func NewSTDIO() (IO, error) {
	return &stdio{}, nil
}

type stdio struct {
}

func (s *stdio) Close() error {
	return nil
}

func (s *stdio) Set(cmd *exec.Cmd) {
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
}

func (s *stdio) Stdin() io.WriteCloser {
	return os.Stdin
}

func (s *stdio) Stdout() io.ReadCloser {
	return os.Stdout
}

func (s *stdio) Stderr() io.ReadCloser {
	return os.Stderr
}

// NewNullIO returns IO setup for /dev/null use with runc
func NewNullIO() (IO, error) {
	f, err := os.Open(os.DevNull)
	if err != nil {
		return nil, err
	}
	return &nullIO{
		devNull: f,
	}, nil
}

type nullIO struct {
	devNull *os.File
}

func (n *nullIO) Close() error {
	// this should be closed after start but if not
	// make sure we close the file but don't return the error
	n.devNull.Close()
	return nil
}

func (n *nullIO) Stdin() io.WriteCloser {
	return nil
}

func (n *nullIO) Stdout() io.ReadCloser {
	return nil
}

func (n *nullIO) Stderr() io.ReadCloser {
	return nil
}

func (n *nullIO) Set(c *exec.Cmd) {
	// don't set STDIN here
	c.Stdout = n.devNull
	c.Stderr = n.devNull
}

func (n *nullIO) CloseAfterStart() error {
	return n.devNull.Close()
}
<|endoftext|>


<commit_before>package gogol

import (
	"bytes"
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"time"
)

type encodeState struct {
	buffer     *bytes.Buffer
	writeBegan bool
}

// Marshal will encode any structure or map in a k=v format
func Marshal(data interface{}) ([]byte, error) {
	e := &encodeState{}
	err := e.marshal("", data)
	return e.buffer.Bytes(), err
}

// MarshalString is a proxy method for Marshal that drops any error and always return a string
func MarshalString(data interface{}) string {
	e := &encodeState{}
	e.marshal("", data)
	return e.buffer.String()
}

func (e *encodeState) marshal(prefix string, data interface{}) error {
	if e.writeBegan {
		e.writeBegan = false
	}
	if e.buffer == nil {
		e.buffer = bytes.NewBuffer(nil)
	}

	value := reflect.ValueOf(data)
	if value.Kind() == reflect.Ptr {
		return e.marshalPtr(prefix, value.Interface())
	}

	if value.Kind() == reflect.Struct {
		return e.marshalStruct(prefix, value.Interface())
	}

	if value.Kind() == reflect.Map {
		return e.marshalMap(prefix, value.Interface())
	}

	return fmt.Errorf("don't know how to marshal this type")
}

func (e *encodeState) writeTab() error {
	if !e.writeBegan {
		return nil
	}

	_, err := e.buffer.WriteRune('\t')
	return err
}

func (e *encodeState) marshalPtr(prefix string, data interface{}) error {
	value := reflect.ValueOf(data)
	value = value.Elem()

	if value.Kind() == reflect.Struct {
		return e.marshalStruct(prefix, value.Interface())
	}

	return fmt.Errorf("don't know how to marshal this pointer")
}

func (e *encodeState) marshalStruct(prefix string, data interface{}) error {
	ref := reflect.TypeOf(data)
	value := reflect.ValueOf(data)

	for i := 0; i < value.NumField(); i++ {
		fieldType := ref.Field(i)
		// Ignore unexported fields
		if fieldType.PkgPath != "" {
			continue
		}
		// Get the key for this field
		var key string
		if key = fieldType.Tag.Get("kv"); key == "-" {
			continue
		} else if key == "" {
			key = fieldType.Name
		}
		e.writeTab()

		field := value.Field(i)
		if field.Kind() == reflect.Ptr || field.Kind() == reflect.Struct || field.Kind() == reflect.Map {
			if len(key) > 0 {
				key = key + "."
			}
			err := e.marshal(key, field.Interface())
			if err != nil {
				return err
			}
			continue
		}

		value := stringify(field.Interface())

		if _, err := e.buffer.WriteString(fmt.Sprintf("%s%s=%s", prefix, key, value)); err != nil {
			return err
		}
		e.writeBegan = true
	}

	return nil
}

func (e *encodeState) marshalMap(prefix string, data interface{}) error {
	value := reflect.ValueOf(data)
	keys := value.MapKeys()
	for _, v := range keys {
		e.writeTab()
		mapKey, _ := strconv.Unquote(stringify(v.Interface()))
		mapValue := stringify(value.MapIndex(v).Interface())
		if _, err := e.buffer.WriteString(fmt.Sprintf("%s%s=%s", prefix, mapKey, mapValue)); err != nil {
			return err
		}
		e.writeBegan = true
	}

	return nil
}

func stringify(data interface{}) string {
	value := ""
	switch t := data.(type) {
	case bool:
		value = strconv.FormatBool(t)
	case rune:
		value = strconv.QuoteRune(t)
	case time.Time:
		value = t.UTC().Format(time.RFC3339)
	case int:
		value = strconv.Itoa(int(t))
    case fmt.Stringer:
		value = t.String()
	case string:
		value = strconv.Quote(t)
	case []string:
		value = strconv.Quote(strings.Join(t, ";"))
	default:
		value = "n/a"
	}
	return value
}
<commit_msg>Format<commit_after>package gogol

import (
	"bytes"
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"time"
)

type encodeState struct {
	buffer     *bytes.Buffer
	writeBegan bool
}

// Marshal will encode any structure or map in a k=v format
func Marshal(data interface{}) ([]byte, error) {
	e := &encodeState{}
	err := e.marshal("", data)
	return e.buffer.Bytes(), err
}

// MarshalString is a proxy method for Marshal that drops any error and always return a string
func MarshalString(data interface{}) string {
	e := &encodeState{}
	e.marshal("", data)
	return e.buffer.String()
}

func (e *encodeState) marshal(prefix string, data interface{}) error {
	if e.writeBegan {
		e.writeBegan = false
	}
	if e.buffer == nil {
		e.buffer = bytes.NewBuffer(nil)
	}

	value := reflect.ValueOf(data)
	if value.Kind() == reflect.Ptr {
		return e.marshalPtr(prefix, value.Interface())
	}

	if value.Kind() == reflect.Struct {
		return e.marshalStruct(prefix, value.Interface())
	}

	if value.Kind() == reflect.Map {
		return e.marshalMap(prefix, value.Interface())
	}

	return fmt.Errorf("don't know how to marshal this type")
}

func (e *encodeState) writeTab() error {
	if !e.writeBegan {
		return nil
	}

	_, err := e.buffer.WriteRune('\t')
	return err
}

func (e *encodeState) marshalPtr(prefix string, data interface{}) error {
	value := reflect.ValueOf(data)
	value = value.Elem()

	if value.Kind() == reflect.Struct {
		return e.marshalStruct(prefix, value.Interface())
	}

	return fmt.Errorf("don't know how to marshal this pointer")
}

func (e *encodeState) marshalStruct(prefix string, data interface{}) error {
	ref := reflect.TypeOf(data)
	value := reflect.ValueOf(data)

	for i := 0; i < value.NumField(); i++ {
		fieldType := ref.Field(i)
		// Ignore unexported fields
		if fieldType.PkgPath != "" {
			continue
		}
		// Get the key for this field
		var key string
		if key = fieldType.Tag.Get("kv"); key == "-" {
			continue
		} else if key == "" {
			key = fieldType.Name
		}
		e.writeTab()

		field := value.Field(i)
		if field.Kind() == reflect.Ptr || field.Kind() == reflect.Struct || field.Kind() == reflect.Map {
			if len(key) > 0 {
				key = key + "."
			}
			err := e.marshal(key, field.Interface())
			if err != nil {
				return err
			}
			continue
		}

		value := stringify(field.Interface())

		if _, err := e.buffer.WriteString(fmt.Sprintf("%s%s=%s", prefix, key, value)); err != nil {
			return err
		}
		e.writeBegan = true
	}

	return nil
}

func (e *encodeState) marshalMap(prefix string, data interface{}) error {
	value := reflect.ValueOf(data)
	keys := value.MapKeys()
	for _, v := range keys {
		e.writeTab()
		mapKey, _ := strconv.Unquote(stringify(v.Interface()))
		mapValue := stringify(value.MapIndex(v).Interface())
		if _, err := e.buffer.WriteString(fmt.Sprintf("%s%s=%s", prefix, mapKey, mapValue)); err != nil {
			return err
		}
		e.writeBegan = true
	}

	return nil
}

func stringify(data interface{}) string {
	value := ""
	switch t := data.(type) {
	case bool:
		value = strconv.FormatBool(t)
	case rune:
		value = strconv.QuoteRune(t)
	case time.Time:
		value = t.UTC().Format(time.RFC3339)
	case int:
		value = strconv.Itoa(int(t))
	case fmt.Stringer:
		value = t.String()
	case string:
		value = strconv.Quote(t)
	case []string:
		value = strconv.Quote(strings.Join(t, ";"))
	default:
		value = "n/a"
	}
	return value
}
<|endoftext|>


<commit_before>package golevelkv

import "bytes"
import "fmt"
import "hash/fnv"

import "github.com/syndtr/goleveldb/leveldb"
import "github.com/syndtr/goleveldb/leveldb/opt"
import "github.com/syndtr/goleveldb/leveldb/util"

const DEFAULT_NUM_BUCKETS = 16
const DEFAULT_BUFFER_SIZE = 8

type Config struct {
	nBuckets uint
	bufferSz uint
}

func NewConfig() *Config {
	return new(Config)
}

func (c *Config) NumBuckets() uint {
	if c.nBuckets != 0 {
		return c.nBuckets
	} else {
		return DEFAULT_NUM_BUCKETS
	}
}

func (c *Config) BufferSize() uint {
	if c.bufferSz != 0 {
		return c.bufferSz
	} else {
		return DEFAULT_BUFFER_SIZE
	}
}

func (c *Config) WithBuckets(n uint) {
	c.nBuckets = n
}

func (c *Config) WithBuffer(n uint) {
	c.bufferSz = n
}

type req struct {
	do func(*leveldb.DB) (interface{}, error)
	r  chan doRet
}

type doRet struct {
	i interface{}
	e error
}

type DB struct {
	db       *leveldb.DB
	requests []chan req
	stop     chan bool
	closed   bool
}

func Wrap(db *leveldb.DB, config *Config) *DB {
	ret := &DB{
		db:       db,
		requests: make([]chan req, 0, config.NumBuckets()),
		stop:     make(chan bool, 1),
	}

	for len(ret.requests) < cap(ret.requests) {
		ret.requests = append(ret.requests, make(chan req, config.BufferSize()))
	}

	for n, _ := range ret.requests {
		go func(idx int) {
			reqs := ret.requests[idx]
			for {
				req := <-reqs
				result, err := req.do(ret.db)
				req.r <- doRet{
					i: result,
					e: err,
				}
			}
		}(n)
	}

	go func() {
		_ = <-ret.stop
		for i, _ := range ret.requests {
			close(ret.requests[i])
		}
	}()

	return ret
}

func (db *DB) getRange(key []byte) int {
	hasher := fnv.New64()
	hasher.Write(key)
	return int(uint(hasher.Sum64()) % uint(len(db.requests)))
}

// TODO: Make sure this is threadsafe
func (db *DB) Close() error {
	db.closed = true
	defer func() {
		db.stop <- true
	}()
	return db.db.Close()
}

func (db *DB) wrapOperation(key []byte, f func(db *leveldb.DB) (interface{}, error)) doRet {
	idx := db.getRange(key)
	result := make(chan doRet, 1)
	db.requests[idx] <- req{
		r:  result,
		do: f,
	}
	ret := <-result
	return ret
}

func (db *DB) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) {
	if db.closed {
		return nil, fmt.Errorf("Get(): db is already closed")
	}
	ret := db.wrapOperation(key, func(db *leveldb.DB) (interface{}, error) {
		val, er := db.Get(key, ro)
		return interface{}(val), er
	})

	value = ret.i.([]byte)
	err = ret.e
	return
}

func (db *DB) Delete(key []byte, wo *opt.WriteOptions) error {
	if db.closed {
		return fmt.Errorf("Get(): db is already closed")
	}
	ret := db.wrapOperation(key, func(db *leveldb.DB) (interface{}, error) {
		er := db.Delete(key, wo)
		return nil, er
	})

	return ret.e
}

func (db *DB) Has(key []byte, ro *opt.ReadOptions) (ret bool, err error) {
	if db.closed {
		return false, fmt.Errorf("Has(): db is already closed")
	}
	r := db.wrapOperation(key, func(db *leveldb.DB) (interface{}, error) {
		bl, er := db.Has(key, ro)
		return interface{}(bl), er
	})

	ret = r.i.(bool)
	err = r.e
	return
}

func (db *DB) Put(key, value []byte, wo *opt.WriteOptions) error {
	if db.closed {
		return fmt.Errorf("Has(): db is already closed")
	}
	ret := db.wrapOperation(key, func(db *leveldb.DB) (interface{}, error) {
		er := db.Put(key, value, wo)
		return nil, er
	})

	return ret.e
}

func (db *DB) Cas(key, value []byte, old []byte, ro *opt.ReadOptions, wo *opt.WriteOptions) (swapped bool, err error) {
	if db.closed {
		return false, fmt.Errorf("Cas(): db is already closed")
	}
	ret := db.wrapOperation(key, func(db *leveldb.DB) (interface{}, error) {
		val, e := db.Get(key, ro)
		if e != nil {
			return false, e
		}
		if bytes.Equal(val, old) {
			if er := db.Put(key, value, wo); er != nil {
				return interface{}(false), er
			}
			return true, nil
		} else {
			return false, nil
		}
	})

	swapped = ret.i.(bool)
	err = ret.e
	return
}

func (db *DB) Cas2(key, value []byte, old []byte, ro *opt.ReadOptions, wo *opt.WriteOptions) (retStr []byte, swapped bool, err error) {
	type cas2ret struct {
		bs []byte
		b  bool
	}
	if db.closed {
		return nil, false, fmt.Errorf("Cas2(): db is already closed")
	}
	ret := db.wrapOperation(key, func(d *leveldb.DB) (interface{}, error) {
		val, e := d.Get(key, ro)
		if e != nil {
			return val, e
		}
		if bytes.Equal(val, old) {
			if er := d.Put(key, value, wo); er != nil {
				return &cas2ret{val, false}, er
			}
			return &cas2ret{value, true}, nil
		} else {
			return &cas2ret{val, false}, nil
		}
	})

	r := ret.i.(*cas2ret)
	retStr = r.bs
	swapped = r.b
	err = ret.e
	return
}

type Tx struct {
	key []byte
	db  *leveldb.DB
}

func (tx *Tx) Get(ro *opt.ReadOptions) ([]byte, error) {
	return tx.db.Get(tx.key, ro)
}

func (tx *Tx) Set(value []byte, wo *opt.WriteOptions) error {
	return tx.db.Put(tx.key, value, wo)
}

func (tx *Tx) Has(ro *opt.ReadOptions) (bool, error) {
	return tx.db.Has(tx.key, ro)
}

func (tx *Tx) Delete(wo *opt.WriteOptions) error {
	return tx.db.Delete(tx.key, wo)
}

func (db *DB) Do(key []byte, f func(*Tx) (interface{}, error)) (interface{}, error) {
	ret := db.wrapOperation(key, func(levelDb *leveldb.DB) (interface{}, error) {
		tx := Tx{
			key: key,
			db:  levelDb,
		}
		return f(&tx)
	})
	return ret.i, ret.e
}

// Wrappers for all the functions at a normal DB has (and that we can guarantee
// to be safe to run)
func (db *DB) CompactRange(r util.Range) error {
	return db.db.CompactRange(r)
}

func (db *DB) GetProperty(name string) (string, error) {
	return db.GetProperty(name)
}

func (db *DB) GetSnapshot() (*leveldb.Snapshot, error) {
	return db.GetSnapshot()
}

func (db *DB) SetReadOnly() error {
	return db.SetReadOnly()
}

func (db *DB) SizeOf(ranges []util.Range) (leveldb.Sizes, error) {
	return db.SizeOf(ranges)
}
<commit_msg>Add a comment to kv.go<commit_after>package golevelkv

import "bytes"
import "fmt"
import "hash/fnv"

import "github.com/syndtr/goleveldb/leveldb"
import "github.com/syndtr/goleveldb/leveldb/opt"
import "github.com/syndtr/goleveldb/leveldb/util"

const DEFAULT_NUM_BUCKETS = 16
const DEFAULT_BUFFER_SIZE = 8

// The configuration defines how many buckets and how big each bucket's channel
// will be. These affect the overhead and scalability of the database.
type Config struct {
	nBuckets uint
	bufferSz uint
}

func NewConfig() *Config {
	return new(Config)
}

func (c *Config) NumBuckets() uint {
	if c.nBuckets != 0 {
		return c.nBuckets
	} else {
		return DEFAULT_NUM_BUCKETS
	}
}

func (c *Config) BufferSize() uint {
	if c.bufferSz != 0 {
		return c.bufferSz
	} else {
		return DEFAULT_BUFFER_SIZE
	}
}

func (c *Config) WithBuckets(n uint) {
	c.nBuckets = n
}

func (c *Config) WithBuffer(n uint) {
	c.bufferSz = n
}

type req struct {
	do func(*leveldb.DB) (interface{}, error)
	r  chan doRet
}

type doRet struct {
	i interface{}
	e error
}

type DB struct {
	db       *leveldb.DB
	requests []chan req
	stop     chan bool
	closed   bool
}

func Wrap(db *leveldb.DB, config *Config) *DB {
	ret := &DB{
		db:       db,
		requests: make([]chan req, 0, config.NumBuckets()),
		stop:     make(chan bool, 1),
	}

	for len(ret.requests) < cap(ret.requests) {
		ret.requests = append(ret.requests, make(chan req, config.BufferSize()))
	}

	for n, _ := range ret.requests {
		go func(idx int) {
			reqs := ret.requests[idx]
			for {
				req := <-reqs
				result, err := req.do(ret.db)
				req.r <- doRet{
					i: result,
					e: err,
				}
			}
		}(n)
	}

	go func() {
		_ = <-ret.stop
		for i, _ := range ret.requests {
			close(ret.requests[i])
		}
	}()

	return ret
}

func (db *DB) getRange(key []byte) int {
	hasher := fnv.New64()
	hasher.Write(key)
	return int(uint(hasher.Sum64()) % uint(len(db.requests)))
}

// TODO: Make sure this is threadsafe
func (db *DB) Close() error {
	db.closed = true
	defer func() {
		db.stop <- true
	}()
	return db.db.Close()
}

func (db *DB) wrapOperation(key []byte, f func(db *leveldb.DB) (interface{}, error)) doRet {
	idx := db.getRange(key)
	result := make(chan doRet, 1)
	db.requests[idx] <- req{
		r:  result,
		do: f,
	}
	ret := <-result
	return ret
}

func (db *DB) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) {
	if db.closed {
		return nil, fmt.Errorf("Get(): db is already closed")
	}
	ret := db.wrapOperation(key, func(db *leveldb.DB) (interface{}, error) {
		val, er := db.Get(key, ro)
		return interface{}(val), er
	})

	value = ret.i.([]byte)
	err = ret.e
	return
}

func (db *DB) Delete(key []byte, wo *opt.WriteOptions) error {
	if db.closed {
		return fmt.Errorf("Get(): db is already closed")
	}
	ret := db.wrapOperation(key, func(db *leveldb.DB) (interface{}, error) {
		er := db.Delete(key, wo)
		return nil, er
	})

	return ret.e
}

func (db *DB) Has(key []byte, ro *opt.ReadOptions) (ret bool, err error) {
	if db.closed {
		return false, fmt.Errorf("Has(): db is already closed")
	}
	r := db.wrapOperation(key, func(db *leveldb.DB) (interface{}, error) {
		bl, er := db.Has(key, ro)
		return interface{}(bl), er
	})

	ret = r.i.(bool)
	err = r.e
	return
}

func (db *DB) Put(key, value []byte, wo *opt.WriteOptions) error {
	if db.closed {
		return fmt.Errorf("Has(): db is already closed")
	}
	ret := db.wrapOperation(key, func(db *leveldb.DB) (interface{}, error) {
		er := db.Put(key, value, wo)
		return nil, er
	})

	return ret.e
}

func (db *DB) Cas(key, value []byte, old []byte, ro *opt.ReadOptions, wo *opt.WriteOptions) (swapped bool, err error) {
	if db.closed {
		return false, fmt.Errorf("Cas(): db is already closed")
	}
	ret := db.wrapOperation(key, func(db *leveldb.DB) (interface{}, error) {
		val, e := db.Get(key, ro)
		if e != nil {
			return false, e
		}
		if bytes.Equal(val, old) {
			if er := db.Put(key, value, wo); er != nil {
				return interface{}(false), er
			}
			return true, nil
		} else {
			return false, nil
		}
	})

	swapped = ret.i.(bool)
	err = ret.e
	return
}

func (db *DB) Cas2(key, value []byte, old []byte, ro *opt.ReadOptions, wo *opt.WriteOptions) (retStr []byte, swapped bool, err error) {
	type cas2ret struct {
		bs []byte
		b  bool
	}
	if db.closed {
		return nil, false, fmt.Errorf("Cas2(): db is already closed")
	}
	ret := db.wrapOperation(key, func(d *leveldb.DB) (interface{}, error) {
		val, e := d.Get(key, ro)
		if e != nil {
			return val, e
		}
		if bytes.Equal(val, old) {
			if er := d.Put(key, value, wo); er != nil {
				return &cas2ret{val, false}, er
			}
			return &cas2ret{value, true}, nil
		} else {
			return &cas2ret{val, false}, nil
		}
	})

	r := ret.i.(*cas2ret)
	retStr = r.bs
	swapped = r.b
	err = ret.e
	return
}

type Tx struct {
	key []byte
	db  *leveldb.DB
}

func (tx *Tx) Get(ro *opt.ReadOptions) ([]byte, error) {
	return tx.db.Get(tx.key, ro)
}

func (tx *Tx) Set(value []byte, wo *opt.WriteOptions) error {
	return tx.db.Put(tx.key, value, wo)
}

func (tx *Tx) Has(ro *opt.ReadOptions) (bool, error) {
	return tx.db.Has(tx.key, ro)
}

func (tx *Tx) Delete(wo *opt.WriteOptions) error {
	return tx.db.Delete(tx.key, wo)
}

func (db *DB) Do(key []byte, f func(*Tx) (interface{}, error)) (interface{}, error) {
	ret := db.wrapOperation(key, func(levelDb *leveldb.DB) (interface{}, error) {
		tx := Tx{
			key: key,
			db:  levelDb,
		}
		return f(&tx)
	})
	return ret.i, ret.e
}

// Wrappers for all the functions at a normal DB has (and that we can guarantee
// to be safe to run)
func (db *DB) CompactRange(r util.Range) error {
	return db.db.CompactRange(r)
}

func (db *DB) GetProperty(name string) (string, error) {
	return db.GetProperty(name)
}

func (db *DB) GetSnapshot() (*leveldb.Snapshot, error) {
	return db.GetSnapshot()
}

func (db *DB) SetReadOnly() error {
	return db.SetReadOnly()
}

func (db *DB) SizeOf(ranges []util.Range) (leveldb.Sizes, error) {
	return db.SizeOf(ranges)
}
<|endoftext|>


<commit_before>package venue

import "image"

// Switch size examples:
// - Tiny: Channel solo or mute on bank (13x13 px)
// - Small: Encoder ON (18x14 px)
// - Medium: 48V, Pad, Guess (26x16 px)
// - Large: Channel solo or mute (32x18)
const (
	tinySwitch = iota
	smallSwitch
	mediumSwitch
	largeSwitch
)

// Switch is a UIElement representing a switch.
type Switch struct {
	pos    image.Point // Position of UI element.
	size   int         // tiny..large
	toggle bool        // Toggle switch? true = toggle, false = hold-to-enable
	def    bool        // Default value; true = on
	state  bool        // Current state; true = on (pressed)
}

func newPushButton(x, y int, size int) *Switch {
	return &Switch{
		pos:  image.Point{x, y},
		size: size,
	}
}

func newToggle(x, y int, size int, def bool) *Switch {
	return &Switch{
		pos:    image.Point{x, y},
		size:   size,
		toggle: true,
		def:    def,
		state:  def,
	}
}

func (e *Switch) Read(v *Venue) error   { return nil }
func (e *Switch) Select(v *Venue)       { v.MouseLeftClick(e.clickOffset()) }
func (e *Switch) Set(v *Venue, val int) {}

func (e *Switch) Update(v *Venue) error {
	e.Select(v)
	if e.toggle {
		e.state = !e.state
	}
	return nil
}

func (e *Switch) clickOffset() image.Point {
	switch e.size {
	case tinySwitch:
		return e.pos.Add(image.Point{7, 7})
	case smallSwitch:
		return e.pos.Add(image.Point{9, 7})
	case mediumSwitch:
		return e.pos.Add(image.Point{13, 8})
	case largeSwitch:
		return e.pos.Add(image.Point{16, 9})
	}
	return e.pos
}
<commit_msg>changed default for newToggle to off<commit_after>package venue

import "image"

// Switch size examples:
// - Tiny: Channel solo or mute on bank (13x13 px)
// - Small: Encoder ON (18x14 px)
// - Medium: 48V, Pad, Guess (26x16 px)
// - Large: Channel solo or mute (32x18)
const (
	tinySwitch = iota
	smallSwitch
	mediumSwitch
	largeSwitch
	buttonOff = false
	buttonOn  = true
)

// Switch is a UIElement representing a switch.
type Switch struct {
	pos    image.Point // Position of UI element.
	size   int         // tiny..large
	toggle bool        // Toggle switch? true = toggle, false = hold-to-enable
	def    bool        // Default value; true = on
	state  bool        // Current state; true = on (pressed)
}

func newPushButton(x, y int, size int) *Switch {
	return &Switch{
		pos:  image.Point{x, y},
		size: size,
	}
}

func newToggle(x, y int, size int, def bool) *Switch {
	return &Switch{
		pos:    image.Point{x, y},
		size:   size,
		toggle: true,
		def:    buttonOff,
		state:  def,
	}
}

func (e *Switch) Read(v *Venue) error   { return nil }
func (e *Switch) Select(v *Venue)       { v.MouseLeftClick(e.clickOffset()) }
func (e *Switch) Set(v *Venue, val int) {}

func (e *Switch) Update(v *Venue) error {
	e.Select(v)
	if e.toggle {
		e.state = !e.state
	}
	return nil
}

func (e *Switch) clickOffset() image.Point {
	switch e.size {
	case tinySwitch:
		return e.pos.Add(image.Point{7, 7})
	case smallSwitch:
		return e.pos.Add(image.Point{9, 7})
	case mediumSwitch:
		return e.pos.Add(image.Point{13, 8})
	case largeSwitch:
		return e.pos.Add(image.Point{16, 9})
	}
	return e.pos
}
<|endoftext|>


<commit_before>// Copyright ©2013 The gonum 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 unit

import (
	"fmt"
	"math"
	"testing"
)

var formatTests = []struct {
	unit   Uniter
	format string
	expect string
}{
	{New(9.81, Dimensions{MassDim: 1, TimeDim: -2}), "%f", "9.81 kg s^-2"},
	{New(9.81, Dimensions{MassDim: 1, TimeDim: -2}), "%1.f", "10 kg s^-2"},
	{New(9.81, Dimensions{MassDim: 1, TimeDim: -2}), "%.1f", "9.8 kg s^-2"},
	{New(9.81, Dimensions{MassDim: 1, TimeDim: -2, LengthDim: 0}), "%f", "9.81 kg s^-2"},
	{New(6.62606957e-34, Dimensions{MassDim: 2, TimeDim: -1}), "%e", "6.62606957e-34 kg^2 s^-1"},
	{New(6.62606957e-34, Dimensions{MassDim: 2, TimeDim: -1}), "%.3e", "6.626e-34 kg^2 s^-1"},
	{New(6.62606957e-34, Dimensions{MassDim: 2, TimeDim: -1}), "%#v", `&unit.Unit{dimensions:unit.Dimensions{4:2, 6:-1}, formatted:"", value:6.62606957e-34}`},
	{New(6.62606957e-34, Dimensions{MassDim: 2, TimeDim: -1}), "%v", "6.62606957e-34 kg^2 s^-1"},
	{New(6.62606957e-34, Dimensions{MassDim: 2, TimeDim: -1}), "%s", "%!s(*Unit=6.62606957e-34 kg^2 s^-1)"},
	{Dimless(math.E), "%v", "2.718281828459045"},
	{Dimless(math.E), "%#v", "unit.Dimless(2.718281828459045)"},
	{Dimless(math.E), "%s", "%!s(unit.Dimless=2.718281828459045)"},
	{Mass(1), "%v", "1 kg"},
	{Mass(1), "%#v", "unit.Mass(1)"},
	{Mass(1), "%s", "%!s(unit.Mass=1 kg)"},
	{Length(1.61619926e-35), "%v", "1.61619926e-35 m"},
	{Length(1.61619926e-35), "%#v", "unit.Length(1.61619926e-35)"},
	{Length(1.61619926e-35), "%s", "%!s(unit.Length=1.61619926e-35 m)"},
	{Time(15.2), "%v", "15.2 s"},
	{Time(15.2), "%#v", "unit.Time(15.2)"},
	{Time(15.2), "%s", "%!s(unit.Time=15.2 s)"},
}

func TestFormat(t *testing.T) {
	for _, ts := range formatTests {
		if r := fmt.Sprintf(ts.format, ts.unit); r != ts.expect {
			t.Errorf("Format %q: got: %q expected: %q", ts.format, r, ts.expect)
		}
	}
}

var initializationTests = []struct {
	unit     *Unit
	expValue float64
	expMap   map[Dimension]int
}{
	{New(9.81, Dimensions{MassDim: 1, TimeDim: -2}), 9.81, Dimensions{MassDim: 1, TimeDim: -2}},
	{New(9.81, Dimensions{MassDim: 1, TimeDim: -2, LengthDim: 0, CurrentDim: 0}), 9.81, Dimensions{MassDim: 1, TimeDim: -2}},
}

func TestInitialization(t *testing.T) {
	for _, ts := range initializationTests {
		if ts.expValue != ts.unit.value {
			t.Errorf("Value wrong on initialization: got: %v expected: %v", ts.unit.value, ts.expValue)
		}
		if len(ts.expMap) != len(ts.unit.dimensions) {
			t.Errorf("Map mismatch: got: %#v expected: %#v", ts.unit.dimensions, ts.expMap)
		}
		for key, val := range ts.expMap {
			if ts.unit.dimensions[key] != val {
				t.Errorf("Map mismatch: got: %#v expected: %#v", ts.unit.dimensions, ts.expMap)
			}
		}
	}
}

var dimensionEqualityTests = []struct {
	name        string
	a           Uniter
	b           Uniter
	shouldMatch bool
}{
	{"same_empty", New(1.0, Dimensions{}), New(1.0, Dimensions{}), true},
	{"same_one", New(1.0, Dimensions{TimeDim: 1}), New(1.0, Dimensions{TimeDim: 1}), true},
	{"same_mult", New(1.0, Dimensions{TimeDim: 1, LengthDim: -2}), New(1.0, Dimensions{TimeDim: 1, LengthDim: -2}), true},
	{"diff_one_empty", New(1.0, Dimensions{}), New(1.0, Dimensions{TimeDim: 1, LengthDim: -2}), false},
	{"diff_same_dim", New(1.0, Dimensions{TimeDim: 1}), New(1.0, Dimensions{TimeDim: 2}), false},
	{"diff_same_pow", New(1.0, Dimensions{LengthDim: 1}), New(1.0, Dimensions{TimeDim: 1}), false},
	{"diff_numdim", New(1.0, Dimensions{TimeDim: 1, LengthDim: 2}), New(1.0, Dimensions{TimeDim: 2}), false},
	{"diff_one_same_dim", New(1.0, Dimensions{LengthDim: 1, TimeDim: 1}), New(1.0, Dimensions{LengthDim: 1, TimeDim: 2}), false},
}

func TestDimensionEquality(t *testing.T) {
	for _, ts := range dimensionEqualityTests {
		if DimensionsMatch(ts.a, ts.b) != ts.shouldMatch {
			t.Errorf("Dimension comparison incorrect for case %s. got: %v, expected: %v", ts.name, !ts.shouldMatch, ts.shouldMatch)
		}
	}
}

type UnitStructer interface {
	UnitStruct() *UnitStruct
}

type UnitStruct struct {
	current     int
	length      int
	luminosity  int
	mass        int
	temperature int
	time        int
	chemamt     int // For mol
	value       float64
}

// Check if the dimensions of two units are the same
func DimensionsMatchStruct(aU, bU UnitStructer) bool {
	a := aU.UnitStruct()
	b := bU.UnitStruct()
	if a.length != b.length {
		return false
	}
	if a.time != b.time {
		return false
	}
	if a.mass != b.mass {
		return false
	}
	if a.current != b.current {
		return false
	}
	if a.temperature != b.temperature {
		return false
	}
	if a.luminosity != b.luminosity {
		return false
	}
	if a.chemamt != b.chemamt {
		return false
	}
	return true
}

func (u *UnitStruct) UnitStruct() *UnitStruct {
	return u
}

func (u *UnitStruct) Add(aU UnitStructer) *UnitStruct {
	a := aU.UnitStruct()
	if !DimensionsMatchStruct(a, u) {
		panic("dimension mismatch")
	}
	u.value += a.value
	return u
}

func (u *UnitStruct) Mul(aU UnitStructer) *UnitStruct {
	a := aU.UnitStruct()
	u.length += a.length
	u.time += a.time
	u.mass += a.mass
	u.current += a.current
	u.temperature += a.temperature
	u.luminosity += a.luminosity
	u.chemamt += a.chemamt
	u.value *= a.value
	return u
}

var u3 *UnitStruct

func BenchmarkAddStruct(b *testing.B) {
	u1 := &UnitStruct{current: 1, chemamt: 5, value: 10}
	u2 := &UnitStruct{current: 1, chemamt: 5, value: 100}
	for i := 0; i < b.N; i++ {
		u2.Add(u1)
	}
}

func BenchmarkMulStruct(b *testing.B) {
	u1 := &UnitStruct{current: 1, chemamt: 5, value: 10}
	u2 := &UnitStruct{mass: 1, time: 1, value: 100}
	for i := 0; i < b.N; i++ {
		u2.Mul(u1)
	}
}

type UnitMapper interface {
	UnitMap() *UnitMap
}

type dimensionMap int

const (
	LengthB dimensionMap = iota
	TimeB
	MassB
	CurrentB
	TemperatureB
	LuminosityB
	ChemAmtB
)

type UnitMap struct {
	dimension map[dimensionMap]int
	value     float64
}

// Check if the dimensions of two units are the same
func DimensionsMatchMap(aU, bU UnitMapper) bool {
	a := aU.UnitMap()
	b := bU.UnitMap()
	if len(a.dimension) != len(b.dimension) {
		panic("Unequal dimension")
	}
	for key, dimA := range a.dimension {
		dimB, ok := b.dimension[key]
		if !ok || dimA != dimB {
			panic("Unequal dimension")
		}
	}
	return true
}

func (u *UnitMap) UnitMap() *UnitMap {
	return u
}

func (u *UnitMap) Add(aU UnitMapper) *UnitMap {
	a := aU.UnitMap()
	if !DimensionsMatchMap(a, u) {
		panic("dimension mismatch")
	}
	u.value += a.value
	return u
}

func (u *UnitMap) Mul(aU UnitMapper) *UnitMap {
	a := aU.UnitMap()
	for key, val := range a.dimension {
		u.dimension[key] += val
	}
	u.value *= a.value
	return u
}

func BenchmarkAddFloat(b *testing.B) {
	a := 0.0
	c := 10.0
	for i := 0; i < b.N; i++ {
		a += c
	}
}

func BenchmarkMulFloat(b *testing.B) {
	a := 0.0
	c := 10.0
	for i := 0; i < b.N; i++ {
		a *= c
	}
}

func BenchmarkAddMapSmall(b *testing.B) {
	u1 := &UnitMap{value: 10}
	u1.dimension = make(map[dimensionMap]int)
	u1.dimension[CurrentB] = 1
	u1.dimension[ChemAmtB] = 5

	u2 := &UnitMap{value: 10}
	u2.dimension = make(map[dimensionMap]int)
	u2.dimension[CurrentB] = 1
	u2.dimension[ChemAmtB] = 5
	for i := 0; i < b.N; i++ {
		u2.Add(u1)
	}
}

func BenchmarkMulMapSmallDiff(b *testing.B) {
	u1 := &UnitMap{value: 10}
	u1.dimension = make(map[dimensionMap]int)
	u1.dimension[LengthB] = 1

	u2 := &UnitMap{value: 10}
	u2.dimension = make(map[dimensionMap]int)
	u2.dimension[MassB] = 1
	for i := 0; i < b.N; i++ {
		u2.Mul(u1)
	}
}

func BenchmarkMulMapSmallSame(b *testing.B) {
	u1 := &UnitMap{value: 10}
	u1.dimension = make(map[dimensionMap]int)
	u1.dimension[LengthB] = 1

	u2 := &UnitMap{value: 10}
	u2.dimension = make(map[dimensionMap]int)
	u2.dimension[LengthB] = 2
	for i := 0; i < b.N; i++ {
		u2.Mul(u1)
	}
}

func BenchmarkMulMapLargeDiff(b *testing.B) {
	u1 := &UnitMap{value: 10}
	u1.dimension = make(map[dimensionMap]int)
	u1.dimension[LengthB] = 1
	u1.dimension[MassB] = 1
	u1.dimension[ChemAmtB] = 1
	u1.dimension[TemperatureB] = 1
	u1.dimension[LuminosityB] = 1
	u1.dimension[TimeB] = 1
	u1.dimension[CurrentB] = 1

	u2 := &UnitMap{value: 10}
	u2.dimension = make(map[dimensionMap]int)
	u2.dimension[MassB] = 1
	for i := 0; i < b.N; i++ {
		u2.Mul(u1)
	}
}

func BenchmarkMulMapLargeSame(b *testing.B) {
	u1 := &UnitMap{value: 10}
	u1.dimension = make(map[dimensionMap]int)
	u1.dimension[LengthB] = 2
	u1.dimension[MassB] = 2
	u1.dimension[ChemAmtB] = 2
	u1.dimension[TemperatureB] = 2
	u1.dimension[LuminosityB] = 2
	u1.dimension[TimeB] = 2
	u1.dimension[CurrentB] = 2

	u2 := &UnitMap{value: 10}
	u2.dimension = make(map[dimensionMap]int)
	u2.dimension[LengthB] = 3
	u2.dimension[MassB] = 3
	u2.dimension[ChemAmtB] = 3
	u2.dimension[TemperatureB] = 3
	u2.dimension[LuminosityB] = 3
	u2.dimension[TimeB] = 3
	u2.dimension[CurrentB] = 3
	for i := 0; i < b.N; i++ {
		u2.Mul(u1)
	}
}
<commit_msg>Removed testcases for missing Time unit<commit_after>// Copyright ©2013 The gonum 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 unit

import (
	"fmt"
	"math"
	"testing"
)

var formatTests = []struct {
	unit   Uniter
	format string
	expect string
}{
	{New(9.81, Dimensions{MassDim: 1, TimeDim: -2}), "%f", "9.81 kg s^-2"},
	{New(9.81, Dimensions{MassDim: 1, TimeDim: -2}), "%1.f", "10 kg s^-2"},
	{New(9.81, Dimensions{MassDim: 1, TimeDim: -2}), "%.1f", "9.8 kg s^-2"},
	{New(9.81, Dimensions{MassDim: 1, TimeDim: -2, LengthDim: 0}), "%f", "9.81 kg s^-2"},
	{New(6.62606957e-34, Dimensions{MassDim: 2, TimeDim: -1}), "%e", "6.62606957e-34 kg^2 s^-1"},
	{New(6.62606957e-34, Dimensions{MassDim: 2, TimeDim: -1}), "%.3e", "6.626e-34 kg^2 s^-1"},
	{New(6.62606957e-34, Dimensions{MassDim: 2, TimeDim: -1}), "%#v", `&unit.Unit{dimensions:unit.Dimensions{4:2, 6:-1}, formatted:"", value:6.62606957e-34}`},
	{New(6.62606957e-34, Dimensions{MassDim: 2, TimeDim: -1}), "%v", "6.62606957e-34 kg^2 s^-1"},
	{New(6.62606957e-34, Dimensions{MassDim: 2, TimeDim: -1}), "%s", "%!s(*Unit=6.62606957e-34 kg^2 s^-1)"},
	{Dimless(math.E), "%v", "2.718281828459045"},
	{Dimless(math.E), "%#v", "unit.Dimless(2.718281828459045)"},
	{Dimless(math.E), "%s", "%!s(unit.Dimless=2.718281828459045)"},
	{Mass(1), "%v", "1 kg"},
	{Mass(1), "%#v", "unit.Mass(1)"},
	{Mass(1), "%s", "%!s(unit.Mass=1 kg)"},
	{Length(1.61619926e-35), "%v", "1.61619926e-35 m"},
	{Length(1.61619926e-35), "%#v", "unit.Length(1.61619926e-35)"},
	{Length(1.61619926e-35), "%s", "%!s(unit.Length=1.61619926e-35 m)"},
}

func TestFormat(t *testing.T) {
	for _, ts := range formatTests {
		if r := fmt.Sprintf(ts.format, ts.unit); r != ts.expect {
			t.Errorf("Format %q: got: %q expected: %q", ts.format, r, ts.expect)
		}
	}
}

var initializationTests = []struct {
	unit     *Unit
	expValue float64
	expMap   map[Dimension]int
}{
	{New(9.81, Dimensions{MassDim: 1, TimeDim: -2}), 9.81, Dimensions{MassDim: 1, TimeDim: -2}},
	{New(9.81, Dimensions{MassDim: 1, TimeDim: -2, LengthDim: 0, CurrentDim: 0}), 9.81, Dimensions{MassDim: 1, TimeDim: -2}},
}

func TestInitialization(t *testing.T) {
	for _, ts := range initializationTests {
		if ts.expValue != ts.unit.value {
			t.Errorf("Value wrong on initialization: got: %v expected: %v", ts.unit.value, ts.expValue)
		}
		if len(ts.expMap) != len(ts.unit.dimensions) {
			t.Errorf("Map mismatch: got: %#v expected: %#v", ts.unit.dimensions, ts.expMap)
		}
		for key, val := range ts.expMap {
			if ts.unit.dimensions[key] != val {
				t.Errorf("Map mismatch: got: %#v expected: %#v", ts.unit.dimensions, ts.expMap)
			}
		}
	}
}

var dimensionEqualityTests = []struct {
	name        string
	a           Uniter
	b           Uniter
	shouldMatch bool
}{
	{"same_empty", New(1.0, Dimensions{}), New(1.0, Dimensions{}), true},
	{"same_one", New(1.0, Dimensions{TimeDim: 1}), New(1.0, Dimensions{TimeDim: 1}), true},
	{"same_mult", New(1.0, Dimensions{TimeDim: 1, LengthDim: -2}), New(1.0, Dimensions{TimeDim: 1, LengthDim: -2}), true},
	{"diff_one_empty", New(1.0, Dimensions{}), New(1.0, Dimensions{TimeDim: 1, LengthDim: -2}), false},
	{"diff_same_dim", New(1.0, Dimensions{TimeDim: 1}), New(1.0, Dimensions{TimeDim: 2}), false},
	{"diff_same_pow", New(1.0, Dimensions{LengthDim: 1}), New(1.0, Dimensions{TimeDim: 1}), false},
	{"diff_numdim", New(1.0, Dimensions{TimeDim: 1, LengthDim: 2}), New(1.0, Dimensions{TimeDim: 2}), false},
	{"diff_one_same_dim", New(1.0, Dimensions{LengthDim: 1, TimeDim: 1}), New(1.0, Dimensions{LengthDim: 1, TimeDim: 2}), false},
}

func TestDimensionEquality(t *testing.T) {
	for _, ts := range dimensionEqualityTests {
		if DimensionsMatch(ts.a, ts.b) != ts.shouldMatch {
			t.Errorf("Dimension comparison incorrect for case %s. got: %v, expected: %v", ts.name, !ts.shouldMatch, ts.shouldMatch)
		}
	}
}

type UnitStructer interface {
	UnitStruct() *UnitStruct
}

type UnitStruct struct {
	current     int
	length      int
	luminosity  int
	mass        int
	temperature int
	time        int
	chemamt     int // For mol
	value       float64
}

// Check if the dimensions of two units are the same
func DimensionsMatchStruct(aU, bU UnitStructer) bool {
	a := aU.UnitStruct()
	b := bU.UnitStruct()
	if a.length != b.length {
		return false
	}
	if a.time != b.time {
		return false
	}
	if a.mass != b.mass {
		return false
	}
	if a.current != b.current {
		return false
	}
	if a.temperature != b.temperature {
		return false
	}
	if a.luminosity != b.luminosity {
		return false
	}
	if a.chemamt != b.chemamt {
		return false
	}
	return true
}

func (u *UnitStruct) UnitStruct() *UnitStruct {
	return u
}

func (u *UnitStruct) Add(aU UnitStructer) *UnitStruct {
	a := aU.UnitStruct()
	if !DimensionsMatchStruct(a, u) {
		panic("dimension mismatch")
	}
	u.value += a.value
	return u
}

func (u *UnitStruct) Mul(aU UnitStructer) *UnitStruct {
	a := aU.UnitStruct()
	u.length += a.length
	u.time += a.time
	u.mass += a.mass
	u.current += a.current
	u.temperature += a.temperature
	u.luminosity += a.luminosity
	u.chemamt += a.chemamt
	u.value *= a.value
	return u
}

var u3 *UnitStruct

func BenchmarkAddStruct(b *testing.B) {
	u1 := &UnitStruct{current: 1, chemamt: 5, value: 10}
	u2 := &UnitStruct{current: 1, chemamt: 5, value: 100}
	for i := 0; i < b.N; i++ {
		u2.Add(u1)
	}
}

func BenchmarkMulStruct(b *testing.B) {
	u1 := &UnitStruct{current: 1, chemamt: 5, value: 10}
	u2 := &UnitStruct{mass: 1, time: 1, value: 100}
	for i := 0; i < b.N; i++ {
		u2.Mul(u1)
	}
}

type UnitMapper interface {
	UnitMap() *UnitMap
}

type dimensionMap int

const (
	LengthB dimensionMap = iota
	TimeB
	MassB
	CurrentB
	TemperatureB
	LuminosityB
	ChemAmtB
)

type UnitMap struct {
	dimension map[dimensionMap]int
	value     float64
}

// Check if the dimensions of two units are the same
func DimensionsMatchMap(aU, bU UnitMapper) bool {
	a := aU.UnitMap()
	b := bU.UnitMap()
	if len(a.dimension) != len(b.dimension) {
		panic("Unequal dimension")
	}
	for key, dimA := range a.dimension {
		dimB, ok := b.dimension[key]
		if !ok || dimA != dimB {
			panic("Unequal dimension")
		}
	}
	return true
}

func (u *UnitMap) UnitMap() *UnitMap {
	return u
}

func (u *UnitMap) Add(aU UnitMapper) *UnitMap {
	a := aU.UnitMap()
	if !DimensionsMatchMap(a, u) {
		panic("dimension mismatch")
	}
	u.value += a.value
	return u
}

func (u *UnitMap) Mul(aU UnitMapper) *UnitMap {
	a := aU.UnitMap()
	for key, val := range a.dimension {
		u.dimension[key] += val
	}
	u.value *= a.value
	return u
}

func BenchmarkAddFloat(b *testing.B) {
	a := 0.0
	c := 10.0
	for i := 0; i < b.N; i++ {
		a += c
	}
}

func BenchmarkMulFloat(b *testing.B) {
	a := 0.0
	c := 10.0
	for i := 0; i < b.N; i++ {
		a *= c
	}
}

func BenchmarkAddMapSmall(b *testing.B) {
	u1 := &UnitMap{value: 10}
	u1.dimension = make(map[dimensionMap]int)
	u1.dimension[CurrentB] = 1
	u1.dimension[ChemAmtB] = 5

	u2 := &UnitMap{value: 10}
	u2.dimension = make(map[dimensionMap]int)
	u2.dimension[CurrentB] = 1
	u2.dimension[ChemAmtB] = 5
	for i := 0; i < b.N; i++ {
		u2.Add(u1)
	}
}

func BenchmarkMulMapSmallDiff(b *testing.B) {
	u1 := &UnitMap{value: 10}
	u1.dimension = make(map[dimensionMap]int)
	u1.dimension[LengthB] = 1

	u2 := &UnitMap{value: 10}
	u2.dimension = make(map[dimensionMap]int)
	u2.dimension[MassB] = 1
	for i := 0; i < b.N; i++ {
		u2.Mul(u1)
	}
}

func BenchmarkMulMapSmallSame(b *testing.B) {
	u1 := &UnitMap{value: 10}
	u1.dimension = make(map[dimensionMap]int)
	u1.dimension[LengthB] = 1

	u2 := &UnitMap{value: 10}
	u2.dimension = make(map[dimensionMap]int)
	u2.dimension[LengthB] = 2
	for i := 0; i < b.N; i++ {
		u2.Mul(u1)
	}
}

func BenchmarkMulMapLargeDiff(b *testing.B) {
	u1 := &UnitMap{value: 10}
	u1.dimension = make(map[dimensionMap]int)
	u1.dimension[LengthB] = 1
	u1.dimension[MassB] = 1
	u1.dimension[ChemAmtB] = 1
	u1.dimension[TemperatureB] = 1
	u1.dimension[LuminosityB] = 1
	u1.dimension[TimeB] = 1
	u1.dimension[CurrentB] = 1

	u2 := &UnitMap{value: 10}
	u2.dimension = make(map[dimensionMap]int)
	u2.dimension[MassB] = 1
	for i := 0; i < b.N; i++ {
		u2.Mul(u1)
	}
}

func BenchmarkMulMapLargeSame(b *testing.B) {
	u1 := &UnitMap{value: 10}
	u1.dimension = make(map[dimensionMap]int)
	u1.dimension[LengthB] = 2
	u1.dimension[MassB] = 2
	u1.dimension[ChemAmtB] = 2
	u1.dimension[TemperatureB] = 2
	u1.dimension[LuminosityB] = 2
	u1.dimension[TimeB] = 2
	u1.dimension[CurrentB] = 2

	u2 := &UnitMap{value: 10}
	u2.dimension = make(map[dimensionMap]int)
	u2.dimension[LengthB] = 3
	u2.dimension[MassB] = 3
	u2.dimension[ChemAmtB] = 3
	u2.dimension[TemperatureB] = 3
	u2.dimension[LuminosityB] = 3
	u2.dimension[TimeB] = 3
	u2.dimension[CurrentB] = 3
	for i := 0; i < b.N; i++ {
		u2.Mul(u1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"regexp"
	"strconv"
	"strings"
)

func LogicalLines(src []byte) [][]byte {
	src = regexp.MustCompile("\\\\\n").ReplaceAll(src, []byte(""))
	lines := [][]byte{}
	head := 0
	for i, b := range src {
		if b == '\n' {
			lines = append(lines, src[head:i])
			head = i + 1
		}
	}
	if head < len(src) {
		lines = append(lines, src[head:])
	}
	return lines
}

func ParseJSONArray(s string) ([]string, error) {
	body := strings.TrimSpace(s)
	if !strings.HasPrefix(body, "[") || !strings.HasSuffix(body, "]") {
		return nil, errors.New("unmatched '[' and ']'")
	}
	args := strings.Split(body[1:len(body)-1], ",")
	for i, a := range args {
		arg := strings.TrimSpace(a)
		if !strings.HasPrefix(arg, "\"") ||
			!strings.HasSuffix(arg, "\"") ||
			len(arg) <= 1 {
			return nil, errors.New("unmatched '\"'")
		}
		args[i] = arg[1 : len(arg)-1]
	}
	return args, nil
}

func ParseLine(line []byte) (Statement, error) {
	s := string(line)
	i := strings.IndexAny(s, " \t")
	if i < 0 {
		return nil, errors.New("missing argument")
	}
	instr := strings.ToUpper(s[:i])
	body := line[i:]
	switch instr {
	case "FROM":
		return ParseFrom(body)
	case "MAINTAINER":
		return ParseMaintainer(body)
	case "RUN":
		return ParseRun(body)
	case "CMD":
		return ParseCmd(body)
	case "LABEL":
		return ParseLabel(body)
	case "EXPOSE":
		return ParseExpose(body)
	case "ENV":
		return ParseEnv(body)
	case "ADD":
		return ParseAdd(body)
	case "COPY":
		return ParseCopy(body)
	case "ENTRYPOINT":
		return ParseEntrypoint(body)
	case "VOLUME":
		return ParseVolume(body)
	case "USER":
		return ParseUser(body)
	case "WORKDIR":
		return ParseWorkdir(body)
	case "ONBUILD":
		return ParseOnbuild(body)
	}
	return nil, errors.New("illegal instruction")
}

func ParseFrom(body []byte) (*From, error) {
	s := strings.TrimSpace(string(body))
	if args := strings.Split(s, "@"); len(args) >= 2 {
		return &From{Image: args[0], Digest: args[1]}, nil
	}
	if args := strings.Split(s, ":"); len(args) >= 2 {
		return &From{Image: args[0], Tag: args[1]}, nil
	}
	return &From{Image: s}, nil
}

func ParseMaintainer(body []byte) (*Maintainer, error) {
	s := strings.TrimSpace(string(body))
	return &Maintainer{Name: s}, nil
}

func ParseRun(body []byte) (*Run, error) {
	ts, err := ParseJSONArray(string(body))
	if err == nil {
		return &Run{Tokens: ts}, nil
	}
	ts = []string{"/bin/sh", "-c"}
	for _, t := range strings.Fields(string(body)) {
		ts = append(ts, t)
	}
	return &Run{Tokens: ts}, nil
}

func ParseCmd(body []byte) (*Cmd, error) {
	ts, err := ParseJSONArray(string(body))
	if err == nil {
		return &Cmd{Tokens: ts}, nil
	}
	ts = []string{"/bin/sh", "-c"}
	for _, t := range strings.Fields(string(body)) {
		ts = append(ts, t)
	}
	return &Cmd{Tokens: ts}, nil
}

func FetchKey(src []byte) (string, []byte, error) {
	for i, b := range src {
		if b == '=' {
			return strings.Trim(string(src[:i]), " \""), src[i+1:], nil
		}
	}
	return "", nil, errors.New("missing '='")
}

func FetchValue(src []byte) (string, []byte, error) {
	if len(src) == 0 {
		return "", nil, errors.New("missing value")
	}
	if src[0] == '"' {
		for i := 1; i < len(src); i++ {
			if src[i] == '"' {
				return string(src[1:i]), src[i+1:], nil
			}
		}
		return "", nil, errors.New("unmatched '\"'")
	} else {
		for i, b := range src {
			if b == ' ' {
				return string(src[:i]), src[i+1:], nil
			}
		}
		return string(src), []byte{}, nil
	}
}

func ParseLabel(body []byte) (*Label, error) {
	if !strings.Contains(string(body), "=") {
		return &Label{Labels: map[string]string{}}, nil
	}
	k, t, err := FetchKey(body)
	if err != nil {
		return nil, err
	}
	v, tail, err := FetchValue(t)
	if err != nil {
		return nil, err
	}
	l, err := ParseLabel(tail)
	if err != nil {
		return nil, err
	}
	l.Labels[k] = v
	return l, nil
}

func ParseExpose(body []byte) (*Expose, error) {
	args := strings.Fields(string(body))
	var ps = make([]int, len(args))
	for i, a := range args {
		p, err := strconv.Atoi(a)
		if err != nil {
			return nil, errors.New("illegal port number")
		}
		ps[i] = p
	}
	return &Expose{Ports: ps}, nil
}

func ParseMultiEnv(body []byte) (*Env, error) {
	if !strings.Contains(string(body), "=") {
		return &Env{Variables: map[string]string{}}, nil
	}
	k, t, err := FetchKey(body)
	if err != nil {
		return nil, err
	}
	v, tail, err := FetchValue(t)
	if err != nil {
		return nil, err
	}
	e, err := ParseMultiEnv(tail)
	if err != nil {
		return nil, err
	}
	e.Variables[k] = v
	return e, nil
}

func ParseSingleEnv(body []byte) (*Env, error) {
	s := strings.TrimSpace(string(body))
	i := strings.IndexAny(s, " \t")
	if i < 0 || i >= len(s)-1 {
		return nil, errors.New("missing value")
	}
	k := strings.TrimSpace(s[:i])
	v := strings.TrimSpace(s[i+1:])
	return &Env{Variables: map[string]string{k: v}}, nil
}

func ParseEnv(body []byte) (*Env, error) {
	if strings.Contains(string(body), "=") {
		return ParseMultiEnv(body)
	} else {
		return ParseSingleEnv(body)
	}
}

func ParseAdd(body []byte) (*Add, error) {
	fs, err := ParseJSONArray(string(body))
	if err != nil {
		fs = strings.Fields(string(body))
	}
	if len(fs) == 0 {
		return nil, errors.New("no destination directory")
	}
	return &Add{Sources: fs[:len(fs)-1], Destination: fs[len(fs)-1]}, nil
}

func ParseCopy(body []byte) (*Copy, error) {
	fs, err := ParseJSONArray(string(body))
	if err != nil {
		fs = strings.Fields(string(body))
	}
	if len(fs) == 0 {
		return nil, errors.New("no destination directory")
	}
	return &Copy{Sources: fs[:len(fs)-1], Destination: fs[len(fs)-1]}, nil
}

func ParseEntrypoint(body []byte) (*Entrypoint, error) {
	ts, err := ParseJSONArray(string(body))
	if err == nil {
		return &Entrypoint{Tokens: ts}, nil
	}
	ts = []string{"/bin/sh", "-c"}
	for _, t := range strings.Fields(string(body)) {
		ts = append(ts, t)
	}
	return &Entrypoint{Tokens: ts}, nil
}

func ParseVolume(body []byte) (*Volume, error) {
	ps, err := ParseJSONArray(string(body))
	if err != nil {
		return &Volume{Points: strings.Fields(string(body))}, nil
	}
	return &Volume{Points: ps}, nil
}

func ParseUser(body []byte) (*User, error) {
	s := strings.TrimSpace(string(body))
	return &User{Name: s}, nil
}

func ParseWorkdir(body []byte) (*Workdir, error) {
	s := strings.TrimSpace(string(body))
	return &Workdir{Path: s}, nil
}

func ParseOnbuild(body []byte) (*Onbuild, error) {
	st, err := ParseLine(body)
	if err != nil {
		return nil, err
	}
	return &Onbuild{Statement: st}, nil
}

func Unmarshal(src []byte) (*Dockerfile, error) {
	sts := []Statement{}
	lines := LogicalLines(src)
	for _, l := range lines {
		if len(strings.TrimSpace(string(l))) != 0 {
			st, err := ParseLine(l)
			if err != nil {
				return nil, err
			}
			sts = append(sts, st)
		}
	}
	return &Dockerfile{Statements: sts}, nil
}
<commit_msg>Tweak the message of failure<commit_after>package main

import (
	"errors"
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

func LogicalLines(src []byte) [][]byte {
	src = regexp.MustCompile("\\\\\n").ReplaceAll(src, []byte(""))
	lines := [][]byte{}
	head := 0
	for i, b := range src {
		if b == '\n' {
			lines = append(lines, src[head:i])
			head = i + 1
		}
	}
	if head < len(src) {
		lines = append(lines, src[head:])
	}
	return lines
}

func ParseJSONArray(s string) ([]string, error) {
	body := strings.TrimSpace(s)
	if !strings.HasPrefix(body, "[") || !strings.HasSuffix(body, "]") {
		return nil, errors.New("unmatched '[' and ']'")
	}
	args := strings.Split(body[1:len(body)-1], ",")
	for i, a := range args {
		arg := strings.TrimSpace(a)
		if !strings.HasPrefix(arg, "\"") ||
			!strings.HasSuffix(arg, "\"") ||
			len(arg) <= 1 {
			return nil, errors.New("unmatched '\"'")
		}
		args[i] = arg[1 : len(arg)-1]
	}
	return args, nil
}

func ParseLine(line []byte) (Statement, error) {
	s := string(line)
	i := strings.IndexAny(s, " \t")
	if i < 0 {
		return nil, errors.New("missing argument")
	}
	instr := strings.ToUpper(s[:i])
	body := line[i:]
	switch instr {
	case "FROM":
		return ParseFrom(body)
	case "MAINTAINER":
		return ParseMaintainer(body)
	case "RUN":
		return ParseRun(body)
	case "CMD":
		return ParseCmd(body)
	case "LABEL":
		return ParseLabel(body)
	case "EXPOSE":
		return ParseExpose(body)
	case "ENV":
		return ParseEnv(body)
	case "ADD":
		return ParseAdd(body)
	case "COPY":
		return ParseCopy(body)
	case "ENTRYPOINT":
		return ParseEntrypoint(body)
	case "VOLUME":
		return ParseVolume(body)
	case "USER":
		return ParseUser(body)
	case "WORKDIR":
		return ParseWorkdir(body)
	case "ONBUILD":
		return ParseOnbuild(body)
	}
	return nil, fmt.Errorf("illegal instruction '%s'", instr)
}

func ParseFrom(body []byte) (*From, error) {
	s := strings.TrimSpace(string(body))
	if args := strings.Split(s, "@"); len(args) >= 2 {
		return &From{Image: args[0], Digest: args[1]}, nil
	}
	if args := strings.Split(s, ":"); len(args) >= 2 {
		return &From{Image: args[0], Tag: args[1]}, nil
	}
	return &From{Image: s}, nil
}

func ParseMaintainer(body []byte) (*Maintainer, error) {
	s := strings.TrimSpace(string(body))
	return &Maintainer{Name: s}, nil
}

func ParseRun(body []byte) (*Run, error) {
	ts, err := ParseJSONArray(string(body))
	if err == nil {
		return &Run{Tokens: ts}, nil
	}
	ts = []string{"/bin/sh", "-c"}
	for _, t := range strings.Fields(string(body)) {
		ts = append(ts, t)
	}
	return &Run{Tokens: ts}, nil
}

func ParseCmd(body []byte) (*Cmd, error) {
	ts, err := ParseJSONArray(string(body))
	if err == nil {
		return &Cmd{Tokens: ts}, nil
	}
	ts = []string{"/bin/sh", "-c"}
	for _, t := range strings.Fields(string(body)) {
		ts = append(ts, t)
	}
	return &Cmd{Tokens: ts}, nil
}

func FetchKey(src []byte) (string, []byte, error) {
	for i, b := range src {
		if b == '=' {
			return strings.Trim(string(src[:i]), " \""), src[i+1:], nil
		}
	}
	return "", nil, errors.New("missing '='")
}

func FetchValue(src []byte) (string, []byte, error) {
	if len(src) == 0 {
		return "", nil, errors.New("missing value")
	}
	if src[0] == '"' {
		for i := 1; i < len(src); i++ {
			if src[i] == '"' {
				return string(src[1:i]), src[i+1:], nil
			}
		}
		return "", nil, errors.New("unmatched '\"'")
	} else {
		for i, b := range src {
			if b == ' ' {
				return string(src[:i]), src[i+1:], nil
			}
		}
		return string(src), []byte{}, nil
	}
}

func ParseLabel(body []byte) (*Label, error) {
	if !strings.Contains(string(body), "=") {
		return &Label{Labels: map[string]string{}}, nil
	}
	k, t, err := FetchKey(body)
	if err != nil {
		return nil, err
	}
	v, tail, err := FetchValue(t)
	if err != nil {
		return nil, err
	}
	l, err := ParseLabel(tail)
	if err != nil {
		return nil, err
	}
	l.Labels[k] = v
	return l, nil
}

func ParseExpose(body []byte) (*Expose, error) {
	args := strings.Fields(string(body))
	var ps = make([]int, len(args))
	for i, a := range args {
		p, err := strconv.Atoi(a)
		if err != nil {
			return nil, errors.New("illegal port number")
		}
		ps[i] = p
	}
	return &Expose{Ports: ps}, nil
}

func ParseMultiEnv(body []byte) (*Env, error) {
	if !strings.Contains(string(body), "=") {
		return &Env{Variables: map[string]string{}}, nil
	}
	k, t, err := FetchKey(body)
	if err != nil {
		return nil, err
	}
	v, tail, err := FetchValue(t)
	if err != nil {
		return nil, err
	}
	e, err := ParseMultiEnv(tail)
	if err != nil {
		return nil, err
	}
	e.Variables[k] = v
	return e, nil
}

func ParseSingleEnv(body []byte) (*Env, error) {
	s := strings.TrimSpace(string(body))
	i := strings.IndexAny(s, " \t")
	if i < 0 || i >= len(s)-1 {
		return nil, errors.New("missing value")
	}
	k := strings.TrimSpace(s[:i])
	v := strings.TrimSpace(s[i+1:])
	return &Env{Variables: map[string]string{k: v}}, nil
}

func ParseEnv(body []byte) (*Env, error) {
	if strings.Contains(string(body), "=") {
		return ParseMultiEnv(body)
	} else {
		return ParseSingleEnv(body)
	}
}

func ParseAdd(body []byte) (*Add, error) {
	fs, err := ParseJSONArray(string(body))
	if err != nil {
		fs = strings.Fields(string(body))
	}
	if len(fs) == 0 {
		return nil, errors.New("no destination directory")
	}
	return &Add{Sources: fs[:len(fs)-1], Destination: fs[len(fs)-1]}, nil
}

func ParseCopy(body []byte) (*Copy, error) {
	fs, err := ParseJSONArray(string(body))
	if err != nil {
		fs = strings.Fields(string(body))
	}
	if len(fs) == 0 {
		return nil, errors.New("no destination directory")
	}
	return &Copy{Sources: fs[:len(fs)-1], Destination: fs[len(fs)-1]}, nil
}

func ParseEntrypoint(body []byte) (*Entrypoint, error) {
	ts, err := ParseJSONArray(string(body))
	if err == nil {
		return &Entrypoint{Tokens: ts}, nil
	}
	ts = []string{"/bin/sh", "-c"}
	for _, t := range strings.Fields(string(body)) {
		ts = append(ts, t)
	}
	return &Entrypoint{Tokens: ts}, nil
}

func ParseVolume(body []byte) (*Volume, error) {
	ps, err := ParseJSONArray(string(body))
	if err != nil {
		return &Volume{Points: strings.Fields(string(body))}, nil
	}
	return &Volume{Points: ps}, nil
}

func ParseUser(body []byte) (*User, error) {
	s := strings.TrimSpace(string(body))
	return &User{Name: s}, nil
}

func ParseWorkdir(body []byte) (*Workdir, error) {
	s := strings.TrimSpace(string(body))
	return &Workdir{Path: s}, nil
}

func ParseOnbuild(body []byte) (*Onbuild, error) {
	st, err := ParseLine(body)
	if err != nil {
		return nil, err
	}
	return &Onbuild{Statement: st}, nil
}

func Unmarshal(src []byte) (*Dockerfile, error) {
	sts := []Statement{}
	lines := LogicalLines(src)
	for _, l := range lines {
		if len(strings.TrimSpace(string(l))) != 0 {
			st, err := ParseLine(l)
			if err != nil {
				return nil, err
			}
			sts = append(sts, st)
		}
	}
	return &Dockerfile{Statements: sts}, nil
}
<|endoftext|>


<commit_before>package discgo_test

import "testing"

var uID = "97133780153683968"

func TestClient_GetUser(t *testing.T) {
	u, err := c.GetUser("@me")
	if err != nil {
		t.Fatal(err)
	}
	t.Log(u.Username)
}

func TestClient_GetMyGuilds(t *testing.T) {
	guilds, err := c.GetMyGuilds(nil)
	if err != nil {
		t.Fatal(err)
	}
	for _, g := range guilds {
		t.Log(g.Name)
		t.Log(g.ID)
	}
}
<commit_msg>user_test.go done<commit_after>package discgo_test

import "testing"

var uID = "97133780153683968"

func TestClient_GetUser(t *testing.T) {
	u, err := c.Me().Get()
	if err != nil {
		t.Fatal(err)
	}
	t.Log(u.Username)
}

func TestClient_GetMyGuilds(t *testing.T) {
	guilds, err := c.Me().Guilds().Get(nil)
	if err != nil {
		t.Fatal(err)
	}
	for _, g := range guilds {
		t.Log(g.Name)
		t.Log(g.ID)
	}
}
<|endoftext|>


<commit_before>package main

import "testing"
import "os/user"
import "fmt"

func Test_Add2Ints_1(t *testing.T) {
	mockuser, err := user.Current() // Worst mock evar
	if err != nil {
		t.Error(fmt.Sprintf("could not get current user: %v", err))
	}
	username, homedir, uid, gid, err := getUser(mockuser)
	if username == "vagrant" {
  		t.Log("username passed.")
	} else {
		t.Error(fmt.Sprintf("Username failed: %s", username))
	}
	if homedir == "/home/vagrant" {
                t.Log("homedir passed.")
        } else {
                t.Error(fmt.Sprintf("homedir failed: %s", homedir))
	}
	if uid == 1000 {
		t.Log("uid passed.")
	} else {
		t.Error(fmt.Sprintf("uid failed: %i", uid))
	}
	if gid == 1000 {
		t.Log("git passed.")
	} else {
		t.Error(fmt.Sprintf("gid failed: %i", gid))
	}
}

<commit_msg>Actual working test case<commit_after>package main

import "testing"
import "os/user"
import "fmt"

func Test_Add2Ints_1(t *testing.T) {
	mockuser, err := user.Current() // Worst mock evar
	if err != nil {
		t.Error(fmt.Sprintf("could not get current user: %v", err))
	}
	username, homedir, uid, gid, err := getUser(mockuser)
	if username == "vagrant" {
		t.Log("username passed.")
	} else {
		t.Error(fmt.Sprintf("Username failed: %s", username))
	}
	if homedir == "/home/vagrant" {
		t.Log("homedir passed.")
	} else {
		t.Error(fmt.Sprintf("homedir failed: %s", homedir))
	}
	if uid == 1000 {
		t.Log("uid passed.")
	} else {
		t.Error(fmt.Sprintf("uid failed: %i", uid))
	}
	if gid == 1000 {
		t.Log("git passed.")
	} else {
		t.Error(fmt.Sprintf("gid failed: %i", gid))
	}
}
<|endoftext|>


<commit_before>package util

import (
	"time"
)

// Now retrieves the current date and time
func Now() time.Time {
	return time.Now().Local()
}

// NextDate uses the selected date and time to add a given duration to it,
// then returns the result
func NextDate(date time.Time, duration time.Duration) time.Time {
	return date.Local().Add(duration)
}

// NextDateFromNow gets the current date and time and then adds the given duration to it,
// then returns the result
func NextDateFromNow(duration time.Duration) time.Time {
	return Now().Add(duration)
}

// CompareDates checks whether two dates are identical or note.
// Both the date and the time are compared up to millisecond precision
func CompareDates(source, target time.Time) bool {
	date1 := source.Local().Truncate(time.Millisecond)
	date2 := target.Local().Truncate(time.Millisecond)

	return date1.Equal(date2)
}

// IsDateExpired tells if the limit date and time are greater than the given one
func IsDateExpired(date, limit time.Time) bool {
	date = date.Local()
	limit = limit.Local()

	return date.Local().Before(limit)
}

// IsDateExpiredFromNow tells if the current date and time are greater than the given one
func IsDateExpiredFromNow(date time.Time) bool {
	today := Now()

	return date.Local().Before(today)
}
<commit_msg>Refactored the date util<commit_after>package util

import (
	"time"
)

// Now retrieves the current date and time
func Now() time.Time {
	return time.Now().Local()
}

// NextDate uses the selected date and time to add a given duration to it,
// then returns the result
func NextDate(date time.Time, duration time.Duration) time.Time {
	return date.Local().Add(duration)
}

// NextDateFromNow gets the current date and time and then adds the given duration to it,
// then returns the result
func NextDateFromNow(duration time.Duration) time.Time {
	return Now().Add(duration)
}

// CompareDates checks whether two dates are identical or note.
// Both the date and the time are compared up to millisecond precision
func CompareDates(source, target time.Time) bool {
	source = source.Local().Truncate(time.Millisecond)
	target = target.Local().Truncate(time.Millisecond)

	return source.Equal(target)
}

// IsDateExpired tells if the limit date and time are greater than the given one
func IsDateExpired(date, limit time.Time) bool {
	date = date.Local()
	limit = limit.Local()

	return date.Before(limit)
}

// IsDateExpiredFromNow tells if the current date and time are greater than the given one
func IsDateExpiredFromNow(date time.Time) bool {
	date = date.Local()
	today := Now()

	return date.Before(today)
}
<|endoftext|>


<commit_before>package util

import (
	"encoding/json"
	"log"
	"os"
)

type Configuration struct {
	ClientID     string `json:"client_id"`
	ClientSecret string `json:"client_secret"`
	AccessToken  string `json:"access_token"`
}

func LoadConfig(fn string) (Configuration, error) {
	c := Configuration{}

	f, err := os.Open(fn)
	defer f.Close()

	if err != nil {
		return c, err
	}

	err = json.NewDecoder(f).Decode(&c)

	if err != nil {
		return c, err
	}

	return c, nil
}

func FailOnError(err error) {
	if err != nil {
		log.Fatal(err)
		panic(err)
	}
}
<commit_msg>Handle error before deferring (otherwise f might be nil)<commit_after>package util

import (
	"encoding/json"
	"log"
	"os"
)

type Configuration struct {
	ClientID     string `json:"client_id"`
	ClientSecret string `json:"client_secret"`
	AccessToken  string `json:"access_token"`
}

func LoadConfig(fn string) (Configuration, error) {
	c := Configuration{}

	f, err := os.Open(fn)
	if err != nil {
		return c, err
	}
	defer f.Close()

	err = json.NewDecoder(f).Decode(&c)

	if err != nil {
		return c, err
	}

	return c, nil
}

func FailOnError(err error) {
	if err != nil {
		log.Fatal(err)
		panic(err)
	}
}
<|endoftext|>