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

package libkbfs

import (
	"crypto/rand"
	"errors"
	"sync"
	"testing"
	"time"

	"github.com/keybase/kbfs/kbfscodec"
	"github.com/stretchr/testify/require"
	"golang.org/x/net/context"
)

// blockReturner contains a block value to copy into requested blocks, and a
// channel to synchronize on with the worker.
type blockReturner struct {
	block Block
	ch    chan struct{}
}

// fakeBlockGetter allows specifying and obtaining fake blocks.
type fakeBlockGetter struct {
	mtx      sync.RWMutex
	blockMap map[BlockPointer]blockReturner
	codec    kbfscodec.Codec
}

// newFakeBlockGetter returns a fakeBlockGetter.
func newFakeBlockGetter() *fakeBlockGetter {
	return &fakeBlockGetter{
		blockMap: make(map[BlockPointer]blockReturner),
		codec:    kbfscodec.NewMsgpack(),
	}
}

// setBlockToReturn sets the block that will be returned for a given
// BlockPointer. Returns a writeable channel that getBlock will wait on, to
// allow synchronization of tests.
func (bg *fakeBlockGetter) setBlockToReturn(blockPtr BlockPointer, block Block) chan<- struct{} {
	bg.mtx.Lock()
	defer bg.mtx.Unlock()
	ch := make(chan struct{})
	bg.blockMap[blockPtr] = blockReturner{
		block: block,
		ch:    ch,
	}
	return ch
}

// getBlock implements the interface for realBlockGetter.
func (bg *fakeBlockGetter) getBlock(ctx context.Context, kmd KeyMetadata, blockPtr BlockPointer, block Block) error {
	bg.mtx.RLock()
	defer bg.mtx.RUnlock()
	source, ok := bg.blockMap[blockPtr]
	if !ok {
		return errors.New("Block doesn't exist in fake block map")
	}
	// Wait until the caller tells us to continue
	select {
	case <-source.ch:
		bytes, err := bg.codec.Encode(source.block)
		if err != nil {
			return err
		}
		err = bg.codec.Decode(bytes, block)
		if err != nil {
			return err
		}
	case <-ctx.Done():
		return ctx.Err()
	}
	return nil
}

func makeFakeFileBlock(t *testing.T) *FileBlock {
	buf := make([]byte, 16)
	_, err := rand.Read(buf)
	require.NoError(t, err)
	return &FileBlock{
		Contents: buf,
	}
}

func TestBlockRetrievalWorkerBasic(t *testing.T) {
	t.Log("Test the basic ability of a worker to return a block.")
	q := newBlockRetrievalQueue(1, kbfscodec.NewMsgpack())
	require.NotNil(t, q)
	defer q.Shutdown()

	bg := newFakeBlockGetter()
	w := newBlockRetrievalWorker(bg, q)
	require.NotNil(t, w)
	defer w.Shutdown()

	ptr1 := makeFakeBlockPointer(t)
	block1 := makeFakeFileBlock(t)
	ch1 := bg.setBlockToReturn(ptr1, block1)

	block := &FileBlock{}
	ch := q.Request(context.Background(), 1, nil, ptr1, block)
	ch1 <- struct{}{}
	err := <-ch
	require.NoError(t, err)
	require.Equal(t, block1, block)
}

func TestBlockRetrievalWorkerMultipleWorkers(t *testing.T) {
	t.Log("Test the ability of multiple workers to retrieve concurrently.")
	q := newBlockRetrievalQueue(2, kbfscodec.NewMsgpack())
	require.NotNil(t, q)
	defer q.Shutdown()

	bg := newFakeBlockGetter()
	w1 := newBlockRetrievalWorker(bg, q)
	require.NotNil(t, w1)
	defer w1.Shutdown()
	w2 := newBlockRetrievalWorker(bg, q)
	require.NotNil(t, w2)
	defer w2.Shutdown()

	ptr1, ptr2 := makeFakeBlockPointer(t), makeFakeBlockPointer(t)
	block1, block2 := makeFakeFileBlock(t), makeFakeFileBlock(t)
	ch1 := bg.setBlockToReturn(ptr1, block1)
	ch2 := bg.setBlockToReturn(ptr2, block2)

	t.Log("Make 2 requests for 2 different blocks")
	block := &FileBlock{}
	req1Ch := q.Request(context.Background(), 1, nil, ptr1, block)
	req2Ch := q.Request(context.Background(), 1, nil, ptr2, block)

	t.Log("Allow the second request to complete before the first")
	ch2 <- struct{}{}
	err := <-req2Ch
	require.NoError(t, err)
	require.Equal(t, block2, block)

	t.Log("Make another request for ptr2")
	req2Ch = q.Request(context.Background(), 1, nil, ptr2, block)
	ch2 <- struct{}{}
	err = <-req2Ch
	require.NoError(t, err)
	require.Equal(t, block2, block)

	t.Log("Complete the ptr1 request")
	ch1 <- struct{}{}
	err = <-req1Ch
	require.NoError(t, err)
	require.Equal(t, block1, block)
}

func TestBlockRetrievalWorkerWithQueue(t *testing.T) {
	t.Log("Test the ability of a worker and queue to work correctly together.")
	q := newBlockRetrievalQueue(1, kbfscodec.NewMsgpack())
	require.NotNil(t, q)
	defer q.Shutdown()

	bg := newFakeBlockGetter()
	w1 := newBlockRetrievalWorker(bg, q)
	require.NotNil(t, w1)
	defer w1.Shutdown()

	ptr1, ptr2, ptr3 := makeFakeBlockPointer(t), makeFakeBlockPointer(t), makeFakeBlockPointer(t)
	block1, block2, block3 := makeFakeFileBlock(t), makeFakeFileBlock(t), makeFakeFileBlock(t)
	ch1 := bg.setBlockToReturn(ptr1, block1)
	ch2 := bg.setBlockToReturn(ptr2, block2)
	ch3 := bg.setBlockToReturn(ptr3, block3)

	t.Log("Make 3 retrievals for 3 different blocks. All retrievals after the first should be queued.")
	block := &FileBlock{}
	testBlock1 := &FileBlock{}
	testBlock2 := &FileBlock{}
	req1Ch := q.Request(context.Background(), 1, nil, ptr1, block)
	req2Ch := q.Request(context.Background(), 1, nil, ptr2, block)
	req3Ch := q.Request(context.Background(), 1, nil, ptr3, testBlock1)
	// Ensure the worker picks up the request
	time.Sleep(50 * time.Millisecond)
	t.Log("Make a high priority request for the third block, which should complete next.")
	req4Ch := q.Request(context.Background(), 2, nil, ptr3, testBlock2)

	t.Log("Allow the ptr1 retrieval to complete.")
	ch1 <- struct{}{}
	err := <-req1Ch
	require.NoError(t, err)
	require.Equal(t, block1, block)

	t.Log("Allow the ptr3 retrieval to complete. Both waiting requests should complete.")
	ch3 <- struct{}{}
	err1 := <-req3Ch
	err2 := <-req4Ch
	require.NoError(t, err1)
	require.NoError(t, err2)
	require.Equal(t, block3, testBlock1)
	require.Equal(t, block3, testBlock2)

	t.Log("Complete the ptr1 retrieval.")
	ch2 <- struct{}{}
	err = <-req2Ch
	require.NoError(t, err)
	require.Equal(t, block2, block)
}

func TestBlockRetrievalWorkerCancel(t *testing.T) {
	t.Log("Test the ability of a worker to handle a request cancelation.")
	q := newBlockRetrievalQueue(1, kbfscodec.NewMsgpack())
	require.NotNil(t, q)
	defer q.Shutdown()

	bg := newFakeBlockGetter()
	w := newBlockRetrievalWorker(bg, q)
	require.NotNil(t, w)
	defer w.Shutdown()

	ptr1 := makeFakeBlockPointer(t)
	block1 := makeFakeFileBlock(t)
	_ = bg.setBlockToReturn(ptr1, block1)

	block := &FileBlock{}
	ctx, cancel := context.WithCancel(context.Background())
	cancel()
	ch := q.Request(ctx, 1, nil, ptr1, block)
	err := <-ch
	require.EqualError(t, err, context.Canceled.Error())
}

func TestBlockRetrievalWorkerShutdown(t *testing.T) {
	t.Log("Test that worker shutdown works.")
	q := newBlockRetrievalQueue(1, kbfscodec.NewMsgpack())
	require.NotNil(t, q)
	defer q.Shutdown()

	bg := newFakeBlockGetter()
	w := newBlockRetrievalWorker(bg, q)
	require.NotNil(t, w)

	ptr1 := makeFakeBlockPointer(t)
	block1 := makeFakeFileBlock(t)
	reqCh := bg.setBlockToReturn(ptr1, block1)

	w.Shutdown()
	block := &FileBlock{}
	ctx, cancel := context.WithCancel(context.Background())
	// Ensure the context loop is stopped so the test doesn't leak goroutines
	defer cancel()
	ch := q.Request(ctx, 1, nil, ptr1, block)
	shutdown := false
	select {
	case <-ch:
	case reqCh <- struct{}{}:
	default:
		shutdown = true
	}
	require.True(t, shutdown)
	w.Shutdown()
	require.True(t, shutdown)
}

func TestBlockRetrievalWorkerMultipleBlockTypes(t *testing.T) {
	t.Log("Test the ability of a worker and queue to work correctly together.")
	codec := kbfscodec.NewMsgpack()
	q := newBlockRetrievalQueue(1, codec)
	require.NotNil(t, q)
	defer q.Shutdown()

	bg := newFakeBlockGetter()
	w1 := newBlockRetrievalWorker(bg, q)
	require.NotNil(t, w1)
	defer w1.Shutdown()

	t.Log("Setup source blocks")
	ptr1 := makeFakeBlockPointer(t)
	block1 := makeFakeFileBlock(t)
	ch1 := bg.setBlockToReturn(ptr1, block1)
	testCommonBlock := &CommonBlock{}
	bytes, err := codec.Encode(block1)
	require.NoError(t, err)
	err = codec.Decode(bytes, testCommonBlock)
	require.NoError(t, err)

	t.Log("Make a retrieval for the same block twice, but with a different target block type.")
	testBlock1 := &FileBlock{}
	testBlock2 := &CommonBlock{}
	req1Ch := q.Request(context.Background(), 1, nil, ptr1, testBlock1)
	req2Ch := q.Request(context.Background(), 1, nil, ptr1, testBlock2)
	// Ensure the worker picks up the request
	time.Sleep(50 * time.Millisecond)

	t.Log("Allow the first ptr1 retrieval to complete.")
	ch1 <- struct{}{}
	err = <-req1Ch
	require.NoError(t, err)
	require.Equal(t, testBlock1, block1)

	t.Log("Allow the second ptr1 retrieval to complete.")
	ch1 <- struct{}{}
	err = <-req2Ch
	require.NoError(t, err)
	require.Equal(t, testBlock2, testCommonBlock)
}
<commit_msg>block_retrieval_worker_test: fix the test description<commit_after>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package libkbfs

import (
	"crypto/rand"
	"errors"
	"sync"
	"testing"
	"time"

	"github.com/keybase/kbfs/kbfscodec"
	"github.com/stretchr/testify/require"
	"golang.org/x/net/context"
)

// blockReturner contains a block value to copy into requested blocks, and a
// channel to synchronize on with the worker.
type blockReturner struct {
	block Block
	ch    chan struct{}
}

// fakeBlockGetter allows specifying and obtaining fake blocks.
type fakeBlockGetter struct {
	mtx      sync.RWMutex
	blockMap map[BlockPointer]blockReturner
	codec    kbfscodec.Codec
}

// newFakeBlockGetter returns a fakeBlockGetter.
func newFakeBlockGetter() *fakeBlockGetter {
	return &fakeBlockGetter{
		blockMap: make(map[BlockPointer]blockReturner),
		codec:    kbfscodec.NewMsgpack(),
	}
}

// setBlockToReturn sets the block that will be returned for a given
// BlockPointer. Returns a writeable channel that getBlock will wait on, to
// allow synchronization of tests.
func (bg *fakeBlockGetter) setBlockToReturn(blockPtr BlockPointer, block Block) chan<- struct{} {
	bg.mtx.Lock()
	defer bg.mtx.Unlock()
	ch := make(chan struct{})
	bg.blockMap[blockPtr] = blockReturner{
		block: block,
		ch:    ch,
	}
	return ch
}

// getBlock implements the interface for realBlockGetter.
func (bg *fakeBlockGetter) getBlock(ctx context.Context, kmd KeyMetadata, blockPtr BlockPointer, block Block) error {
	bg.mtx.RLock()
	defer bg.mtx.RUnlock()
	source, ok := bg.blockMap[blockPtr]
	if !ok {
		return errors.New("Block doesn't exist in fake block map")
	}
	// Wait until the caller tells us to continue
	select {
	case <-source.ch:
		bytes, err := bg.codec.Encode(source.block)
		if err != nil {
			return err
		}
		err = bg.codec.Decode(bytes, block)
		if err != nil {
			return err
		}
	case <-ctx.Done():
		return ctx.Err()
	}
	return nil
}

func makeFakeFileBlock(t *testing.T) *FileBlock {
	buf := make([]byte, 16)
	_, err := rand.Read(buf)
	require.NoError(t, err)
	return &FileBlock{
		Contents: buf,
	}
}

func TestBlockRetrievalWorkerBasic(t *testing.T) {
	t.Log("Test the basic ability of a worker to return a block.")
	q := newBlockRetrievalQueue(1, kbfscodec.NewMsgpack())
	require.NotNil(t, q)
	defer q.Shutdown()

	bg := newFakeBlockGetter()
	w := newBlockRetrievalWorker(bg, q)
	require.NotNil(t, w)
	defer w.Shutdown()

	ptr1 := makeFakeBlockPointer(t)
	block1 := makeFakeFileBlock(t)
	ch1 := bg.setBlockToReturn(ptr1, block1)

	block := &FileBlock{}
	ch := q.Request(context.Background(), 1, nil, ptr1, block)
	ch1 <- struct{}{}
	err := <-ch
	require.NoError(t, err)
	require.Equal(t, block1, block)
}

func TestBlockRetrievalWorkerMultipleWorkers(t *testing.T) {
	t.Log("Test the ability of multiple workers to retrieve concurrently.")
	q := newBlockRetrievalQueue(2, kbfscodec.NewMsgpack())
	require.NotNil(t, q)
	defer q.Shutdown()

	bg := newFakeBlockGetter()
	w1 := newBlockRetrievalWorker(bg, q)
	require.NotNil(t, w1)
	defer w1.Shutdown()
	w2 := newBlockRetrievalWorker(bg, q)
	require.NotNil(t, w2)
	defer w2.Shutdown()

	ptr1, ptr2 := makeFakeBlockPointer(t), makeFakeBlockPointer(t)
	block1, block2 := makeFakeFileBlock(t), makeFakeFileBlock(t)
	ch1 := bg.setBlockToReturn(ptr1, block1)
	ch2 := bg.setBlockToReturn(ptr2, block2)

	t.Log("Make 2 requests for 2 different blocks")
	block := &FileBlock{}
	req1Ch := q.Request(context.Background(), 1, nil, ptr1, block)
	req2Ch := q.Request(context.Background(), 1, nil, ptr2, block)

	t.Log("Allow the second request to complete before the first")
	ch2 <- struct{}{}
	err := <-req2Ch
	require.NoError(t, err)
	require.Equal(t, block2, block)

	t.Log("Make another request for ptr2")
	req2Ch = q.Request(context.Background(), 1, nil, ptr2, block)
	ch2 <- struct{}{}
	err = <-req2Ch
	require.NoError(t, err)
	require.Equal(t, block2, block)

	t.Log("Complete the ptr1 request")
	ch1 <- struct{}{}
	err = <-req1Ch
	require.NoError(t, err)
	require.Equal(t, block1, block)
}

func TestBlockRetrievalWorkerWithQueue(t *testing.T) {
	t.Log("Test the ability of a worker and queue to work correctly together.")
	q := newBlockRetrievalQueue(1, kbfscodec.NewMsgpack())
	require.NotNil(t, q)
	defer q.Shutdown()

	bg := newFakeBlockGetter()
	w1 := newBlockRetrievalWorker(bg, q)
	require.NotNil(t, w1)
	defer w1.Shutdown()

	ptr1, ptr2, ptr3 := makeFakeBlockPointer(t), makeFakeBlockPointer(t), makeFakeBlockPointer(t)
	block1, block2, block3 := makeFakeFileBlock(t), makeFakeFileBlock(t), makeFakeFileBlock(t)
	ch1 := bg.setBlockToReturn(ptr1, block1)
	ch2 := bg.setBlockToReturn(ptr2, block2)
	ch3 := bg.setBlockToReturn(ptr3, block3)

	t.Log("Make 3 retrievals for 3 different blocks. All retrievals after the first should be queued.")
	block := &FileBlock{}
	testBlock1 := &FileBlock{}
	testBlock2 := &FileBlock{}
	req1Ch := q.Request(context.Background(), 1, nil, ptr1, block)
	req2Ch := q.Request(context.Background(), 1, nil, ptr2, block)
	req3Ch := q.Request(context.Background(), 1, nil, ptr3, testBlock1)
	// Ensure the worker picks up the request
	time.Sleep(50 * time.Millisecond)
	t.Log("Make a high priority request for the third block, which should complete next.")
	req4Ch := q.Request(context.Background(), 2, nil, ptr3, testBlock2)

	t.Log("Allow the ptr1 retrieval to complete.")
	ch1 <- struct{}{}
	err := <-req1Ch
	require.NoError(t, err)
	require.Equal(t, block1, block)

	t.Log("Allow the ptr3 retrieval to complete. Both waiting requests should complete.")
	ch3 <- struct{}{}
	err1 := <-req3Ch
	err2 := <-req4Ch
	require.NoError(t, err1)
	require.NoError(t, err2)
	require.Equal(t, block3, testBlock1)
	require.Equal(t, block3, testBlock2)

	t.Log("Complete the ptr1 retrieval.")
	ch2 <- struct{}{}
	err = <-req2Ch
	require.NoError(t, err)
	require.Equal(t, block2, block)
}

func TestBlockRetrievalWorkerCancel(t *testing.T) {
	t.Log("Test the ability of a worker to handle a request cancelation.")
	q := newBlockRetrievalQueue(1, kbfscodec.NewMsgpack())
	require.NotNil(t, q)
	defer q.Shutdown()

	bg := newFakeBlockGetter()
	w := newBlockRetrievalWorker(bg, q)
	require.NotNil(t, w)
	defer w.Shutdown()

	ptr1 := makeFakeBlockPointer(t)
	block1 := makeFakeFileBlock(t)
	_ = bg.setBlockToReturn(ptr1, block1)

	block := &FileBlock{}
	ctx, cancel := context.WithCancel(context.Background())
	cancel()
	ch := q.Request(ctx, 1, nil, ptr1, block)
	err := <-ch
	require.EqualError(t, err, context.Canceled.Error())
}

func TestBlockRetrievalWorkerShutdown(t *testing.T) {
	t.Log("Test that worker shutdown works.")
	q := newBlockRetrievalQueue(1, kbfscodec.NewMsgpack())
	require.NotNil(t, q)
	defer q.Shutdown()

	bg := newFakeBlockGetter()
	w := newBlockRetrievalWorker(bg, q)
	require.NotNil(t, w)

	ptr1 := makeFakeBlockPointer(t)
	block1 := makeFakeFileBlock(t)
	reqCh := bg.setBlockToReturn(ptr1, block1)

	w.Shutdown()
	block := &FileBlock{}
	ctx, cancel := context.WithCancel(context.Background())
	// Ensure the context loop is stopped so the test doesn't leak goroutines
	defer cancel()
	ch := q.Request(ctx, 1, nil, ptr1, block)
	shutdown := false
	select {
	case <-ch:
	case reqCh <- struct{}{}:
	default:
		shutdown = true
	}
	require.True(t, shutdown)
	w.Shutdown()
	require.True(t, shutdown)
}

func TestBlockRetrievalWorkerMultipleBlockTypes(t *testing.T) {
	t.Log("Test that we can retrieve the same block into different block types.")
	codec := kbfscodec.NewMsgpack()
	q := newBlockRetrievalQueue(1, codec)
	require.NotNil(t, q)
	defer q.Shutdown()

	bg := newFakeBlockGetter()
	w1 := newBlockRetrievalWorker(bg, q)
	require.NotNil(t, w1)
	defer w1.Shutdown()

	t.Log("Setup source blocks")
	ptr1 := makeFakeBlockPointer(t)
	block1 := makeFakeFileBlock(t)
	ch1 := bg.setBlockToReturn(ptr1, block1)
	testCommonBlock := &CommonBlock{}
	bytes, err := codec.Encode(block1)
	require.NoError(t, err)
	err = codec.Decode(bytes, testCommonBlock)
	require.NoError(t, err)

	t.Log("Make a retrieval for the same block twice, but with a different target block type.")
	testBlock1 := &FileBlock{}
	testBlock2 := &CommonBlock{}
	req1Ch := q.Request(context.Background(), 1, nil, ptr1, testBlock1)
	req2Ch := q.Request(context.Background(), 1, nil, ptr1, testBlock2)
	// Ensure the worker picks up the request
	time.Sleep(50 * time.Millisecond)

	t.Log("Allow the first ptr1 retrieval to complete.")
	ch1 <- struct{}{}
	err = <-req1Ch
	require.NoError(t, err)
	require.Equal(t, testBlock1, block1)

	t.Log("Allow the second ptr1 retrieval to complete.")
	ch1 <- struct{}{}
	err = <-req2Ch
	require.NoError(t, err)
	require.Equal(t, testBlock2, testCommonBlock)
}
<|endoftext|>


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

// This package implements a basic LISP interpretor for embedding in a go program for scripting.
// This file contains the i/o primitive functions.

package golisp

import (
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"unicode"
)

func RegisterIOPrimitives() {
	MakeRestrictedPrimitiveFunction("open-input-file", "1", OpenInputFileImpl)
	MakeRestrictedPrimitiveFunction("open-output-file", "1|2", OpenOutputFileImpl)
	MakeRestrictedPrimitiveFunction("close-port", "1", ClosePortImpl)
	MakeRestrictedPrimitiveFunction("write-bytes", "2", WriteBytesImpl)

	MakePrimitiveFunction("write-string", "1|2", WriteStringImpl)
	MakePrimitiveFunction("newline", "0|1", NewlineImpl)
	MakePrimitiveFunction("write", "1|2", WriteImpl)
	MakePrimitiveFunction("read", "1", ReadImpl)
	MakePrimitiveFunction("eof-object?", "1", EofObjectImpl)

	MakePrimitiveFunction("list-directory", "1|2", ListDirectoryImpl)

	MakePrimitiveFunction("format", ">=2", FormatImpl)
}

func OpenOutputFileImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	filename := Car(args)
	if !StringP(filename) {
		err = ProcessError("open-output-port expects its argument to be a string", env)
		return
	}

	var openFlag = os.O_WRONLY | os.O_CREATE | os.O_TRUNC
	if Length(args) == 2 && BooleanValue(Cadr(args)) {
		openFlag = os.O_WRONLY | os.O_CREATE | os.O_APPEND
	}

	f, err := os.OpenFile(StringValue(filename), openFlag, 0666)
	if err != nil {
		return
	}
	return PortWithValue(f), nil
}

func OpenInputFileImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	filename := Car(args)
	if !StringP(filename) {
		err = ProcessError("open-input-port expects its argument to be a string", env)
		return
	}

	f, err := os.Open(StringValue(filename))
	if err != nil {
		return
	}
	return PortWithValue(f), nil
}

func ClosePortImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	p := Car(args)
	if !PortP(p) {
		err = ProcessError("close-port expects its argument be a port", env)
		return
	}

	(*os.File)(PortValue(p)).Close()
	return

}

func WriteBytesImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	bytes := Car(args)
	if !ObjectP(bytes) || ObjectType(bytes) != "[]byte" {
		err = ProcessError("write expects its first argument to be a bytearray", env)
		return
	}

	p := Cadr(args)
	if !PortP(p) {
		err = ProcessError("write expects its second argument be a port", env)
		return
	}

	_, err = (*os.File)(PortValue(p)).Write(*(*[]byte)(ObjectValue(bytes)))
	return
}

func WriteStringImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	str := Car(args)
	if !StringP(str) {
		err = ProcessError("write-string expects its first argument to be a string", env)
		return
	}

	var port *os.File
	if Length(args) == 1 {
		port = os.Stdout
	} else {
		p := Cadr(args)
		if !PortP(p) {
			err = ProcessError("write-string expects its second argument be a port", env)
			return
		}
		port = PortValue(p)
	}

	_, err = port.WriteString(StringValue(str))
	return
}

func WriteImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	var port *os.File

	if Length(args) == 1 {
		port = os.Stdout
	} else {
		p := Cadr(args)
		if !PortP(p) {
			err = ProcessError("write expects its second argument be a port", env)
			return
		}
		port = PortValue(p)
	}

	_, err = port.WriteString(String(Car(args)))
	return
}

func NewlineImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	var port *os.File

	if Length(args) == 0 {
		port = os.Stdout
	} else {
		p := Car(args)
		if !PortP(p) {
			err = ProcessError("newline expects its argument be a port", env)
			return
		}
		port = PortValue(p)
	}

	_, err = port.WriteString("\n")
	return
}

func ReadImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	var port *os.File

	if Length(args) == 0 {
		port = os.Stdin
	} else {
		p := Car(args)
		if !PortP(p) {
			err = ProcessError("read expects its argument be a port", env)
			return
		}
		port = PortValue(p)
	}

	result, err = ParseObjectFromFileInEnv(port, env)
	return
}

func EofObjectImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	return BooleanWithValue(IsEqual(Car(args), EofObject)), nil
}

func ListDirectoryImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	dir := StringValue(Car(args))
	fpart := "*"
	if Length(args) == 2 {
		fpart = StringValue(Cadr(args))
	}
	pattern := filepath.Join(dir, fpart)

	filenames, err := filepath.Glob(pattern)
	if err != nil {
		return
	}

	names := make([]*Data, 0, 0)
	for _, fname := range filenames {
		names = append(names, StringWithValue(fname))
	}
	return ArrayToList(names), nil
}

func FormatImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	destination := Car(args)
	if !BooleanP(destination) && !PortP(destination) {
		err = ProcessError(fmt.Sprintf("format expects its second argument be a boolean or port, but was %s", String(destination)), env)
		return
	}

	controlStringObj := Cadr(args)
	if !StringP(controlStringObj) {
		err = ProcessError("format expects its second argument be a string", env)
		return
	}
	controlString := StringValue(controlStringObj)

	arguments := Cddr(args)

	numberOfSubstitutions := strings.Count(controlString, "~")
	parts := make([]string, 0, numberOfSubstitutions*2+1)
	start := 0
	var i int
	var numericArg int
	var atModifier bool
	var substitution string
	var padding string
	var n int64

	for i < len(controlString) {
		if controlString[i] == '~' { // start of a substitution
			parts = append(parts, controlString[start:i])
			i++
			start = i
			for unicode.IsDigit(rune(controlString[i])) {
				i++
			}
			if i == start {
				if controlString[i] == '#' {
					numericArg = Length(arguments)
					i++
				} else if controlString[i] == 'V' || controlString[i] == 'v' {
					if IntegerP(Car(arguments)) {
						numericArg = int(IntegerValue(Car(arguments)))
						arguments = Cdr(arguments)
					} else {
						err = ProcessError(fmt.Sprintf("format encountered a size argument mismatch at index %d", i), env)
						return
					}
					i++
				} else {
					numericArg = 0
				}
			} else {
				n, err = strconv.ParseInt(string(controlString[start:i]), 10, 64)
				if err != nil {
					return
				}
				numericArg = int(n)
			}
			if controlString[i] == '@' {
				atModifier = true
				i++
			}
			switch controlString[i] {
			case 'A', 'a':
				substitution = PrintString(Car(arguments))
				if len(substitution) < numericArg {
					padding = strings.Repeat(" ", numericArg-len(substitution))
				} else {
					padding = ""
				}
				if atModifier {
					parts = append(parts, padding)
				}
				parts = append(parts, substitution)
				if !atModifier {
					parts = append(parts, padding)
				}
				arguments = Cdr(arguments)
				start = i + 1

			case 'S', 's':
				substitution = String(Car(arguments))
				if len(substitution) < numericArg {
					padding = strings.Repeat(" ", numericArg-len(substitution))
				} else {
					padding = ""
				}
				if atModifier {
					parts = append(parts, padding)
				}
				parts = append(parts, substitution)
				if !atModifier {
					parts = append(parts, padding)
				}
				arguments = Cdr(arguments)
				start = i + 1

			case '%':
				if numericArg > 0 {
					parts = append(parts, strings.Repeat("\n", numericArg))
				} else {
					parts = append(parts, "\n")
				}
				start = i + 1

			case '~':
				if numericArg > 0 {
					parts = append(parts, strings.Repeat("~", numericArg))
				} else {
					parts = append(parts, "~")
				}
				start = i + 1

			case '\n':
				for i < len(controlString) && unicode.IsSpace(rune(controlString[i])) {
					i++
				}
				if atModifier {
					parts = append(parts, "\n")
				}
				start = i
				i--

			default:
				err = ProcessError(fmt.Sprintf("format encountered an unsupported substitution at index %d", i), env)
				return
			}
		}
		i++
	}

	if start < len(controlString) {
		parts = append(parts, controlString[start:i])
	}

	if i < len(controlString) || !NilP(arguments) {
		err = ProcessError("number of replacements in the control string and number of arguments must be equal", env)
		return
	}

	combinedString := strings.Join(parts, "")

	if PortP(destination) {
		port := PortValue(destination)
		_, err = port.WriteString(combinedString)
	} else if BooleanValue(destination) {
		_, err = os.Stdout.WriteString(combinedString)
	} else {
		result = StringWithValue(combinedString)
	}

	return
}
<commit_msg>Fix an issue with writing to stdout<commit_after>// Copyright 2014 SteelSeries ApS.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This package implements a basic LISP interpretor for embedding in a go program for scripting.
// This file contains the i/o primitive functions.

package golisp

import (
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"unicode"
)

func RegisterIOPrimitives() {
	MakeRestrictedPrimitiveFunction("open-input-file", "1", OpenInputFileImpl)
	MakeRestrictedPrimitiveFunction("open-output-file", "1|2", OpenOutputFileImpl)
	MakeRestrictedPrimitiveFunction("close-port", "1", ClosePortImpl)
	MakeRestrictedPrimitiveFunction("write-bytes", "2", WriteBytesImpl)

	MakePrimitiveFunction("write-string", "1|2", WriteStringImpl)
	MakePrimitiveFunction("newline", "0|1", NewlineImpl)
	MakePrimitiveFunction("write", "1|2", WriteImpl)
	MakePrimitiveFunction("read", "1", ReadImpl)
	MakePrimitiveFunction("eof-object?", "1", EofObjectImpl)

	MakePrimitiveFunction("list-directory", "1|2", ListDirectoryImpl)

	MakePrimitiveFunction("format", ">=2", FormatImpl)
}

func OpenOutputFileImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	filename := Car(args)
	if !StringP(filename) {
		err = ProcessError("open-output-port expects its argument to be a string", env)
		return
	}

	var openFlag = os.O_WRONLY | os.O_CREATE | os.O_TRUNC
	if Length(args) == 2 && BooleanValue(Cadr(args)) {
		openFlag = os.O_WRONLY | os.O_CREATE | os.O_APPEND
	}

	f, err := os.OpenFile(StringValue(filename), openFlag, 0666)
	if err != nil {
		return
	}
	return PortWithValue(f), nil
}

func OpenInputFileImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	filename := Car(args)
	if !StringP(filename) {
		err = ProcessError("open-input-port expects its argument to be a string", env)
		return
	}

	f, err := os.Open(StringValue(filename))
	if err != nil {
		return
	}
	return PortWithValue(f), nil
}

func ClosePortImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	p := Car(args)
	if !PortP(p) {
		err = ProcessError("close-port expects its argument be a port", env)
		return
	}

	(*os.File)(PortValue(p)).Close()
	return

}

func WriteBytesImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	bytes := Car(args)
	if !ObjectP(bytes) || ObjectType(bytes) != "[]byte" {
		err = ProcessError("write expects its first argument to be a bytearray", env)
		return
	}

	p := Cadr(args)
	if !PortP(p) {
		err = ProcessError("write expects its second argument be a port", env)
		return
	}

	_, err = (*os.File)(PortValue(p)).Write(*(*[]byte)(ObjectValue(bytes)))
	return
}

func WriteStringImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	str := Car(args)
	if !StringP(str) {
		err = ProcessError("write-string expects its first argument to be a string", env)
		return
	}

	var port *os.File
	if Length(args) == 1 {
		port = os.Stdout
	} else {
		p := Cadr(args)
		if !PortP(p) {
			err = ProcessError("write-string expects its second argument be a port", env)
			return
		}
		port = PortValue(p)
	}

	_, err = port.WriteString(StringValue(str))
	return
}

func WriteImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	var port *os.File

	if Length(args) == 1 {
		port = os.Stdout
	} else {
		p := Cadr(args)
		if !PortP(p) {
			err = ProcessError("write expects its second argument be a port", env)
			return
		}
		port = PortValue(p)
	}

	_, err = port.WriteString(String(Car(args)))
	return
}

func NewlineImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	var port *os.File

	if Length(args) == 0 {
		port = os.Stdout
	} else {
		p := Car(args)
		if !PortP(p) {
			err = ProcessError("newline expects its argument be a port", env)
			return
		}
		port = PortValue(p)
	}

	_, err = port.WriteString("\n")
	return
}

func ReadImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	var port *os.File

	if Length(args) == 0 {
		port = os.Stdin
	} else {
		p := Car(args)
		if !PortP(p) {
			err = ProcessError("read expects its argument be a port", env)
			return
		}
		port = PortValue(p)
	}

	result, err = ParseObjectFromFileInEnv(port, env)
	return
}

func EofObjectImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	return BooleanWithValue(IsEqual(Car(args), EofObject)), nil
}

func ListDirectoryImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	dir := StringValue(Car(args))
	fpart := "*"
	if Length(args) == 2 {
		fpart = StringValue(Cadr(args))
	}
	pattern := filepath.Join(dir, fpart)

	filenames, err := filepath.Glob(pattern)
	if err != nil {
		return
	}

	names := make([]*Data, 0, 0)
	for _, fname := range filenames {
		names = append(names, StringWithValue(fname))
	}
	return ArrayToList(names), nil
}

func FormatImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	destination := Car(args)
	if !BooleanP(destination) && !PortP(destination) {
		err = ProcessError(fmt.Sprintf("format expects its second argument be a boolean or port, but was %s", String(destination)), env)
		return
	}

	controlStringObj := Cadr(args)
	if !StringP(controlStringObj) {
		err = ProcessError("format expects its second argument be a string", env)
		return
	}
	controlString := StringValue(controlStringObj)

	arguments := Cddr(args)

	numberOfSubstitutions := strings.Count(controlString, "~")
	parts := make([]string, 0, numberOfSubstitutions*2+1)
	start := 0
	var i int
	var numericArg int
	var atModifier bool
	var substitution string
	var padding string
	var n int64

	for i < len(controlString) {
		if controlString[i] == '~' { // start of a substitution
			parts = append(parts, controlString[start:i])
			i++
			start = i
			for unicode.IsDigit(rune(controlString[i])) {
				i++
			}
			if i == start {
				if controlString[i] == '#' {
					numericArg = Length(arguments)
					i++
				} else if controlString[i] == 'V' || controlString[i] == 'v' {
					if IntegerP(Car(arguments)) {
						numericArg = int(IntegerValue(Car(arguments)))
						arguments = Cdr(arguments)
					} else {
						err = ProcessError(fmt.Sprintf("format encountered a size argument mismatch at index %d", i), env)
						return
					}
					i++
				} else {
					numericArg = 0
				}
			} else {
				n, err = strconv.ParseInt(string(controlString[start:i]), 10, 64)
				if err != nil {
					return
				}
				numericArg = int(n)
			}
			if controlString[i] == '@' {
				atModifier = true
				i++
			}
			switch controlString[i] {
			case 'A', 'a':
				substitution = PrintString(Car(arguments))
				if len(substitution) < numericArg {
					padding = strings.Repeat(" ", numericArg-len(substitution))
				} else {
					padding = ""
				}
				if atModifier {
					parts = append(parts, padding)
				}
				parts = append(parts, substitution)
				if !atModifier {
					parts = append(parts, padding)
				}
				arguments = Cdr(arguments)
				start = i + 1

			case 'S', 's':
				substitution = String(Car(arguments))
				if len(substitution) < numericArg {
					padding = strings.Repeat(" ", numericArg-len(substitution))
				} else {
					padding = ""
				}
				if atModifier {
					parts = append(parts, padding)
				}
				parts = append(parts, substitution)
				if !atModifier {
					parts = append(parts, padding)
				}
				arguments = Cdr(arguments)
				start = i + 1

			case '%':
				if numericArg > 0 {
					parts = append(parts, strings.Repeat("\n", numericArg))
				} else {
					parts = append(parts, "\n")
				}
				start = i + 1

			case '~':
				if numericArg > 0 {
					parts = append(parts, strings.Repeat("~", numericArg))
				} else {
					parts = append(parts, "~")
				}
				start = i + 1

			case '\n':
				for i < len(controlString) && unicode.IsSpace(rune(controlString[i])) {
					i++
				}
				if atModifier {
					parts = append(parts, "\n")
				}
				start = i
				i--

			default:
				err = ProcessError(fmt.Sprintf("format encountered an unsupported substitution at index %d", i), env)
				return
			}
		}
		i++
	}

	if start < len(controlString) {
		parts = append(parts, controlString[start:i])
	}

	if i < len(controlString) || !NilP(arguments) {
		err = ProcessError("number of replacements in the control string and number of arguments must be equal", env)
		return
	}

	combinedString := strings.Join(parts, "")

	if PortP(destination) {
		port := PortValue(destination)
		_, err = port.WriteString(combinedString)
	} else if BooleanValue(destination) {
		// Make sure Stdout exists before writing to it, prevents issues with LDFLAGS="-H windowsgui"
		stat, statErr := os.Stdout.Stat()
		if stat != nil && statErr == nil {
			_, err = os.Stdout.WriteString(combinedString)
		}
	} else {
		result = StringWithValue(combinedString)
	}

	return
}
<|endoftext|>


<commit_before>package main

import "fmt"

func sender(c chan int) {
    c <- 2
    c <- 3
    close(c)
}

func printer(c chan int) {
    for i := range c {
        fmt.Println("message received ", i)
    }
}

func main() {
    c := make(chan int)

    go sender(c)
    printer(c)
}<commit_msg>improved printer<commit_after>package main

import "fmt"

func sender(c chan int) {
    c <- 2
    c <- 3
    close(c)
}

func printer(c chan int) {
    for i := range c {
        fmt.Println("message received:", i)
    }
}

func main() {
    c := make(chan int)

    go sender(c)
    printer(c)
}<|endoftext|>


<commit_before>package hcsshim

import (
	"io"
	"time"

	"github.com/Microsoft/hcsshim/internal/hcs"
)

// ContainerError is an error encountered in HCS
type process struct {
	p *hcs.Process
	c *container
}

// Pid returns the process ID of the process within the container.
func (process *process) Pid() int {
	return process.p.Pid()
}

// Kill signals the process to terminate but does not wait for it to finish terminating.
func (process *process) Kill() error {
	return convertProcessError(process.p.Kill(), process)
}

// Wait waits for the process to exit.
func (process *process) Wait() error {
	return convertProcessError(process.p.Wait(), process)
}

// WaitTimeout waits for the process to exit or the duration to elapse. It returns
// false if timeout occurs.
func (process *process) WaitTimeout(timeout time.Duration) error {
	return convertProcessError(process.p.WaitTimeout(timeout), process)
}

// ExitCode returns the exit code of the process. The process must have
// already terminated.
func (process *process) ExitCode() (int, error) {
	code, err := process.p.ExitCode()
	if err != nil {
		err = convertProcessError(err, process)
	}
	return code, err
}

// ResizeConsole resizes the console of the process.
func (process *process) ResizeConsole(width, height uint16) error {
	return convertProcessError(process.p.ResizeConsole(width, height), process)
}

// Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing
// these pipes does not close the underlying pipes; it should be possible to
// call this multiple times to get multiple interfaces.
func (process *process) Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, error) {
	stdin, stdout, stderr, err := process.Stdio()
	if err != nil {
		err = convertProcessError(err, process)
	}
	return stdin, stdout, stderr, err
}

// CloseStdin closes the write side of the stdin pipe so that the process is
// notified on the read side that there is no more data in stdin.
func (process *process) CloseStdin() error {
	return convertProcessError(process.p.CloseStdin(), process)
}

// Close cleans up any state associated with the process but does not kill
// or wait on it.
func (process *process) Close() error {
	return convertProcessError(process.p.Close(), process)
}
<commit_msg>Fix process.Stdio()<commit_after>package hcsshim

import (
	"io"
	"time"

	"github.com/Microsoft/hcsshim/internal/hcs"
)

// ContainerError is an error encountered in HCS
type process struct {
	p *hcs.Process
	c *container
}

// Pid returns the process ID of the process within the container.
func (process *process) Pid() int {
	return process.p.Pid()
}

// Kill signals the process to terminate but does not wait for it to finish terminating.
func (process *process) Kill() error {
	return convertProcessError(process.p.Kill(), process)
}

// Wait waits for the process to exit.
func (process *process) Wait() error {
	return convertProcessError(process.p.Wait(), process)
}

// WaitTimeout waits for the process to exit or the duration to elapse. It returns
// false if timeout occurs.
func (process *process) WaitTimeout(timeout time.Duration) error {
	return convertProcessError(process.p.WaitTimeout(timeout), process)
}

// ExitCode returns the exit code of the process. The process must have
// already terminated.
func (process *process) ExitCode() (int, error) {
	code, err := process.p.ExitCode()
	if err != nil {
		err = convertProcessError(err, process)
	}
	return code, err
}

// ResizeConsole resizes the console of the process.
func (process *process) ResizeConsole(width, height uint16) error {
	return convertProcessError(process.p.ResizeConsole(width, height), process)
}

// Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing
// these pipes does not close the underlying pipes; it should be possible to
// call this multiple times to get multiple interfaces.
func (process *process) Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, error) {
	stdin, stdout, stderr, err := process.p.Stdio()
	if err != nil {
		err = convertProcessError(err, process)
	}
	return stdin, stdout, stderr, err
}

// CloseStdin closes the write side of the stdin pipe so that the process is
// notified on the read side that there is no more data in stdin.
func (process *process) CloseStdin() error {
	return convertProcessError(process.p.CloseStdin(), process)
}

// Close cleans up any state associated with the process but does not kill
// or wait on it.
func (process *process) Close() error {
	return convertProcessError(process.p.Close(), process)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/disintegration/gift"
	"github.com/disintegration/imaging"
	"github.com/minotar/minecraft"
	"image"
	"image/color"
	"image/draw"
	"image/png"
	"io"
	"math"
)

const (
	HeadX      = 8
	HeadY      = 8
	HeadWidth  = 8
	HeadHeight = 8

	HelmX      = 40
	HelmY      = 8
	HelmWidth  = 8
	HelmHeight = 8

	TorsoX      = 20
	TorsoY      = 20
	TorsoWidth  = 8
	TorsoHeight = 12

	RaX      = 44
	RaY      = 20
	RaWidth  = 4
	RaHeight = 12

	RlX      = 4
	RlY      = 20
	RlWidth  = 4
	RlHeight = 12

	LaX      = 36
	LaY      = 52
	LaWidth  = 4
	LaHeight = 12

	LlX      = 20
	LlY      = 52
	LlWidth  = 4
	LlHeight = 12

	// The height of the 'bust' relative to the width of the body (16)
	BustHeight = 16
)

type mcSkin struct {
	Processed image.Image
	minecraft.Skin
}

// Returns the "face" of the skin.
func (skin *mcSkin) GetHead(width int) error {
	skin.Processed = skin.cropHead(skin.Image)
	skin.Resize(width, imaging.NearestNeighbor)
	return nil
}

// Returns the face of the skin overlayed with the helmet texture.
func (skin *mcSkin) GetHelm(width int) error {
	skin.Processed = skin.cropHelm(skin.Image)
	skin.Resize(width, imaging.NearestNeighbor)
	return nil
}

// Returns the head, torso, and arms part of the body image.
func (skin *mcSkin) RenderUpperBody() error {
	helmImg := skin.cropHead(skin.Image)
	torsoImg := imaging.Crop(skin.Image, image.Rect(TorsoX, TorsoY, TorsoX+TorsoWidth, TorsoY+TorsoHeight))
	raImg := imaging.Crop(skin.Image, image.Rect(RaX, RaY, RaX+RaWidth, RaY+TorsoHeight))

	var laImg image.Image

	// If the skin is 1.8 then we will use the left arm, otherwise
	// flip the right ones and use them.
	if skin.is18Skin() {
		laImg = imaging.Crop(skin.Image, image.Rect(LaX, LaY, LaX+LaWidth, LaY+TorsoHeight))
	} else {
		laImg = imaging.FlipH(raImg)
	}

	// Create a blank canvas for us to draw our upper body on
	upperBodyImg := image.NewNRGBA(image.Rect(0, 0, LaWidth+TorsoWidth+RaWidth, HeadHeight+TorsoHeight))
	// Helm
	fastDraw(upperBodyImg, helmImg.(*image.NRGBA), LaWidth, 0)
	// Torso
	fastDraw(upperBodyImg, torsoImg, LaWidth, HelmHeight)
	// Left Arm
	fastDraw(upperBodyImg, laImg.(*image.NRGBA), 0, HelmHeight)
	// Right Arm
	fastDraw(upperBodyImg, raImg, LaWidth+TorsoWidth, HelmHeight)

	skin.Processed = upperBodyImg
	return nil
}

// Returns the upper portion of the body - like GetBody, but without the legs.
func (skin *mcSkin) GetBust(width int) error {
	// Go get the upper body but not all of it.
	err := skin.RenderUpperBody()
	if err != nil {
		return err
	}

	// Slice off the last little tidbit of the image.
	img := skin.Processed.(*image.NRGBA)
	img.Rect.Max.Y = BustHeight

	skin.Resize(width, imaging.NearestNeighbor)

	return nil
}

func (skin *mcSkin) GetCube(width int) error {
	// Crop out the top of the head
	topFlat := imaging.Crop(skin.Image, image.Rect(8, 0, 16, 8))
	// Resize appropriately, so that it fills the `width` when rotated 45 def.
	topFlat = imaging.Resize(topFlat, int(float64(width)*math.Sqrt(2)/3+1), 0, imaging.NearestNeighbor)
	// Create the Gift filter
	filter := gift.New(
		gift.Rotate(45, color.Transparent, gift.LinearInterpolation),
	)
	bounds := filter.Bounds(topFlat.Bounds())
	top := image.NewNRGBA(bounds)
	// Draw it on the filter, then smush it!
	filter.Draw(top, topFlat)
	top = imaging.Resize(top, width+2, width/3, imaging.NearestNeighbor)
	// Skew the front and sides at 15 degree angles to match up with the
	// head that has been smushed
	front := skin.cropHead(skin.Image).(*image.NRGBA)
	side := imaging.Crop(skin.Image, image.Rect(0, 8, 8, 16))
	front = imaging.Resize(front, width/2, int(float64(width)/1.75), imaging.NearestNeighbor)
	side = imaging.Resize(side, width/2, int(float64(width)/1.75), imaging.NearestNeighbor)
	front = skewVertical(front, math.Pi/12)
	side = skewVertical(imaging.FlipH(side), math.Pi/-12)

	// Create a new image to assemble upon
	skin.Processed = image.NewNRGBA(image.Rect(0, 0, width, width))
	// Draw each side
	draw.Draw(skin.Processed.(draw.Image), image.Rect(0, width/6, width/2, width), side, image.Pt(0, 0), draw.Src)
	draw.Draw(skin.Processed.(draw.Image), image.Rect(width/2, width/6, width, width), front, image.Pt(0, 0), draw.Src)
	// Draw the top we created
	draw.Draw(skin.Processed.(draw.Image), image.Rect(-1, 0, width+1, width/3), top, image.Pt(0, 0), draw.Over)

	return nil
}

func (skin *mcSkin) GetBody(width int) error {
	// Go get the upper body (all of it).
	err := skin.RenderUpperBody()
	if err != nil {
		return err
	}

	rlImg := imaging.Crop(skin.Image, image.Rect(RlX, RlY, RlX+RlWidth, RlY+RlHeight))

	// If the skin is 1.8 then we will use the left arms and legs, otherwise flip the right ones and use them.
	var llImg image.Image
	if skin.is18Skin() {
		llImg = imaging.Crop(skin.Image, image.Rect(LlX, LlY, LlX+LlWidth, LlY+LlHeight))
	} else {
		llImg = imaging.FlipH(rlImg)
	}

	// Create a blank canvas for us to draw our body on. Expand bodyImg so
	// that we can draw on our legs.
	bodyImg := skin.Processed.(*image.NRGBA)
	bodyImg.Pix = append(bodyImg.Pix, make([]uint8, LlHeight*bodyImg.Stride)...)
	bodyImg.Rect.Max.Y += LlHeight
	// Left Leg
	fastDraw(bodyImg, llImg.(*image.NRGBA), LaWidth, HelmHeight+TorsoHeight)
	// Right Leg
	fastDraw(bodyImg, rlImg, LaWidth+LlWidth, HelmHeight+TorsoHeight)

	skin.Processed = bodyImg
	skin.Resize(width, imaging.NearestNeighbor)

	return nil
}

// Writes the *processed* image as a PNG to the given writer.
func (skin *mcSkin) WritePNG(w io.Writer) error {
	return png.Encode(w, skin.Processed)
}

// Writes the *original* skin image as a png to the given writer.
func (skin *mcSkin) WriteSkin(w io.Writer) error {
	return png.Encode(w, skin.Image)
}

// Resizes the skin to the given dimensions, keeping aspect ratio.
func (skin *mcSkin) Resize(width int, filter imaging.ResampleFilter) {
	skin.Processed = imaging.Resize(skin.Processed, width, 0, filter)
}

// Removes the skin's alpha matte from the given image.
func (skin *mcSkin) removeAlpha(img *image.NRGBA) {
	// If it's already a transparent image, do nothing
	if skin.AlphaSig[3] == 0 {
		return
	}

	// Otherwise loop through all the pixels. Check to see which ones match
	// the alpha signature and set their opacity to be zero.
	for i := 0; i < len(img.Pix); i += 4 {
		if img.Pix[i+0] == skin.AlphaSig[0] &&
			img.Pix[i+1] == skin.AlphaSig[1] &&
			img.Pix[i+2] == skin.AlphaSig[2] &&
			img.Pix[i+3] == skin.AlphaSig[3] {
			img.Pix[i+3] = 0
		}
	}
}

// Checks if the skin is a 1.8 skin using its height.
func (skin *mcSkin) is18Skin() bool {
	bounds := skin.Image.Bounds()
	return bounds.Max.Y == 64
}

// Returns the head of the skin image.
func (skin *mcSkin) cropHead(img image.Image) image.Image {
	return imaging.Crop(img, image.Rect(HeadX, HeadY, HeadX+HeadWidth, HeadY+HeadHeight))
}

// Returns the head of the skin image overlayed with the helm.
func (skin *mcSkin) cropHelm(img image.Image) image.Image {
	headImg := skin.cropHead(img)
	helmImg := imaging.Crop(img, image.Rect(HelmX, HelmY, HelmX+HelmWidth, HelmY+HelmHeight))
	skin.removeAlpha(helmImg)
	fastDraw(headImg.(*image.NRGBA), helmImg, 0, 0)

	return headImg
}

// Draws the "src" onto the "dst" image at the given x/y bounds, maintaining
// the original size. Pixels with have an alpha of 0x00 are not draw, and
// all others are drawn with an alpha of 0xFF
func fastDraw(dst *image.NRGBA, src *image.NRGBA, x, y int) {
	bounds := src.Bounds()
	maxY := bounds.Max.Y
	maxX := bounds.Max.X * 4

	pointer := dst.PixOffset(x, y)
	for row := 0; row < maxY; row += 1 {
		for i := 0; i < maxX; i += 4 {
			srcPx := row*src.Stride + i
			dstPx := row*dst.Stride + i + pointer
			if src.Pix[srcPx+3] != 0 {
				dst.Pix[dstPx+0] = src.Pix[srcPx+0]
				dst.Pix[dstPx+1] = src.Pix[srcPx+1]
				dst.Pix[dstPx+2] = src.Pix[srcPx+2]
				dst.Pix[dstPx+3] = 0xFF
			}
		}
	}
}

func skewVertical(src *image.NRGBA, degrees float64) *image.NRGBA {
	bounds := src.Bounds()
	maxY := bounds.Max.Y
	maxX := bounds.Max.X * 4
	distance := float64(bounds.Max.X) * math.Tan(degrees)
	shouldFlip := false
	if distance < 0 {
		distance = -distance
		shouldFlip = true
	}

	newHeight := maxY + int(1+distance)
	dst := image.NewNRGBA(image.Rect(0, 0, bounds.Max.X, newHeight))

	step := distance
	for x := 0; x < maxX; x += 4 {
		for row := 0; row < maxY; row += 1 {
			srcPx := row*src.Stride + x
			dstLower := (int(step)+row)*dst.Stride + x
			dstUpper := dstLower + dst.Stride
			_, delta := math.Modf(step)

			if src.Pix[srcPx+3] != 0 {
				dst.Pix[dstLower+0] += uint8(float64(src.Pix[srcPx+0]) * (1 - delta))
				dst.Pix[dstLower+1] += uint8(float64(src.Pix[srcPx+1]) * (1 - delta))
				dst.Pix[dstLower+2] += uint8(float64(src.Pix[srcPx+2]) * (1 - delta))
				dst.Pix[dstLower+3] += uint8(float64(src.Pix[srcPx+3]) * (1 - delta))

				dst.Pix[dstUpper+0] += uint8(float64(src.Pix[srcPx+0]) * delta)
				dst.Pix[dstUpper+1] += uint8(float64(src.Pix[srcPx+1]) * delta)
				dst.Pix[dstUpper+2] += uint8(float64(src.Pix[srcPx+2]) * delta)
				dst.Pix[dstUpper+3] += uint8(float64(src.Pix[srcPx+3]) * delta)
			}
		}

		step -= distance / float64(bounds.Max.X)
	}

	if shouldFlip {
		return imaging.FlipH(dst)
	} else {
		return dst
	}
}
<commit_msg>Add armour constants<commit_after>package main

import (
	"github.com/disintegration/gift"
	"github.com/disintegration/imaging"
	"github.com/minotar/minecraft"
	"image"
	"image/color"
	"image/draw"
	"image/png"
	"io"
	"math"
)

const (
	HeadX      = 8
	HeadY      = 8
	HeadWidth  = 8
	HeadHeight = 8

	HelmX      = 40
	HelmY      = 8

	TorsoX      = 20
	TorsoY      = 20
	TorsoWidth  = 8
	TorsoHeight = 12

	Torso2X      = 20
	Torso2Y      = 36

	RaX      = 44
	RaY      = 20
	RaWidth  = 4
	RaHeight = 12

	Ra2X      = 44
	Ra2Y      = 36

	RlX      = 4
	RlY      = 20
	RlWidth  = 4
	RlHeight = 12

	Rl2X      = 4
	Rl2Y      = 36

	LaX      = 36
	LaY      = 52
	LaWidth  = 4
	LaHeight = 12

	La2X      = 52
	La2Y      = 52

	LlX      = 20
	LlY      = 52
	LlWidth  = 4
	LlHeight = 12

	Ll2X      = 4
	Ll2Y      = 52

	// The height of the 'bust' relative to the width of the body (16)
	BustHeight = 16
)

type mcSkin struct {
	Processed image.Image
	minecraft.Skin
}

// Returns the "face" of the skin.
func (skin *mcSkin) GetHead(width int) error {
	skin.Processed = skin.cropHead(skin.Image)
	skin.Resize(width, imaging.NearestNeighbor)
	return nil
}

// Returns the face of the skin overlayed with the helmet texture.
func (skin *mcSkin) GetHelm(width int) error {
	skin.Processed = skin.cropHelm(skin.Image)
	skin.Resize(width, imaging.NearestNeighbor)
	return nil
}

// Returns the head, torso, and arms part of the body image.
func (skin *mcSkin) RenderUpperBody() error {
	helmImg := skin.cropHead(skin.Image)
	torsoImg := imaging.Crop(skin.Image, image.Rect(TorsoX, TorsoY, TorsoX+TorsoWidth, TorsoY+TorsoHeight))
	raImg := imaging.Crop(skin.Image, image.Rect(RaX, RaY, RaX+RaWidth, RaY+TorsoHeight))

	var laImg image.Image

	// If the skin is 1.8 then we will use the left arm, otherwise
	// flip the right ones and use them.
	if skin.is18Skin() {
		laImg = imaging.Crop(skin.Image, image.Rect(LaX, LaY, LaX+LaWidth, LaY+TorsoHeight))
	} else {
		laImg = imaging.FlipH(raImg)
	}

	// Create a blank canvas for us to draw our upper body on
	upperBodyImg := image.NewNRGBA(image.Rect(0, 0, LaWidth+TorsoWidth+RaWidth, HeadHeight+TorsoHeight))
	// Helm
	fastDraw(upperBodyImg, helmImg.(*image.NRGBA), LaWidth, 0)
	// Torso
	fastDraw(upperBodyImg, torsoImg, LaWidth, HeadHeight)
	// Left Arm
	fastDraw(upperBodyImg, laImg.(*image.NRGBA), 0, HeadHeight)
	// Right Arm
	fastDraw(upperBodyImg, raImg, LaWidth+TorsoWidth, HeadHeight)

	skin.Processed = upperBodyImg
	return nil
}

// Returns the upper portion of the body - like GetBody, but without the legs.
func (skin *mcSkin) GetBust(width int) error {
	// Go get the upper body but not all of it.
	err := skin.RenderUpperBody()
	if err != nil {
		return err
	}

	// Slice off the last little tidbit of the image.
	img := skin.Processed.(*image.NRGBA)
	img.Rect.Max.Y = BustHeight

	skin.Resize(width, imaging.NearestNeighbor)

	return nil
}

func (skin *mcSkin) GetCube(width int) error {
	// Crop out the top of the head
	topFlat := imaging.Crop(skin.Image, image.Rect(8, 0, 16, 8))
	// Resize appropriately, so that it fills the `width` when rotated 45 def.
	topFlat = imaging.Resize(topFlat, int(float64(width)*math.Sqrt(2)/3+1), 0, imaging.NearestNeighbor)
	// Create the Gift filter
	filter := gift.New(
		gift.Rotate(45, color.Transparent, gift.LinearInterpolation),
	)
	bounds := filter.Bounds(topFlat.Bounds())
	top := image.NewNRGBA(bounds)
	// Draw it on the filter, then smush it!
	filter.Draw(top, topFlat)
	top = imaging.Resize(top, width+2, width/3, imaging.NearestNeighbor)
	// Skew the front and sides at 15 degree angles to match up with the
	// head that has been smushed
	front := skin.cropHead(skin.Image).(*image.NRGBA)
	side := imaging.Crop(skin.Image, image.Rect(0, 8, 8, 16))
	front = imaging.Resize(front, width/2, int(float64(width)/1.75), imaging.NearestNeighbor)
	side = imaging.Resize(side, width/2, int(float64(width)/1.75), imaging.NearestNeighbor)
	front = skewVertical(front, math.Pi/12)
	side = skewVertical(imaging.FlipH(side), math.Pi/-12)

	// Create a new image to assemble upon
	skin.Processed = image.NewNRGBA(image.Rect(0, 0, width, width))
	// Draw each side
	draw.Draw(skin.Processed.(draw.Image), image.Rect(0, width/6, width/2, width), side, image.Pt(0, 0), draw.Src)
	draw.Draw(skin.Processed.(draw.Image), image.Rect(width/2, width/6, width, width), front, image.Pt(0, 0), draw.Src)
	// Draw the top we created
	draw.Draw(skin.Processed.(draw.Image), image.Rect(-1, 0, width+1, width/3), top, image.Pt(0, 0), draw.Over)

	return nil
}

func (skin *mcSkin) GetBody(width int) error {
	// Go get the upper body (all of it).
	err := skin.RenderUpperBody()
	if err != nil {
		return err
	}

	rlImg := imaging.Crop(skin.Image, image.Rect(RlX, RlY, RlX+RlWidth, RlY+RlHeight))

	// If the skin is 1.8 then we will use the left arms and legs, otherwise flip the right ones and use them.
	var llImg image.Image
	if skin.is18Skin() {
		llImg = imaging.Crop(skin.Image, image.Rect(LlX, LlY, LlX+LlWidth, LlY+LlHeight))
	} else {
		llImg = imaging.FlipH(rlImg)
	}

	// Create a blank canvas for us to draw our body on. Expand bodyImg so
	// that we can draw on our legs.
	bodyImg := skin.Processed.(*image.NRGBA)
	bodyImg.Pix = append(bodyImg.Pix, make([]uint8, LlHeight*bodyImg.Stride)...)
	bodyImg.Rect.Max.Y += LlHeight
	// Left Leg
	fastDraw(bodyImg, llImg.(*image.NRGBA), LaWidth, HeadHeight+TorsoHeight)
	// Right Leg
	fastDraw(bodyImg, rlImg, LaWidth+LlWidth, HeadHeight+TorsoHeight)

	skin.Processed = bodyImg
	skin.Resize(width, imaging.NearestNeighbor)

	return nil
}

// Writes the *processed* image as a PNG to the given writer.
func (skin *mcSkin) WritePNG(w io.Writer) error {
	return png.Encode(w, skin.Processed)
}

// Writes the *original* skin image as a png to the given writer.
func (skin *mcSkin) WriteSkin(w io.Writer) error {
	return png.Encode(w, skin.Image)
}

// Resizes the skin to the given dimensions, keeping aspect ratio.
func (skin *mcSkin) Resize(width int, filter imaging.ResampleFilter) {
	skin.Processed = imaging.Resize(skin.Processed, width, 0, filter)
}

// Removes the skin's alpha matte from the given image.
func (skin *mcSkin) removeAlpha(img *image.NRGBA) {
	// If it's already a transparent image, do nothing
	if skin.AlphaSig[3] == 0 {
		return
	}

	// Otherwise loop through all the pixels. Check to see which ones match
	// the alpha signature and set their opacity to be zero.
	for i := 0; i < len(img.Pix); i += 4 {
		if img.Pix[i+0] == skin.AlphaSig[0] &&
			img.Pix[i+1] == skin.AlphaSig[1] &&
			img.Pix[i+2] == skin.AlphaSig[2] &&
			img.Pix[i+3] == skin.AlphaSig[3] {
			img.Pix[i+3] = 0
		}
	}
}

// Checks if the skin is a 1.8 skin using its height.
func (skin *mcSkin) is18Skin() bool {
	bounds := skin.Image.Bounds()
	return bounds.Max.Y == 64
}

// Returns the head of the skin image.
func (skin *mcSkin) cropHead(img image.Image) image.Image {
	return imaging.Crop(img, image.Rect(HeadX, HeadY, HeadX+HeadWidth, HeadY+HeadHeight))
}

// Returns the head of the skin image overlayed with the helm.
func (skin *mcSkin) cropHelm(img image.Image) image.Image {
	headImg := skin.cropHead(img)
	helmImg := imaging.Crop(img, image.Rect(HelmX, HelmY, HelmX+HeadWidth, HelmY+HeadHeight))
	skin.removeAlpha(helmImg)
	fastDraw(headImg.(*image.NRGBA), helmImg, 0, 0)

	return headImg
}

// Draws the "src" onto the "dst" image at the given x/y bounds, maintaining
// the original size. Pixels with have an alpha of 0x00 are not draw, and
// all others are drawn with an alpha of 0xFF
func fastDraw(dst *image.NRGBA, src *image.NRGBA, x, y int) {
	bounds := src.Bounds()
	maxY := bounds.Max.Y
	maxX := bounds.Max.X * 4

	pointer := dst.PixOffset(x, y)
	for row := 0; row < maxY; row += 1 {
		for i := 0; i < maxX; i += 4 {
			srcPx := row*src.Stride + i
			dstPx := row*dst.Stride + i + pointer
			if src.Pix[srcPx+3] != 0 {
				dst.Pix[dstPx+0] = src.Pix[srcPx+0]
				dst.Pix[dstPx+1] = src.Pix[srcPx+1]
				dst.Pix[dstPx+2] = src.Pix[srcPx+2]
				dst.Pix[dstPx+3] = 0xFF
			}
		}
	}
}

func skewVertical(src *image.NRGBA, degrees float64) *image.NRGBA {
	bounds := src.Bounds()
	maxY := bounds.Max.Y
	maxX := bounds.Max.X * 4
	distance := float64(bounds.Max.X) * math.Tan(degrees)
	shouldFlip := false
	if distance < 0 {
		distance = -distance
		shouldFlip = true
	}

	newHeight := maxY + int(1+distance)
	dst := image.NewNRGBA(image.Rect(0, 0, bounds.Max.X, newHeight))

	step := distance
	for x := 0; x < maxX; x += 4 {
		for row := 0; row < maxY; row += 1 {
			srcPx := row*src.Stride + x
			dstLower := (int(step)+row)*dst.Stride + x
			dstUpper := dstLower + dst.Stride
			_, delta := math.Modf(step)

			if src.Pix[srcPx+3] != 0 {
				dst.Pix[dstLower+0] += uint8(float64(src.Pix[srcPx+0]) * (1 - delta))
				dst.Pix[dstLower+1] += uint8(float64(src.Pix[srcPx+1]) * (1 - delta))
				dst.Pix[dstLower+2] += uint8(float64(src.Pix[srcPx+2]) * (1 - delta))
				dst.Pix[dstLower+3] += uint8(float64(src.Pix[srcPx+3]) * (1 - delta))

				dst.Pix[dstUpper+0] += uint8(float64(src.Pix[srcPx+0]) * delta)
				dst.Pix[dstUpper+1] += uint8(float64(src.Pix[srcPx+1]) * delta)
				dst.Pix[dstUpper+2] += uint8(float64(src.Pix[srcPx+2]) * delta)
				dst.Pix[dstUpper+3] += uint8(float64(src.Pix[srcPx+3]) * delta)
			}
		}

		step -= distance / float64(bounds.Max.X)
	}

	if shouldFlip {
		return imaging.FlipH(dst)
	} else {
		return dst
	}
}
<|endoftext|>


<commit_before>package coinbase

import(
  "encoding/json"
  "errors"
  "fmt"
  "net/url"
  "strconv"
  "time"
)

type Product struct {
  Id string `json:"id"`
  BaseCurency string `json:"base_currency"`
  QuoteCurrency string `json:"quote_currency"`
  BaseMinSize float64 `json:"base_min_size,number"`
  BaseMaxSize float64 `json:"base_max_size,number"`
  QuoteIncrement float64 `json:"quote_increment,number"`
}

type Ticker struct {
  TradeId int `json:"trade_id,number"`
  Price float64 `json:"price,string"`
  Size float64 `json:"size,string"`
  Time Time `json:"time,string"`
}

type Trade struct {
  TradeId int `json:"trade_id,number"`
  Price float64 `json:"price,string"`
  Size float64 `json:"size,string"`
  Time Time `json:"time,string"`
  Side string `json:"side"`
}

type HistoricRate struct {
  Time time.Time
  Low float64
  High float64
  Open float64
  Close float64
  Volume float64
}

type Stats struct {
  Low float64 `json:"low,number"`
  High float64 `json:"high,number"`
  Open float64 `json:"open,number"`
  Volume float64 `json:"volume,number"`
}

type BookEntry struct {
  Price float64
  Size float64
  NumberOfOrders int
  OrderId string
}

type Book struct {
  Sequence int `json:"sequence"`
  Bids []BookEntry `json:"bids"`
  Asks []BookEntry `json:"asks"`
}

type ListTradesParams struct {
  Pagination PaginationParams
}

type GetHistoricRatesParams struct {
  Start time.Time
  End time.Time
  Granularity int
}

func(e *BookEntry) UnmarshalJSON(data []byte) error {
  var entry[]interface{}

  if err := json.Unmarshal(data, &entry); err != nil {
    return err
  }

  priceString, ok := entry[0].(string)
  if !ok {
    return errors.New("Expected string")
  }

  sizeString, ok := entry[1].(string)
  if !ok {
    return errors.New("Expected string")
  }

  price, err := strconv.ParseFloat(priceString, 32)
  if err != nil {
    return err
  }

  size, err := strconv.ParseFloat(sizeString, 32)
  if err != nil {
    return err
  }

  *e = BookEntry{
    Price: price,
    Size: size,
  }

  var stringOrderId string
  numberOfOrdersFloat, ok := entry[2].(float64)
  if !ok {
    // Try to see if it's a string
    stringOrderId, ok = entry[2].(string)
    if !ok {
      return errors.New("Could not parse 3rd column, tried float and string")
    }
    e.OrderId = stringOrderId

  } else {
    e.NumberOfOrders = int(numberOfOrdersFloat)
  }

  return nil
}

func(e *HistoricRate) UnmarshalJSON(data []byte) error {
  var entry[]interface{}

  if err := json.Unmarshal(data, &entry); err != nil {
    return err
  }

  timeFloat, ok := entry[0].(float64)
  if !ok {
    return errors.New("Expected float")
  }

  lowFloat, ok := entry[1].(float64)
  if !ok {
    return errors.New("Expected float")
  }

  highFloat, ok := entry[2].(float64)
  if !ok {
    return errors.New("Expected float")
  }

  openFloat, ok := entry[3].(float64)
  if !ok {
    return errors.New("Expected float")
  }

  closeFloat, ok := entry[4].(float64)
  if !ok {
    return errors.New("Expected float")
  }

  volumeFloat, ok := entry[5].(float64)
  if !ok {
    return errors.New("Expected float")
  }

  *e = HistoricRate{
    Time: time.Unix(int64(timeFloat), 0),
    Low: lowFloat,
    High: highFloat,
    Open: openFloat,
    Close: closeFloat,
    Volume: volumeFloat,
  }

  return nil
}

func (c *Client) GetBook(product string, level int) (Book, error) {
  var book Book

  requestURL := fmt.Sprintf("/products/%s/book?level=%d", product, level)
  _, err := c.Request("GET", requestURL, nil, &book)
  return book, err
}

func (c *Client) GetTicker(product string) (Ticker, error) {
  var ticker Ticker

  requestURL := fmt.Sprintf("/products/%s/ticker", product)
  _, err := c.Request("GET", requestURL, nil, &ticker)
  return ticker, err
}

func (c *Client) ListTrades(product string,
  p ...ListTradesParams) *Cursor {
  paginationParams := PaginationParams{}
  if len(p) > 0 {
    paginationParams = p[0].Pagination
  }

  return NewCursor(c, "GET", fmt.Sprintf("/products/%s/trades", product),
    &paginationParams)
}

func (c *Client) GetProducts() ([]Product, error) {
  var products []Product

  requestURL := fmt.Sprintf("/products")
  _, err := c.Request("GET", requestURL, nil, &products)
  return products, err
}

func (c *Client) GetHistoricRates(product string,
  p ...GetHistoricRatesParams) ([]HistoricRate, error) {
  var historicRates []HistoricRate
  requestURL := fmt.Sprintf("/products/%s/candles", product)
  params := GetHistoricRatesParams{}
  if len(p) > 0 {
    params = p[0]
  }

  if !params.Start.IsZero() && !params.End.IsZero() && params.Granularity != 0 {
    values := url.Values{}
    layout := "2006-01-02T15:04:05Z"
    values.Add("start", params.Start.Format(layout))
    values.Add("end", params.End.Format(layout))
    values.Add("granularity", strconv.Itoa(params.Granularity))

    requestURL = fmt.Sprintf("%s?%s", requestURL, values.Encode())
  }

  _, err := c.Request("GET", requestURL, nil, &historicRates)
  return historicRates, err
}

func (c *Client) GetStats(product string) (Stats, error) {
  var stats Stats
  requestURL := fmt.Sprintf("/products/%s/stats", product)
  _, err := c.Request("GET", requestURL, nil, &stats)
  return stats, err
}
<commit_msg>Changed encoding for stats<commit_after>package coinbase

import(
  "encoding/json"
  "errors"
  "fmt"
  "net/url"
  "strconv"
  "time"
)

type Product struct {
  Id string `json:"id"`
  BaseCurency string `json:"base_currency"`
  QuoteCurrency string `json:"quote_currency"`
  BaseMinSize float64 `json:"base_min_size,number"`
  BaseMaxSize float64 `json:"base_max_size,number"`
  QuoteIncrement float64 `json:"quote_increment,number"`
}

type Ticker struct {
  TradeId int `json:"trade_id,number"`
  Price float64 `json:"price,string"`
  Size float64 `json:"size,string"`
  Time Time `json:"time,string"`
}

type Trade struct {
  TradeId int `json:"trade_id,number"`
  Price float64 `json:"price,string"`
  Size float64 `json:"size,string"`
  Time Time `json:"time,string"`
  Side string `json:"side"`
}

type HistoricRate struct {
  Time time.Time
  Low float64
  High float64
  Open float64
  Close float64
  Volume float64
}

type Stats struct {
  Low float64 `json:"low,string"`
  High float64 `json:"high,string"`
  Open float64 `json:"open,string"`
  Volume float64 `json:"volume,string"`
}

type BookEntry struct {
  Price float64
  Size float64
  NumberOfOrders int
  OrderId string
}

type Book struct {
  Sequence int `json:"sequence"`
  Bids []BookEntry `json:"bids"`
  Asks []BookEntry `json:"asks"`
}

type ListTradesParams struct {
  Pagination PaginationParams
}

type GetHistoricRatesParams struct {
  Start time.Time
  End time.Time
  Granularity int
}

func(e *BookEntry) UnmarshalJSON(data []byte) error {
  var entry[]interface{}

  if err := json.Unmarshal(data, &entry); err != nil {
    return err
  }

  priceString, ok := entry[0].(string)
  if !ok {
    return errors.New("Expected string")
  }

  sizeString, ok := entry[1].(string)
  if !ok {
    return errors.New("Expected string")
  }

  price, err := strconv.ParseFloat(priceString, 32)
  if err != nil {
    return err
  }

  size, err := strconv.ParseFloat(sizeString, 32)
  if err != nil {
    return err
  }

  *e = BookEntry{
    Price: price,
    Size: size,
  }

  var stringOrderId string
  numberOfOrdersFloat, ok := entry[2].(float64)
  if !ok {
    // Try to see if it's a string
    stringOrderId, ok = entry[2].(string)
    if !ok {
      return errors.New("Could not parse 3rd column, tried float and string")
    }
    e.OrderId = stringOrderId

  } else {
    e.NumberOfOrders = int(numberOfOrdersFloat)
  }

  return nil
}

func(e *HistoricRate) UnmarshalJSON(data []byte) error {
  var entry[]interface{}

  if err := json.Unmarshal(data, &entry); err != nil {
    return err
  }

  timeFloat, ok := entry[0].(float64)
  if !ok {
    return errors.New("Expected float")
  }

  lowFloat, ok := entry[1].(float64)
  if !ok {
    return errors.New("Expected float")
  }

  highFloat, ok := entry[2].(float64)
  if !ok {
    return errors.New("Expected float")
  }

  openFloat, ok := entry[3].(float64)
  if !ok {
    return errors.New("Expected float")
  }

  closeFloat, ok := entry[4].(float64)
  if !ok {
    return errors.New("Expected float")
  }

  volumeFloat, ok := entry[5].(float64)
  if !ok {
    return errors.New("Expected float")
  }

  *e = HistoricRate{
    Time: time.Unix(int64(timeFloat), 0),
    Low: lowFloat,
    High: highFloat,
    Open: openFloat,
    Close: closeFloat,
    Volume: volumeFloat,
  }

  return nil
}

func (c *Client) GetBook(product string, level int) (Book, error) {
  var book Book

  requestURL := fmt.Sprintf("/products/%s/book?level=%d", product, level)
  _, err := c.Request("GET", requestURL, nil, &book)
  return book, err
}

func (c *Client) GetTicker(product string) (Ticker, error) {
  var ticker Ticker

  requestURL := fmt.Sprintf("/products/%s/ticker", product)
  _, err := c.Request("GET", requestURL, nil, &ticker)
  return ticker, err
}

func (c *Client) ListTrades(product string,
  p ...ListTradesParams) *Cursor {
  paginationParams := PaginationParams{}
  if len(p) > 0 {
    paginationParams = p[0].Pagination
  }

  return NewCursor(c, "GET", fmt.Sprintf("/products/%s/trades", product),
    &paginationParams)
}

func (c *Client) GetProducts() ([]Product, error) {
  var products []Product

  requestURL := fmt.Sprintf("/products")
  _, err := c.Request("GET", requestURL, nil, &products)
  return products, err
}

func (c *Client) GetHistoricRates(product string,
  p ...GetHistoricRatesParams) ([]HistoricRate, error) {
  var historicRates []HistoricRate
  requestURL := fmt.Sprintf("/products/%s/candles", product)
  params := GetHistoricRatesParams{}
  if len(p) > 0 {
    params = p[0]
  }

  if !params.Start.IsZero() && !params.End.IsZero() && params.Granularity != 0 {
    values := url.Values{}
    layout := "2006-01-02T15:04:05Z"
    values.Add("start", params.Start.Format(layout))
    values.Add("end", params.End.Format(layout))
    values.Add("granularity", strconv.Itoa(params.Granularity))

    requestURL = fmt.Sprintf("%s?%s", requestURL, values.Encode())
  }

  _, err := c.Request("GET", requestURL, nil, &historicRates)
  return historicRates, err
}

func (c *Client) GetStats(product string) (Stats, error) {
  var stats Stats
  requestURL := fmt.Sprintf("/products/%s/stats", product)
  _, err := c.Request("GET", requestURL, nil, &stats)
  return stats, err
}
<|endoftext|>


<commit_before>// Package profile provides a simple way to manage runtime/pprof
// profiling of your Go application.
package profile

import (
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"runtime/pprof"
)

// Config controls the operation of the profile package.
type Config struct {
	// Verbose controls the output of informational messages
	// during profiling. It defaults to true. If set to false
	// only error messages will be output.
	Verbose bool

	// CPUProfile controls if cpu profiling will be enabled.
	// It defaults to false.
	CPUProfile bool

	// MemProfile controls if memory profiling will be enabled.
	// It defaults to false.
	MemProfile bool

	// ProfilePath controls the base path where various profiling
	// files are written. It defaults to the output of
	// ioutil.TempDir.
	ProfilePath string

	// HandleInterrupt controls whether the profiling package should
	// hook SIGINT to write profiles cleanly.
	// It defaults to true, programs with more sophisticated signal
	// handling should set this to false and ensure the Stop() function
	// returned from Start() is called during shutdown.
	HandleInterrupt bool
}

var CPUProfile = &Config{
	Verbose:         true,
	CPUProfile:      true,
	HandleInterrupt: true,
}

var MemProfile = &Config{
	Verbose:         true,
	MemProfile:      true,
	HandleInterrupt: true,
}

func (c *Config) getVerbose() bool {
	if c == nil {
		return true
	}
	return c.Verbose
}

func (c *Config) getCPUProfile() bool {
	if c == nil {
		return false
	}
	return c.CPUProfile
}

func (c *Config) getMemProfile() bool {
	if c == nil {
		return false
	}
	return c.MemProfile
}

func (c *Config) getProfilePath() string {
	if c == nil {
		return ""
	}
	return c.ProfilePath
}

func (c *Config) getHandleInterrupt() bool {
	if c == nil {
		return true
	}
	return c.HandleInterrupt
}

type profile struct {
	path string
	*Config
	closers []func()
}

func (p *profile) Stop() {
	for _, c := range p.closers {
		c()
	}
}

// Start starts a new profiling session configured using *Config.
// The caller should call the Stop method on the value returned
// to cleanly stop profiling.
// Passing a nil *Config is the same as passing a *Config with
// defaults chosen.
func Start(cfg *Config) interface {
	Stop()
} {
	path := cfg.getProfilePath()
	var err error
	if path == "" {
		path, err = ioutil.TempDir("", "profile")
	} else {
		err = os.MkdirAll(path, 0777)
	}
	if err != nil {
		log.Fatalf("profile: could not create initial output directory: %v", err)
	}
	prof := &profile{
		path:   path,
		Config: cfg,
	}

	if prof.getCPUProfile() {
		fn := filepath.Join(prof.path, "cpu.pprof")
		f, err := os.Create(fn)
		if err != nil {
			log.Fatal("profile: could not create cpu profile file %q: %v", fn, err)
		}
		if prof.getVerbose() {
			log.Printf("profile: cpu profiling enabled, %s", fn)
		}
		pprof.StartCPUProfile(f)
		prof.closers = append(prof.closers, func() {
			pprof.StopCPUProfile()
			f.Close()
		})
	}

	if prof.getMemProfile() {
		fn := filepath.Join(prof.path, "mem.pprof")
		f, err := os.Create(fn)
		if err != nil {
			log.Fatal("profile: could not create memory profile file %q: %v", fn, err)
		}
		if prof.getVerbose() {
			log.Printf("profile: memory profiling enabled, %s", fn)
		}
		prof.closers = append(prof.closers, func() {
			pprof.Lookup("heap").WriteTo(f, 0)
			f.Close()
		})
	}

	if prof.getHandleInterrupt() {
		go func() {
			c := make(chan os.Signal, 1)
			signal.Notify(c, os.Interrupt)
			<-c

			log.Println("profile: caught interrupt, stopping profiles")
			prof.Stop()

			os.Exit(0)
		}()
	}

	return prof
}
<commit_msg>Added block profiling<commit_after>// Package profile provides a simple way to manage runtime/pprof
// profiling of your Go application.
package profile

import (
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"runtime"
	"runtime/pprof"
)

// Config controls the operation of the profile package.
type Config struct {
	// Verbose controls the output of informational messages
	// during profiling. It defaults to true. If set to false
	// only error messages will be output.
	Verbose bool

	// CPUProfile controls if cpu profiling will be enabled.
	// It defaults to false.
	CPUProfile bool

	// MemProfile controls if memory profiling will be enabled.
	// It defaults to false.
	MemProfile bool

	// BlockProfile controls if block (contention) profiling will
	// be enabled.
	// It defaults to false.
	BlockProfile bool

	// ProfilePath controls the base path where various profiling
	// files are written. It defaults to the output of
	// ioutil.TempDir.
	ProfilePath string

	// HandleInterrupt controls whether the profiling package should
	// hook SIGINT to write profiles cleanly.
	// It defaults to true, programs with more sophisticated signal
	// handling should set this to false and ensure the Stop() function
	// returned from Start() is called during shutdown.
	HandleInterrupt bool
}

var (
	CPUProfile = &Config{
		Verbose:         true,
		CPUProfile:      true,
		HandleInterrupt: true,
	}

	MemProfile = &Config{
		Verbose:         true,
		MemProfile:      true,
		HandleInterrupt: true,
	}

	BlockProfile = &Config{
		Verbose:         true,
		BlockProfile:    true,
		HandleInterrupt: true,
	}
)

func (c *Config) getVerbose() bool {
	if c == nil {
		return true
	}
	return c.Verbose
}

func (c *Config) getCPUProfile() bool {
	if c == nil {
		return false
	}
	return c.CPUProfile
}

func (c *Config) getMemProfile() bool {
	if c == nil {
		return false
	}
	return c.MemProfile
}

func (c *Config) getMemProfileRate() int {
	return 4096
}

func (c *Config) getBlockProfile() bool {
	if c == nil {
		return false
	}
	return c.BlockProfile
}

func (c *Config) getProfilePath() string {
	if c == nil {
		return ""
	}
	return c.ProfilePath
}

func (c *Config) getHandleInterrupt() bool {
	if c == nil {
		return true
	}
	return c.HandleInterrupt
}

type profile struct {
	path string
	*Config
	closers []func()
}

func (p *profile) Stop() {
	for _, c := range p.closers {
		c()
	}
}

// Start starts a new profiling session configured using *Config.
// The caller should call the Stop method on the value returned
// to cleanly stop profiling.
// Passing a nil *Config is the same as passing a *Config with
// defaults chosen.
func Start(cfg *Config) interface {
	Stop()
} {
	path := cfg.getProfilePath()
	var err error
	if path == "" {
		path, err = ioutil.TempDir("", "profile")
	} else {
		err = os.MkdirAll(path, 0777)
	}
	if err != nil {
		log.Fatalf("profile: could not create initial output directory: %v", err)
	}
	prof := &profile{
		path:   path,
		Config: cfg,
	}

	if prof.getCPUProfile() {
		fn := filepath.Join(prof.path, "cpu.pprof")
		f, err := os.Create(fn)
		if err != nil {
			log.Fatal("profile: could not create cpu profile %q: %v", fn, err)
		}
		if prof.getVerbose() {
			log.Printf("profile: cpu profiling enabled, %s", fn)
		}
		pprof.StartCPUProfile(f)
		prof.closers = append(prof.closers, func() {
			pprof.StopCPUProfile()
			f.Close()
		})
	}

	if prof.getMemProfile() {
		fn := filepath.Join(prof.path, "mem.pprof")
		f, err := os.Create(fn)
		if err != nil {
			log.Fatal("profile: could not create memory profile %q: %v", fn, err)
		}
		old := runtime.MemProfileRate
		runtime.MemProfileRate = prof.getMemProfileRate()
		if prof.getVerbose() {
			log.Printf("profile: memory profiling enabled, %s", fn)
		}
		prof.closers = append(prof.closers, func() {
			pprof.Lookup("heap").WriteTo(f, 0)
			f.Close()
			runtime.MemProfileRate = old
		})
	}

	if prof.getBlockProfile() {
		fn := filepath.Join(prof.path, "block.pprof")
		f, err := os.Create(fn)
		if err != nil {
			log.Fatal("profile: could not create block profile %q: %v", fn, err)
		}
		runtime.SetBlockProfileRate(1)
		if prof.getVerbose() {
			log.Printf("profile: block profiling enabled, %s", fn)
		}
		prof.closers = append(prof.closers, func() {
			pprof.Lookup("block").WriteTo(f, 0)
			f.Close()
			runtime.SetBlockProfileRate(0)
		})
	}

	if prof.getHandleInterrupt() {
		go func() {
			c := make(chan os.Signal, 1)
			signal.Notify(c, os.Interrupt)
			<-c

			log.Println("profile: caught interrupt, stopping profiles")
			prof.Stop()

			os.Exit(0)
		}()
	}

	return prof
}
<|endoftext|>


<commit_before>package regexputil

import (
	"regexp"
)

// McReplaceAllString is a single line MustCompile regexp for ReplaceAllString

func McReplaceAllString(pattern string, s string, repl string) string {
	return regexp.MustCompile(pattern).ReplaceAllString(s, repl)
}

// RegexpSet is a struct that holds compiled regular expressions.
// Primary goals of this struct is to reduce MustCompile regular
// expressions into a single function call and to store the compiled
// regular expressions if desired

type RegexpSet struct {
	Regexps map[string]*regexp.Regexp
}

// NewRegexpSet returns a new RegexpSet struct

func NewRegexpSet() RegexpSet {
	set := RegexpSet{
		Regexps: map[string]*regexp.Regexp{}}
	return set
}

func (set *RegexpSet) GetRegexp(pattern string, useStore bool, key string) *regexp.Regexp {
	var rx *regexp.Regexp
	if useStore {
		if len(key) == 0 {
			key = pattern
		}
		var ok bool
		rx, ok = set.Regexps[key]
		if !ok {
			rx = regexp.MustCompile(pattern)
		}
		set.Regexps[key] = rx
	} else {
		rx = regexp.MustCompile(pattern)
	}
	return rx
}

func (set *RegexpSet) FindAllString(pattern string, s string, n int, useStore bool, key string) []string {
	rx := set.GetRegexp(pattern, useStore, key)
	rs := rx.FindAllString(s, n)
	return rs
}

// FindAllStringSubmatch performs a regular expression find against the
// supplied pattern and string. It will store the compiled regular expression
// for later use.

func (set *RegexpSet) FindAllStringSubmatch(pattern string, s string, n int, useStore bool, key string) [][]string {
	rx := set.GetRegexp(pattern, useStore, key)
	rs := rx.FindAllStringSubmatch(s, n)
	return rs
}

func (set *RegexpSet) FindStringSubmatch(pattern string, s string, useStore bool, key string) []string {
	rx := set.GetRegexp(pattern, useStore, key)
	rs := rx.FindStringSubmatch(s)
	return rs
}

func FindStringSubmatchNamedMap(rx *regexp.Regexp, s string) map[string]string {
	match := rx.FindStringSubmatch(s)
	result := make(map[string]string)
	for i, name := range rx.SubexpNames() {
		if i != 0 && name != "" {
			result[name] = match[i]
		}
	}
	return result
}
<commit_msg>update style<commit_after>package regexputil

import (
	"regexp"
)

// McReplaceAllString is a single line MustCompile regexp for ReplaceAllString
func McReplaceAllString(pattern string, s string, repl string) string {
	return regexp.MustCompile(pattern).ReplaceAllString(s, repl)
}

// RegexpSet is a struct that holds compiled regular expressions.
// Primary goals of this struct is to reduce MustCompile regular
// expressions into a single function call and to store the compiled
// regular expressions if desired
type RegexpSet struct {
	Regexps map[string]*regexp.Regexp
}

// NewRegexpSet returns a new RegexpSet struct

func NewRegexpSet() RegexpSet {
	set := RegexpSet{
		Regexps: map[string]*regexp.Regexp{}}
	return set
}

func (set *RegexpSet) GetRegexp(pattern string, useStore bool, key string) *regexp.Regexp {
	var rx *regexp.Regexp
	if useStore {
		if len(key) == 0 {
			key = pattern
		}
		var ok bool
		rx, ok = set.Regexps[key]
		if !ok {
			rx = regexp.MustCompile(pattern)
		}
		set.Regexps[key] = rx
	} else {
		rx = regexp.MustCompile(pattern)
	}
	return rx
}

func (set *RegexpSet) FindAllString(pattern string, s string, n int, useStore bool, key string) []string {
	rx := set.GetRegexp(pattern, useStore, key)
	rs := rx.FindAllString(s, n)
	return rs
}

// FindAllStringSubmatch performs a regular expression find against the
// supplied pattern and string. It will store the compiled regular expression
// for later use.
func (set *RegexpSet) FindAllStringSubmatch(pattern string, s string, n int, useStore bool, key string) [][]string {
	rx := set.GetRegexp(pattern, useStore, key)
	rs := rx.FindAllStringSubmatch(s, n)
	return rs
}

func (set *RegexpSet) FindStringSubmatch(pattern string, s string, useStore bool, key string) []string {
	rx := set.GetRegexp(pattern, useStore, key)
	rs := rx.FindStringSubmatch(s)
	return rs
}

func FindStringSubmatchNamedMap(rx *regexp.Regexp, s string) map[string]string {
	match := rx.FindStringSubmatch(s)
	result := make(map[string]string)
	for i, name := range rx.SubexpNames() {
		if i != 0 && name != "" {
			result[name] = match[i]
		}
	}
	return result
}
<|endoftext|>


<commit_before>package auth

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/docker/distribution/registry/client"
	"github.com/docker/distribution/registry/client/transport"
)

// AuthenticationHandler is an interface for authorizing a request from
// params from a "WWW-Authenicate" header for a single scheme.
type AuthenticationHandler interface {
	// Scheme returns the scheme as expected from the "WWW-Authenicate" header.
	Scheme() string

	// AuthorizeRequest adds the authorization header to a request (if needed)
	// using the parameters from "WWW-Authenticate" method. The parameters
	// values depend on the scheme.
	AuthorizeRequest(req *http.Request, params map[string]string) error
}

// CredentialStore is an interface for getting credentials for
// a given URL
type CredentialStore interface {
	// Basic returns basic auth for the given URL
	Basic(*url.URL) (string, string)
}

// NewAuthorizer creates an authorizer which can handle multiple authentication
// schemes. The handlers are tried in order, the higher priority authentication
// methods should be first. The challengeMap holds a list of challenges for
// a given root API endpoint (for example "https://registry-1.docker.io/v2/").
func NewAuthorizer(manager ChallengeManager, handlers ...AuthenticationHandler) transport.RequestModifier {
	return &endpointAuthorizer{
		challenges: manager,
		handlers:   handlers,
	}
}

type endpointAuthorizer struct {
	challenges ChallengeManager
	handlers   []AuthenticationHandler
	transport  http.RoundTripper
}

func (ea *endpointAuthorizer) ModifyRequest(req *http.Request) error {
	v2Root := strings.Index(req.URL.Path, "/v2/")
	if v2Root == -1 {
		return nil
	}

	ping := url.URL{
		Host:   req.URL.Host,
		Scheme: req.URL.Scheme,
		Path:   req.URL.Path[:v2Root+4],
	}

	pingEndpoint := ping.String()

	challenges, err := ea.challenges.GetChallenges(pingEndpoint)
	if err != nil {
		return err
	}

	if len(challenges) > 0 {
		for _, handler := range ea.handlers {
			for _, challenge := range challenges {
				if challenge.Scheme != handler.Scheme() {
					continue
				}
				if err := handler.AuthorizeRequest(req, challenge.Parameters); err != nil {
					return err
				}
			}
		}
	}

	return nil
}

// This is the minimum duration a token can last (in seconds).
// A token must not live less than 60 seconds because older versions
// of the Docker client didn't read their expiration from the token
// response and assumed 60 seconds.  So to remain compatible with
// those implementations, a token must live at least this long.
const minimumTokenLifetimeSeconds = 60

// Private interface for time used by this package to enable tests to provide their own implementation.
type clock interface {
	Now() time.Time
}

type tokenHandler struct {
	header    http.Header
	creds     CredentialStore
	scope     tokenScope
	transport http.RoundTripper
	clock     clock

	tokenLock       sync.Mutex
	tokenCache      string
	tokenExpiration time.Time
}

// tokenScope represents the scope at which a token will be requested.
// This represents a specific action on a registry resource.
type tokenScope struct {
	Resource string
	Scope    string
	Actions  []string
}

func (ts tokenScope) String() string {
	return fmt.Sprintf("%s:%s:%s", ts.Resource, ts.Scope, strings.Join(ts.Actions, ","))
}

// An implementation of clock for providing real time data.
type realClock struct{}

// Now implements clock
func (realClock) Now() time.Time { return time.Now() }

// NewTokenHandler creates a new AuthenicationHandler which supports
// fetching tokens from a remote token server.
func NewTokenHandler(transport http.RoundTripper, creds CredentialStore, scope string, actions ...string) AuthenticationHandler {
	return newTokenHandler(transport, creds, realClock{}, scope, actions...)
}

// newTokenHandler exposes the option to provide a clock to manipulate time in unit testing.
func newTokenHandler(transport http.RoundTripper, creds CredentialStore, c clock, scope string, actions ...string) AuthenticationHandler {
	return &tokenHandler{
		transport: transport,
		creds:     creds,
		clock:     c,
		scope: tokenScope{
			Resource: "repository",
			Scope:    scope,
			Actions:  actions,
		},
	}
}

func (th *tokenHandler) client() *http.Client {
	return &http.Client{
		Transport: th.transport,
		Timeout:   15 * time.Second,
	}
}

func (th *tokenHandler) Scheme() string {
	return "bearer"
}

func (th *tokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
	if err := th.refreshToken(params); err != nil {
		return err
	}

	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", th.tokenCache))

	return nil
}

func (th *tokenHandler) refreshToken(params map[string]string) error {
	th.tokenLock.Lock()
	defer th.tokenLock.Unlock()
	now := th.clock.Now()
	if now.After(th.tokenExpiration) {
		tr, err := th.fetchToken(params)
		if err != nil {
			return err
		}
		th.tokenCache = tr.Token
		th.tokenExpiration = tr.IssuedAt.Add(time.Duration(tr.ExpiresIn) * time.Second)
	}

	return nil
}

type tokenResponse struct {
	Token       string    `json:"token"`
	AccessToken string    `json:"access_token"`
	ExpiresIn   int       `json:"expires_in"`
	IssuedAt    time.Time `json:"issued_at"`
}

func (th *tokenHandler) fetchToken(params map[string]string) (token *tokenResponse, err error) {
	//log.Debugf("Getting bearer token with %s for %s", challenge.Parameters, ta.auth.Username)
	realm, ok := params["realm"]
	if !ok {
		return nil, errors.New("no realm specified for token auth challenge")
	}

	// TODO(dmcgowan): Handle empty scheme

	realmURL, err := url.Parse(realm)
	if err != nil {
		return nil, fmt.Errorf("invalid token auth challenge realm: %s", err)
	}

	req, err := http.NewRequest("GET", realmURL.String(), nil)
	if err != nil {
		return nil, err
	}

	reqParams := req.URL.Query()
	service := params["service"]
	scope := th.scope.String()

	if service != "" {
		reqParams.Add("service", service)
	}

	for _, scopeField := range strings.Fields(scope) {
		reqParams.Add("scope", scopeField)
	}

	if th.creds != nil {
		username, password := th.creds.Basic(realmURL)
		if username != "" && password != "" {
			reqParams.Add("account", username)
			req.SetBasicAuth(username, password)
		}
	}

	req.URL.RawQuery = reqParams.Encode()

	resp, err := th.client().Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if !client.SuccessStatus(resp.StatusCode) {
		err := client.HandleErrorResponse(resp)
		return nil, err
	}

	decoder := json.NewDecoder(resp.Body)

	tr := new(tokenResponse)
	if err = decoder.Decode(tr); err != nil {
		return nil, fmt.Errorf("unable to decode token response: %s", err)
	}

	// `access_token` is equivalent to `token` and if both are specified
	// the choice is undefined.  Canonicalize `access_token` by sticking
	// things in `token`.
	if tr.AccessToken != "" {
		tr.Token = tr.AccessToken
	}

	if tr.Token == "" {
		return nil, errors.New("authorization server did not include a token in the response")
	}

	if tr.ExpiresIn < minimumTokenLifetimeSeconds {
		logrus.Debugf("Increasing token expiration to: %d seconds", tr.ExpiresIn)
		// The default/minimum lifetime.
		tr.ExpiresIn = minimumTokenLifetimeSeconds
	}

	if tr.IssuedAt.IsZero() {
		// issued_at is optional in the token response.
		tr.IssuedAt = th.clock.Now()
	}

	return tr, nil
}

type basicHandler struct {
	creds CredentialStore
}

// NewBasicHandler creaters a new authentiation handler which adds
// basic authentication credentials to a request.
func NewBasicHandler(creds CredentialStore) AuthenticationHandler {
	return &basicHandler{
		creds: creds,
	}
}

func (*basicHandler) Scheme() string {
	return "basic"
}

func (bh *basicHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
	if bh.creds != nil {
		username, password := bh.creds.Basic(req.URL)
		if username != "" && password != "" {
			req.SetBasicAuth(username, password)
			return nil
		}
	}
	return errors.New("no basic auth credentials")
}
<commit_msg>Allows token authentication handler to request additional scopes<commit_after>package auth

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/docker/distribution/registry/client"
	"github.com/docker/distribution/registry/client/transport"
)

// AuthenticationHandler is an interface for authorizing a request from
// params from a "WWW-Authenicate" header for a single scheme.
type AuthenticationHandler interface {
	// Scheme returns the scheme as expected from the "WWW-Authenicate" header.
	Scheme() string

	// AuthorizeRequest adds the authorization header to a request (if needed)
	// using the parameters from "WWW-Authenticate" method. The parameters
	// values depend on the scheme.
	AuthorizeRequest(req *http.Request, params map[string]string) error
}

// CredentialStore is an interface for getting credentials for
// a given URL
type CredentialStore interface {
	// Basic returns basic auth for the given URL
	Basic(*url.URL) (string, string)
}

// NewAuthorizer creates an authorizer which can handle multiple authentication
// schemes. The handlers are tried in order, the higher priority authentication
// methods should be first. The challengeMap holds a list of challenges for
// a given root API endpoint (for example "https://registry-1.docker.io/v2/").
func NewAuthorizer(manager ChallengeManager, handlers ...AuthenticationHandler) transport.RequestModifier {
	return &endpointAuthorizer{
		challenges: manager,
		handlers:   handlers,
	}
}

type endpointAuthorizer struct {
	challenges ChallengeManager
	handlers   []AuthenticationHandler
	transport  http.RoundTripper
}

func (ea *endpointAuthorizer) ModifyRequest(req *http.Request) error {
	v2Root := strings.Index(req.URL.Path, "/v2/")
	if v2Root == -1 {
		return nil
	}

	ping := url.URL{
		Host:   req.URL.Host,
		Scheme: req.URL.Scheme,
		Path:   req.URL.Path[:v2Root+4],
	}

	pingEndpoint := ping.String()

	challenges, err := ea.challenges.GetChallenges(pingEndpoint)
	if err != nil {
		return err
	}

	if len(challenges) > 0 {
		for _, handler := range ea.handlers {
			for _, challenge := range challenges {
				if challenge.Scheme != handler.Scheme() {
					continue
				}
				if err := handler.AuthorizeRequest(req, challenge.Parameters); err != nil {
					return err
				}
			}
		}
	}

	return nil
}

// This is the minimum duration a token can last (in seconds).
// A token must not live less than 60 seconds because older versions
// of the Docker client didn't read their expiration from the token
// response and assumed 60 seconds.  So to remain compatible with
// those implementations, a token must live at least this long.
const minimumTokenLifetimeSeconds = 60

// Private interface for time used by this package to enable tests to provide their own implementation.
type clock interface {
	Now() time.Time
}

type tokenHandler struct {
	header    http.Header
	creds     CredentialStore
	scope     tokenScope
	transport http.RoundTripper
	clock     clock

	tokenLock       sync.Mutex
	tokenCache      string
	tokenExpiration time.Time

	additionalScopes map[string]struct{}
}

// tokenScope represents the scope at which a token will be requested.
// This represents a specific action on a registry resource.
type tokenScope struct {
	Resource string
	Scope    string
	Actions  []string
}

func (ts tokenScope) String() string {
	return fmt.Sprintf("%s:%s:%s", ts.Resource, ts.Scope, strings.Join(ts.Actions, ","))
}

// An implementation of clock for providing real time data.
type realClock struct{}

// Now implements clock
func (realClock) Now() time.Time { return time.Now() }

// NewTokenHandler creates a new AuthenicationHandler which supports
// fetching tokens from a remote token server.
func NewTokenHandler(transport http.RoundTripper, creds CredentialStore, scope string, actions ...string) AuthenticationHandler {
	return newTokenHandler(transport, creds, realClock{}, scope, actions...)
}

// newTokenHandler exposes the option to provide a clock to manipulate time in unit testing.
func newTokenHandler(transport http.RoundTripper, creds CredentialStore, c clock, scope string, actions ...string) AuthenticationHandler {
	return &tokenHandler{
		transport: transport,
		creds:     creds,
		clock:     c,
		scope: tokenScope{
			Resource: "repository",
			Scope:    scope,
			Actions:  actions,
		},
		additionalScopes: map[string]struct{}{},
	}
}

func (th *tokenHandler) client() *http.Client {
	return &http.Client{
		Transport: th.transport,
		Timeout:   15 * time.Second,
	}
}

func (th *tokenHandler) Scheme() string {
	return "bearer"
}

func (th *tokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
	var additionalScopes []string
	if fromParam := req.URL.Query().Get("from"); fromParam != "" {
		additionalScopes = append(additionalScopes, tokenScope{
			Resource: "repository",
			Scope:    fromParam,
			Actions:  []string{"pull"},
		}.String())
	}
	if err := th.refreshToken(params, additionalScopes...); err != nil {
		return err
	}

	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", th.tokenCache))

	return nil
}

func (th *tokenHandler) refreshToken(params map[string]string, additionalScopes ...string) error {
	th.tokenLock.Lock()
	defer th.tokenLock.Unlock()
	var addedScopes bool
	for _, scope := range additionalScopes {
		if _, ok := th.additionalScopes[scope]; !ok {
			th.additionalScopes[scope] = struct{}{}
			addedScopes = true
		}
	}
	now := th.clock.Now()
	if now.After(th.tokenExpiration) || addedScopes {
		tr, err := th.fetchToken(params)
		if err != nil {
			return err
		}
		th.tokenCache = tr.Token
		th.tokenExpiration = tr.IssuedAt.Add(time.Duration(tr.ExpiresIn) * time.Second)
	}

	return nil
}

type tokenResponse struct {
	Token       string    `json:"token"`
	AccessToken string    `json:"access_token"`
	ExpiresIn   int       `json:"expires_in"`
	IssuedAt    time.Time `json:"issued_at"`
}

func (th *tokenHandler) fetchToken(params map[string]string) (token *tokenResponse, err error) {
	//log.Debugf("Getting bearer token with %s for %s", challenge.Parameters, ta.auth.Username)
	realm, ok := params["realm"]
	if !ok {
		return nil, errors.New("no realm specified for token auth challenge")
	}

	// TODO(dmcgowan): Handle empty scheme

	realmURL, err := url.Parse(realm)
	if err != nil {
		return nil, fmt.Errorf("invalid token auth challenge realm: %s", err)
	}

	req, err := http.NewRequest("GET", realmURL.String(), nil)
	if err != nil {
		return nil, err
	}

	reqParams := req.URL.Query()
	service := params["service"]
	scope := th.scope.String()

	if service != "" {
		reqParams.Add("service", service)
	}

	for _, scopeField := range strings.Fields(scope) {
		reqParams.Add("scope", scopeField)
	}

	for scope := range th.additionalScopes {
		reqParams.Add("scope", scope)
	}

	if th.creds != nil {
		username, password := th.creds.Basic(realmURL)
		if username != "" && password != "" {
			reqParams.Add("account", username)
			req.SetBasicAuth(username, password)
		}
	}

	req.URL.RawQuery = reqParams.Encode()

	resp, err := th.client().Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if !client.SuccessStatus(resp.StatusCode) {
		err := client.HandleErrorResponse(resp)
		return nil, err
	}

	decoder := json.NewDecoder(resp.Body)

	tr := new(tokenResponse)
	if err = decoder.Decode(tr); err != nil {
		return nil, fmt.Errorf("unable to decode token response: %s", err)
	}

	// `access_token` is equivalent to `token` and if both are specified
	// the choice is undefined.  Canonicalize `access_token` by sticking
	// things in `token`.
	if tr.AccessToken != "" {
		tr.Token = tr.AccessToken
	}

	if tr.Token == "" {
		return nil, errors.New("authorization server did not include a token in the response")
	}

	if tr.ExpiresIn < minimumTokenLifetimeSeconds {
		logrus.Debugf("Increasing token expiration to: %d seconds", tr.ExpiresIn)
		// The default/minimum lifetime.
		tr.ExpiresIn = minimumTokenLifetimeSeconds
	}

	if tr.IssuedAt.IsZero() {
		// issued_at is optional in the token response.
		tr.IssuedAt = th.clock.Now()
	}

	return tr, nil
}

type basicHandler struct {
	creds CredentialStore
}

// NewBasicHandler creaters a new authentiation handler which adds
// basic authentication credentials to a request.
func NewBasicHandler(creds CredentialStore) AuthenticationHandler {
	return &basicHandler{
		creds: creds,
	}
}

func (*basicHandler) Scheme() string {
	return "basic"
}

func (bh *basicHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
	if bh.creds != nil {
		username, password := bh.creds.Basic(req.URL)
		if username != "" && password != "" {
			req.SetBasicAuth(username, password)
			return nil
		}
	}
	return errors.New("no basic auth credentials")
}
<|endoftext|>