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

package pgzip

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/klauspost/compress/flate"
	"github.com/klauspost/crc32"
	"hash"
	"io"
)

const (
	defaultBlockSize = 250000
	tailSize         = 16384
	defaultBlocks    = 16
)

// These constants are copied from the flate package, so that code that imports
// "compress/gzip" does not also have to import "compress/flate".
const (
	NoCompression       = flate.NoCompression
	BestSpeed           = flate.BestSpeed
	BestCompression     = flate.BestCompression
	DefaultCompression  = flate.DefaultCompression
	ConstantCompression = flate.ConstantCompression
)

// A Writer is an io.WriteCloser.
// Writes to a Writer are compressed and written to w.
type Writer struct {
	Header
	w             io.Writer
	level         int
	wroteHeader   bool
	blockSize     int
	blocks        int
	currentBuffer []byte
	prevTail      []byte
	digest        hash.Hash32
	size          int
	closed        bool
	buf           [10]byte
	err           error
	pushedErr     chan error
	results       chan result
}

type result struct {
	result        chan []byte
	notifyWritten chan struct{}
}

// Use SetConcurrency to finetune the concurrency level if needed.
//
// With this you can control the approximate size of your blocks,
// as well as how many you want to be processing in parallel.
//
// Default values for this is SetConcurrency(250000, 16),
// meaning blocks are split at 250000 bytes and up to 16 blocks
// can be processing at once before the writer blocks.
func (z *Writer) SetConcurrency(blockSize, blocks int) error {
	if blockSize <= tailSize {
		return fmt.Errorf("gzip: block size cannot be less than or equal to %d", tailSize)
	}
	if blocks <= 0 {
		return errors.New("gzip: blocks cannot be zero or less")
	}
	z.blockSize = blockSize
	z.results = make(chan result, blocks)
	z.blocks = blocks
	return nil
}

// NewWriter returns a new Writer.
// Writes to the returned writer are compressed and written to w.
//
// It is the caller's responsibility to call Close on the WriteCloser when done.
// Writes may be buffered and not flushed until Close.
//
// Callers that wish to set the fields in Writer.Header must do so before
// the first call to Write or Close. The Comment and Name header fields are
// UTF-8 strings in Go, but the underlying format requires NUL-terminated ISO
// 8859-1 (Latin-1). NUL or non-Latin-1 runes in those strings will lead to an
// error on Write.
func NewWriter(w io.Writer) *Writer {
	z, _ := NewWriterLevel(w, DefaultCompression)
	return z
}

// NewWriterLevel is like NewWriter but specifies the compression level instead
// of assuming DefaultCompression.
//
// The compression level can be DefaultCompression, NoCompression, or any
// integer value between BestSpeed and BestCompression inclusive. The error
// returned will be nil if the level is valid.
func NewWriterLevel(w io.Writer, level int) (*Writer, error) {
	if level < ConstantCompression || level > BestCompression {
		return nil, fmt.Errorf("gzip: invalid compression level: %d", level)
	}
	z := new(Writer)
	z.SetConcurrency(defaultBlockSize, defaultBlocks)
	z.init(w, level)
	return z, nil
}

// This function must be used by goroutines to set an
// error condition, since z.err access is restricted
// to the callers goruotine.
func (z Writer) pushError(err error) {
	z.pushedErr <- err
	close(z.pushedErr)
}

func (z *Writer) init(w io.Writer, level int) {
	digest := z.digest
	if digest != nil {
		digest.Reset()
	} else {
		digest = crc32.NewIEEE()
	}

	*z = Writer{
		Header: Header{
			OS: 255, // unknown
		},
		w:         w,
		level:     level,
		digest:    digest,
		pushedErr: make(chan error, 1),
		results:   make(chan result, z.blocks),
		blockSize: z.blockSize,
		blocks:    z.blocks,
	}
}

// Reset discards the Writer z's state and makes it equivalent to the
// result of its original state from NewWriter or NewWriterLevel, but
// writing to w instead. This permits reusing a Writer rather than
// allocating a new one.
func (z *Writer) Reset(w io.Writer) {
	if z.results != nil && !z.closed {
		close(z.results)
	}
	z.SetConcurrency(defaultBlockSize, defaultBlocks)
	z.init(w, z.level)
}

// GZIP (RFC 1952) is little-endian, unlike ZLIB (RFC 1950).
func put2(p []byte, v uint16) {
	p[0] = uint8(v >> 0)
	p[1] = uint8(v >> 8)
}

func put4(p []byte, v uint32) {
	p[0] = uint8(v >> 0)
	p[1] = uint8(v >> 8)
	p[2] = uint8(v >> 16)
	p[3] = uint8(v >> 24)
}

// writeBytes writes a length-prefixed byte slice to z.w.
func (z *Writer) writeBytes(b []byte) error {
	if len(b) > 0xffff {
		return errors.New("gzip.Write: Extra data is too large")
	}
	put2(z.buf[0:2], uint16(len(b)))
	_, err := z.w.Write(z.buf[0:2])
	if err != nil {
		return err
	}
	_, err = z.w.Write(b)
	return err
}

// writeString writes a UTF-8 string s in GZIP's format to z.w.
// GZIP (RFC 1952) specifies that strings are NUL-terminated ISO 8859-1 (Latin-1).
func (z *Writer) writeString(s string) (err error) {
	// GZIP stores Latin-1 strings; error if non-Latin-1; convert if non-ASCII.
	needconv := false
	for _, v := range s {
		if v == 0 || v > 0xff {
			return errors.New("gzip.Write: non-Latin-1 header string")
		}
		if v > 0x7f {
			needconv = true
		}
	}
	if needconv {
		b := make([]byte, 0, len(s))
		for _, v := range s {
			b = append(b, byte(v))
		}
		_, err = z.w.Write(b)
	} else {
		_, err = io.WriteString(z.w, s)
	}
	if err != nil {
		return err
	}
	// GZIP strings are NUL-terminated.
	z.buf[0] = 0
	_, err = z.w.Write(z.buf[0:1])
	return err
}

// compressCurrent will compress the data currently buffered
// This should only be called from the main writer/flush/closer
func (z *Writer) compressCurrent(flush bool) {
	r := result{}
	r.result = make(chan []byte, 1)
	r.notifyWritten = make(chan struct{}, 0)
	z.results <- r

	// If block given is more than twice the block size, split it.
	c := z.currentBuffer
	if len(c) > z.blockSize*2 {
		c = c[:z.blockSize]
		go compressBlock(c, z.prevTail, *z, r)
		z.prevTail = c[len(c)-tailSize:]
		z.currentBuffer = z.currentBuffer[z.blockSize:]
		z.compressCurrent(flush)
		// Last one flushes if needed
		return
	}

	go compressBlock(c, z.prevTail, *z, r)
	if len(c) > tailSize {
		z.prevTail = c[len(c)-tailSize:]
	} else {
		z.prevTail = nil
	}
	z.currentBuffer = make([]byte, 0, z.blockSize+(z.blockSize/4))

	// Wait if flushing
	if flush {
		_ = <-r.notifyWritten
	}
}

// Returns an error if it has been set.
// Cannot be used by functions that are from internal goroutines.
func (z *Writer) checkError() error {
	if z.err != nil {
		return z.err
	}
	select {
	case err := <-z.pushedErr:
		z.err = err
		close(z.pushedErr)
	default:
	}
	return z.err
}

// Write writes a compressed form of p to the underlying io.Writer. The
// compressed bytes are not necessarily flushed to output until
// the Writer is closed or Flush() is called.
//
// The function will return quickly, if there are unused buffers.
// The sent slice (p) is copied, and the caller is free to re-use the buffer
// when the function returns.
//
// Errors that occur during compression will be reported later, and a nil error
// does not signify that the compression succeeded (since it is most likely still running)
// That means that the call that returns an error may not be the call that caused it.
// Only Flush and Close functions are guaranteed to return any errors up to that point.
func (z *Writer) Write(p []byte) (int, error) {
	if z.checkError() != nil {
		return 0, z.err
	}
	// Write the GZIP header lazily.
	if !z.wroteHeader {
		z.wroteHeader = true
		z.buf[0] = gzipID1
		z.buf[1] = gzipID2
		z.buf[2] = gzipDeflate
		z.buf[3] = 0
		if z.Extra != nil {
			z.buf[3] |= 0x04
		}
		if z.Name != "" {
			z.buf[3] |= 0x08
		}
		if z.Comment != "" {
			z.buf[3] |= 0x10
		}
		put4(z.buf[4:8], uint32(z.ModTime.Unix()))
		if z.level == BestCompression {
			z.buf[8] = 2
		} else if z.level == BestSpeed {
			z.buf[8] = 4
		} else {
			z.buf[8] = 0
		}
		z.buf[9] = z.OS
		var n int
		n, z.err = z.w.Write(z.buf[0:10])
		if z.err != nil {
			return n, z.err
		}
		if z.Extra != nil {
			z.err = z.writeBytes(z.Extra)
			if z.err != nil {
				return n, z.err
			}
		}
		if z.Name != "" {
			z.err = z.writeString(z.Name)
			if z.err != nil {
				return n, z.err
			}
		}
		if z.Comment != "" {
			z.err = z.writeString(z.Comment)
			if z.err != nil {
				return n, z.err
			}
		}
		// Start receiving data from compressors
		go func() {
			listen := z.results
			for {
				r, ok := <-listen
				// If closed, we are finished.
				if !ok {
					return
				}
				buf := <-r.result
				n, err := z.w.Write(buf)
				if err != nil {
					z.pushError(err)
					close(r.notifyWritten)
					return
				}
				if n != len(buf) {
					z.pushError(fmt.Errorf("gzip: short write %d should be %d", n, len(buf)))
					close(r.notifyWritten)
					return
				}
				close(r.notifyWritten)
			}
		}()
		z.currentBuffer = make([]byte, 0, z.blockSize+(z.blockSize/4))
	}
	z.size += len(p)
	z.digest.Write(p)
	z.currentBuffer = append(z.currentBuffer, p...)
	if len(z.currentBuffer) >= z.blockSize {
		z.compressCurrent(false)
	}
	return len(p), z.err
}

// Step 1: compresses buffer to buffer
// Step 2: send writer to channel
// Step 3: Close result channel to indicate we are done
func compressBlock(p, prevTail []byte, z Writer, r result) {
	defer close(r.result)
	buf := make([]byte, 0, len(p))
	dest := bytes.NewBuffer(buf)

	var compressor *flate.Writer
	var err error
	if len(prevTail) > 0 {
		compressor, err = flate.NewWriterDict(dest, z.level, prevTail)
	} else {
		compressor, err = flate.NewWriter(dest, z.level)
	}
	if err != nil {
		z.pushError(err)
		return
	}
	compressor.Write(p)

	err = compressor.Flush()
	if err != nil {
		z.pushError(err)
		return
	}
	if z.closed {
		err = compressor.Close()
		if err != nil {
			z.pushError(err)
			return
		}
	}
	// Read back buffer
	buf = dest.Bytes()
	r.result <- buf
}

// Flush flushes any pending compressed data to the underlying writer.
//
// It is useful mainly in compressed network protocols, to ensure that
// a remote reader has enough data to reconstruct a packet. Flush does
// not return until the data has been written. If the underlying
// writer returns an error, Flush returns that error.
//
// In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH.
func (z *Writer) Flush() error {
	if z.checkError() != nil {
		return z.err
	}
	if z.closed {
		return nil
	}
	if !z.wroteHeader {
		z.Write(nil)
		if z.err != nil {
			return z.err
		}
	}
	// We send current block to compression
	z.compressCurrent(true)
	if z.checkError() != nil {
		return z.err
	}

	return nil
}

// UncompressedSize will return the number of bytes written.
// pgzip only, not a function in the official gzip package.
func (z Writer) UncompressedSize() int {
	return z.size
}

// Close closes the Writer, flushing any unwritten data to the underlying
// io.Writer, but does not close the underlying io.Writer.
func (z *Writer) Close() error {
	if z.checkError() != nil {
		return z.err
	}
	if z.closed {
		return nil
	}

	z.closed = true
	if !z.wroteHeader {
		z.Write(nil)
		if z.err != nil {
			return z.err
		}
	}
	z.compressCurrent(true)
	if z.checkError() != nil {
		return z.err
	}
	close(z.results)
	put4(z.buf[0:4], z.digest.Sum32())
	put4(z.buf[4:8], uint32(z.size))
	_, z.err = z.w.Write(z.buf[0:8])
	return z.err
}
<commit_msg>Fix double close of error channel.<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package pgzip

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/klauspost/compress/flate"
	"github.com/klauspost/crc32"
	"hash"
	"io"
)

const (
	defaultBlockSize = 250000
	tailSize         = 16384
	defaultBlocks    = 16
)

// These constants are copied from the flate package, so that code that imports
// "compress/gzip" does not also have to import "compress/flate".
const (
	NoCompression       = flate.NoCompression
	BestSpeed           = flate.BestSpeed
	BestCompression     = flate.BestCompression
	DefaultCompression  = flate.DefaultCompression
	ConstantCompression = flate.ConstantCompression
)

// A Writer is an io.WriteCloser.
// Writes to a Writer are compressed and written to w.
type Writer struct {
	Header
	w             io.Writer
	level         int
	wroteHeader   bool
	blockSize     int
	blocks        int
	currentBuffer []byte
	prevTail      []byte
	digest        hash.Hash32
	size          int
	closed        bool
	buf           [10]byte
	err           error
	pushedErr     chan error
	results       chan result
}

type result struct {
	result        chan []byte
	notifyWritten chan struct{}
}

// Use SetConcurrency to finetune the concurrency level if needed.
//
// With this you can control the approximate size of your blocks,
// as well as how many you want to be processing in parallel.
//
// Default values for this is SetConcurrency(250000, 16),
// meaning blocks are split at 250000 bytes and up to 16 blocks
// can be processing at once before the writer blocks.
func (z *Writer) SetConcurrency(blockSize, blocks int) error {
	if blockSize <= tailSize {
		return fmt.Errorf("gzip: block size cannot be less than or equal to %d", tailSize)
	}
	if blocks <= 0 {
		return errors.New("gzip: blocks cannot be zero or less")
	}
	z.blockSize = blockSize
	z.results = make(chan result, blocks)
	z.blocks = blocks
	return nil
}

// NewWriter returns a new Writer.
// Writes to the returned writer are compressed and written to w.
//
// It is the caller's responsibility to call Close on the WriteCloser when done.
// Writes may be buffered and not flushed until Close.
//
// Callers that wish to set the fields in Writer.Header must do so before
// the first call to Write or Close. The Comment and Name header fields are
// UTF-8 strings in Go, but the underlying format requires NUL-terminated ISO
// 8859-1 (Latin-1). NUL or non-Latin-1 runes in those strings will lead to an
// error on Write.
func NewWriter(w io.Writer) *Writer {
	z, _ := NewWriterLevel(w, DefaultCompression)
	return z
}

// NewWriterLevel is like NewWriter but specifies the compression level instead
// of assuming DefaultCompression.
//
// The compression level can be DefaultCompression, NoCompression, or any
// integer value between BestSpeed and BestCompression inclusive. The error
// returned will be nil if the level is valid.
func NewWriterLevel(w io.Writer, level int) (*Writer, error) {
	if level < ConstantCompression || level > BestCompression {
		return nil, fmt.Errorf("gzip: invalid compression level: %d", level)
	}
	z := new(Writer)
	z.SetConcurrency(defaultBlockSize, defaultBlocks)
	z.init(w, level)
	return z, nil
}

// This function must be used by goroutines to set an
// error condition, since z.err access is restricted
// to the callers goruotine.
func (z Writer) pushError(err error) {
	z.pushedErr <- err
	close(z.pushedErr)
}

func (z *Writer) init(w io.Writer, level int) {
	digest := z.digest
	if digest != nil {
		digest.Reset()
	} else {
		digest = crc32.NewIEEE()
	}

	*z = Writer{
		Header: Header{
			OS: 255, // unknown
		},
		w:         w,
		level:     level,
		digest:    digest,
		pushedErr: make(chan error, 1),
		results:   make(chan result, z.blocks),
		blockSize: z.blockSize,
		blocks:    z.blocks,
	}
}

// Reset discards the Writer z's state and makes it equivalent to the
// result of its original state from NewWriter or NewWriterLevel, but
// writing to w instead. This permits reusing a Writer rather than
// allocating a new one.
func (z *Writer) Reset(w io.Writer) {
	if z.results != nil && !z.closed {
		close(z.results)
	}
	z.SetConcurrency(defaultBlockSize, defaultBlocks)
	z.init(w, z.level)
}

// GZIP (RFC 1952) is little-endian, unlike ZLIB (RFC 1950).
func put2(p []byte, v uint16) {
	p[0] = uint8(v >> 0)
	p[1] = uint8(v >> 8)
}

func put4(p []byte, v uint32) {
	p[0] = uint8(v >> 0)
	p[1] = uint8(v >> 8)
	p[2] = uint8(v >> 16)
	p[3] = uint8(v >> 24)
}

// writeBytes writes a length-prefixed byte slice to z.w.
func (z *Writer) writeBytes(b []byte) error {
	if len(b) > 0xffff {
		return errors.New("gzip.Write: Extra data is too large")
	}
	put2(z.buf[0:2], uint16(len(b)))
	_, err := z.w.Write(z.buf[0:2])
	if err != nil {
		return err
	}
	_, err = z.w.Write(b)
	return err
}

// writeString writes a UTF-8 string s in GZIP's format to z.w.
// GZIP (RFC 1952) specifies that strings are NUL-terminated ISO 8859-1 (Latin-1).
func (z *Writer) writeString(s string) (err error) {
	// GZIP stores Latin-1 strings; error if non-Latin-1; convert if non-ASCII.
	needconv := false
	for _, v := range s {
		if v == 0 || v > 0xff {
			return errors.New("gzip.Write: non-Latin-1 header string")
		}
		if v > 0x7f {
			needconv = true
		}
	}
	if needconv {
		b := make([]byte, 0, len(s))
		for _, v := range s {
			b = append(b, byte(v))
		}
		_, err = z.w.Write(b)
	} else {
		_, err = io.WriteString(z.w, s)
	}
	if err != nil {
		return err
	}
	// GZIP strings are NUL-terminated.
	z.buf[0] = 0
	_, err = z.w.Write(z.buf[0:1])
	return err
}

// compressCurrent will compress the data currently buffered
// This should only be called from the main writer/flush/closer
func (z *Writer) compressCurrent(flush bool) {
	r := result{}
	r.result = make(chan []byte, 1)
	r.notifyWritten = make(chan struct{}, 0)
	z.results <- r

	// If block given is more than twice the block size, split it.
	c := z.currentBuffer
	if len(c) > z.blockSize*2 {
		c = c[:z.blockSize]
		go compressBlock(c, z.prevTail, *z, r)
		z.prevTail = c[len(c)-tailSize:]
		z.currentBuffer = z.currentBuffer[z.blockSize:]
		z.compressCurrent(flush)
		// Last one flushes if needed
		return
	}

	go compressBlock(c, z.prevTail, *z, r)
	if len(c) > tailSize {
		z.prevTail = c[len(c)-tailSize:]
	} else {
		z.prevTail = nil
	}
	z.currentBuffer = make([]byte, 0, z.blockSize+(z.blockSize/4))

	// Wait if flushing
	if flush {
		_ = <-r.notifyWritten
	}
}

// Returns an error if it has been set.
// Cannot be used by functions that are from internal goroutines.
func (z *Writer) checkError() error {
	if z.err != nil {
		return z.err
	}
	select {
	case err := <-z.pushedErr:
		z.err = err
	default:
	}
	return z.err
}

// Write writes a compressed form of p to the underlying io.Writer. The
// compressed bytes are not necessarily flushed to output until
// the Writer is closed or Flush() is called.
//
// The function will return quickly, if there are unused buffers.
// The sent slice (p) is copied, and the caller is free to re-use the buffer
// when the function returns.
//
// Errors that occur during compression will be reported later, and a nil error
// does not signify that the compression succeeded (since it is most likely still running)
// That means that the call that returns an error may not be the call that caused it.
// Only Flush and Close functions are guaranteed to return any errors up to that point.
func (z *Writer) Write(p []byte) (int, error) {
	if z.checkError() != nil {
		return 0, z.err
	}
	// Write the GZIP header lazily.
	if !z.wroteHeader {
		z.wroteHeader = true
		z.buf[0] = gzipID1
		z.buf[1] = gzipID2
		z.buf[2] = gzipDeflate
		z.buf[3] = 0
		if z.Extra != nil {
			z.buf[3] |= 0x04
		}
		if z.Name != "" {
			z.buf[3] |= 0x08
		}
		if z.Comment != "" {
			z.buf[3] |= 0x10
		}
		put4(z.buf[4:8], uint32(z.ModTime.Unix()))
		if z.level == BestCompression {
			z.buf[8] = 2
		} else if z.level == BestSpeed {
			z.buf[8] = 4
		} else {
			z.buf[8] = 0
		}
		z.buf[9] = z.OS
		var n int
		n, z.err = z.w.Write(z.buf[0:10])
		if z.err != nil {
			return n, z.err
		}
		if z.Extra != nil {
			z.err = z.writeBytes(z.Extra)
			if z.err != nil {
				return n, z.err
			}
		}
		if z.Name != "" {
			z.err = z.writeString(z.Name)
			if z.err != nil {
				return n, z.err
			}
		}
		if z.Comment != "" {
			z.err = z.writeString(z.Comment)
			if z.err != nil {
				return n, z.err
			}
		}
		// Start receiving data from compressors
		go func() {
			listen := z.results
			for {
				r, ok := <-listen
				// If closed, we are finished.
				if !ok {
					return
				}
				buf := <-r.result
				n, err := z.w.Write(buf)
				if err != nil {
					z.pushError(err)
					close(r.notifyWritten)
					return
				}
				if n != len(buf) {
					z.pushError(fmt.Errorf("gzip: short write %d should be %d", n, len(buf)))
					close(r.notifyWritten)
					return
				}
				close(r.notifyWritten)
			}
		}()
		z.currentBuffer = make([]byte, 0, z.blockSize+(z.blockSize/4))
	}
	z.size += len(p)
	z.digest.Write(p)
	z.currentBuffer = append(z.currentBuffer, p...)
	if len(z.currentBuffer) >= z.blockSize {
		z.compressCurrent(false)
	}
	return len(p), z.err
}

// Step 1: compresses buffer to buffer
// Step 2: send writer to channel
// Step 3: Close result channel to indicate we are done
func compressBlock(p, prevTail []byte, z Writer, r result) {
	defer close(r.result)
	buf := make([]byte, 0, len(p))
	dest := bytes.NewBuffer(buf)

	var compressor *flate.Writer
	var err error
	if len(prevTail) > 0 {
		compressor, err = flate.NewWriterDict(dest, z.level, prevTail)
	} else {
		compressor, err = flate.NewWriter(dest, z.level)
	}
	if err != nil {
		z.pushError(err)
		return
	}
	compressor.Write(p)

	err = compressor.Flush()
	if err != nil {
		z.pushError(err)
		return
	}
	if z.closed {
		err = compressor.Close()
		if err != nil {
			z.pushError(err)
			return
		}
	}
	// Read back buffer
	buf = dest.Bytes()
	r.result <- buf
}

// Flush flushes any pending compressed data to the underlying writer.
//
// It is useful mainly in compressed network protocols, to ensure that
// a remote reader has enough data to reconstruct a packet. Flush does
// not return until the data has been written. If the underlying
// writer returns an error, Flush returns that error.
//
// In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH.
func (z *Writer) Flush() error {
	if z.checkError() != nil {
		return z.err
	}
	if z.closed {
		return nil
	}
	if !z.wroteHeader {
		z.Write(nil)
		if z.err != nil {
			return z.err
		}
	}
	// We send current block to compression
	z.compressCurrent(true)
	if z.checkError() != nil {
		return z.err
	}

	return nil
}

// UncompressedSize will return the number of bytes written.
// pgzip only, not a function in the official gzip package.
func (z Writer) UncompressedSize() int {
	return z.size
}

// Close closes the Writer, flushing any unwritten data to the underlying
// io.Writer, but does not close the underlying io.Writer.
func (z *Writer) Close() error {
	if z.checkError() != nil {
		return z.err
	}
	if z.closed {
		return nil
	}

	z.closed = true
	if !z.wroteHeader {
		z.Write(nil)
		if z.err != nil {
			return z.err
		}
	}
	z.compressCurrent(true)
	if z.checkError() != nil {
		return z.err
	}
	close(z.results)
	put4(z.buf[0:4], z.digest.Sum32())
	put4(z.buf[4:8], uint32(z.size))
	_, z.err = z.w.Write(z.buf[0:8])
	return z.err
}
<|endoftext|>


<commit_before>package gzip

import (
	"compress/gzip"
	"github.com/go-martini/martini"
	"net/http"
	"strings"
)

const (
	HeaderAcceptEncoding  = "Accept-Encoding"
	HeaderContentEncoding = "Content-Encoding"
	HeaderContentLength   = "Content-Length"
	HeaderContentType     = "Content-Type"
	HeaderVary            = "Vary"
)

var serveGzip = func(w http.ResponseWriter, r *http.Request, c martini.Context) {
	if !strings.Contains(r.Header.Get(HeaderAcceptEncoding), "gzip") {
		return
	}

	headers := w.Header()
	headers.Set(HeaderContentEncoding, "gzip")
	headers.Set(HeaderVary, HeaderAcceptEncoding)

	gz := gzip.NewWriter(w)
	defer gz.Close()

	gzw := gzipResponseWriter{gz, w.(martini.ResponseWriter)}
	c.MapTo(gzw, (*http.ResponseWriter)(nil))

	c.Next()

	// delete content length after we know we have been written to
	gzw.Header().Del("Content-Length")
}

// All returns a Handler that adds gzip compression to all requests
func All() martini.Handler {
	return serveGzip
}

type gzipResponseWriter struct {
	w *gzip.Writer
	martini.ResponseWriter
}

func (grw gzipResponseWriter) Write(p []byte) (int, error) {
	if len(grw.Header().Get(HeaderContentType)) == 0 {
		grw.Header().Set(HeaderContentType, http.DetectContentType(p))
	}

	return grw.w.Write(p)
}
<commit_msg>Added support for http.Hijacker interface<commit_after>package gzip

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

	"github.com/go-martini/martini"
)

const (
	HeaderAcceptEncoding  = "Accept-Encoding"
	HeaderContentEncoding = "Content-Encoding"
	HeaderContentLength   = "Content-Length"
	HeaderContentType     = "Content-Type"
	HeaderVary            = "Vary"
)

var serveGzip = func(w http.ResponseWriter, r *http.Request, c martini.Context) {
	if !strings.Contains(r.Header.Get(HeaderAcceptEncoding), "gzip") {
		return
	}

	headers := w.Header()
	headers.Set(HeaderContentEncoding, "gzip")
	headers.Set(HeaderVary, HeaderAcceptEncoding)

	gz := gzip.NewWriter(w)
	defer gz.Close()

	gzw := gzipResponseWriter{gz, w.(martini.ResponseWriter)}
	c.MapTo(gzw, (*http.ResponseWriter)(nil))

	c.Next()

	// delete content length after we know we have been written to
	gzw.Header().Del("Content-Length")
}

// All returns a Handler that adds gzip compression to all requests
func All() martini.Handler {
	return serveGzip
}

type gzipResponseWriter struct {
	w *gzip.Writer
	martini.ResponseWriter
}

func (grw gzipResponseWriter) Write(p []byte) (int, error) {
	if len(grw.Header().Get(HeaderContentType)) == 0 {
		grw.Header().Set(HeaderContentType, http.DetectContentType(p))
	}

	return grw.w.Write(p)
}

func (grw gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	hijacker, ok := grw.ResponseWriter.(http.Hijacker)
	if !ok {
		return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface")
	}
	return hijacker.Hijack()
}
<|endoftext|>


<commit_before>package torrent

import (
	"net"
	"time"

	"github.com/cenkalti/rain/internal/acceptor"
	"github.com/cenkalti/rain/internal/allocator"
	"github.com/cenkalti/rain/internal/announcer"
	"github.com/cenkalti/rain/internal/peer"
	"github.com/cenkalti/rain/internal/piece"
	"github.com/cenkalti/rain/internal/piecedownloader"
	"github.com/cenkalti/rain/internal/verifier"
)

func (t *torrent) start() {
	// Do not start if already started.
	if t.errC != nil {
		return
	}

	// Stop announcing Stopped event if in "Stopping" state.
	if t.stoppedEventAnnouncer != nil {
		t.stoppedEventAnnouncer.Close()
		t.stoppedEventAnnouncer = nil
	}

	t.log.Info("starting torrent")
	t.errC = make(chan error, 1)
	t.portC = make(chan int, 1)
	t.lastError = nil

	if t.info != nil {
		if t.pieces != nil {
			if t.bitfield != nil {
				t.startAcceptor()
				t.startAnnouncers()
				t.startPieceDownloaders()
				t.startUnchokeTimers()
			} else {
				t.startVerifier()
			}
		} else {
			t.startAllocator()
		}
	} else {
		t.startAcceptor()
		t.startAnnouncers()
		t.startInfoDownloaders()
	}

	t.startStatsWriter()
	t.startSpeedCounter()
}

func (t *torrent) startStatsWriter() {
	if t.statsWriteTicker != nil {
		return
	}
	t.statsWriteTicker = time.NewTicker(t.config.StatsWriteInterval)
	t.statsWriteTickerC = t.statsWriteTicker.C
}

func (t *torrent) startSpeedCounter() {
	if t.speedCounterTicker != nil {
		return
	}
	t.speedCounterTicker = time.NewTicker(5 * time.Second)
	t.speedCounterTickerC = t.speedCounterTicker.C
}

func (t *torrent) startVerifier() {
	if t.verifier != nil {
		panic("verifier exists")
	}
	t.verifier = verifier.New()
	go t.verifier.Run(t.pieces, t.verifierProgressC, t.verifierResultC)
}

func (t *torrent) startAllocator() {
	if t.allocator != nil {
		panic("allocator exists")
	}
	t.allocator = allocator.New()
	go t.allocator.Run(t.info, t.storage, t.allocatorProgressC, t.allocatorResultC)
}

func (t *torrent) startAnnouncers() {
	if len(t.announcers) > 0 {
		return
	}
	for _, tr := range t.trackers {
		an := announcer.NewPeriodicalAnnouncer(
			tr,
			t.config.TrackerNumWant,
			t.config.TrackerMinAnnounceInterval,
			t.announcerRequestC,
			t.completeC,
			t.addrsFromTrackers,
			t.log,
		)
		t.announcers = append(t.announcers, an)
		go an.Run()
	}
	if t.dhtNode != nil && t.dhtAnnouncer == nil {
		t.dhtAnnouncer = announcer.NewDHTAnnouncer()
		go t.dhtAnnouncer.Run(t.dhtNode.Announce, t.config.DHTAnnounceInterval, t.config.DHTMinAnnounceInterval, t.log)
	}
}

func (t *torrent) startAcceptor() {
	if t.acceptor != nil {
		return
	}
	listener, err := net.ListenTCP("tcp4", &net.TCPAddr{Port: t.port})
	if err != nil {
		t.log.Warningf("cannot listen port %d: %s", t.port, err)
	} else {
		t.log.Info("Listening peers on tcp://" + listener.Addr().String())
		t.port = listener.Addr().(*net.TCPAddr).Port
		t.portC <- t.port
		t.acceptor = acceptor.New(listener, t.incomingConnC, t.log)
		go t.acceptor.Run()
	}
}

func (t *torrent) startUnchokeTimers() {
	if t.unchokeTimer == nil {
		t.unchokeTimer = time.NewTicker(10 * time.Second)
		t.unchokeTimerC = t.unchokeTimer.C
	}
	if t.optimisticUnchokeTimer == nil {
		t.optimisticUnchokeTimer = time.NewTicker(30 * time.Second)
		t.optimisticUnchokeTimerC = t.optimisticUnchokeTimer.C
	}
}

func (t *torrent) startInfoDownloaders() {
	if t.info != nil {
		return
	}
	for len(t.infoDownloaders)-len(t.infoDownloadersSnubbed) < t.config.ParallelMetadataDownloads {
		id := t.nextInfoDownload()
		if id == nil {
			break
		}
		t.log.Debugln("downloading info from", id.Peer.String())
		t.infoDownloaders[id.Peer] = id
		id.RequestBlocks(t.config.RequestQueueLength)
		id.Peer.ResetSnubTimer()
	}
}

func (t *torrent) startPieceDownloaderFor(pe *peer.Peer) {
	if t.bitfield == nil {
		return
	}
	if t.pieces == nil {
		return
	}
	if t.completed {
		return
	}
	if t.ram == nil {
		t.startSinglePieceDownloader(pe)
		return
	}
	ok := t.ram.Request(string(t.peerID[:]), int64(t.info.PieceLength), t.ramNotifyC, t.doneC)
	if ok {
		t.startSinglePieceDownloader(pe)
	}
}

func (t *torrent) startPieceDownloaders() {
	if t.bitfield == nil {
		return
	}
	if t.pieces == nil {
		return
	}
	if t.completed {
		return
	}
	if t.ram == nil {
		t.startSinglePieceDownloader(nil)
		return
	}
	ok := t.ram.Request(string(t.peerID[:]), int64(t.info.PieceLength), t.ramNotifyC, t.doneC)
	if ok {
		t.startSinglePieceDownloader(nil)
	}
}

func (t *torrent) startSinglePieceDownloader(pe *peer.Peer) {
	var pi *piece.Piece
	if pe != nil {
		pi = t.piecePicker.PickFor(pe)
	} else {
		pi, pe = t.piecePicker.Pick()
	}
	if pi == nil || pe == nil {
		if t.ram != nil {
			t.ram.Release(int64(t.info.PieceLength))
		}
		return
	}
	pd := piecedownloader.New(pi, pe, t.piecePool.Get().([]byte))
	// t.log.Debugln("downloading piece", pd.Piece.Index, "from", pd.Peer.String())
	if _, ok := t.pieceDownloaders[pd.Peer]; ok {
		panic("peer already has a piece downloader")
	}
	t.pieceDownloaders[pd.Peer] = pd
	pd.Peer.Downloading = true
	pd.RequestBlocks(t.config.RequestQueueLength)
	pd.Peer.ResetSnubTimer()

	if t.ram == nil {
		t.startSinglePieceDownloader(pe)
		return
	}
	ok := t.ram.Request(string(t.peerID[:]), int64(t.info.PieceLength), t.ramNotifyC, t.doneC)
	if ok {
		t.startSinglePieceDownloader(pe)
	}
}
<commit_msg>do not start piece downloader if completed<commit_after>package torrent

import (
	"net"
	"time"

	"github.com/cenkalti/rain/internal/acceptor"
	"github.com/cenkalti/rain/internal/allocator"
	"github.com/cenkalti/rain/internal/announcer"
	"github.com/cenkalti/rain/internal/peer"
	"github.com/cenkalti/rain/internal/piece"
	"github.com/cenkalti/rain/internal/piecedownloader"
	"github.com/cenkalti/rain/internal/verifier"
)

func (t *torrent) start() {
	// Do not start if already started.
	if t.errC != nil {
		return
	}

	// Stop announcing Stopped event if in "Stopping" state.
	if t.stoppedEventAnnouncer != nil {
		t.stoppedEventAnnouncer.Close()
		t.stoppedEventAnnouncer = nil
	}

	t.log.Info("starting torrent")
	t.errC = make(chan error, 1)
	t.portC = make(chan int, 1)
	t.lastError = nil

	if t.info != nil {
		if t.pieces != nil {
			if t.bitfield != nil {
				t.startAcceptor()
				t.startAnnouncers()
				t.startPieceDownloaders()
				t.startUnchokeTimers()
			} else {
				t.startVerifier()
			}
		} else {
			t.startAllocator()
		}
	} else {
		t.startAcceptor()
		t.startAnnouncers()
		t.startInfoDownloaders()
	}

	t.startStatsWriter()
	t.startSpeedCounter()
}

func (t *torrent) startStatsWriter() {
	if t.statsWriteTicker != nil {
		return
	}
	t.statsWriteTicker = time.NewTicker(t.config.StatsWriteInterval)
	t.statsWriteTickerC = t.statsWriteTicker.C
}

func (t *torrent) startSpeedCounter() {
	if t.speedCounterTicker != nil {
		return
	}
	t.speedCounterTicker = time.NewTicker(5 * time.Second)
	t.speedCounterTickerC = t.speedCounterTicker.C
}

func (t *torrent) startVerifier() {
	if t.verifier != nil {
		panic("verifier exists")
	}
	t.verifier = verifier.New()
	go t.verifier.Run(t.pieces, t.verifierProgressC, t.verifierResultC)
}

func (t *torrent) startAllocator() {
	if t.allocator != nil {
		panic("allocator exists")
	}
	t.allocator = allocator.New()
	go t.allocator.Run(t.info, t.storage, t.allocatorProgressC, t.allocatorResultC)
}

func (t *torrent) startAnnouncers() {
	if len(t.announcers) > 0 {
		return
	}
	for _, tr := range t.trackers {
		an := announcer.NewPeriodicalAnnouncer(
			tr,
			t.config.TrackerNumWant,
			t.config.TrackerMinAnnounceInterval,
			t.announcerRequestC,
			t.completeC,
			t.addrsFromTrackers,
			t.log,
		)
		t.announcers = append(t.announcers, an)
		go an.Run()
	}
	if t.dhtNode != nil && t.dhtAnnouncer == nil {
		t.dhtAnnouncer = announcer.NewDHTAnnouncer()
		go t.dhtAnnouncer.Run(t.dhtNode.Announce, t.config.DHTAnnounceInterval, t.config.DHTMinAnnounceInterval, t.log)
	}
}

func (t *torrent) startAcceptor() {
	if t.acceptor != nil {
		return
	}
	listener, err := net.ListenTCP("tcp4", &net.TCPAddr{Port: t.port})
	if err != nil {
		t.log.Warningf("cannot listen port %d: %s", t.port, err)
	} else {
		t.log.Info("Listening peers on tcp://" + listener.Addr().String())
		t.port = listener.Addr().(*net.TCPAddr).Port
		t.portC <- t.port
		t.acceptor = acceptor.New(listener, t.incomingConnC, t.log)
		go t.acceptor.Run()
	}
}

func (t *torrent) startUnchokeTimers() {
	if t.unchokeTimer == nil {
		t.unchokeTimer = time.NewTicker(10 * time.Second)
		t.unchokeTimerC = t.unchokeTimer.C
	}
	if t.optimisticUnchokeTimer == nil {
		t.optimisticUnchokeTimer = time.NewTicker(30 * time.Second)
		t.optimisticUnchokeTimerC = t.optimisticUnchokeTimer.C
	}
}

func (t *torrent) startInfoDownloaders() {
	if t.info != nil {
		return
	}
	for len(t.infoDownloaders)-len(t.infoDownloadersSnubbed) < t.config.ParallelMetadataDownloads {
		id := t.nextInfoDownload()
		if id == nil {
			break
		}
		t.log.Debugln("downloading info from", id.Peer.String())
		t.infoDownloaders[id.Peer] = id
		id.RequestBlocks(t.config.RequestQueueLength)
		id.Peer.ResetSnubTimer()
	}
}

func (t *torrent) startPieceDownloaderFor(pe *peer.Peer) {
	if t.bitfield == nil {
		return
	}
	if t.pieces == nil {
		return
	}
	if t.completed {
		return
	}
	if t.ram == nil {
		t.startSinglePieceDownloader(pe)
		return
	}
	ok := t.ram.Request(string(t.peerID[:]), int64(t.info.PieceLength), t.ramNotifyC, t.doneC)
	if ok {
		t.startSinglePieceDownloader(pe)
	}
}

func (t *torrent) startPieceDownloaders() {
	if t.bitfield == nil {
		return
	}
	if t.pieces == nil {
		return
	}
	if t.completed {
		return
	}
	if t.ram == nil {
		t.startSinglePieceDownloader(nil)
		return
	}
	ok := t.ram.Request(string(t.peerID[:]), int64(t.info.PieceLength), t.ramNotifyC, t.doneC)
	if ok {
		t.startSinglePieceDownloader(nil)
	}
}

func (t *torrent) startSinglePieceDownloader(pe *peer.Peer) {
	if t.completed {
		return
	}
	var pi *piece.Piece
	if pe != nil {
		pi = t.piecePicker.PickFor(pe)
	} else {
		pi, pe = t.piecePicker.Pick()
	}
	if pi == nil || pe == nil {
		if t.ram != nil {
			t.ram.Release(int64(t.info.PieceLength))
		}
		return
	}
	pd := piecedownloader.New(pi, pe, t.piecePool.Get().([]byte))
	// t.log.Debugln("downloading piece", pd.Piece.Index, "from", pd.Peer.String())
	if _, ok := t.pieceDownloaders[pd.Peer]; ok {
		panic("peer already has a piece downloader")
	}
	t.pieceDownloaders[pd.Peer] = pd
	pd.Peer.Downloading = true
	pd.RequestBlocks(t.config.RequestQueueLength)
	pd.Peer.ResetSnubTimer()

	if t.ram == nil {
		t.startSinglePieceDownloader(pe)
		return
	}
	ok := t.ram.Request(string(t.peerID[:]), int64(t.info.PieceLength), t.ramNotifyC, t.doneC)
	if ok {
		t.startSinglePieceDownloader(pe)
	}
}
<|endoftext|>


<commit_before>package torrent

import "math"

// Stats contains statistics about Torrent.
type Stats struct {
	// Status of the torrent.
	Status Status
	// Contains the error if torrent is stopped unexpectedly.
	Error  error
	Pieces struct {
		Have      uint32
		Missing   uint32
		Available uint32
		Total     uint32
	}
	Bytes struct {
		// Bytes that are downloaded and passed hash check.
		Complete int64
		// The number of bytes that is needed to complete all missing pieces.
		Incomplete int64
		// The number of total bytes of files in torrent.  Total = Complete + Incomplete
		Total int64
		// Downloaded is the number of bytes downloaded from swarm.
		// Because some pieces may be downloaded more than once, this number may be greater than BytesCompleted returns.
		// TODO put into resume
		Downloaded int64
		// Protocol messages are not included, only piece data is counted.
		Uploaded int64
		Wasted   int64
		// BytesUploaded is the number of bytes uploaded to the swarm.
		// TODO BytesUploaded   int64
	}
	Peers struct {
		Connected struct {
			// Number of peers that are connected, handshaked and ready to send and receive messages.
			// ConnectedPeers = IncomingPeers + OutgoingPeers
			Total int
			// Number of peers that have connected to us.
			Incoming int
			// Number of peers that we have connected to.
			Outgoing int
		}
		Handshake struct {
			// Number of peers that are not handshaked yet.
			Total int
			// Number of incoming peers in handshake state.
			Incoming int
			// Number of outgoing peers in handshake state.
			Outgoing int
		}
		// Number of peer addresses that are ready to be connected.
		Ready int
	}
	Downloads struct {
		Piece struct {
			// Number of active piece downloads.
			Total int
			// Number of pieces that are being downloaded normally.
			Running int
			// Number of pieces that are being downloaded too slow.
			Snubbed int
			// Number of piece downloads in choked state.
			Choked int
		}
		Metadata struct {
			// Number of active metadata downloads.
			Total int
			// Number of peers that uploading too slow.
			Snubbed int
			// Number of peers that are being downloaded normally.
			Running int
		}
	}
	// Name can change after metadata is downloaded.
	Name string
	// Is private torrent?
	Private bool
	// Length of a single piece.
	PieceLength uint32
}

func (t *Torrent) stats() Stats {
	var s Stats
	s.Status = t.status()
	s.Error = t.lastError
	s.Peers.Ready = t.addrList.Len()
	s.Peers.Handshake.Incoming = len(t.incomingHandshakers)
	s.Peers.Handshake.Outgoing = len(t.outgoingHandshakers)
	s.Peers.Handshake.Total = len(t.incomingHandshakers) + len(t.outgoingHandshakers)
	s.Peers.Connected.Total = len(t.peers)
	s.Peers.Connected.Incoming = len(t.incomingPeers)
	s.Peers.Connected.Outgoing = len(t.outgoingPeers)
	s.Downloads.Metadata.Total = len(t.infoDownloaders)
	s.Downloads.Metadata.Snubbed = len(t.infoDownloadersSnubbed)
	s.Downloads.Metadata.Running = len(t.infoDownloaders) - len(t.infoDownloadersSnubbed)
	s.Downloads.Piece.Total = len(t.pieceDownloaders)
	s.Downloads.Piece.Snubbed = len(t.pieceDownloadersSnubbed)
	s.Downloads.Piece.Choked = len(t.pieceDownloadersChoked)
	s.Downloads.Piece.Running = len(t.pieceDownloaders) - len(t.pieceDownloadersChoked) - len(t.pieceDownloadersSnubbed)
	s.Pieces.Available = t.avaliablePieceCount()
	s.Bytes.Downloaded = t.bytesDownloaded
	s.Bytes.Uploaded = t.bytesUploaded
	s.Bytes.Wasted = t.bytesWasted

	if t.info != nil {
		s.Bytes.Total = t.info.TotalLength
		s.Bytes.Complete = t.bytesComplete()
		s.Bytes.Incomplete = s.Bytes.Total - s.Bytes.Complete

		s.Name = t.info.Name
		s.Private = (t.info.Private == 1)
		s.PieceLength = t.info.PieceLength
	} else {
		// Some trackers don't send any peer address if don't tell we have missing bytes.
		s.Bytes.Incomplete = math.MaxUint32

		s.Name = t.name
	}
	if t.bitfield != nil {
		s.Pieces.Total = t.bitfield.Len()
		s.Pieces.Have = t.bitfield.Count()
		s.Pieces.Missing = s.Pieces.Total - s.Pieces.Have
	}
	return s
}

func (t *Torrent) avaliablePieceCount() uint32 {
	var n uint32
	for _, pi := range t.pieces {
		if len(pi.HavingPeers) > 0 {
			n++
		}
	}
	return n
}

func (t *Torrent) bytesComplete() int64 {
	if t.bitfield == nil || len(t.pieces) == 0 {
		return 0
	}
	n := int64(t.info.PieceLength) * int64(t.bitfield.Count())
	if t.bitfield.Test(t.bitfield.Len() - 1) {
		n -= int64(t.info.PieceLength)
		n += int64(t.pieces[t.bitfield.Len()-1].Length)
	}
	return n
}
<commit_msg>make stats struct json compatible<commit_after>package torrent

import "math"

// Stats contains statistics about Torrent.
type Stats struct {
	// Status of the torrent.
	Status Status
	// Contains the error message if torrent is stopped unexpectedly.
	Error  *string
	Pieces struct {
		Have      uint32
		Missing   uint32
		Available uint32
		Total     uint32
	}
	Bytes struct {
		// Bytes that are downloaded and passed hash check.
		Complete int64
		// The number of bytes that is needed to complete all missing pieces.
		Incomplete int64
		// The number of total bytes of files in torrent.  Total = Complete + Incomplete
		Total int64
		// Downloaded is the number of bytes downloaded from swarm.
		// Because some pieces may be downloaded more than once, this number may be greater than BytesCompleted returns.
		// TODO put into resume
		Downloaded int64
		// Protocol messages are not included, only piece data is counted.
		Uploaded int64
		Wasted   int64
		// BytesUploaded is the number of bytes uploaded to the swarm.
		// TODO BytesUploaded   int64
	}
	Peers struct {
		Connected struct {
			// Number of peers that are connected, handshaked and ready to send and receive messages.
			// ConnectedPeers = IncomingPeers + OutgoingPeers
			Total int
			// Number of peers that have connected to us.
			Incoming int
			// Number of peers that we have connected to.
			Outgoing int
		}
		Handshake struct {
			// Number of peers that are not handshaked yet.
			Total int
			// Number of incoming peers in handshake state.
			Incoming int
			// Number of outgoing peers in handshake state.
			Outgoing int
		}
		// Number of peer addresses that are ready to be connected.
		Ready int
	}
	Downloads struct {
		Piece struct {
			// Number of active piece downloads.
			Total int
			// Number of pieces that are being downloaded normally.
			Running int
			// Number of pieces that are being downloaded too slow.
			Snubbed int
			// Number of piece downloads in choked state.
			Choked int
		}
		Metadata struct {
			// Number of active metadata downloads.
			Total int
			// Number of peers that uploading too slow.
			Snubbed int
			// Number of peers that are being downloaded normally.
			Running int
		}
	}
	// Name can change after metadata is downloaded.
	Name string
	// Is private torrent?
	Private bool
	// Length of a single piece.
	PieceLength uint32
}

func (t *Torrent) stats() Stats {
	var s Stats
	s.Status = t.status()
	if t.lastError != nil {
		errStr := t.lastError.Error()
		s.Error = &errStr
	} else {
		s.Error = nil
	}
	s.Peers.Ready = t.addrList.Len()
	s.Peers.Handshake.Incoming = len(t.incomingHandshakers)
	s.Peers.Handshake.Outgoing = len(t.outgoingHandshakers)
	s.Peers.Handshake.Total = len(t.incomingHandshakers) + len(t.outgoingHandshakers)
	s.Peers.Connected.Total = len(t.peers)
	s.Peers.Connected.Incoming = len(t.incomingPeers)
	s.Peers.Connected.Outgoing = len(t.outgoingPeers)
	s.Downloads.Metadata.Total = len(t.infoDownloaders)
	s.Downloads.Metadata.Snubbed = len(t.infoDownloadersSnubbed)
	s.Downloads.Metadata.Running = len(t.infoDownloaders) - len(t.infoDownloadersSnubbed)
	s.Downloads.Piece.Total = len(t.pieceDownloaders)
	s.Downloads.Piece.Snubbed = len(t.pieceDownloadersSnubbed)
	s.Downloads.Piece.Choked = len(t.pieceDownloadersChoked)
	s.Downloads.Piece.Running = len(t.pieceDownloaders) - len(t.pieceDownloadersChoked) - len(t.pieceDownloadersSnubbed)
	s.Pieces.Available = t.avaliablePieceCount()
	s.Bytes.Downloaded = t.bytesDownloaded
	s.Bytes.Uploaded = t.bytesUploaded
	s.Bytes.Wasted = t.bytesWasted

	if t.info != nil {
		s.Bytes.Total = t.info.TotalLength
		s.Bytes.Complete = t.bytesComplete()
		s.Bytes.Incomplete = s.Bytes.Total - s.Bytes.Complete

		s.Name = t.info.Name
		s.Private = (t.info.Private == 1)
		s.PieceLength = t.info.PieceLength
	} else {
		// Some trackers don't send any peer address if don't tell we have missing bytes.
		s.Bytes.Incomplete = math.MaxUint32

		s.Name = t.name
	}
	if t.bitfield != nil {
		s.Pieces.Total = t.bitfield.Len()
		s.Pieces.Have = t.bitfield.Count()
		s.Pieces.Missing = s.Pieces.Total - s.Pieces.Have
	}
	return s
}

func (t *Torrent) avaliablePieceCount() uint32 {
	var n uint32
	for _, pi := range t.pieces {
		if len(pi.HavingPeers) > 0 {
			n++
		}
	}
	return n
}

func (t *Torrent) bytesComplete() int64 {
	if t.bitfield == nil || len(t.pieces) == 0 {
		return 0
	}
	n := int64(t.info.PieceLength) * int64(t.bitfield.Count())
	if t.bitfield.Test(t.bitfield.Len() - 1) {
		n -= int64(t.info.PieceLength)
		n += int64(t.pieces[t.bitfield.Len()-1].Length)
	}
	return n
}
<|endoftext|>


<commit_before>package torrent

import "math"

// Stats contains statistics about Torrent.
type Stats struct {
	// Status of the torrent.
	Status Status
	// Contains the error message if torrent is stopped unexpectedly.
	Error  *string
	Pieces struct {
		Have      uint32
		Missing   uint32
		Available uint32
		Total     uint32
	}
	Bytes struct {
		// Bytes that are downloaded and passed hash check.
		Complete int64
		// The number of bytes that is needed to complete all missing pieces.
		Incomplete int64
		// The number of total bytes of files in torrent.  Total = Complete + Incomplete
		Total int64
		// Downloaded is the number of bytes downloaded from swarm.
		// Because some pieces may be downloaded more than once, this number may be greater than BytesCompleted returns.
		// TODO put into resume
		Downloaded int64
		// Protocol messages are not included, only piece data is counted.
		Uploaded int64
		Wasted   int64
		// BytesUploaded is the number of bytes uploaded to the swarm.
		// TODO BytesUploaded   int64
	}
	Peers struct {
		Connected struct {
			// Number of peers that are connected, handshaked and ready to send and receive messages.
			// ConnectedPeers = IncomingPeers + OutgoingPeers
			Total int
			// Number of peers that have connected to us.
			Incoming int
			// Number of peers that we have connected to.
			Outgoing int
		}
		Handshake struct {
			// Number of peers that are not handshaked yet.
			Total int
			// Number of incoming peers in handshake state.
			Incoming int
			// Number of outgoing peers in handshake state.
			Outgoing int
		}
		// Number of peer addresses that are ready to be connected.
		Ready int
	}
	Downloads struct {
		Piece struct {
			// Number of active piece downloads.
			Total int
			// Number of pieces that are being downloaded normally.
			Running int
			// Number of pieces that are being downloaded too slow.
			Snubbed int
			// Number of piece downloads in choked state.
			Choked int
		}
		Metadata struct {
			// Number of active metadata downloads.
			Total int
			// Number of peers that uploading too slow.
			Snubbed int
			// Number of peers that are being downloaded normally.
			Running int
		}
	}
	// Name can change after metadata is downloaded.
	Name string
	// Is private torrent?
	Private bool
	// Length of a single piece.
	PieceLength uint32
}

func (t *Torrent) stats() Stats {
	var s Stats
	s.Status = t.status()
	if t.lastError != nil {
		errStr := t.lastError.Error()
		s.Error = &errStr
	} else {
		s.Error = nil
	}
	s.Peers.Ready = t.addrList.Len()
	s.Peers.Handshake.Incoming = len(t.incomingHandshakers)
	s.Peers.Handshake.Outgoing = len(t.outgoingHandshakers)
	s.Peers.Handshake.Total = len(t.incomingHandshakers) + len(t.outgoingHandshakers)
	s.Peers.Connected.Total = len(t.peers)
	s.Peers.Connected.Incoming = len(t.incomingPeers)
	s.Peers.Connected.Outgoing = len(t.outgoingPeers)
	s.Downloads.Metadata.Total = len(t.infoDownloaders)
	s.Downloads.Metadata.Snubbed = len(t.infoDownloadersSnubbed)
	s.Downloads.Metadata.Running = len(t.infoDownloaders) - len(t.infoDownloadersSnubbed)
	s.Downloads.Piece.Total = len(t.pieceDownloaders)
	s.Downloads.Piece.Snubbed = len(t.pieceDownloadersSnubbed)
	s.Downloads.Piece.Choked = len(t.pieceDownloadersChoked)
	s.Downloads.Piece.Running = len(t.pieceDownloaders) - len(t.pieceDownloadersChoked) - len(t.pieceDownloadersSnubbed)
	s.Pieces.Available = t.avaliablePieceCount()
	s.Bytes.Downloaded = t.bytesDownloaded
	s.Bytes.Uploaded = t.bytesUploaded
	s.Bytes.Wasted = t.bytesWasted

	if t.info != nil {
		s.Bytes.Total = t.info.TotalLength
		s.Bytes.Complete = t.bytesComplete()
		s.Bytes.Incomplete = s.Bytes.Total - s.Bytes.Complete

		s.Name = t.info.Name
		s.Private = (t.info.Private == 1)
		s.PieceLength = t.info.PieceLength
	} else {
		// Some trackers don't send any peer address if don't tell we have missing bytes.
		s.Bytes.Incomplete = math.MaxUint32

		s.Name = t.name
	}
	if t.bitfield != nil {
		s.Pieces.Total = t.bitfield.Len()
		s.Pieces.Have = t.bitfield.Count()
		s.Pieces.Missing = s.Pieces.Total - s.Pieces.Have
	}
	return s
}

func (t *Torrent) avaliablePieceCount() uint32 {
	var n uint32
	for _, pi := range t.pieces {
		if len(pi.HavingPeers) > 0 {
			n++
		}
	}
	return n
}

func (t *Torrent) bytesComplete() int64 {
	if t.bitfield == nil || len(t.pieces) == 0 {
		return 0
	}
	n := int64(t.info.PieceLength) * int64(t.bitfield.Count())
	if t.bitfield.Test(t.bitfield.Len() - 1) {
		n -= int64(t.info.PieceLength)
		n += int64(t.pieces[t.bitfield.Len()-1].Length)
	}
	return n
}
<commit_msg>add todo<commit_after>package torrent

import "math"

// Stats contains statistics about Torrent.
type Stats struct {
	// Status of the torrent.
	Status Status
	// Contains the error message if torrent is stopped unexpectedly.
	Error  *string
	Pieces struct {
		Have      uint32
		Missing   uint32
		Available uint32
		Total     uint32
	}
	Bytes struct {
		// Bytes that are downloaded and passed hash check.
		Complete int64
		// The number of bytes that is needed to complete all missing pieces.
		Incomplete int64
		// The number of total bytes of files in torrent.  Total = Complete + Incomplete
		Total int64
		// Downloaded is the number of bytes downloaded from swarm.
		// Because some pieces may be downloaded more than once, this number may be greater than BytesCompleted returns.
		// TODO put into resume
		Downloaded int64
		// Protocol messages are not included, only piece data is counted.
		Uploaded int64
		Wasted   int64
		// BytesUploaded is the number of bytes uploaded to the swarm.
		// TODO BytesUploaded   int64
	}
	Peers struct {
		Connected struct {
			// Number of peers that are connected, handshaked and ready to send and receive messages.
			// ConnectedPeers = IncomingPeers + OutgoingPeers
			Total int
			// Number of peers that have connected to us.
			Incoming int
			// Number of peers that we have connected to.
			Outgoing int
		}
		Handshake struct {
			// Number of peers that are not handshaked yet.
			Total int
			// Number of incoming peers in handshake state.
			Incoming int
			// Number of outgoing peers in handshake state.
			Outgoing int
		}
		// Number of peer addresses that are ready to be connected.
		Ready int
	}
	Downloads struct {
		Piece struct {
			// Number of active piece downloads.
			Total int
			// Number of pieces that are being downloaded normally.
			Running int
			// Number of pieces that are being downloaded too slow.
			Snubbed int
			// Number of piece downloads in choked state.
			Choked int
		}
		Metadata struct {
			// Number of active metadata downloads.
			Total int
			// Number of peers that uploading too slow.
			Snubbed int
			// Number of peers that are being downloaded normally.
			Running int
		}
	}
	// Name can change after metadata is downloaded.
	Name string
	// Is private torrent?
	Private bool
	// Length of a single piece.
	PieceLength uint32
}

func (t *Torrent) stats() Stats {
	var s Stats
	s.Status = t.status()
	if t.lastError != nil {
		errStr := t.lastError.Error()
		s.Error = &errStr
	} else {
		s.Error = nil
	}
	s.Peers.Ready = t.addrList.Len()
	s.Peers.Handshake.Incoming = len(t.incomingHandshakers)
	s.Peers.Handshake.Outgoing = len(t.outgoingHandshakers)
	s.Peers.Handshake.Total = len(t.incomingHandshakers) + len(t.outgoingHandshakers)
	s.Peers.Connected.Total = len(t.peers)
	s.Peers.Connected.Incoming = len(t.incomingPeers)
	s.Peers.Connected.Outgoing = len(t.outgoingPeers)
	s.Downloads.Metadata.Total = len(t.infoDownloaders)
	s.Downloads.Metadata.Snubbed = len(t.infoDownloadersSnubbed)
	s.Downloads.Metadata.Running = len(t.infoDownloaders) - len(t.infoDownloadersSnubbed)
	s.Downloads.Piece.Total = len(t.pieceDownloaders)
	s.Downloads.Piece.Snubbed = len(t.pieceDownloadersSnubbed)
	s.Downloads.Piece.Choked = len(t.pieceDownloadersChoked)
	s.Downloads.Piece.Running = len(t.pieceDownloaders) - len(t.pieceDownloadersChoked) - len(t.pieceDownloadersSnubbed)
	s.Pieces.Available = t.avaliablePieceCount()
	s.Bytes.Downloaded = t.bytesDownloaded
	s.Bytes.Uploaded = t.bytesUploaded
	s.Bytes.Wasted = t.bytesWasted

	if t.info != nil {
		s.Bytes.Total = t.info.TotalLength
		s.Bytes.Complete = t.bytesComplete()
		s.Bytes.Incomplete = s.Bytes.Total - s.Bytes.Complete

		s.Name = t.info.Name
		s.Private = (t.info.Private == 1)
		s.PieceLength = t.info.PieceLength
	} else {
		// Some trackers don't send any peer address if don't tell we have missing bytes.
		s.Bytes.Incomplete = math.MaxUint32

		s.Name = t.name
	}
	if t.bitfield != nil {
		s.Pieces.Total = t.bitfield.Len()
		s.Pieces.Have = t.bitfield.Count()
		s.Pieces.Missing = s.Pieces.Total - s.Pieces.Have
	}
	return s
}

func (t *Torrent) avaliablePieceCount() uint32 {
	var n uint32
	// TODO eliminate for loop in stats
	for _, pi := range t.pieces {
		if len(pi.HavingPeers) > 0 {
			n++
		}
	}
	return n
}

func (t *Torrent) bytesComplete() int64 {
	if t.bitfield == nil || len(t.pieces) == 0 {
		return 0
	}
	n := int64(t.info.PieceLength) * int64(t.bitfield.Count())
	if t.bitfield.Test(t.bitfield.Len() - 1) {
		n -= int64(t.info.PieceLength)
		n += int64(t.pieces[t.bitfield.Len()-1].Length)
	}
	return n
}
<|endoftext|>


<commit_before>// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

// Package grpool implements a goroutine reusable pool.
package grpool

import (
	"github.com/gogf/gf/g/container/glist"
	"github.com/gogf/gf/g/container/gtype"
)

// Goroutine Pool
type Pool struct {
    limit   int           // Max goroutine count limit.
    count   *gtype.Int    // Current running goroutine count.
    list    *glist.List   // Job list for asynchronous job adding purpose.
    closed  *gtype.Bool   // Is pool closed or not.
    workers chan struct{} // Goroutine workers using channel to implements blocking feature.
}

// Default goroutine pool.
var pool = New()

// New creates and returns a new goroutine pool object.
// The param <limit> is used to limit the max goroutine count,
// which is not limited in default.
func New(limit...int) *Pool {
    p := &Pool {
	    limit  : -1,
        count  : gtype.NewInt(),
        list   : glist.New(),
        closed : gtype.NewBool(),
    }
    if len(limit) > 0 && limit[0] > 0 {
    	p.limit = limit[0]
    }
    return p
}

// Add pushes a new job to the pool using default goroutine pool.
// The job will be executed asynchronously.
func Add(f func()) {
	pool.Add(f)
}

// Size returns current goroutine count of default goroutine pool.
func Size() int {
    return pool.count.Val()
}

// Jobs returns current job count of default goroutine pool.
func Jobs() int {
    return pool.list.Len()
}

// Add pushes a new job to the pool.
// The job will be executed asynchronously.
func (p *Pool) Add(f func()) {
    p.list.PushFront(f)
    // check whether to create a new goroutine or not.
    if p.count.Val() == p.limit {
		return
    }
	// ensure atomicity.
	if p.limit != -1 && p.count.Add(1) > p.limit {
		p.count.Add(-1)
		return
	}
    // fork a new goroutine to consume the job list.
	p.fork()
}

// Size returns current goroutine count of the pool.
func (p *Pool) Size() int {
    return p.count.Val()
}

// Jobs returns current job count of the pool.
func (p *Pool) Jobs() int {
    return p.list.Size()
}

// fork creates a new goroutine pool.
func (p *Pool) fork() {
    go func() {
    	defer p.count.Add(-1)
    	job := (interface{})(nil)
        for !p.closed.Val() {
        	if job = p.list.PopBack(); job != nil {
		        job.(func())()
	        } else {
	        	return
	        }
        }
    }()
}

// Close closes the goroutine pool, which makes all goroutines exit.
func (p *Pool) Close() {
	p.closed.Set(true)
}<commit_msg>improve grpool<commit_after>// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

// Package grpool implements a goroutine reusable pool.
package grpool

import (
	"github.com/gogf/gf/g/container/glist"
	"github.com/gogf/gf/g/container/gtype"
)

// Goroutine Pool
type Pool struct {
    limit  int           // Max goroutine count limit.
    count  *gtype.Int    // Current running goroutine count.
    list   *glist.List   // Job list for asynchronous job adding purpose.
    closed *gtype.Bool   // Is pool closed or not.
}

// Default goroutine pool.
var pool = New()

// New creates and returns a new goroutine pool object.
// The param <limit> is used to limit the max goroutine count,
// which is not limited in default.
func New(limit...int) *Pool {
    p := &Pool {
	    limit  : -1,
        count  : gtype.NewInt(),
        list   : glist.New(),
        closed : gtype.NewBool(),
    }
    if len(limit) > 0 && limit[0] > 0 {
    	p.limit = limit[0]
    }
    return p
}

// Add pushes a new job to the pool using default goroutine pool.
// The job will be executed asynchronously.
func Add(f func()) {
	pool.Add(f)
}

// Size returns current goroutine count of default goroutine pool.
func Size() int {
    return pool.count.Val()
}

// Jobs returns current job count of default goroutine pool.
func Jobs() int {
    return pool.list.Len()
}

// Add pushes a new job to the pool.
// The job will be executed asynchronously.
func (p *Pool) Add(f func()) {
    p.list.PushFront(f)
    // check whether to create a new goroutine or not.
    if p.count.Val() == p.limit {
		return
    }
	// ensure atomicity.
	if p.limit != -1 && p.count.Add(1) > p.limit {
		p.count.Add(-1)
		return
	}
    // fork a new goroutine to consume the job list.
	p.fork()
}

// Size returns current goroutine count of the pool.
func (p *Pool) Size() int {
    return p.count.Val()
}

// Jobs returns current job count of the pool.
func (p *Pool) Jobs() int {
    return p.list.Size()
}

// fork creates a new goroutine pool.
func (p *Pool) fork() {
    go func() {
    	defer p.count.Add(-1)
    	job := (interface{})(nil)
        for !p.closed.Val() {
        	if job = p.list.PopBack(); job != nil {
		        job.(func())()
	        } else {
	        	return
	        }
        }
    }()
}

// Close closes the goroutine pool, which makes all goroutines exit.
func (p *Pool) Close() {
	p.closed.Set(true)
}<|endoftext|>


<commit_before>/*

	Autoreader is a simple program, designed to be run from go:generate, that
	helps generate the annoying boilerplate to implement
	boardgame.PropertyReader and boardgame.PropertyReadSetter.

	Autoreader processes a package of go files, searching for structs that
	have a comment immediately above their declaration that begins with
	"+autoreader". For each such struct, it creates a Reader() and
	PropertyReader() method that just use boardgame.DefaultReader and
	boardgame.DefaultReadSetter.

	If you want only a reader or only a readsetter for a given struct, include
	the keyword "reader" or "readsetter", like so: "+autoreader reader"

	You can configure which package to process and where to write output via
	command-line flags. By default it processes the current package and writes
	its output to auto_reader.go, overwriting whatever file was there before.
	See command-line options by passing -h.

	The defaults are set reasonably so that you can use go:generate very
	easily. See examplepkg/ for a very simple example.

*/
package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"github.com/MarcGrol/golangAnnotations/parser"
	"go/format"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"text/template"
)

var headerTemplate *template.Template
var structHeaderTemplate *template.Template
var readerTemplate *template.Template
var readSetterTemplate *template.Template

const magicDocLinePrefix = "+autoreader"

type appOptions struct {
	OutputFile       string
	PackageDirectory string
	PrintToConsole   bool
	Help             bool
	flagSet          *flag.FlagSet
}

type templateConfig struct {
	FirstLetter string
	StructName  string
}

func init() {
	headerTemplate = template.Must(template.New("header").Parse(headerTemplateText))
	structHeaderTemplate = template.Must(template.New("structHeader").Parse(structHeaderTemplateText))
	readerTemplate = template.Must(template.New("reader").Parse(readerTemplateText))
	readSetterTemplate = template.Must(template.New("readsetter").Parse(readSetterTemplateText))
}

func defineFlags(options *appOptions) {
	options.flagSet.StringVar(&options.OutputFile, "out", "auto_reader.go", "Defines which file to render output to. WARNING: it will be overwritten!")
	options.flagSet.StringVar(&options.PackageDirectory, "pkg", ".", "Which package to process")
	options.flagSet.BoolVar(&options.Help, "h", false, "If set, print help message and quit.")
	options.flagSet.BoolVar(&options.PrintToConsole, "print", false, "If true, will print result to console instead of writing to out.")
}

func getOptions(flagSet *flag.FlagSet, flagArguments []string) *appOptions {
	options := &appOptions{flagSet: flagSet}
	defineFlags(options)
	flagSet.Parse(flagArguments)
	return options
}

func main() {
	flagSet := flag.CommandLine
	process(getOptions(flagSet, os.Args[1:]), os.Stdout, os.Stderr)
}

func process(options *appOptions, out io.ReadWriter, errOut io.ReadWriter) {

	if options.Help {
		options.flagSet.SetOutput(out)
		options.flagSet.PrintDefaults()
		return
	}

	output, err := processPackage(options.PackageDirectory)

	if err != nil {
		fmt.Fprintln(errOut, "ERROR", err)
		return
	}

	if options.PrintToConsole {
		fmt.Fprintln(out, output)
	} else {
		ioutil.WriteFile(options.OutputFile, []byte(output), 0644)
	}

}

func processPackage(location string) (output string, err error) {
	sources, err := parser.ParseSourceDir(location, ".*")

	if err != nil {
		return "", errors.New("Couldn't parse sources: " + err.Error())
	}

	haveOutputHeader := false

	for _, theStruct := range sources.Structs {

		if !haveOutputHeader {
			output += headerForPackage(theStruct.PackageName)
			haveOutputHeader = true
		}

		outputReader, outputReadSetter := structConfig(theStruct.DocLines)

		if outputReader || outputReadSetter {
			output += headerForStruct(theStruct.Name)
		}

		if outputReader {
			output += readerForStruct(theStruct.Name)
		}
		if outputReadSetter {
			output += readSetterForStruct(theStruct.Name)
		}
	}

	formattedBytes, err := format.Source([]byte(output))

	if err != nil {
		return "", errors.New("Couldn't go fmt code: " + err.Error())
	}

	return string(formattedBytes), nil
}

func structConfig(docLines []string) (outputReader bool, outputReadSetter bool) {

	for _, docLine := range docLines {
		docLine = strings.ToLower(docLine)
		docLine = strings.TrimPrefix(docLine, "//")
		docLine = strings.TrimSpace(docLine)
		if !strings.HasPrefix(docLine, magicDocLinePrefix) {
			continue
		}
		docLine = strings.TrimPrefix(docLine, magicDocLinePrefix)
		docLine = strings.TrimSpace(docLine)

		switch docLine {
		case "":
			return true, true
		case "both":
			return true, true
		case "reader":
			return true, false
		case "readsetter":
			return false, true
		}

	}
	return false, false
}

func templateOutput(template *template.Template, values interface{}) string {
	buf := new(bytes.Buffer)

	err := template.Execute(buf, values)

	if err != nil {
		log.Println(err)
	}

	return buf.String()
}

func headerForPackage(packageName string) string {
	return templateOutput(headerTemplate, map[string]string{
		"packageName": packageName,
	}) + reflectImportText
}

func headerForStruct(structName string) string {
	return templateOutput(structHeaderTemplate, map[string]string{
		"structName": structName,
	})
}

func readerForStruct(structName string) string {

	return templateOutput(readerTemplate, templateConfig{
		FirstLetter: structName[:1],
		StructName:  structName,
	})

}

func readSetterForStruct(structName string) string {
	return templateOutput(readSetterTemplate, templateConfig{
		FirstLetter: structName[:1],
		StructName:  structName,
	})
}

const headerTemplateText = `/************************************
 *
 * This file contains auto-generated methods to help certain structs
 * implement boardgame.SubState and boardgame.MutableSubState. It was
 * generated by autoreader.
 *
 * DO NOT EDIT by hand.
 *
 ************************************/
package {{.packageName}}
`

const reflectImportText = `import (
	"github.com/jkomoros/boardgame"
)

`

const structHeaderTemplateText = `// Implementation for {{.structName}}

 `

const readerTemplateText = `func ({{.FirstLetter}} *{{.StructName}}) Reader() boardgame.PropertyReader {
	return boardgame.DefaultReader({{.FirstLetter}})
}

`

const readSetterTemplateText = `func ({{.FirstLetter}} *{{.StructName}}) ReadSetter() boardgame.PropertyReadSetter {
	return boardgame.DefaultReadSetter({{.FirstLetter}})
}

`
<commit_msg>Started taking a Reflection CLI arg. Doesn't do much. Part of #310.<commit_after>/*

	Autoreader is a simple program, designed to be run from go:generate, that
	helps generate the annoying boilerplate to implement
	boardgame.PropertyReader and boardgame.PropertyReadSetter.

	Autoreader processes a package of go files, searching for structs that
	have a comment immediately above their declaration that begins with
	"+autoreader". For each such struct, it creates a Reader() and
	PropertyReader() method that just use boardgame.DefaultReader and
	boardgame.DefaultReadSetter.

	If you want only a reader or only a readsetter for a given struct, include
	the keyword "reader" or "readsetter", like so: "+autoreader reader"

	You can configure which package to process and where to write output via
	command-line flags. By default it processes the current package and writes
	its output to auto_reader.go, overwriting whatever file was there before.
	See command-line options by passing -h.

	The defaults are set reasonably so that you can use go:generate very
	easily. See examplepkg/ for a very simple example.

*/
package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"github.com/MarcGrol/golangAnnotations/parser"
	"go/format"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"text/template"
)

var headerTemplate *template.Template
var structHeaderTemplate *template.Template
var readerTemplate *template.Template
var readSetterTemplate *template.Template

const magicDocLinePrefix = "+autoreader"

type appOptions struct {
	OutputFile       string
	PackageDirectory string
	PrintToConsole   bool
	Help             bool
	UseReflection    bool
	flagSet          *flag.FlagSet
}

type templateConfig struct {
	FirstLetter string
	StructName  string
}

func init() {
	headerTemplate = template.Must(template.New("header").Parse(headerTemplateText))
	structHeaderTemplate = template.Must(template.New("structHeader").Parse(structHeaderTemplateText))
	readerTemplate = template.Must(template.New("reader").Parse(readerTemplateText))
	readSetterTemplate = template.Must(template.New("readsetter").Parse(readSetterTemplateText))
}

func defineFlags(options *appOptions) {
	options.flagSet.StringVar(&options.OutputFile, "out", "auto_reader.go", "Defines which file to render output to. WARNING: it will be overwritten!")
	options.flagSet.StringVar(&options.PackageDirectory, "pkg", ".", "Which package to process")
	options.flagSet.BoolVar(&options.Help, "h", false, "If set, print help message and quit.")
	options.flagSet.BoolVar(&options.PrintToConsole, "print", false, "If true, will print result to console instead of writing to out.")
	options.flagSet.BoolVar(&options.UseReflection, "reflect", true, "If true, will use reflection based output.")
}

func getOptions(flagSet *flag.FlagSet, flagArguments []string) *appOptions {
	options := &appOptions{flagSet: flagSet}
	defineFlags(options)
	flagSet.Parse(flagArguments)
	return options
}

func main() {
	flagSet := flag.CommandLine
	process(getOptions(flagSet, os.Args[1:]), os.Stdout, os.Stderr)
}

func process(options *appOptions, out io.ReadWriter, errOut io.ReadWriter) {

	if options.Help {
		options.flagSet.SetOutput(out)
		options.flagSet.PrintDefaults()
		return
	}

	output, err := processPackage(options.UseReflection, options.PackageDirectory)

	if err != nil {
		fmt.Fprintln(errOut, "ERROR", err)
		return
	}

	if options.PrintToConsole {
		fmt.Fprintln(out, output)
	} else {
		ioutil.WriteFile(options.OutputFile, []byte(output), 0644)
	}

}

func processPackage(useReflection bool, location string) (output string, err error) {
	sources, err := parser.ParseSourceDir(location, ".*")

	if err != nil {
		return "", errors.New("Couldn't parse sources: " + err.Error())
	}

	haveOutputHeader := false

	for _, theStruct := range sources.Structs {

		if !haveOutputHeader {
			output += headerForPackage(theStruct.PackageName)
			haveOutputHeader = true
		}

		outputReader, outputReadSetter := structConfig(theStruct.DocLines)

		if outputReader || outputReadSetter {
			output += headerForStruct(theStruct.Name)
		}

		if outputReader {
			output += readerForStruct(theStruct.Name)
		}
		if outputReadSetter {
			output += readSetterForStruct(theStruct.Name)
		}
	}

	formattedBytes, err := format.Source([]byte(output))

	if err != nil {
		return "", errors.New("Couldn't go fmt code: " + err.Error())
	}

	return string(formattedBytes), nil
}

func structConfig(docLines []string) (outputReader bool, outputReadSetter bool) {

	for _, docLine := range docLines {
		docLine = strings.ToLower(docLine)
		docLine = strings.TrimPrefix(docLine, "//")
		docLine = strings.TrimSpace(docLine)
		if !strings.HasPrefix(docLine, magicDocLinePrefix) {
			continue
		}
		docLine = strings.TrimPrefix(docLine, magicDocLinePrefix)
		docLine = strings.TrimSpace(docLine)

		switch docLine {
		case "":
			return true, true
		case "both":
			return true, true
		case "reader":
			return true, false
		case "readsetter":
			return false, true
		}

	}
	return false, false
}

func templateOutput(template *template.Template, values interface{}) string {
	buf := new(bytes.Buffer)

	err := template.Execute(buf, values)

	if err != nil {
		log.Println(err)
	}

	return buf.String()
}

func headerForPackage(packageName string) string {
	return templateOutput(headerTemplate, map[string]string{
		"packageName": packageName,
	}) + reflectImportText
}

func headerForStruct(structName string) string {
	return templateOutput(structHeaderTemplate, map[string]string{
		"structName": structName,
	})
}

func readerForStruct(structName string) string {

	return templateOutput(readerTemplate, templateConfig{
		FirstLetter: structName[:1],
		StructName:  structName,
	})

}

func readSetterForStruct(structName string) string {
	return templateOutput(readSetterTemplate, templateConfig{
		FirstLetter: structName[:1],
		StructName:  structName,
	})
}

const headerTemplateText = `/************************************
 *
 * This file contains auto-generated methods to help certain structs
 * implement boardgame.SubState and boardgame.MutableSubState. It was
 * generated by autoreader.
 *
 * DO NOT EDIT by hand.
 *
 ************************************/
package {{.packageName}}
`

const reflectImportText = `import (
	"github.com/jkomoros/boardgame"
)

`

const prodImportText = `import (
	"errors"
	"github.com/jkomoros/boardgame"
)

`

const structHeaderTemplateText = `// Implementation for {{.structName}}

 `

const readerTemplateText = `func ({{.FirstLetter}} *{{.StructName}}) Reader() boardgame.PropertyReader {
	return boardgame.DefaultReader({{.FirstLetter}})
}

`

const readSetterTemplateText = `func ({{.FirstLetter}} *{{.StructName}}) ReadSetter() boardgame.PropertyReadSetter {
	return boardgame.DefaultReadSetter({{.FirstLetter}})
}

`
<|endoftext|>


<commit_before>// Copyright 2016 Andrew O'Neill, Nordstrom

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

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

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"log"
	"net"
	"net/http"

	"k8s.io/apimachinery/pkg/labels"

	"golang.org/x/net/context"

	"github.com/boltdb/bolt"
	"github.com/foolusion/elwinprotos/storage"
	"github.com/gogo/protobuf/proto"
	"github.com/grpc-ecosystem/go-grpc-prometheus"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/spf13/viper"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
)

func bind(s []string) error {
	if len(s) == 0 {
		return nil
	}
	if err := viper.BindEnv(s[0]); err != nil {
		return err
	}
	return bind(s[1:])
}

func main() {
	log.Println("Starting bolt-store...")

	viper.SetDefault("db_file", "test.db")
	viper.SetDefault("db_bucket", "dev")
	viper.SetDefault("listen_address", ":8080")
	viper.SetDefault("metrics_address", ":8081")

	viper.SetConfigName("config")
	viper.AddConfigPath(".")
	viper.AddConfigPath("/etc/elwin/bolt-store")
	if err := viper.ReadInConfig(); err != nil {
		switch err.(type) {
		case viper.ConfigFileNotFoundError:
			log.Println("no config file found")
		default:
			log.Fatalf("could not read config: %v", err)
		}
	}

	viper.SetEnvPrefix("bolt_store")
	if err := bind([]string{
		"db_file",
		"listen_address",
		"metrics_address",
	}); err != nil {
		log.Fatal(err)
	}

	server, err := newServer(viper.GetString("db_file"), viper.GetString("db_bucket"))

	log.Printf("lisening for grpc on %q", viper.GetString("listen_address"))
	lis, err := net.Listen("tcp", viper.GetString("listen_address"))
	if err != nil {
		log.Fatal(err)
	}

	s := grpc.NewServer(
		grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor),
	)
	storage.RegisterElwinStorageServer(s, server)
	grpc_prometheus.Register(s)
	go func() {
		http.Handle("/metrics", prometheus.Handler())
		log.Printf("listening for /metrics on %q", viper.GetString("metrics_address"))
		log.Fatal(http.ListenAndServe(viper.GetString("metrics_address"), nil))
	}()

	log.Fatal(s.Serve(lis))
}

var (
	environmentStaging    = []byte("dev")
	environmentProduction = []byte("prod")
)

type server struct {
	db     *bolt.DB
	bucket []byte
}

func newServer(file, bucket string) (*server, error) {
	db, err := bolt.Open(file, 0600, nil)
	if err != nil {
		return nil, err
	}

	if bucket == "" {
		return nil, errors.New("bucket is empty")
	}

	if err := db.Update(func(tx *bolt.Tx) error {
		if _, err := tx.CreateBucket([]byte(bucket)); err != nil {
			if err != bolt.ErrBucketExists {
				return err
			}
		}
		return nil
	}); err != nil {
		return nil, err
	}
	return &server{db: db, bucket: []byte(bucket)}, nil
}

func (s *server) Close() error {
	return s.db.Close()
}

// List returns all the expriments that match a query.
func (s *server) List(ctx context.Context, r *storage.ListRequest) (*storage.ListReply, error) {
	if r == nil {
		return nil, fmt.Errorf("request is nil")
	}

	selector, err := labels.Parse(r.Query)
	if err != nil {
		return nil, errors.Wrap(err, "could not parse query")
	}

	ar := &storage.ListReply{}
	if err := s.db.View(func(tx *bolt.Tx) error {
		c := tx.Bucket(s.bucket).Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			var exp storage.Experiment
			if err := proto.Unmarshal(v, &exp); err != nil {
				return err
			}
			if selector.Matches(labels.Set(exp.Labels)) {
				ar.Experiments = append(ar.Experiments, &exp)
			}
		}
		return nil
	}); err != nil {
		return nil, err
	}
	return ar, nil
}

// Set creates an experiment in the given environment.
func (s *server) Set(ctx context.Context, r *storage.SetRequest) (*storage.SetReply, error) {
	if r == nil {
		return nil, fmt.Errorf("request is nil")
	}

	exp := r.Experiment
	if exp == nil {
		return nil, fmt.Errorf("experiment is nil")
	}

	if exp.Id == "" {
		// TODO: set exp.Id to a new generated id
		if name, err := randName(32); err != nil {
			errors.Wrap(err, "could not create random name")
		} else {
			exp.Id = name
		}
	}

	pexp, err := proto.Marshal(exp)
	if err != nil {
		return nil, err
	}
	if err := s.db.Update(func(tx *bolt.Tx) error {
		err := tx.Bucket(s.bucket).Put([]byte(exp.Id), pexp)
		if err != nil {
			return err
		}
		return nil
	}); err != nil {
		return nil, err
	}
	return &storage.SetReply{Experiment: exp}, nil
}

func randName(n int) (string, error) {
	const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

	var str string
	b := make([]byte, 8*n)
	if _, err := rand.Read(b); err != nil {
		return "", errors.Wrap(err, "could not read from rand")
	}
	for i := 0; i < n; i++ {
		a := binary.BigEndian.Uint64(b[i*8:(i+1)*8]) % uint64(len(alphabet))
		str += alphabet[a : a+1]
	}
	return str, nil
}

// Get returns the experiment matching the supplied id from the given
// environment.
func (s *server) Get(ctx context.Context, r *storage.GetRequest) (*storage.GetReply, error) {
	if r == nil {
		return nil, fmt.Errorf("request is nil")
	}

	if r.Id == "" {
		return nil, fmt.Errorf("name is empty")
	}

	exp := storage.Experiment{}
	if err := s.db.View(func(tx *bolt.Tx) error {
		buf := tx.Bucket(s.bucket).Get([]byte(r.Id))
		if buf == nil {
			return grpc.Errorf(codes.NotFound, "key not found")
		}
		if err := proto.Unmarshal(buf, &exp); err != nil {
			return err
		}
		return nil
	}); err != nil {
		return nil, err
	}
	return &storage.GetReply{Experiment: &exp}, nil
}

// Remove deletes the experiment from the given environment.
func (s *server) Remove(ctx context.Context, r *storage.RemoveRequest) (*storage.RemoveReply, error) {
	if r == nil {
		return nil, fmt.Errorf("request is nil")
	}

	if r.Id == "" {
		return nil, fmt.Errorf("name is empty")
	}

	exp := storage.Experiment{}
	if err := s.db.Update(func(tx *bolt.Tx) error {
		buf := tx.Bucket(s.bucket).Get([]byte(r.Id))
		if buf == nil {
			return grpc.Errorf(codes.NotFound, "key not found")
		}
		if err := proto.Unmarshal(buf, &exp); err != nil {
			return err
		}
		return tx.Bucket(s.bucket).Delete([]byte(r.Id))
	}); err != nil {
		return nil, err
	}
	return &storage.RemoveReply{Experiment: &exp}, nil
}
<commit_msg>cleanup bolt-store<commit_after>// Copyright 2016 Andrew O'Neill, Nordstrom

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

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

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"crypto/rand"
	"encoding/binary"
	"log"
	"net"
	"net/http"

	"github.com/boltdb/bolt"
	"github.com/foolusion/elwinprotos/storage"
	"github.com/gogo/protobuf/proto"
	"github.com/grpc-ecosystem/go-grpc-prometheus"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/spf13/viper"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"k8s.io/apimachinery/pkg/labels"
)

func bind(s ...string) error {
	if len(s) == 0 {
		return nil
	}
	if err := viper.BindEnv(s[0]); err != nil {
		return err
	}
	return bind(s[1:]...)
}

var (
	ErrNilRequest = errors.New("request is nil")
)

func main() {
	log.Println("Starting bolt-store...")

	viper.SetDefault("db_file", "test.db")
	viper.SetDefault("db_bucket", "dev")
	viper.SetDefault("listen_address", ":8080")
	viper.SetDefault("metrics_address", ":8081")

	viper.SetConfigName("config")
	viper.AddConfigPath(".")
	viper.AddConfigPath("/etc/elwin/bolt-store")
	if err := viper.ReadInConfig(); err != nil {
		switch err.(type) {
		case viper.ConfigFileNotFoundError:
			log.Println("no config file found")
		default:
			log.Fatalf("could not read config: %v", err)
		}
	}

	viper.SetEnvPrefix("bolt_store")
	if err := bind(
		"db_file",
		"listen_address",
		"metrics_address",
	); err != nil {
		log.Fatal(err)
	}

	server, err := newServer(viper.GetString("db_file"), viper.GetString("db_bucket"))

	log.Printf("lisening for grpc on %q", viper.GetString("listen_address"))
	lis, err := net.Listen("tcp", viper.GetString("listen_address"))
	if err != nil {
		log.Fatal(err)
	}

	s := grpc.NewServer(
		grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor),
	)
	storage.RegisterElwinStorageServer(s, server)
	grpc_prometheus.Register(s)
	go func() {
		http.Handle("/metrics", promhttp.Handler())
		log.Printf("listening for /metrics on %q", viper.GetString("metrics_address"))
		log.Fatal(http.ListenAndServe(viper.GetString("metrics_address"), nil))
	}()

	log.Fatal(s.Serve(lis))
}

type server struct {
	db     *bolt.DB
	bucket []byte
}

func newServer(file, bucket string) (*server, error) {
	db, err := bolt.Open(file, 0600, nil)
	if err != nil {
		return nil, err
	}

	if bucket == "" {
		return nil, errors.New("bucket is empty")
	}

	if err := db.Update(func(tx *bolt.Tx) error {
		if _, err := tx.CreateBucket([]byte(bucket)); err != nil {
			if err != bolt.ErrBucketExists {
				return err
			}
		}
		return nil
	}); err != nil {
		return nil, err
	}
	return &server{db: db, bucket: []byte(bucket)}, nil
}

func (s *server) Close() error {
	return s.db.Close()
}

// List returns all the experiments that match a query.
func (s *server) List(ctx context.Context, r *storage.ListRequest) (*storage.ListReply, error) {
	if r == nil {
		return nil, ErrNilRequest
	}

	selector, err := labels.Parse(r.Query)
	if err != nil {
		return nil, errors.Wrap(err, "could not parse query")
	}

	ar := &storage.ListReply{}
	if err := s.db.View(func(tx *bolt.Tx) error {
		c := tx.Bucket(s.bucket).Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			var exp storage.Experiment
			if err := proto.Unmarshal(v, &exp); err != nil {
				return err
			}
			if selector.Matches(labels.Set(exp.Labels)) {
				ar.Experiments = append(ar.Experiments, &exp)
			}
		}
		return nil
	}); err != nil {
		return nil, err
	}
	return ar, nil
}

// Set creates an experiment in the given environment.
func (s *server) Set(ctx context.Context, r *storage.SetRequest) (*storage.SetReply, error) {
	if r == nil {
		return nil, ErrNilRequest
	}

	exp := r.Experiment
	if exp == nil {
		return nil, errors.New("experiment is nil")
	}

	if exp.Id == "" {
		// TODO: set exp.Id to a new generated id
		if name, err := randName(32); err != nil {
			errors.Wrap(err, "could not create random name")
		} else {
			exp.Id = name
		}
	}

	pexp, err := proto.Marshal(exp)
	if err != nil {
		return nil, err
	}
	if err := s.db.Update(func(tx *bolt.Tx) error {
		err := tx.Bucket(s.bucket).Put([]byte(exp.Id), pexp)
		if err != nil {
			return err
		}
		return nil
	}); err != nil {
		return nil, err
	}
	return &storage.SetReply{Experiment: exp}, nil
}

func randName(n int) (string, error) {
	const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

	var str string
	b := make([]byte, 8*n)
	if _, err := rand.Read(b); err != nil {
		return "", errors.Wrap(err, "could not read from rand")
	}
	for i := 0; i < n; i++ {
		a := binary.BigEndian.Uint64(b[i*8:(i+1)*8]) % uint64(len(alphabet))
		str += alphabet[a : a+1]
	}
	return str, nil
}

// Get returns the experiment matching the supplied id from the given
// environment.
func (s *server) Get(ctx context.Context, r *storage.GetRequest) (*storage.GetReply, error) {
	if r == nil {
		return nil, ErrNilRequest
	}

	if r.Id == "" {
		return nil, errors.New("id is empty")
	}

	exp := storage.Experiment{}
	if err := s.db.View(func(tx *bolt.Tx) error {
		buf := tx.Bucket(s.bucket).Get([]byte(r.Id))
		if buf == nil {
			return grpc.Errorf(codes.NotFound, "key not found")
		}
		if err := proto.Unmarshal(buf, &exp); err != nil {
			return err
		}
		return nil
	}); err != nil {
		return nil, err
	}
	return &storage.GetReply{Experiment: &exp}, nil
}

// Remove deletes the experiment from the given environment.
func (s *server) Remove(ctx context.Context, r *storage.RemoveRequest) (*storage.RemoveReply, error) {
	if r == nil {
		return nil, ErrNilRequest
	}

	if r.Id == "" {
		return nil, errors.New("id is empty")
	}

	exp := storage.Experiment{}
	if err := s.db.Update(func(tx *bolt.Tx) error {
		buf := tx.Bucket(s.bucket).Get([]byte(r.Id))
		if buf == nil {
			return grpc.Errorf(codes.NotFound, "key not found")
		}
		if err := proto.Unmarshal(buf, &exp); err != nil {
			return err
		}
		return tx.Bucket(s.bucket).Delete([]byte(r.Id))
	}); err != nil {
		return nil, err
	}
	return &storage.RemoveReply{Experiment: &exp}, nil
}
<|endoftext|>


<commit_before>package bothandlers

import (
	"github.com/djosephsen/hal"
	"fmt"
	"reflect"
	)

var Help = &hal.Handler{
	Method:  hal.HEAR,
	Pattern: `help (.*)`,
	Usage: `help: prints this message when you type "botname help"`,
	Run: func(res *hal.Response) error {
		var reply string
		handlers:=hal.Handlers
		for _,h:=range handlers{
			hval:=reflect.ValueOf(h)
			usage:=hval.FieldByName(`usage`)
			reply=fmt.Sprintf("%s\n%s",reply,usage)
			}
		return res.Send(reply)
	},
}
<commit_msg>working but not for fullhandlers<commit_after>package bothandlers

import (
	"github.com/djosephsen/hal"
	"fmt"
	"reflect"
	)

var Help = &hal.Handler{
	Method:  hal.HEAR,
	Pattern: `help`,
	Usage: `help: prints this message when you type "botname help"`,
	Run: func(res *hal.Response) error {
		var reply string
		handlers:=res.Robot.Handlers()
		HandlerType:=reflect.ValueOf(new(hal.Handler)).Elem()
		//I can't reflect out the fullHandler type becaues hal doesn't export it
		//so this plugin can only print the usage of hal.Handler's
		//FullHandlerType:=reflect.ValueOf(new(hal.FullHandler)).Elem()
		for _,h:=range handlers{
			hval:=reflect.ValueOf(h).Elem()
			hal.Logger.Debug("this hval is a ",hval.Type())
			if hval.Type() == HandlerType.Type(){
					usage := hval.FieldByName(`Usage`)
					reply=fmt.Sprintf("%s\n%s",reply,usage)
			}
		}
		return res.Send(reply)
	},
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/colegion/goal/internal/command"
	"github.com/colegion/goal/log"
)

// helpHandler is an instance of a subcommand that is used
// for showing info about supported commands.
var helpHandler = command.Handler{
	Name: "help",
	Main: help,
}

// help is used for showing info about supported commands.
func help(action string, params command.Data) {
	// Make sure we can show help message about the requested
	// subcommand or it is not supported.
	if h, ok := Handlers[params[action]]; ok {
		log.Info.Printf(infoMsg, h.Usage, h.Desc)
		return
	}
	log.Info.Printf(helpMsg, showCommands()) // Show general message.
}

// showCommands returns a description of supported commands
// to be included in help message.
func showCommands() (s string) {
	for n := range Handlers {
		inf := Handlers[n].Info
		if inf != "" {
			s += fmt.Sprintf("\t%-12s%s\n", n, inf)
		}
	}
	return
}

var header = `~
~ https://github.com/colegion/goal
~
`

var helpMsg = `goal is a toolkit for rapid web development in Go language.

Usage:
	goal {command} [arguments]

The commands are:
%s
Use "goal help {command}" for more information.
`

var infoMsg = `Usage:
	goal %s

%s
`
<commit_msg>Removed help command handler<commit_after><|endoftext|>