<commit_before>package wallaby

import (
	"bufio"
	"os"
	"sync"
	"sync/atomic"
	"time"

	"github.com/eliquious/xbinary"
)

// IndexFactory creates an index. The reason for this is solely for future growth... perhaps a bit premature. The main reason is for future versions of log files or different backing stores.
type IndexFactory interface {
	GetOrCreateIndex(flags uint32) (*LogIndex, error)
}

// LogIndex maintains a list of all the records in the log file. IndexRecords
type LogIndex interface {
	Size() uint64
	Header() FileHeader
	Append(record IndexRecord) (n int, err error)
	Slice(offset int64, limit int64) (IndexSlice, error)
	Flush() error
	Close() error
}

// FileHeader describes which version the file was written with. Flags
// represents boolean flags.
type FileHeader interface {
	Version() uint8
	Flags() uint32
}

// IndexSlice contains several buffered index records for fast access.
type IndexSlice interface {
	Get(index int) (IndexRecord, error)
	Size() int
}

// IndexRecord describes each item in an index file.
type IndexRecord interface {
	Time() int64
	Index() uint64
	Offset() int64
	TimeToLive() uint64
	IsExpired() bool
}

// BasicIndexRecord implements the bare IndexRecord interface.
type BasicIndexRecord struct {
	nanos  int64
	index  uint64
	offset int64
	ttl    uint64
}

// Time returns when the record was written to the data file.
func (i BasicIndexRecord) Time() int64 {
	return i.nanos
}

// Index is a record's numerical id.
func (i BasicIndexRecord) Index() uint64 {
	return i.index
}

// Offset is the distance the data record is from the start of the data file.
func (i BasicIndexRecord) Offset() int64 {
	return i.offset
}

// TimeToLive allows for records to expire after a period of time. TTL is in
// seconds.
func (i BasicIndexRecord) TimeToLive() uint64 {
	return i.ttl
}

// IsExpired is a helper function for the index record which returns `true` if
// the current time is beyond the expiration time. The expiration time is
// calculated as written time + TTL.
func (i BasicIndexRecord) IsExpired() bool {
	return time.Now().After(time.Unix(i.nanos, 0).Add(time.Duration(i.ttl)))
}

// BasicFileHeader is the simplest implementation of the FileHeader interface.
type BasicFileHeader struct {
	version uint8
	flags   uint32
}

// Version returns the file version.
func (i BasicFileHeader) Version() uint8 {
	return i.version
}

// Flags returns the boolean flags for the file.
func (i BasicFileHeader) Flags() uint32 {
	return i.flags
}

// VersionOneIndexRecord implements the bare IndexRecord interface.
type VersionOneIndexRecord struct {
	buffer *[]byte
	offset int
}

// Time returns when the record was written to the data file.
func (i VersionOneIndexRecord) Time() int64 {
	nanos, err := xbinary.LittleEndian.Int64(*i.buffer, 0+i.offset)
	if err != nil {
		nanos = 0
	}

	return nanos
}

// Index is a record's numerical id.
func (i VersionOneIndexRecord) Index() uint64 {
	index, err := xbinary.LittleEndian.Uint64(*i.buffer, 8+i.offset)
	if err != nil {
		index = 0
	}

	return index
}

// Offset is the distance the data record is from the start of the data file.
func (i VersionOneIndexRecord) Offset() int64 {
	offset, err := xbinary.LittleEndian.Int64(*i.buffer, 16+i.offset)
	if err != nil {
		offset = 0
	}

	return offset
}

// TimeToLive allows for records to expire after a period of time. TTL is in
// seconds.
func (i VersionOneIndexRecord) TimeToLive() uint64 {
	ttl, err := xbinary.LittleEndian.Uint64(*i.buffer, 24+i.offset)
	if err != nil {
		ttl = 0
	}

	return ttl
}

// IsExpired is a helper function for the index record which returns `true` if
// the current time is beyond the expiration time. The expiration time is
// calculated as written time + TTL.
func (i VersionOneIndexRecord) IsExpired() bool {
	return time.Now().After(time.Unix(i.Time(), 0).Add(time.Duration(i.TimeToLive())))
}

// VersionOneIndexFactory creates index files of v1.
type VersionOneIndexFactory struct {
	Filename string
}

// GetOrCreateIndex either creates a new file or reads from an existing index
// file.
//
// VersionOneLogHeader starts with a 3-byte string, "IDX", followed by an 8-bit
// version. After the version, a uint32 represents the boolean flags.
// The records start immediately following the bit flags.
func (f VersionOneIndexFactory) GetOrCreateIndex(flags uint32) (LogIndex, error) {

	// try to open index file, return error on fail
	file, err := os.OpenFile(f.Filename, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0600)
	if err != nil {
		return nil, err
	}

	// get file stat, close file and return on error
	stat, err := file.Stat()
	if err != nil {
		file.Close()
		return nil, err
	}

	// get header, close file and return on error
	buf := make([]byte, VersionOneIndexHeaderSize)
	var header BasicFileHeader
	var size uint64

	// if file already has header
	if stat.Size() >= VersionOneIndexHeaderSize {

		// read file header, close and return upon error
		n, err := file.ReadAt(buf, 0)
		if err != nil {
			file.Close()
			return nil, err

			// failed to read entire header
		} else if n != VersionOneIndexHeaderSize {
			file.Close()
			return nil, ErrReadIndexHeader
		}

		// read flags
		flags, err := xbinary.LittleEndian.Uint32(buf, 4)
		if err != nil {
			file.Close()
			return nil, err
		}

		// create header
		header = BasicFileHeader{flags: flags, version: buf[3]}

		size = (uint64(stat.Size()) - uint64(VersionOneIndexHeaderSize)) / uint64(VersionOneIndexRecordSize)
	} else {

		// write magic string
		xbinary.LittleEndian.PutString(buf, 0, "IDX")

		// write version
		buf[3] = byte(VersionOne)

		// write boolean flags
		xbinary.LittleEndian.PutUint32(buf, 4, flags)

		// write index header to file
		_, err := file.Write(buf)
		if err != nil {
			file.Close()
			return nil, err
		}

		// flush data to disk
		err = file.Sync()
		if err != nil {
			return nil, ErrWriteIndexHeader
		}

		// create index header
		header = BasicFileHeader{VersionOne, flags}

	}

	var lock sync.Mutex
	writer := bufio.NewWriterSize(file, 64*1024)
	idx := VersionOneIndexFile{
		fd:     file,
		writer: writer,
		header: header,
		mutex:  lock,
		extbuf: xbinary.LittleEndian,
		size:   &size,
		buffer: make([]byte, VersionOneIndexRecordSize)}
	return idx, nil
}

// VersionOneIndexFile implements the IndexFile interface and is created by VersionOneIndexFactory.
type VersionOneIndexFile struct {
	fd     *os.File
	writer *bufio.Writer
	header BasicFileHeader
	mutex  sync.Mutex
	extbuf xbinary.ExtendedBuffer
	size   *uint64
	buffer []byte
}

// Flush writes any buffered data onto permanant storage.
func (i VersionOneIndexFile) Flush() error {
	i.writer.Flush()

	// sync changes to disk
	err := i.fd.Sync()
	if err != nil {
		return err
	}
	return nil
}

// Close flushed the index with permanant storage and closes the index.
func (i VersionOneIndexFile) Close() error {
	err := i.Flush()
	if err != nil {
		return err
	}

	return i.fd.Close()
}

// Append adds an index record to the end of the index file. V1 index records have a time, an index and an offset in the data file.
func (i VersionOneIndexFile) Append(record IndexRecord) (n int, err error) {
	i.mutex.Lock()
	defer i.mutex.Unlock()

	// create buffer and byte count
	var written int

	// write time
	n, _ = i.extbuf.PutInt64(i.buffer, 0, record.Time())
	written += n

	// write index
	n, _ = i.extbuf.PutUint64(i.buffer, 8, record.Index())
	written += n

	// write byte offset in record file
	n, _ = i.extbuf.PutInt64(i.buffer, 16, record.Offset())
	written += n

	// check bytes written
	if written < VersionOneIndexRecordSize {
		return written, ErrWriteIndexHeader
	}

	// write index buffer to file
	n, err = i.writer.Write(i.buffer)
	if err != nil {
		return n, err
	}

	// increment index size
	i.incrementSize()

	// return num bytes and nil error
	return written, nil
}

// IncrementSize bumps the index size by one.
func (i *VersionOneIndexFile) incrementSize() {
	// *i.size++
	atomic.AddUint64(i.size, 1)
}

// Size is the number of elements in the index. Which should coorespond with the number of records in the data file.
func (i VersionOneIndexFile) Size() uint64 {
	return atomic.LoadUint64(i.size)
}

// Header returns the file header which describes the index file.
func (i VersionOneIndexFile) Header() FileHeader {
	return i.header
}

// Slice returns multiple index records starting at the given offset.
func (i VersionOneIndexFile) Slice(offset int64, limit int64) (IndexSlice, error) {

	// get size
	var size = atomic.LoadUint64(i.size)

	// offset too  or less than 0
	if offset > int64(size) || offset < 0 {
		return nil, ErrSliceOutOfBounds

		// invalid limit
	} else if limit < 1 {
		return nil, ErrSliceOutOfBounds
	}

	var buf []byte
	// requested slice too large
	if limit > MaximumIndexSlice {
		buf = make([]byte, VersionOneIndexRecordSize*MaximumIndexSlice)

		// request exceeds index size
	} else if uint64(offset+limit) > size {
		buf = make([]byte, VersionOneIndexRecordSize*(uint64(offset+limit)-(size)))

		// full request can be satisfied
	} else {
		buf = make([]byte, VersionOneIndexRecordSize*limit)
	}

	// read records into buffer
	read, err := i.fd.ReadAt(buf, int64(offset*VersionOneIndexRecordSize)+VersionOneIndexHeaderSize)
	if err != nil {
		return nil, ErrReadIndex
	}

	return VersionOneIndexSlice{buf, read / VersionOneIndexRecordSize}, nil
}

// VersionOneIndexSlice provides for retrieval of buffered index records
type VersionOneIndexSlice struct {
	buffer []byte
	size   int
}

// Size returns the number of records in this slice.
func (s VersionOneIndexSlice) Size() int {
	return s.size
}

// Get returns a particular index record. ErrSliceOutOfBounds is returned is the record index given is < 0 or greater than the size of the slice.
func (s VersionOneIndexSlice) Get(index int) (IndexRecord, error) {
	if index > s.size || index < 0 {
		return nil, ErrSliceOutOfBounds
	}

	return VersionOneIndexRecord{&s.buffer, index * VersionOneIndexRecordSize}, nil
}

func (s VersionOneIndexSlice) MarshalBinary() (data []byte, err error) {
	return s.buffer, nil
}
<commit_msg>Minor refactoring of Index related data structures<commit_after>package wallaby

import (
	"bufio"
	"os"
	"sync"
	"sync/atomic"
	"time"

	"github.com/eliquious/xbinary"
)

// IndexFactory creates an index. The reason for this is solely for future growth... perhaps a bit premature. The main reason is for future versions of log files or different backing stores.
// type IndexFactory interface {
// 	GetOrCreateIndex(flags uint32) (*LogIndex, error)
// }
type IndexFactory func(filename string, version uint8, flags uint32) (LogIndex, error)

// LogIndex maintains a list of all the records in the log file. IndexRecords
type LogIndex interface {
	Size() uint64
	Header() FileHeader
	Append(record IndexRecord) (n int, err error)
	Slice(offset int64, limit int64) (IndexSlice, error)
	Flush() error
	Close() error
}

// FileHeader describes which version the file was written with. Flags
// represents boolean flags.
type FileHeader interface {
	Version() uint8
	Flags() uint32
}

// IndexSlice contains several buffered index records for fast access.
type IndexSlice interface {
	Get(index int) (IndexRecord, error)
	Size() int
}

// IndexRecord describes each item in an index file.
type IndexRecord interface {
	Time() int64
	Index() uint64
	Offset() int64
	TimeToLive() int64
	IsExpired() bool
}

// BasicIndexRecord implements the bare IndexRecord interface.
type BasicIndexRecord struct {
	nanos  int64
	index  uint64
	offset int64
	ttl    int64
}

// Time returns when the record was written to the data file.
func (i BasicIndexRecord) Time() int64 {
	return i.nanos
}

// Index is a record's numerical id.
func (i BasicIndexRecord) Index() uint64 {
	return i.index
}

// Offset is the distance the data record is from the start of the data file.
func (i BasicIndexRecord) Offset() int64 {
	return i.offset
}

// TimeToLive allows for records to expire after a period of time. TTL is in
// seconds.
func (i BasicIndexRecord) TimeToLive() int64 {
	return i.ttl
}

// IsExpired is a helper function for the index record which returns `true` if
// the current time is beyond the expiration time. The expiration time is
// calculated as written time + TTL.
func (i BasicIndexRecord) IsExpired() bool {
	return time.Now().After(time.Unix(i.nanos, 0).Add(time.Duration(i.ttl)))
}

// BasicFileHeader is the simplest implementation of the FileHeader interface.
type BasicFileHeader struct {
	version uint8
	flags   uint32
}

// Version returns the file version.
func (i BasicFileHeader) Version() uint8 {
	return i.version
}

// Flags returns the boolean flags for the file.
func (i BasicFileHeader) Flags() uint32 {
	return i.flags
}

// VersionOneIndexRecord implements the bare IndexRecord interface.
type VersionOneIndexRecord struct {
	buffer *[]byte
	offset int
}

// Time returns when the record was written to the data file.
func (i VersionOneIndexRecord) Time() int64 {
	nanos, err := xbinary.LittleEndian.Int64(*i.buffer, 0+i.offset)
	if err != nil {
		nanos = 0
	}

	return nanos
}

// Index is a record's numerical id.
func (i VersionOneIndexRecord) Index() uint64 {
	index, err := xbinary.LittleEndian.Uint64(*i.buffer, 8+i.offset)
	if err != nil {
		index = 0
	}

	return index
}

// Offset is the distance the data record is from the start of the data file.
func (i VersionOneIndexRecord) Offset() int64 {
	offset, err := xbinary.LittleEndian.Int64(*i.buffer, 16+i.offset)
	if err != nil {
		offset = 0
	}

	return offset
}

// TimeToLive allows for records to expire after a period of time. TTL is in
// seconds.
func (i VersionOneIndexRecord) TimeToLive() int64 {
	ttl, err := xbinary.LittleEndian.Int64(*i.buffer, 24+i.offset)
	if err != nil {
		ttl = 0
	}

	return ttl
}

// IsExpired is a helper function for the index record which returns `true` if
// the current time is beyond the expiration time. The expiration time is
// calculated as written time + TTL.
func (i VersionOneIndexRecord) IsExpired() bool {
	return time.Now().UnixNano() > i.Time()+i.TimeToLive()
}

// GetOrCreateIndex either creates a new file or reads from an existing index
// file.
//
// VersionOneLogHeader starts with a 3-byte string, "IDX", followed by an 8-bit
// version. After the version, a uint32 represents the boolean flags.
// The records start immediately following the bit flags.
func VersionOneIndexFactory(filename string, version uint8, flags uint32) (LogIndex, error) {

	// try to open index file, return error on fail
	file, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0600)
	if err != nil {
		return nil, err
	}

	// get file stat, close file and return on error
	stat, err := file.Stat()
	if err != nil {
		file.Close()
		return nil, err
	}

	// get header, close file and return on error
	buf := make([]byte, VersionOneIndexHeaderSize)
	var header BasicFileHeader
	var size uint64

	// if file already has header
	if stat.Size() >= VersionOneIndexHeaderSize {

		// read file header, close and return upon error
		n, err := file.ReadAt(buf, 0)
		if err != nil {
			file.Close()
			return nil, err

			// failed to read entire header
		} else if n != VersionOneIndexHeaderSize {
			file.Close()
			return nil, ErrReadIndexHeader
		}

		// read flags
		flags, err := xbinary.LittleEndian.Uint32(buf, 4)
		if err != nil {
			file.Close()
			return nil, err
		}

		// create header
		header = BasicFileHeader{flags: flags, version: buf[3]}

		size = (uint64(stat.Size()) - uint64(VersionOneIndexHeaderSize)) / uint64(VersionOneIndexRecordSize)
	} else {

		// write magic string
		xbinary.LittleEndian.PutString(buf, 0, string(IndexFileSignature))

		// write version
		buf[3] = byte(VersionOne)

		// write boolean flags
		xbinary.LittleEndian.PutUint32(buf, 4, flags)

		// write index header to file
		_, err := file.Write(buf)
		if err != nil {
			file.Close()
			return nil, err
		}

		// flush data to disk
		err = file.Sync()
		if err != nil {
			return nil, ErrWriteIndexHeader
		}

		// create index header
		header = BasicFileHeader{VersionOne, flags}

	}

	var lock sync.Mutex
	writer := bufio.NewWriterSize(file, 64*1024)
	idx := VersionOneIndexFile{
		fd:     file,
		writer: writer,
		header: header,
		mutex:  lock,
		extbuf: xbinary.LittleEndian,
		size:   &size,
		buffer: make([]byte, VersionOneIndexRecordSize)}
	return idx, nil
}

// VersionOneIndexFile implements the IndexFile interface and is created by VersionOneIndexFactory.
type VersionOneIndexFile struct {
	fd     *os.File
	writer *bufio.Writer
	header BasicFileHeader
	mutex  sync.Mutex
	extbuf xbinary.ExtendedBuffer
	size   *uint64
	buffer []byte
}

// Flush writes any buffered data onto permanant storage.
func (i VersionOneIndexFile) Flush() error {
	i.writer.Flush()

	// sync changes to disk
	err := i.fd.Sync()
	if err != nil {
		return err
	}
	return nil
}

// Close flushed the index with permanant storage and closes the index.
func (i VersionOneIndexFile) Close() error {
	err := i.Flush()
	if err != nil {
		return err
	}

	return i.fd.Close()
}

// Append adds an index record to the end of the index file. V1 index records have a time, an index and an offset in the data file.
func (i VersionOneIndexFile) Append(record IndexRecord) (n int, err error) {
	i.mutex.Lock()
	defer i.mutex.Unlock()

	// create buffer and byte count
	var written int

	// write time
	n, _ = i.extbuf.PutInt64(i.buffer, 0, record.Time())
	written += n

	// write index
	n, _ = i.extbuf.PutUint64(i.buffer, 8, record.Index())
	written += n

	// write byte offset in record file
	n, _ = i.extbuf.PutInt64(i.buffer, 16, record.Offset())
	written += n

	// write ttl
	n, _ = i.extbuf.PutInt64(i.buffer, 24, record.TimeToLive())
	written += n

	// check bytes written
	if written < VersionOneIndexRecordSize {
		return written, ErrWriteIndexHeader
	}

	// write index buffer to file
	n, err = i.writer.Write(i.buffer)
	if err != nil {
		return n, err
	}

	// increment index size
	i.incrementSize()

	// return num bytes and nil error
	return written, nil
}

// IncrementSize bumps the index size by one.
func (i *VersionOneIndexFile) incrementSize() {
	// *i.size++
	atomic.AddUint64(i.size, 1)
}

// Size is the number of elements in the index. Which should coorespond with the number of records in the data file.
func (i VersionOneIndexFile) Size() uint64 {
	return atomic.LoadUint64(i.size)
}

// Header returns the file header which describes the index file.
func (i VersionOneIndexFile) Header() FileHeader {
	return i.header
}

// Slice returns multiple index records starting at the given offset.
func (i VersionOneIndexFile) Slice(offset int64, limit int64) (IndexSlice, error) {

	// get size
	var size = atomic.LoadUint64(i.size)

	// offset too  or less than 0
	if offset > int64(size) || offset < 0 {
		return nil, ErrSliceOutOfBounds

		// invalid limit
	} else if limit < 1 {
		return nil, ErrSliceOutOfBounds
	}

	var buf []byte
	// requested slice too large
	if limit > MaximumIndexSlice {
		buf = make([]byte, VersionOneIndexRecordSize*MaximumIndexSlice)

		// request exceeds index size
	} else if uint64(offset+limit) > size {
		buf = make([]byte, VersionOneIndexRecordSize*(uint64(offset+limit)-(size)))

		// full request can be satisfied
	} else {
		buf = make([]byte, VersionOneIndexRecordSize*limit)
	}

	// read records into buffer
	read, err := i.fd.ReadAt(buf, int64(offset*VersionOneIndexRecordSize)+VersionOneIndexHeaderSize)
	if err != nil {
		return nil, ErrReadIndex
	}

	return VersionOneIndexSlice{buf, read / VersionOneIndexRecordSize}, nil
}

// VersionOneIndexSlice provides for retrieval of buffered index records
type VersionOneIndexSlice struct {
	buffer []byte
	size   int
}

// Size returns the number of records in this slice.
func (s VersionOneIndexSlice) Size() int {
	return s.size
}

// Get returns a particular index record. ErrSliceOutOfBounds is returned is the record index given is < 0 or greater than the size of the slice.
func (s VersionOneIndexSlice) Get(index int) (IndexRecord, error) {
	if index > s.size || index < 0 {
		return nil, ErrSliceOutOfBounds
	}

	return VersionOneIndexRecord{&s.buffer, index * VersionOneIndexRecordSize}, nil
}

func (s VersionOneIndexSlice) MarshalBinary() (data []byte, err error) {
	return s.buffer, nil
}
<|endoftext|>


<commit_before>package rest

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
)

// Resource represents a domain model.
type Resource interface{}

// ResourceHandler specifies the endpoint handlers for working with a resource. This
// consists of the business logic for performing CRUD operations.
type ResourceHandler interface {
	// ResourceName is used to identify what resource a handler corresponds to and is
	// used in the endpoint URLs, i.e. /api/:version/resourceName. This should be
	// unique across all ResourceHandlers.
	ResourceName() string

	// CreateURI returns the URI for creating a resource.
	CreateURI() string

	// ReadURI returns the URI for reading a specific resource.
	ReadURI() string

	// ReadListURI returns the URI for reading a list of resources.
	ReadListURI() string

	// UpdateURI returns the URI for updating a specific resource.
	UpdateURI() string

	// UpdateListURI returns the URI for updating a list of resources.
	UpdateListURI() string

	// DeleteURI returns the URI for deleting a specific resource.
	DeleteURI() string

	// CreateResource is the logic that corresponds to creating a new resource at
	// POST /api/:version/resourceName. Typically, this would insert a record into a
	// database. It returns the newly created resource or an error if the create failed.
	CreateResource(RequestContext, Payload, string) (Resource, error)

	// ReadResourceList is the logic that corresponds to reading multiple resources,
	// perhaps with specified query parameters accessed through the RequestContext. This
	// is mapped to GET /api/:version/resourceName. Typically, this would make some sort
	// of database query to fetch the resources. It returns the slice of results, a
	// cursor (or empty) string, and error (or nil).
	ReadResourceList(RequestContext, int, string, string) ([]Resource, string, error)

	// ReadResource is the logic that corresponds to reading a single resource by its ID
	// at GET /api/:version/resourceName/{id}. Typically, this would make some sort of
	// database query to load the resource. If the resource doesn't exist, nil should be
	// returned along with an appropriate error.
	ReadResource(RequestContext, string, string) (Resource, error)

	// UpdateResourceList is the logic that corresponds to updating a collection of
	// resources at PUT /api/:version/resourceName. Typically, this would make some
	// sort of database update call. It returns the updated resources or an error if
	// the update failed.
	UpdateResourceList(RequestContext, []Payload, string) ([]Resource, error)

	// UpdateResource is the logic that corresponds to updating an existing resource at
	// PUT /api/:version/resourceName/{id}. Typically, this would make some sort of
	// database update call. It returns the updated resource or an error if the update
	// failed.
	UpdateResource(RequestContext, string, Payload, string) (Resource, error)

	// DeleteResource is the logic that corresponds to deleting an existing resource at
	// DELETE /api/:version/resourceName/{id}. Typically, this would make some sort of
	// database delete call. It returns the deleted resource or an error if the delete
	// failed.
	DeleteResource(RequestContext, string, string) (Resource, error)

	// Authenticate is logic that is used to authenticate requests. The default behavior
	// of Authenticate, seen in BaseResourceHandler, always returns nil, meaning all
	// requests are authenticated. Returning an error means that the request is
	// unauthorized and any error message will be sent back with the response.
	Authenticate(*http.Request) error

	// Rules returns the resource rules to apply to incoming requests and outgoing
	// responses. The default behavior, seen in BaseResourceHandler, is to apply no
	// rules.
	Rules() Rules
}

// requestHandler constructs http.HandlerFuncs responsible for handling HTTP requests.
type requestHandler struct {
	API
}

// handleCreate returns a HandlerFunc which will deserialize the request payload, pass
// it to the provided create function, and then serialize and dispatch the response.
// The serialization mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleCreate(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := rulesForVersion(handler.Rules(), version)

		data, err := decodePayload(payloadString(r.Body))
		if err != nil {
			// Payload decoding failed.
			ctx = ctx.setError(err)
			ctx = ctx.setStatus(http.StatusInternalServerError)
		} else {
			data, err := applyInboundRules(data, rules)
			if err != nil {
				// Type coercion failed.
				ctx = ctx.setError(UnprocessableRequest(err.Error()))
			} else {
				resource, err := handler.CreateResource(ctx, data, ctx.Version())
				if err == nil {
					resource = applyOutboundRules(resource, rules)
				}
				ctx = ctx.setResult(resource)
				ctx = ctx.setStatus(http.StatusCreated)
				if err != nil {
					ctx = ctx.setError(err)
				}
			}
		}

		h.sendResponse(w, ctx)
	}
}

// handleReadList returns a HandlerFunc which will pass the request context to the
// provided read function and then serialize and dispatch the response. The
// serialization mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleReadList(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := rulesForVersion(handler.Rules(), version)

		resources, cursor, err := handler.ReadResourceList(
			ctx, ctx.Limit(), ctx.Cursor(), version)

		if err == nil {
			// Apply rules to results.
			for idx, resource := range resources {
				resources[idx] = applyOutboundRules(resource, rules)
			}
		}

		ctx = ctx.setResult(resources)
		ctx = ctx.setCursor(cursor)
		ctx = ctx.setError(err)
		ctx = ctx.setStatus(http.StatusOK)

		h.sendResponse(w, ctx)
	}
}

// handleRead returns a HandlerFunc which will pass the resource id to the provided
// read function and then serialize and dispatch the response. The serialization
// mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleRead(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := rulesForVersion(handler.Rules(), version)

		resource, err := handler.ReadResource(ctx, ctx.ResourceID(), version)
		if err == nil {
			resource = applyOutboundRules(resource, rules)
		}

		ctx = ctx.setResult(resource)
		ctx = ctx.setError(err)
		ctx = ctx.setStatus(http.StatusOK)

		h.sendResponse(w, ctx)
	}
}

// handleUpdateList returns a HandlerFunc which will deserialize the request payload,
// pass it to the provided update function, and then serialize and dispatch the
// response. The serialization mechanism used is specified by the "format" query
// parameter.
func (h requestHandler) handleUpdateList(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := rulesForVersion(handler.Rules(), version)

		payloadStr := payloadString(r.Body)
		var data []Payload
		var err error
		data, err = decodePayloadSlice(payloadStr)
		if err != nil {
			var p Payload
			p, err = decodePayload(payloadStr)
			data = []Payload{p}
			fmt.Println(data)
		}

		if err != nil {
			// Payload decoding failed.
			ctx = ctx.setError(BadRequest(err.Error()))
		} else {
			for i := range data {
				data[i], err = applyInboundRules(data[i], rules)
			}
			if err != nil {
				// Type coercion failed.
				ctx = ctx.setError(UnprocessableRequest(err.Error()))
			} else {
				resources, err := handler.UpdateResourceList(ctx, data, version)
				if err == nil {
					// Apply rules to results.
					for idx, resource := range resources {
						resources[idx] = applyOutboundRules(resource, rules)
					}
				}

				ctx = ctx.setResult(resources)
				ctx = ctx.setError(err)
				ctx = ctx.setStatus(http.StatusOK)
			}
		}

		h.sendResponse(w, ctx)
	}
}

// handleUpdate returns a HandlerFunc which will deserialize the request payload,
// pass it to the provided update function, and then serialize and dispatch the
// response. The serialization mechanism used is specified by the "format" query
// parameter.
func (h requestHandler) handleUpdate(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := rulesForVersion(handler.Rules(), version)

		data, err := decodePayload(payloadString(r.Body))
		if err != nil {
			// Payload decoding failed.
			ctx = ctx.setError(err)
			ctx = ctx.setStatus(http.StatusInternalServerError)
		} else {
			data, err := applyInboundRules(data, rules)
			if err != nil {
				// Type coercion failed.
				ctx = ctx.setError(UnprocessableRequest(err.Error()))
			} else {
				resource, err := handler.UpdateResource(
					ctx, ctx.ResourceID(), data, version)
				if err == nil {
					resource = applyOutboundRules(resource, rules)
				}

				ctx = ctx.setResult(resource)
				ctx = ctx.setError(err)
				ctx = ctx.setStatus(http.StatusOK)
			}
		}

		h.sendResponse(w, ctx)
	}
}

// handleDelete returns a HandlerFunc which will pass the resource id to the provided
// delete function and then serialize and dispatch the response. The serialization
// mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleDelete(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := rulesForVersion(handler.Rules(), version)

		resource, err := handler.DeleteResource(ctx, ctx.ResourceID(), version)
		if err == nil {
			resource = applyOutboundRules(resource, rules)
		}

		ctx = ctx.setResult(resource)
		ctx = ctx.setError(err)
		ctx = ctx.setStatus(http.StatusOK)

		h.sendResponse(w, ctx)
	}
}

// sendResponse writes a success or error response to the provided http.ResponseWriter
// based on the contents of the RequestContext.
func (h requestHandler) sendResponse(w http.ResponseWriter, ctx RequestContext) {
	status := ctx.Status()
	requestError := ctx.Error()
	result := ctx.Result()
	format := ctx.ResponseFormat()

	serializer, err := h.responseSerializer(format)
	if err != nil {
		// Fall back to json serialization.
		serializer = jsonSerializer{}
		requestError = NotImplemented(fmt.Sprintf("Format not implemented: %s", format))
	}

	var response response
	if requestError != nil {
		response = NewErrorResponse(requestError)
	} else {
		nextURL, _ := ctx.NextURL()
		response = NewSuccessResponse(result, status, nextURL)
	}

	sendResponse(w, response, serializer)
}

// sendResponse writes a response to the http.ResponseWriter.
func sendResponse(w http.ResponseWriter, r response, serializer ResponseSerializer) {
	status := r.Status
	contentType := serializer.ContentType()
	response, err := serializer.Serialize(r.Payload)
	if err != nil {
		log.Printf("Response serialization failed: %s", err)
		status = http.StatusInternalServerError
		contentType = "text/plain"
		response = []byte(err.Error())
	}

	w.Header().Set("Content-Type", contentType)
	w.WriteHeader(status)
	w.Write(response)
}

// rulesForVersion returns a slice of Rules which apply to the given version.
func rulesForVersion(r Rules, version string) Rules {
	if r == nil {
		return &rules{}
	}

	filtered := make([]*Rule, 0, r.Size())
	for _, rule := range r.Contents() {
		if rule.Applies(version) {
			filtered = append(filtered, rule)
		}
	}

	return &rules{contents: filtered, resourceType: r.ResourceType()}
}

// decodePayload unmarshals the JSON payload and returns the resulting map. If the
// content is empty, an empty map is returned. If decoding fails, nil is returned
// with an error.
func decodePayload(payload []byte) (Payload, error) {
	if len(payload) == 0 {
		return map[string]interface{}{}, nil
	}

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

	return data, nil
}

// decodePayloadSlice unmarshals the JSON payload and returns the resulting slice.
// If the content is empty, an empty list is returned. If decoding fails, nil is
// returned with an error.
func decodePayloadSlice(payload []byte) ([]Payload, error) {
	if len(payload) == 0 {
		return []Payload{}, nil
	}

	var data []Payload
	if err := json.Unmarshal(payload, &data); err != nil {
		return nil, err
	}

	return data, nil
}

// payloadString returns the given io.Reader as a string. The reader must be rewound
// after calling this in order to be read again.
func payloadString(payload io.Reader) []byte {
	buf := new(bytes.Buffer)
	buf.ReadFrom(payload)
	return buf.Bytes()
}
<commit_msg>Remove debug print statement<commit_after>package rest

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
)

// Resource represents a domain model.
type Resource interface{}

// ResourceHandler specifies the endpoint handlers for working with a resource. This
// consists of the business logic for performing CRUD operations.
type ResourceHandler interface {
	// ResourceName is used to identify what resource a handler corresponds to and is
	// used in the endpoint URLs, i.e. /api/:version/resourceName. This should be
	// unique across all ResourceHandlers.
	ResourceName() string

	// CreateURI returns the URI for creating a resource.
	CreateURI() string

	// ReadURI returns the URI for reading a specific resource.
	ReadURI() string

	// ReadListURI returns the URI for reading a list of resources.
	ReadListURI() string

	// UpdateURI returns the URI for updating a specific resource.
	UpdateURI() string

	// UpdateListURI returns the URI for updating a list of resources.
	UpdateListURI() string

	// DeleteURI returns the URI for deleting a specific resource.
	DeleteURI() string

	// CreateResource is the logic that corresponds to creating a new resource at
	// POST /api/:version/resourceName. Typically, this would insert a record into a
	// database. It returns the newly created resource or an error if the create failed.
	CreateResource(RequestContext, Payload, string) (Resource, error)

	// ReadResourceList is the logic that corresponds to reading multiple resources,
	// perhaps with specified query parameters accessed through the RequestContext. This
	// is mapped to GET /api/:version/resourceName. Typically, this would make some sort
	// of database query to fetch the resources. It returns the slice of results, a
	// cursor (or empty) string, and error (or nil).
	ReadResourceList(RequestContext, int, string, string) ([]Resource, string, error)

	// ReadResource is the logic that corresponds to reading a single resource by its ID
	// at GET /api/:version/resourceName/{id}. Typically, this would make some sort of
	// database query to load the resource. If the resource doesn't exist, nil should be
	// returned along with an appropriate error.
	ReadResource(RequestContext, string, string) (Resource, error)

	// UpdateResourceList is the logic that corresponds to updating a collection of
	// resources at PUT /api/:version/resourceName. Typically, this would make some
	// sort of database update call. It returns the updated resources or an error if
	// the update failed.
	UpdateResourceList(RequestContext, []Payload, string) ([]Resource, error)

	// UpdateResource is the logic that corresponds to updating an existing resource at
	// PUT /api/:version/resourceName/{id}. Typically, this would make some sort of
	// database update call. It returns the updated resource or an error if the update
	// failed.
	UpdateResource(RequestContext, string, Payload, string) (Resource, error)

	// DeleteResource is the logic that corresponds to deleting an existing resource at
	// DELETE /api/:version/resourceName/{id}. Typically, this would make some sort of
	// database delete call. It returns the deleted resource or an error if the delete
	// failed.
	DeleteResource(RequestContext, string, string) (Resource, error)

	// Authenticate is logic that is used to authenticate requests. The default behavior
	// of Authenticate, seen in BaseResourceHandler, always returns nil, meaning all
	// requests are authenticated. Returning an error means that the request is
	// unauthorized and any error message will be sent back with the response.
	Authenticate(*http.Request) error

	// Rules returns the resource rules to apply to incoming requests and outgoing
	// responses. The default behavior, seen in BaseResourceHandler, is to apply no
	// rules.
	Rules() Rules
}

// requestHandler constructs http.HandlerFuncs responsible for handling HTTP requests.
type requestHandler struct {
	API
}

// handleCreate returns a HandlerFunc which will deserialize the request payload, pass
// it to the provided create function, and then serialize and dispatch the response.
// The serialization mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleCreate(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := rulesForVersion(handler.Rules(), version)

		data, err := decodePayload(payloadString(r.Body))
		if err != nil {
			// Payload decoding failed.
			ctx = ctx.setError(err)
			ctx = ctx.setStatus(http.StatusInternalServerError)
		} else {
			data, err := applyInboundRules(data, rules)
			if err != nil {
				// Type coercion failed.
				ctx = ctx.setError(UnprocessableRequest(err.Error()))
			} else {
				resource, err := handler.CreateResource(ctx, data, ctx.Version())
				if err == nil {
					resource = applyOutboundRules(resource, rules)
				}
				ctx = ctx.setResult(resource)
				ctx = ctx.setStatus(http.StatusCreated)
				if err != nil {
					ctx = ctx.setError(err)
				}
			}
		}

		h.sendResponse(w, ctx)
	}
}

// handleReadList returns a HandlerFunc which will pass the request context to the
// provided read function and then serialize and dispatch the response. The
// serialization mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleReadList(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := rulesForVersion(handler.Rules(), version)

		resources, cursor, err := handler.ReadResourceList(
			ctx, ctx.Limit(), ctx.Cursor(), version)

		if err == nil {
			// Apply rules to results.
			for idx, resource := range resources {
				resources[idx] = applyOutboundRules(resource, rules)
			}
		}

		ctx = ctx.setResult(resources)
		ctx = ctx.setCursor(cursor)
		ctx = ctx.setError(err)
		ctx = ctx.setStatus(http.StatusOK)

		h.sendResponse(w, ctx)
	}
}

// handleRead returns a HandlerFunc which will pass the resource id to the provided
// read function and then serialize and dispatch the response. The serialization
// mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleRead(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := rulesForVersion(handler.Rules(), version)

		resource, err := handler.ReadResource(ctx, ctx.ResourceID(), version)
		if err == nil {
			resource = applyOutboundRules(resource, rules)
		}

		ctx = ctx.setResult(resource)
		ctx = ctx.setError(err)
		ctx = ctx.setStatus(http.StatusOK)

		h.sendResponse(w, ctx)
	}
}

// handleUpdateList returns a HandlerFunc which will deserialize the request payload,
// pass it to the provided update function, and then serialize and dispatch the
// response. The serialization mechanism used is specified by the "format" query
// parameter.
func (h requestHandler) handleUpdateList(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := rulesForVersion(handler.Rules(), version)

		payloadStr := payloadString(r.Body)
		var data []Payload
		var err error
		data, err = decodePayloadSlice(payloadStr)
		if err != nil {
			var p Payload
			p, err = decodePayload(payloadStr)
			data = []Payload{p}
		}

		if err != nil {
			// Payload decoding failed.
			ctx = ctx.setError(BadRequest(err.Error()))
		} else {
			for i := range data {
				data[i], err = applyInboundRules(data[i], rules)
			}
			if err != nil {
				// Type coercion failed.
				ctx = ctx.setError(UnprocessableRequest(err.Error()))
			} else {
				resources, err := handler.UpdateResourceList(ctx, data, version)
				if err == nil {
					// Apply rules to results.
					for idx, resource := range resources {
						resources[idx] = applyOutboundRules(resource, rules)
					}
				}

				ctx = ctx.setResult(resources)
				ctx = ctx.setError(err)
				ctx = ctx.setStatus(http.StatusOK)
			}
		}

		h.sendResponse(w, ctx)
	}
}

// handleUpdate returns a HandlerFunc which will deserialize the request payload,
// pass it to the provided update function, and then serialize and dispatch the
// response. The serialization mechanism used is specified by the "format" query
// parameter.
func (h requestHandler) handleUpdate(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := rulesForVersion(handler.Rules(), version)

		data, err := decodePayload(payloadString(r.Body))
		if err != nil {
			// Payload decoding failed.
			ctx = ctx.setError(err)
			ctx = ctx.setStatus(http.StatusInternalServerError)
		} else {
			data, err := applyInboundRules(data, rules)
			if err != nil {
				// Type coercion failed.
				ctx = ctx.setError(UnprocessableRequest(err.Error()))
			} else {
				resource, err := handler.UpdateResource(
					ctx, ctx.ResourceID(), data, version)
				if err == nil {
					resource = applyOutboundRules(resource, rules)
				}

				ctx = ctx.setResult(resource)
				ctx = ctx.setError(err)
				ctx = ctx.setStatus(http.StatusOK)
			}
		}

		h.sendResponse(w, ctx)
	}
}

// handleDelete returns a HandlerFunc which will pass the resource id to the provided
// delete function and then serialize and dispatch the response. The serialization
// mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleDelete(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := rulesForVersion(handler.Rules(), version)

		resource, err := handler.DeleteResource(ctx, ctx.ResourceID(), version)
		if err == nil {
			resource = applyOutboundRules(resource, rules)
		}

		ctx = ctx.setResult(resource)
		ctx = ctx.setError(err)
		ctx = ctx.setStatus(http.StatusOK)

		h.sendResponse(w, ctx)
	}
}

// sendResponse writes a success or error response to the provided http.ResponseWriter
// based on the contents of the RequestContext.
func (h requestHandler) sendResponse(w http.ResponseWriter, ctx RequestContext) {
	status := ctx.Status()
	requestError := ctx.Error()
	result := ctx.Result()
	format := ctx.ResponseFormat()

	serializer, err := h.responseSerializer(format)
	if err != nil {
		// Fall back to json serialization.
		serializer = jsonSerializer{}
		requestError = NotImplemented(fmt.Sprintf("Format not implemented: %s", format))
	}

	var response response
	if requestError != nil {
		response = NewErrorResponse(requestError)
	} else {
		nextURL, _ := ctx.NextURL()
		response = NewSuccessResponse(result, status, nextURL)
	}

	sendResponse(w, response, serializer)
}

// sendResponse writes a response to the http.ResponseWriter.
func sendResponse(w http.ResponseWriter, r response, serializer ResponseSerializer) {
	status := r.Status
	contentType := serializer.ContentType()
	response, err := serializer.Serialize(r.Payload)
	if err != nil {
		log.Printf("Response serialization failed: %s", err)
		status = http.StatusInternalServerError
		contentType = "text/plain"
		response = []byte(err.Error())
	}

	w.Header().Set("Content-Type", contentType)
	w.WriteHeader(status)
	w.Write(response)
}

// rulesForVersion returns a slice of Rules which apply to the given version.
func rulesForVersion(r Rules, version string) Rules {
	if r == nil {
		return &rules{}
	}

	filtered := make([]*Rule, 0, r.Size())
	for _, rule := range r.Contents() {
		if rule.Applies(version) {
			filtered = append(filtered, rule)
		}
	}

	return &rules{contents: filtered, resourceType: r.ResourceType()}
}

// decodePayload unmarshals the JSON payload and returns the resulting map. If the
// content is empty, an empty map is returned. If decoding fails, nil is returned
// with an error.
func decodePayload(payload []byte) (Payload, error) {
	if len(payload) == 0 {
		return map[string]interface{}{}, nil
	}

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

	return data, nil
}

// decodePayloadSlice unmarshals the JSON payload and returns the resulting slice.
// If the content is empty, an empty list is returned. If decoding fails, nil is
// returned with an error.
func decodePayloadSlice(payload []byte) ([]Payload, error) {
	if len(payload) == 0 {
		return []Payload{}, nil
	}

	var data []Payload
	if err := json.Unmarshal(payload, &data); err != nil {
		return nil, err
	}

	return data, nil
}

// payloadString returns the given io.Reader as a string. The reader must be rewound
// after calling this in order to be read again.
func payloadString(payload io.Reader) []byte {
	buf := new(bytes.Buffer)
	buf.ReadFrom(payload)
	return buf.Bytes()
}
<|endoftext|>


<commit_before>package gogame

/*
#cgo pkg-config: sdl2
#include "SDL.h"

extern SDL_AudioDeviceID newAudioDevice(int frequency);
extern void closeAudioDevice(SDL_AudioDeviceID id);

*/
import "C"
import "errors"
import "unsafe"
import "math"

const FREQUENCY = 44100

var toneCache = make(map[int]*ToneGenerator)

//export soundGoCallback
func soundGoCallback(id int, ptr unsafe.Pointer, len int) {
	len /= 4
	// To create a Go slice backed by a C array
	// (without copying the original data), one needs to acquire this length
	// at runtime and use a type conversion to a pointer to a very big array and then
	// slice it to the length that you want (also remember to set
	// the cap if you're using Go 1.2 or later)
	slice := (*[1 << 30]float32)(ptr)[:len:len]
	toneCache[id].feedSamples(slice)
}

type ToneGenerator struct {
	dev    C.SDL_AudioDeviceID
	amp    float32
	freq   float32
	v      float32
	period int
	j      int
}

func NewToneGenerator() (*ToneGenerator, error) {
	dev := C.newAudioDevice(FREQUENCY)
	if dev == 0 {
		return nil, errors.New("Can't open tone generator")
	}
	sd := new(ToneGenerator)
	sd.dev = dev
	toneCache[int(sd.dev)] = sd
	return sd, nil
}

func (self *ToneGenerator) Start() {
	C.SDL_PauseAudioDevice(self.dev, 0)
}

func (self *ToneGenerator) Stop() {
	C.SDL_PauseAudioDevice(self.dev, 1)
}

func (self *ToneGenerator) SetFreq(freq int) {
	self.freq = float32(freq)
	self.period = int(2 * FREQUENCY / self.freq)
}

func (self *ToneGenerator) SetAmplitude(amp float32) {
	self.amp = amp
}

func (self *ToneGenerator) Close() {
	C.closeAudioDevice(self.dev)
	delete(toneCache, int(self.dev))
}

func (self *ToneGenerator) feedSamples(data []float32) {
	for i := 0; i < len(data); i++ {
		data[i] = self.amp * float32(math.Sin(float64(self.v*2*math.Pi/FREQUENCY)))
		if self.j > self.period {
			self.v -= float32(self.period) * self.freq
			self.j = 0
		} else {
			self.v += self.freq
			self.j++
		}
	}
}
<commit_msg>Working on sound...<commit_after>package gogame

/*
#cgo pkg-config: sdl2
#include "SDL.h"

extern SDL_AudioDeviceID newAudioDevice(int frequency);
extern void closeAudioDevice(SDL_AudioDeviceID id);

*/
import "C"
import "errors"
import "unsafe"
import "math"
import "math/rand"

const FREQUENCY = 44100

var toneCache = make(map[int]*ToneGenerator)

const (
	GENERATOR_TYPE_TONE = iota
	GENERATOR_TYPE_NOISE
)

//export soundGoCallback
func soundGoCallback(id int, ptr unsafe.Pointer, len int) {
	len /= 4
	// To create a Go slice backed by a C array
	// (without copying the original data), one needs to acquire this length
	// at runtime and use a type conversion to a pointer to a very big array and then
	// slice it to the length that you want (also remember to set
	// the cap if you're using Go 1.2 or later)
	slice := (*[1 << 30]float32)(ptr)[:len:len]
	toneCache[id].feedSamples(slice)
}

type ToneGenerator struct {
	genType int
	dev     C.SDL_AudioDeviceID
	amp     float32
	freq    float32
	v       float32
	period  int
	j       int
}

func NewToneGenerator(genType int) (*ToneGenerator, error) {
	dev := C.newAudioDevice(FREQUENCY)
	if dev == 0 {
		return nil, errors.New("Can't open tone generator")
	}
	sd := new(ToneGenerator)
	sd.dev = dev
	sd.genType = genType
	toneCache[int(sd.dev)] = sd
	return sd, nil
}

func (self *ToneGenerator) Start() {
	C.SDL_PauseAudioDevice(self.dev, 0)
}

func (self *ToneGenerator) Stop() {
	C.SDL_PauseAudioDevice(self.dev, 1)
}

func (self *ToneGenerator) SetFreq(freq int) {
	self.freq = float32(freq)
	self.period = int(2 * FREQUENCY / self.freq)
}

func (self *ToneGenerator) SetAmplitude(amp float32) {
	self.amp = amp
}

func (self *ToneGenerator) Close() {
	C.closeAudioDevice(self.dev)
	delete(toneCache, int(self.dev))
}

func (self *ToneGenerator) feedSamples(data []float32) {
	if self.genType == GENERATOR_TYPE_TONE {
		for i := 0; i < len(data); i++ {
			data[i] = self.amp * float32(math.Sin(float64(self.v*2*math.Pi/FREQUENCY)))
			if self.j > self.period {
				self.v -= float32(self.period) * self.freq
				self.j = 0
				} else {
					self.v += self.freq
					self.j++
				}
			}
	}  else if self.genType == GENERATOR_TYPE_NOISE {
		for i := 0; i < len(data); i++ {
			data[i] = self.amp * rand.Float32();
		}
	}
}
<|endoftext|>


<commit_before>package metainfo

import (
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"os"
	"path"
	"path/filepath"
	"strings"
	"unicode"

	"github.com/cenkalti/rain/internal/logger"
	"github.com/zeebo/bencode"
)

var (
	errInvalidPieceData = errors.New("invalid piece data")
	errZeroPieceLength  = errors.New("torrent has zero piece length")
	errZeroPieces       = errors.New("torrent has zero pieces")
	errPieceLength      = errors.New("piece length must be multiple of 16K")
)

// Info contains information about torrent.
type Info struct {
	PieceLength uint32
	Name        string
	Hash        [20]byte
	Length      int64
	NumPieces   uint32
	Bytes       []byte
	Private     bool
	Files       []File
	pieces      []byte
}

// File represents a file inside a Torrent.
type File struct {
	Length int64
	Path   string
}

type file struct {
	Length int64    `bencode:"length"`
	Path   []string `bencode:"path"`
}

// NewInfo returns info from bencoded bytes in b.
func NewInfo(b []byte) (*Info, error) {
	var ib struct {
		PieceLength uint32             `bencode:"piece length"`
		Pieces      []byte             `bencode:"pieces"`
		Name        string             `bencode:"name"`
		Private     bencode.RawMessage `bencode:"private"`
		Length      int64              `bencode:"length"` // Single File Mode
		Files       []file             `bencode:"files"`  // Multiple File mode
	}
	if err := bencode.DecodeBytes(b, &ib); err != nil {
		return nil, err
	}
	if ib.PieceLength == 0 {
		return nil, errZeroPieceLength
	}
	if len(ib.Pieces)%sha1.Size != 0 {
		return nil, errInvalidPieceData
	}
	numPieces := len(ib.Pieces) / sha1.Size
	if numPieces == 0 {
		return nil, errZeroPieces
	}
	// ".." is not allowed in file names
	for _, file := range ib.Files {
		for _, path := range file.Path {
			if strings.TrimSpace(path) == ".." {
				return nil, fmt.Errorf("invalid file name: %q", filepath.Join(file.Path...))
			}
		}
	}
	i := Info{
		PieceLength: ib.PieceLength,
		NumPieces:   uint32(numPieces),
		pieces:      ib.Pieces,
		Name:        ib.Name,
		Private:     parsePrivateField(ib.Private),
	}
	multiFile := len(ib.Files) > 0
	if multiFile {
		for _, f := range ib.Files {
			i.Length += f.Length
		}
	} else {
		i.Length = ib.Length
	}
	totalPieceDataLength := int64(i.PieceLength) * int64(i.NumPieces)
	delta := totalPieceDataLength - i.Length
	if delta >= int64(i.PieceLength) || delta < 0 {
		return nil, errInvalidPieceData
	}
	i.Bytes = b

	// calculate info hash
	hash := sha1.New()
	_, _ = hash.Write(b)
	copy(i.Hash[:], hash.Sum(nil))

	// name field is optional
	if ib.Name != "" {
		i.Name = ib.Name
	} else {
		i.Name = hex.EncodeToString(i.Hash[:])
	}

	// construct files
	if multiFile {
		i.Files = make([]File, len(ib.Files))
		for j, f := range ib.Files {
			parts := make([]string, 0, len(f.Path)+1)
			parts = append(parts, cleanName(i.Name))
			for _, p := range f.Path {
				parts = append(parts, cleanName(p))
			}
			i.Files[j] = File{
				Path:   filepath.Join(parts...),
				Length: f.Length,
			}
		}
	} else {
		i.Files = []File{{Path: cleanName(i.Name), Length: i.Length}}
	}
	return &i, nil
}

func cleanName(s string) string {
	return cleanNameN(s, 255)
}

func cleanNameN(s string, max int) string {
	s = strings.ToValidUTF8(s, string(unicode.ReplacementChar))
	s = trimName(s, max)
	s = strings.ToValidUTF8(s, "")
	return replaceSeparator(s)
}

// trimName trims the file name that it won't exceed 255 characters while keeping the extension.
func trimName(s string, max int) string {
	if len(s) <= max {
		return s
	}
	ext := path.Ext(s)
	if len(ext) > max {
		return s[:max]
	}
	return s[:max-len(ext)] + ext
}

func replaceSeparator(s string) string {
	return strings.Map(func(r rune) rune {
		if r == '/' {
			return '_'
		}
		return r
	}, s)
}

func parsePrivateField(s bencode.RawMessage) bool {
	if len(s) == 0 {
		return false
	}
	var intVal int64
	err := bencode.DecodeBytes(s, &intVal)
	if err == nil {
		return intVal != 0
	}
	var stringVal string
	err = bencode.DecodeBytes(s, &stringVal)
	if err != nil {
		return true
	}
	return !(stringVal == "" || stringVal == "0")
}

// NewInfoBytes creates a new Info dictionary by reading and hashing the files on the disk.
func NewInfoBytes(root string, paths []string, private bool, pieceLength uint32, name string, log logger.Logger) ([]byte, error) {
	var singleFileTorrent bool
	switch len(paths) {
	case 0:
		return nil, errors.New("no path specified")
	case 1:
		if name == "" {
			name = filepath.Base(paths[0])
		}
		fi, err := os.Stat(paths[0])
		if err != nil {
			return nil, err
		}
		singleFileTorrent = !fi.IsDir()
	default:
		if root == "" {
			return nil, errors.New("no root specified")
		}
		if name == "" {
			return nil, errors.New("no name specified")
		}
	}
	totalLength, err := findTotalLength(paths)
	if err != nil {
		return nil, err
	}
	if totalLength == 0 {
		return nil, errors.New("no files")
	}
	if pieceLength == 0 {
		pieceLength = calculatePieceLength(totalLength)
		log.Infof("Calculated piece length: %d K", pieceLength>>10)
	} else if pieceLength%(16<<10) != 0 {
		return nil, errPieceLength
	}
	buf := make([]byte, pieceLength)
	offset := 0
	remaining := func() []byte { return buf[offset:] }
	hash := sha1.New()
	var files []file
	var pieces []byte
	for _, path := range paths {
		relroot := path
		if root != "" {
			relroot = root
		}
		visit := func(vpath string, fi os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if fi.IsDir() {
				return nil
			}
			f, err := os.Open(vpath)
			if err != nil {
				return err
			}
			defer f.Close()
			relpath, err := filepath.Rel(relroot, vpath)
			log.Infof("Adding %q", relpath)
			if err != nil {
				return err
			}
			files = append(files, file{Path: filepath.SplitList(relpath), Length: fi.Size()})
			for {
				n, err := io.ReadFull(f, remaining())
				offset += n
				if err == io.ErrUnexpectedEOF || err == io.EOF {
					return nil // file finished, continue with next file
				}
				if err != nil {
					return err
				}
				// buffer finished, calculate piece hash and append to pieces
				_, _ = hash.Write(buf)
				pieces = hash.Sum(pieces)
				hash.Reset()
				offset = 0
			}
		}
		err = filepath.Walk(path, visit)
		if err != nil {
			return nil, err
		}
	}
	// hash remaining buffer
	if offset > 0 {
		_, _ = hash.Write(buf[:offset])
		pieces = hash.Sum(pieces)
	}
	b := struct {
		Name        string `bencode:"name"`
		Private     bool   `bencode:"private"`
		PieceLength uint32 `bencode:"piece length"`
		Pieces      []byte `bencode:"pieces"`
		Length      int64  `bencode:"length,omitempty"` // Single File Mode
		Files       []file `bencode:"files,omitempty"`  // Multiple File mode
	}{
		Name:        name,
		Private:     private,
		PieceLength: pieceLength,
		Pieces:      pieces,
	}
	if singleFileTorrent {
		b.Length = totalLength
	} else {
		b.Files = files
	}
	return bencode.EncodeBytes(b)
}

// PieceHash returns the hash of a piece at index.
func (i *Info) PieceHash(index uint32) []byte {
	begin := index * sha1.Size
	end := begin + sha1.Size
	return i.pieces[begin:end]
}

func findTotalLength(paths []string) (n int64, err error) {
	for _, path := range paths {
		err = filepath.Walk(path, func(path string, fi os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if fi.IsDir() {
				return nil
			}
			n += fi.Size()
			return nil
		})
		if err != nil {
			return
		}
	}
	return
}

func calculatePieceLength(totalLength int64) uint32 {
	const maxPieces = 2000
	pieceLength := totalLength / maxPieces
	switch {
	case pieceLength < 32<<10:
		return 32 << 10
	case pieceLength > 16<<20:
		return 16 << 20
	default:
		// round to next power of 2
		v := uint32(pieceLength)
		v--
		v |= v >> 1
		v |= v >> 2
		v |= v >> 4
		v |= v >> 8
		v |= v >> 16
		v++
		return v
	}
}
<commit_msg>fix path concatenation in torrent creation<commit_after>package metainfo

import (
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"os"
	"path"
	"path/filepath"
	"strings"
	"unicode"

	"github.com/cenkalti/rain/internal/logger"
	"github.com/zeebo/bencode"
)

var (
	errInvalidPieceData = errors.New("invalid piece data")
	errZeroPieceLength  = errors.New("torrent has zero piece length")
	errZeroPieces       = errors.New("torrent has zero pieces")
	errPieceLength      = errors.New("piece length must be multiple of 16K")
)

// Info contains information about torrent.
type Info struct {
	PieceLength uint32
	Name        string
	Hash        [20]byte
	Length      int64
	NumPieces   uint32
	Bytes       []byte
	Private     bool
	Files       []File
	pieces      []byte
}

// File represents a file inside a Torrent.
type File struct {
	Length int64
	Path   string
}

type file struct {
	Length int64    `bencode:"length"`
	Path   []string `bencode:"path"`
}

// NewInfo returns info from bencoded bytes in b.
func NewInfo(b []byte) (*Info, error) {
	var ib struct {
		PieceLength uint32             `bencode:"piece length"`
		Pieces      []byte             `bencode:"pieces"`
		Name        string             `bencode:"name"`
		Private     bencode.RawMessage `bencode:"private"`
		Length      int64              `bencode:"length"` // Single File Mode
		Files       []file             `bencode:"files"`  // Multiple File mode
	}
	if err := bencode.DecodeBytes(b, &ib); err != nil {
		return nil, err
	}
	if ib.PieceLength == 0 {
		return nil, errZeroPieceLength
	}
	if len(ib.Pieces)%sha1.Size != 0 {
		return nil, errInvalidPieceData
	}
	numPieces := len(ib.Pieces) / sha1.Size
	if numPieces == 0 {
		return nil, errZeroPieces
	}
	// ".." is not allowed in file names
	for _, file := range ib.Files {
		for _, path := range file.Path {
			if strings.TrimSpace(path) == ".." {
				return nil, fmt.Errorf("invalid file name: %q", filepath.Join(file.Path...))
			}
		}
	}
	i := Info{
		PieceLength: ib.PieceLength,
		NumPieces:   uint32(numPieces),
		pieces:      ib.Pieces,
		Name:        ib.Name,
		Private:     parsePrivateField(ib.Private),
	}
	multiFile := len(ib.Files) > 0
	if multiFile {
		for _, f := range ib.Files {
			i.Length += f.Length
		}
	} else {
		i.Length = ib.Length
	}
	totalPieceDataLength := int64(i.PieceLength) * int64(i.NumPieces)
	delta := totalPieceDataLength - i.Length
	if delta >= int64(i.PieceLength) || delta < 0 {
		return nil, errInvalidPieceData
	}
	i.Bytes = b

	// calculate info hash
	hash := sha1.New()
	_, _ = hash.Write(b)
	copy(i.Hash[:], hash.Sum(nil))

	// name field is optional
	if ib.Name != "" {
		i.Name = ib.Name
	} else {
		i.Name = hex.EncodeToString(i.Hash[:])
	}

	// construct files
	if multiFile {
		i.Files = make([]File, len(ib.Files))
		for j, f := range ib.Files {
			parts := make([]string, 0, len(f.Path)+1)
			parts = append(parts, cleanName(i.Name))
			for _, p := range f.Path {
				parts = append(parts, cleanName(p))
			}
			i.Files[j] = File{
				Path:   filepath.Join(parts...),
				Length: f.Length,
			}
		}
	} else {
		i.Files = []File{{Path: cleanName(i.Name), Length: i.Length}}
	}
	return &i, nil
}

func cleanName(s string) string {
	return cleanNameN(s, 255)
}

func cleanNameN(s string, max int) string {
	s = strings.ToValidUTF8(s, string(unicode.ReplacementChar))
	s = trimName(s, max)
	s = strings.ToValidUTF8(s, "")
	return replaceSeparator(s)
}

// trimName trims the file name that it won't exceed 255 characters while keeping the extension.
func trimName(s string, max int) string {
	if len(s) <= max {
		return s
	}
	ext := path.Ext(s)
	if len(ext) > max {
		return s[:max]
	}
	return s[:max-len(ext)] + ext
}

func replaceSeparator(s string) string {
	return strings.Map(func(r rune) rune {
		if r == '/' {
			return '_'
		}
		return r
	}, s)
}

func parsePrivateField(s bencode.RawMessage) bool {
	if len(s) == 0 {
		return false
	}
	var intVal int64
	err := bencode.DecodeBytes(s, &intVal)
	if err == nil {
		return intVal != 0
	}
	var stringVal string
	err = bencode.DecodeBytes(s, &stringVal)
	if err != nil {
		return true
	}
	return !(stringVal == "" || stringVal == "0")
}

// NewInfoBytes creates a new Info dictionary by reading and hashing the files on the disk.
func NewInfoBytes(root string, paths []string, private bool, pieceLength uint32, name string, log logger.Logger) ([]byte, error) {
	var singleFileTorrent bool
	switch len(paths) {
	case 0:
		return nil, errors.New("no path specified")
	case 1:
		if name == "" {
			name = filepath.Base(paths[0])
		}
		fi, err := os.Stat(paths[0])
		if err != nil {
			return nil, err
		}
		singleFileTorrent = !fi.IsDir()
	default:
		if root == "" {
			return nil, errors.New("no root specified")
		}
		if name == "" {
			return nil, errors.New("no name specified")
		}
	}
	totalLength, err := findTotalLength(paths)
	if err != nil {
		return nil, err
	}
	if totalLength == 0 {
		return nil, errors.New("no files")
	}
	if pieceLength == 0 {
		pieceLength = calculatePieceLength(totalLength)
		log.Infof("Calculated piece length: %d K", pieceLength>>10)
	} else if pieceLength%(16<<10) != 0 {
		return nil, errPieceLength
	}
	buf := make([]byte, pieceLength)
	offset := 0
	remaining := func() []byte { return buf[offset:] }
	hash := sha1.New()
	var files []file
	var pieces []byte
	for _, path := range paths {
		relroot := path
		if root != "" {
			relroot = root
		}
		visit := func(vpath string, fi os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if fi.IsDir() {
				return nil
			}
			f, err := os.Open(vpath)
			if err != nil {
				return err
			}
			defer f.Close()
			relpath, err := filepath.Rel(relroot, vpath)
			log.Infof("Adding %q", relpath)
			if err != nil {
				return err
			}
			files = append(files, file{Path: strings.Split(relpath, string(os.PathSeparator)), Length: fi.Size()})
			for {
				n, err := io.ReadFull(f, remaining())
				offset += n
				if err == io.ErrUnexpectedEOF || err == io.EOF {
					return nil // file finished, continue with next file
				}
				if err != nil {
					return err
				}
				// buffer finished, calculate piece hash and append to pieces
				_, _ = hash.Write(buf)
				pieces = hash.Sum(pieces)
				hash.Reset()
				offset = 0
			}
		}
		err = filepath.Walk(path, visit)
		if err != nil {
			return nil, err
		}
	}
	// hash remaining buffer
	if offset > 0 {
		_, _ = hash.Write(buf[:offset])
		pieces = hash.Sum(pieces)
	}
	b := struct {
		Name        string `bencode:"name"`
		Private     bool   `bencode:"private"`
		PieceLength uint32 `bencode:"piece length"`
		Pieces      []byte `bencode:"pieces"`
		Length      int64  `bencode:"length,omitempty"` // Single File Mode
		Files       []file `bencode:"files,omitempty"`  // Multiple File mode
	}{
		Name:        name,
		Private:     private,
		PieceLength: pieceLength,
		Pieces:      pieces,
	}
	if singleFileTorrent {
		b.Length = totalLength
	} else {
		b.Files = files
	}
	return bencode.EncodeBytes(b)
}

// PieceHash returns the hash of a piece at index.
func (i *Info) PieceHash(index uint32) []byte {
	begin := index * sha1.Size
	end := begin + sha1.Size
	return i.pieces[begin:end]
}

func findTotalLength(paths []string) (n int64, err error) {
	for _, path := range paths {
		err = filepath.Walk(path, func(path string, fi os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if fi.IsDir() {
				return nil
			}
			n += fi.Size()
			return nil
		})
		if err != nil {
			return
		}
	}
	return
}

func calculatePieceLength(totalLength int64) uint32 {
	const maxPieces = 2000
	pieceLength := totalLength / maxPieces
	switch {
	case pieceLength < 32<<10:
		return 32 << 10
	case pieceLength > 16<<20:
		return 16 << 20
	default:
		// round to next power of 2
		v := uint32(pieceLength)
		v--
		v |= v >> 1
		v |= v >> 2
		v |= v >> 4
		v |= v >> 8
		v |= v >> 16
		v++
		return v
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 The Ebiten Authors
//
// 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 mipmap

import (
	"fmt"
	"image/color"
	"math"

	"github.com/hajimehoshi/ebiten/internal/affine"
	"github.com/hajimehoshi/ebiten/internal/buffered"
	"github.com/hajimehoshi/ebiten/internal/driver"
	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/shaderir"
)

var graphicsDriver driver.Graphics

func SetGraphicsDriver(graphics driver.Graphics) {
	graphicsDriver = graphics
}

func BeginFrame() error {
	return buffered.BeginFrame()
}

func EndFrame() error {
	return buffered.EndFrame()
}

// Mipmap is a set of buffered.Image sorted by the order of mipmap level.
// The level 0 image is a regular image and higher-level images are used for mipmap.
type Mipmap struct {
	width    int
	height   int
	volatile bool
	orig     *buffered.Image
	imgs     map[int]*buffered.Image
}

func New(width, height int, volatile bool) *Mipmap {
	return &Mipmap{
		width:    width,
		height:   height,
		volatile: volatile,
		orig:     buffered.NewImage(width, height, volatile),
		imgs:     map[int]*buffered.Image{},
	}
}

func NewScreenFramebufferMipmap(width, height int) *Mipmap {
	return &Mipmap{
		width:  width,
		height: height,
		orig:   buffered.NewScreenFramebufferImage(width, height),
		imgs:   map[int]*buffered.Image{},
	}
}

func (m *Mipmap) Dump(name string, blackbg bool) error {
	return m.orig.Dump(name, blackbg)
}

func (m *Mipmap) Fill(clr color.RGBA) {
	m.orig.Fill(clr)
	m.disposeMipmaps()
}

func (m *Mipmap) ReplacePixels(pix []byte, x, y, width, height int) error {
	if err := m.orig.ReplacePixels(pix, x, y, width, height); err != nil {
		return err
	}
	m.disposeMipmaps()
	return nil
}

func (m *Mipmap) Pixels(x, y, width, height int) ([]byte, error) {
	return m.orig.Pixels(x, y, width, height)
}

func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageNum]*Mipmap, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}, canSkipMipmap bool) {
	if len(indices) == 0 {
		return
	}

	level := 0
	// TODO: Do we need to check all the sources' states of being volatile?
	if !canSkipMipmap && srcs[0] != nil && !srcs[0].volatile && filter != driver.FilterScreen {
		level = math.MaxInt32
		for i := 0; i < len(indices)/3; i++ {
			const n = graphics.VertexFloatNum
			dx0 := vertices[n*indices[3*i]+0]
			dy0 := vertices[n*indices[3*i]+1]
			sx0 := vertices[n*indices[3*i]+2]
			sy0 := vertices[n*indices[3*i]+3]
			dx1 := vertices[n*indices[3*i+1]+0]
			dy1 := vertices[n*indices[3*i+1]+1]
			sx1 := vertices[n*indices[3*i+1]+2]
			sy1 := vertices[n*indices[3*i+1]+3]
			dx2 := vertices[n*indices[3*i+2]+0]
			dy2 := vertices[n*indices[3*i+2]+1]
			sx2 := vertices[n*indices[3*i+2]+2]
			sy2 := vertices[n*indices[3*i+2]+3]
			if l := mipmapLevelFromDistance(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1, filter); level > l {
				level = l
			}
			if l := mipmapLevelFromDistance(dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, filter); level > l {
				level = l
			}
			if l := mipmapLevelFromDistance(dx2, dy2, dx0, dy0, sx2, sy2, sx0, sy0, filter); level > l {
				level = l
			}
		}
		if level == math.MaxInt32 {
			panic("mipmap: level must be calculated at least once but not")
		}
	}

	if colorm != nil && colorm.ScaleOnly() {
		body, _ := colorm.UnsafeElements()
		cr := body[0]
		cg := body[5]
		cb := body[10]
		ca := body[15]
		colorm = nil
		const n = graphics.VertexFloatNum
		for i := 0; i < len(vertices)/n; i++ {
			vertices[i*n+4] *= cr
			vertices[i*n+5] *= cg
			vertices[i*n+6] *= cb
			vertices[i*n+7] *= ca
		}
	}

	var s *buffered.Shader
	if shader != nil {
		s = shader.shader
	}

	var imgs [graphics.ShaderImageNum]*buffered.Image
	for i, src := range srcs {
		if src == nil {
			continue
		}
		if level != 0 {
			if img := src.level(level); img != nil {
				const n = graphics.VertexFloatNum
				s := float32(pow2(level))
				for i := 0; i < len(vertices)/n; i++ {
					vertices[i*n+2] /= s
					vertices[i*n+3] /= s
				}
				imgs[i] = img
				continue
			}
		}
		imgs[i] = src.orig
	}

	m.orig.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms)
	m.disposeMipmaps()
}

func (m *Mipmap) level(level int) *buffered.Image {
	if level == 0 {
		panic("ebiten: level must be non-zero at level")
	}

	if m.volatile {
		panic("ebiten: mipmap images for a volatile image is not implemented yet")
	}

	if img, ok := m.imgs[level]; ok {
		return img
	}

	var src *buffered.Image
	var vs []float32
	var filter driver.Filter
	switch {
	case level == 1:
		src = m.orig
		vs = graphics.QuadVertices(0, 0, float32(m.width), float32(m.height), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1, false)
		filter = driver.FilterLinear
	case level > 1:
		src = m.level(level - 1)
		if src == nil {
			m.imgs[level] = nil
			return nil
		}
		w := sizeForLevel(m.width, level-1)
		h := sizeForLevel(m.height, level-1)
		vs = graphics.QuadVertices(0, 0, float32(w), float32(h), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1, false)
		filter = driver.FilterLinear
	case level == -1:
		src = m.orig
		vs = graphics.QuadVertices(0, 0, float32(m.width), float32(m.height), 2, 0, 0, 2, 0, 0, 1, 1, 1, 1, false)
		filter = driver.FilterNearest
	case level < -1:
		src = m.level(level + 1)
		if src == nil {
			m.imgs[level] = nil
			return nil
		}
		w := sizeForLevel(m.width, level-1)
		h := sizeForLevel(m.height, level-1)
		vs = graphics.QuadVertices(0, 0, float32(w), float32(h), 2, 0, 0, 2, 0, 0, 1, 1, 1, 1, false)
		filter = driver.FilterNearest
	default:
		panic(fmt.Sprintf("ebiten: invalid level: %d", level))
	}
	is := graphics.QuadIndices()

	w2 := sizeForLevel(m.width, level-1)
	h2 := sizeForLevel(m.height, level-1)
	if w2 == 0 || h2 == 0 {
		m.imgs[level] = nil
		return nil
	}
	s := buffered.NewImage(w2, h2, m.volatile)
	s.DrawTriangles([graphics.ShaderImageNum]*buffered.Image{src}, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressUnsafe, driver.Region{}, nil, nil)
	m.imgs[level] = s

	return m.imgs[level]
}

func sizeForLevel(x int, level int) int {
	if level > 0 {
		for i := 0; i < level; i++ {
			x /= 2
			if x == 0 {
				return 0
			}
		}
	} else {
		for i := 0; i < -level; i++ {
			x *= 2
		}
	}
	return x
}

func (m *Mipmap) MarkDisposed() {
	m.disposeMipmaps()
	m.orig.MarkDisposed()
	m.orig = nil
}

func (m *Mipmap) disposeMipmaps() {
	for _, img := range m.imgs {
		img.MarkDisposed()
	}
	for k := range m.imgs {
		delete(m.imgs, k)
	}
}

// mipmapLevel returns an appropriate mipmap level for the given distance.
func mipmapLevelFromDistance(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1 float32, filter driver.Filter) int {
	if filter == driver.FilterScreen {
		return 0
	}

	d := (dx1-dx0)*(dx1-dx0) + (dy1-dy0)*(dy1-dy0)
	s := (sx1-sx0)*(sx1-sx0) + (sy1-sy0)*(sy1-sy0)
	if s == 0 {
		return 0
	}
	scale := d / s

	// Use 'negative' mipmap to render edges correctly (#611, #907).
	// It looks like 128 is the enlargement factor that causes edge missings to pass the test TestImageStretch.
	var tooBigScale float32 = 128
	if !graphicsDriver.HasHighPrecisionFloat() {
		tooBigScale = 4
	}

	if scale >= tooBigScale*tooBigScale {
		// If the filter is not nearest, the target needs to be rendered with graduation. Don't use mipmaps.
		if filter != driver.FilterNearest {
			return 0
		}

		const mipmapMaxSize = 1024
		w, h := sx1-sx0, sy1-sy0
		if w >= mipmapMaxSize || h >= mipmapMaxSize {
			return 0
		}

		level := 0
		for scale >= tooBigScale*tooBigScale {
			level--
			scale /= 4
			w *= 2
			h *= 2
			if w >= mipmapMaxSize || h >= mipmapMaxSize {
				break
			}
		}

		// If tooBigScale is 4, level -10 means that the maximum scale is 4 * 2^10 = 4096. This should be
		// enough.
		if level < -10 {
			level = -10
		}
		return level
	}

	if filter != driver.FilterLinear {
		return 0
	}

	level := 0
	for scale < 0.25 {
		level++
		scale *= 4
	}

	if level > 0 {
		// If the image can be scaled into 0 size, adjust the level. (#839)
		w, h := int(sx1-sx0), int(sy1-sy0)
		for level >= 0 {
			s := 1 << uint(level)
			if (w > 0 && w/s == 0) || (h > 0 && h/s == 0) {
				level--
				continue
			}
			break
		}

		if level < 0 {
			// As the render source is too small, nothing is rendered.
			return 0
		}
	}

	if level > 6 {
		level = 6
	}

	return level
}

func pow2(power int) float32 {
	if power >= 0 {
		x := 1
		return float32(x << uint(power))
	}

	x := float32(1)
	for i := 0; i < -power; i++ {
		x /= 2
	}
	return x
}

type Shader struct {
	shader *buffered.Shader
}

func NewShader(program *shaderir.Program) *Shader {
	return &Shader{
		shader: buffered.NewShader(program),
	}
}

func (s *Shader) MarkDisposed() {
	s.shader.MarkDisposed()
	s.shader = nil
}
<commit_msg>mipmap: Bug fix: HasHighPrecisionFloat cannot be called before the main loop<commit_after>// Copyright 2018 The Ebiten Authors
//
// 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 mipmap

import (
	"fmt"
	"image/color"
	"math"

	"github.com/hajimehoshi/ebiten/internal/affine"
	"github.com/hajimehoshi/ebiten/internal/buffered"
	"github.com/hajimehoshi/ebiten/internal/driver"
	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/shaderir"
)

var graphicsDriver driver.Graphics

func SetGraphicsDriver(graphics driver.Graphics) {
	graphicsDriver = graphics
}

func BeginFrame() error {
	return buffered.BeginFrame()
}

func EndFrame() error {
	return buffered.EndFrame()
}

// Mipmap is a set of buffered.Image sorted by the order of mipmap level.
// The level 0 image is a regular image and higher-level images are used for mipmap.
type Mipmap struct {
	width    int
	height   int
	volatile bool
	orig     *buffered.Image
	imgs     map[int]*buffered.Image
}

func New(width, height int, volatile bool) *Mipmap {
	return &Mipmap{
		width:    width,
		height:   height,
		volatile: volatile,
		orig:     buffered.NewImage(width, height, volatile),
		imgs:     map[int]*buffered.Image{},
	}
}

func NewScreenFramebufferMipmap(width, height int) *Mipmap {
	return &Mipmap{
		width:  width,
		height: height,
		orig:   buffered.NewScreenFramebufferImage(width, height),
		imgs:   map[int]*buffered.Image{},
	}
}

func (m *Mipmap) Dump(name string, blackbg bool) error {
	return m.orig.Dump(name, blackbg)
}

func (m *Mipmap) Fill(clr color.RGBA) {
	m.orig.Fill(clr)
	m.disposeMipmaps()
}

func (m *Mipmap) ReplacePixels(pix []byte, x, y, width, height int) error {
	if err := m.orig.ReplacePixels(pix, x, y, width, height); err != nil {
		return err
	}
	m.disposeMipmaps()
	return nil
}

func (m *Mipmap) Pixels(x, y, width, height int) ([]byte, error) {
	return m.orig.Pixels(x, y, width, height)
}

func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageNum]*Mipmap, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}, canSkipMipmap bool) {
	if len(indices) == 0 {
		return
	}

	level := 0
	// TODO: Do we need to check all the sources' states of being volatile?
	if !canSkipMipmap && srcs[0] != nil && !srcs[0].volatile && filter != driver.FilterScreen {
		level = math.MaxInt32
		for i := 0; i < len(indices)/3; i++ {
			const n = graphics.VertexFloatNum
			dx0 := vertices[n*indices[3*i]+0]
			dy0 := vertices[n*indices[3*i]+1]
			sx0 := vertices[n*indices[3*i]+2]
			sy0 := vertices[n*indices[3*i]+3]
			dx1 := vertices[n*indices[3*i+1]+0]
			dy1 := vertices[n*indices[3*i+1]+1]
			sx1 := vertices[n*indices[3*i+1]+2]
			sy1 := vertices[n*indices[3*i+1]+3]
			dx2 := vertices[n*indices[3*i+2]+0]
			dy2 := vertices[n*indices[3*i+2]+1]
			sx2 := vertices[n*indices[3*i+2]+2]
			sy2 := vertices[n*indices[3*i+2]+3]
			if l := mipmapLevelFromDistance(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1, filter); level > l {
				level = l
			}
			if l := mipmapLevelFromDistance(dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, filter); level > l {
				level = l
			}
			if l := mipmapLevelFromDistance(dx2, dy2, dx0, dy0, sx2, sy2, sx0, sy0, filter); level > l {
				level = l
			}
		}
		if level == math.MaxInt32 {
			panic("mipmap: level must be calculated at least once but not")
		}
	}

	if colorm != nil && colorm.ScaleOnly() {
		body, _ := colorm.UnsafeElements()
		cr := body[0]
		cg := body[5]
		cb := body[10]
		ca := body[15]
		colorm = nil
		const n = graphics.VertexFloatNum
		for i := 0; i < len(vertices)/n; i++ {
			vertices[i*n+4] *= cr
			vertices[i*n+5] *= cg
			vertices[i*n+6] *= cb
			vertices[i*n+7] *= ca
		}
	}

	var s *buffered.Shader
	if shader != nil {
		s = shader.shader
	}

	var imgs [graphics.ShaderImageNum]*buffered.Image
	for i, src := range srcs {
		if src == nil {
			continue
		}
		if level != 0 {
			if img := src.level(level); img != nil {
				const n = graphics.VertexFloatNum
				s := float32(pow2(level))
				for i := 0; i < len(vertices)/n; i++ {
					vertices[i*n+2] /= s
					vertices[i*n+3] /= s
				}
				imgs[i] = img
				continue
			}
		}
		imgs[i] = src.orig
	}

	m.orig.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms)
	m.disposeMipmaps()
}

func (m *Mipmap) level(level int) *buffered.Image {
	if level == 0 {
		panic("ebiten: level must be non-zero at level")
	}

	if m.volatile {
		panic("ebiten: mipmap images for a volatile image is not implemented yet")
	}

	if img, ok := m.imgs[level]; ok {
		return img
	}

	var src *buffered.Image
	var vs []float32
	var filter driver.Filter
	switch {
	case level == 1:
		src = m.orig
		vs = graphics.QuadVertices(0, 0, float32(m.width), float32(m.height), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1, false)
		filter = driver.FilterLinear
	case level > 1:
		src = m.level(level - 1)
		if src == nil {
			m.imgs[level] = nil
			return nil
		}
		w := sizeForLevel(m.width, level-1)
		h := sizeForLevel(m.height, level-1)
		vs = graphics.QuadVertices(0, 0, float32(w), float32(h), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1, false)
		filter = driver.FilterLinear
	case level == -1:
		src = m.orig
		vs = graphics.QuadVertices(0, 0, float32(m.width), float32(m.height), 2, 0, 0, 2, 0, 0, 1, 1, 1, 1, false)
		filter = driver.FilterNearest
	case level < -1:
		src = m.level(level + 1)
		if src == nil {
			m.imgs[level] = nil
			return nil
		}
		w := sizeForLevel(m.width, level-1)
		h := sizeForLevel(m.height, level-1)
		vs = graphics.QuadVertices(0, 0, float32(w), float32(h), 2, 0, 0, 2, 0, 0, 1, 1, 1, 1, false)
		filter = driver.FilterNearest
	default:
		panic(fmt.Sprintf("ebiten: invalid level: %d", level))
	}
	is := graphics.QuadIndices()

	w2 := sizeForLevel(m.width, level-1)
	h2 := sizeForLevel(m.height, level-1)
	if w2 == 0 || h2 == 0 {
		m.imgs[level] = nil
		return nil
	}
	s := buffered.NewImage(w2, h2, m.volatile)
	s.DrawTriangles([graphics.ShaderImageNum]*buffered.Image{src}, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressUnsafe, driver.Region{}, nil, nil)
	m.imgs[level] = s

	return m.imgs[level]
}

func sizeForLevel(x int, level int) int {
	if level > 0 {
		for i := 0; i < level; i++ {
			x /= 2
			if x == 0 {
				return 0
			}
		}
	} else {
		for i := 0; i < -level; i++ {
			x *= 2
		}
	}
	return x
}

func (m *Mipmap) MarkDisposed() {
	m.disposeMipmaps()
	m.orig.MarkDisposed()
	m.orig = nil
}

func (m *Mipmap) disposeMipmaps() {
	for _, img := range m.imgs {
		img.MarkDisposed()
	}
	for k := range m.imgs {
		delete(m.imgs, k)
	}
}

// mipmapLevel returns an appropriate mipmap level for the given distance.
func mipmapLevelFromDistance(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1 float32, filter driver.Filter) int {
	if filter == driver.FilterScreen {
		return 0
	}

	d := (dx1-dx0)*(dx1-dx0) + (dy1-dy0)*(dy1-dy0)
	s := (sx1-sx0)*(sx1-sx0) + (sy1-sy0)*(sy1-sy0)
	if s == 0 {
		return 0
	}
	scale := d / s

	// Use 'negative' mipmap to render edges correctly (#611, #907).
	// It looks like 128 is the enlargement factor that causes edge missings to pass the test TestImageStretch,
	// but we use 64 here for environments where the float precision is low (#1044, #1270).
	var tooBigScale float32 = 64

	if scale >= tooBigScale*tooBigScale {
		// If the filter is not nearest, the target needs to be rendered with graduation. Don't use mipmaps.
		if filter != driver.FilterNearest {
			return 0
		}

		const mipmapMaxSize = 1024
		w, h := sx1-sx0, sy1-sy0
		if w >= mipmapMaxSize || h >= mipmapMaxSize {
			return 0
		}

		level := 0
		for scale >= tooBigScale*tooBigScale {
			level--
			scale /= 4
			w *= 2
			h *= 2
			if w >= mipmapMaxSize || h >= mipmapMaxSize {
				break
			}
		}

		// If tooBigScale is 64, level -6 means that the maximum scale is 64 * 2^6 = 4096. This should be
		// enough.
		if level < -6 {
			level = -6
		}
		return level
	}

	if filter != driver.FilterLinear {
		return 0
	}

	level := 0
	for scale < 0.25 {
		level++
		scale *= 4
	}

	if level > 0 {
		// If the image can be scaled into 0 size, adjust the level. (#839)
		w, h := int(sx1-sx0), int(sy1-sy0)
		for level >= 0 {
			s := 1 << uint(level)
			if (w > 0 && w/s == 0) || (h > 0 && h/s == 0) {
				level--
				continue
			}
			break
		}

		if level < 0 {
			// As the render source is too small, nothing is rendered.
			return 0
		}
	}

	if level > 6 {
		level = 6
	}

	return level
}

func pow2(power int) float32 {
	if power >= 0 {
		x := 1
		return float32(x << uint(power))
	}

	x := float32(1)
	for i := 0; i < -power; i++ {
		x /= 2
	}
	return x
}

type Shader struct {
	shader *buffered.Shader
}

func NewShader(program *shaderir.Program) *Shader {
	return &Shader{
		shader: buffered.NewShader(program),
	}
}

func (s *Shader) MarkDisposed() {
	s.shader.MarkDisposed()
	s.shader = nil
}
<|endoftext|>


<commit_before>package kubelego

import (
	"reflect"
	"time"

	"github.com/jetstack/kube-lego/pkg/ingress"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/apis/extensions"
	"k8s.io/kubernetes/pkg/client/cache"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/controller/framework"
	"k8s.io/kubernetes/pkg/runtime"
	"k8s.io/kubernetes/pkg/util/workqueue"
	"k8s.io/kubernetes/pkg/watch"
)

func ingressListFunc(c *client.Client, ns string) func(api.ListOptions) (runtime.Object, error) {
	return func(opts api.ListOptions) (runtime.Object, error) {
		return c.Extensions().Ingress(ns).List(opts)
	}
}

func ingressWatchFunc(c *client.Client, ns string) func(options api.ListOptions) (watch.Interface, error) {
	return func(options api.ListOptions) (watch.Interface, error) {
		return c.Extensions().Ingress(ns).Watch(options)
	}
}

func (kl *KubeLego) requestReconfigure() {
	kl.workQueue.Add(true)
}

func (kl *KubeLego) WatchReconfigure() {

	kl.workQueue = workqueue.New()

	// handle worker shutdown
	go func() {
		<-kl.stopCh
		kl.workQueue.ShutDown()
	}()

	go func() {
		kl.waitGroup.Add(1)
		defer kl.waitGroup.Done()
		for {
			item, quit := kl.workQueue.Get()
			if quit {
				return
			}
			kl.Log().Debugf("worker: begin processing %v", item)
			kl.Reconfigure()
			kl.Log().Debugf("worker: done processing %v", item)
			kl.workQueue.Done(item)
		}
	}()
}

func (kl *KubeLego) WatchEvents() {

	kl.Log().Debugf("start watching ingress objects")

	resyncPeriod := 10 * time.Second

	ingEventHandler := framework.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			addIng := obj.(*extensions.Ingress)
			if ingress.IgnoreIngress(addIng) != nil {
				return
			}
			kl.Log().Debugf("CREATE ingress/%s/%s", addIng.Namespace, addIng.Name)
			kl.workQueue.Add(true)
		},
		DeleteFunc: func(obj interface{}) {
			delIng := obj.(*extensions.Ingress)
			if ingress.IgnoreIngress(delIng) != nil {
				return
			}
			kl.Log().Debugf("DELETE ingress/%s/%s", delIng.Namespace, delIng.Name)
			kl.workQueue.Add(true)
		},
		UpdateFunc: func(old, cur interface{}) {
			if !reflect.DeepEqual(old, cur) {
				upIng := cur.(*extensions.Ingress)
				if ingress.IgnoreIngress(upIng) != nil {
					return
				}
				kl.Log().Debugf("UPDATE ingress/%s/%s", upIng.Namespace, upIng.Name)
				kl.workQueue.Add(true)
			}
		},
	}

	_, controller := framework.NewInformer(
		&cache.ListWatch{
			ListFunc:  ingressListFunc(kl.kubeClient, api.NamespaceAll),
			WatchFunc: ingressWatchFunc(kl.kubeClient, api.NamespaceAll),
		},
		&extensions.Ingress{},
		resyncPeriod,
		ingEventHandler,
	)

	go controller.Run(kl.stopCh)
}
<commit_msg>Raise resync period to 60 seconds, should be more than enough<commit_after>package kubelego

import (
	"reflect"
	"time"

	"github.com/jetstack/kube-lego/pkg/ingress"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/apis/extensions"
	"k8s.io/kubernetes/pkg/client/cache"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/controller/framework"
	"k8s.io/kubernetes/pkg/runtime"
	"k8s.io/kubernetes/pkg/util/workqueue"
	"k8s.io/kubernetes/pkg/watch"
)

func ingressListFunc(c *client.Client, ns string) func(api.ListOptions) (runtime.Object, error) {
	return func(opts api.ListOptions) (runtime.Object, error) {
		return c.Extensions().Ingress(ns).List(opts)
	}
}

func ingressWatchFunc(c *client.Client, ns string) func(options api.ListOptions) (watch.Interface, error) {
	return func(options api.ListOptions) (watch.Interface, error) {
		return c.Extensions().Ingress(ns).Watch(options)
	}
}

func (kl *KubeLego) requestReconfigure() {
	kl.workQueue.Add(true)
}

func (kl *KubeLego) WatchReconfigure() {

	kl.workQueue = workqueue.New()

	// handle worker shutdown
	go func() {
		<-kl.stopCh
		kl.workQueue.ShutDown()
	}()

	go func() {
		kl.waitGroup.Add(1)
		defer kl.waitGroup.Done()
		for {
			item, quit := kl.workQueue.Get()
			if quit {
				return
			}
			kl.Log().Debugf("worker: begin processing %v", item)
			kl.Reconfigure()
			kl.Log().Debugf("worker: done processing %v", item)
			kl.workQueue.Done(item)
		}
	}()
}

func (kl *KubeLego) WatchEvents() {

	kl.Log().Debugf("start watching ingress objects")

	resyncPeriod := 60 * time.Second

	ingEventHandler := framework.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			addIng := obj.(*extensions.Ingress)
			if ingress.IgnoreIngress(addIng) != nil {
				return
			}
			kl.Log().Debugf("CREATE ingress/%s/%s", addIng.Namespace, addIng.Name)
			kl.workQueue.Add(true)
		},
		DeleteFunc: func(obj interface{}) {
			delIng := obj.(*extensions.Ingress)
			if ingress.IgnoreIngress(delIng) != nil {
				return
			}
			kl.Log().Debugf("DELETE ingress/%s/%s", delIng.Namespace, delIng.Name)
			kl.workQueue.Add(true)
		},
		UpdateFunc: func(old, cur interface{}) {
			if !reflect.DeepEqual(old, cur) {
				upIng := cur.(*extensions.Ingress)
				if ingress.IgnoreIngress(upIng) != nil {
					return
				}
				kl.Log().Debugf("UPDATE ingress/%s/%s", upIng.Namespace, upIng.Name)
				kl.workQueue.Add(true)
			}
		},
	}

	_, controller := framework.NewInformer(
		&cache.ListWatch{
			ListFunc:  ingressListFunc(kl.kubeClient, api.NamespaceAll),
			WatchFunc: ingressWatchFunc(kl.kubeClient, api.NamespaceAll),
		},
		&extensions.Ingress{},
		resyncPeriod,
		ingEventHandler,
	)

	go controller.Run(kl.stopCh)
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/gorilla/mux"
	nlog "github.com/nuveo/log"
	"github.com/prest/prest/adapters/postgres"
	"github.com/prest/prest/config"
	"github.com/prest/prest/config/router"
	"github.com/prest/prest/controllers"
	"github.com/prest/prest/middlewares"
	"github.com/spf13/cobra"
	"github.com/urfave/negroni"
)

// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
	Use:   "prest",
	Short: "Serve a RESTful API from any PostgreSQL database",
	Long:  `Serve a RESTful API from any PostgreSQL database, start HTTP server`,
	Run: func(cmd *cobra.Command, args []string) {
		if config.PrestConf.Adapter == nil {
			nlog.Warningln("adapter is not set. Using the default (postgres)")
			postgres.Load()
		}
		startServer()
	},
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	upCmd.AddCommand(authUpCmd)
	downCmd.AddCommand(authDownCmd)
	migrateCmd.AddCommand(downCmd)
	migrateCmd.AddCommand(mversionCmd)
	migrateCmd.AddCommand(nextCmd)
	migrateCmd.AddCommand(redoCmd)
	migrateCmd.AddCommand(upCmd)
	migrateCmd.AddCommand(resetCmd)
	RootCmd.AddCommand(versionCmd)
	RootCmd.AddCommand(migrateCmd)
	migrateCmd.PersistentFlags().StringVar(&urlConn, "url", driverURL(), "Database driver url")
	migrateCmd.PersistentFlags().StringVar(&path, "path", config.PrestConf.MigrationsPath, "Migrations directory")

	if err := RootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}

// MakeHandler reagister all routes
func MakeHandler() http.Handler {
	n := middlewares.GetApp()
	r := router.Get()
	r.HandleFunc("/databases", controllers.GetDatabases).Methods("GET")
	r.HandleFunc("/schemas", controllers.GetSchemas).Methods("GET")
	r.HandleFunc("/tables", controllers.GetTables).Methods("GET")
	r.HandleFunc("/_QUERIES/{queriesLocation}/{script}", controllers.ExecuteFromScripts)
	r.HandleFunc("/{database}/{schema}", controllers.GetTablesByDatabaseAndSchema).Methods("GET")
	r.HandleFunc("/show/{database}/{schema}/{table}", controllers.ShowTable).Methods("GET")
	crudRoutes := mux.NewRouter().PathPrefix("/").Subrouter().StrictSlash(true)
	crudRoutes.HandleFunc("/{database}/{schema}/{table}", controllers.SelectFromTables).Methods("GET")
	crudRoutes.HandleFunc("/{database}/{schema}/{table}", controllers.InsertInTables).Methods("POST")
	crudRoutes.HandleFunc("/batch/{database}/{schema}/{table}", controllers.BatchInsertInTables).Methods("POST")
	crudRoutes.HandleFunc("/{database}/{schema}/{table}", controllers.DeleteFromTable).Methods("DELETE")
	crudRoutes.HandleFunc("/{database}/{schema}/{table}", controllers.UpdateTable).Methods("PUT", "PATCH")
	r.PathPrefix("/").Handler(negroni.New(
		middlewares.AccessControl(),
		negroni.Wrap(crudRoutes),
	))
	n.UseHandler(r)
	return n
}

func startServer() {
	http.Handle(config.PrestConf.ContextPath, MakeHandler())
	l := log.New(os.Stdout, "[prest] ", 0)

	if !config.PrestConf.AccessConf.Restrict {
		nlog.Warningln("You are running pREST in public mode.")
	}

	if config.PrestConf.Debug {
		nlog.DebugMode = config.PrestConf.Debug
		nlog.Warningln("You are running pREST in debug mode.")
	}
	addr := fmt.Sprintf("%s:%d", config.PrestConf.HTTPHost, config.PrestConf.HTTPPort)
	l.Printf("listening on %s and serving on %s", addr, config.PrestConf.ContextPath)
	if config.PrestConf.HTTPSMode {
		l.Fatal(http.ListenAndServeTLS(addr, config.PrestConf.HTTPSCert, config.PrestConf.HTTPSKey, nil))
	}
	l.Fatal(http.ListenAndServe(addr, nil))
}
<commit_msg>feat(cmd): add /auth route when auth is enabled<commit_after>package cmd

import (
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/gorilla/mux"
	nlog "github.com/nuveo/log"
	"github.com/prest/prest/adapters/postgres"
	"github.com/prest/prest/config"
	"github.com/prest/prest/config/router"
	"github.com/prest/prest/controllers"
	"github.com/prest/prest/middlewares"
	"github.com/spf13/cobra"
	"github.com/urfave/negroni"
)

// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
	Use:   "prest",
	Short: "Serve a RESTful API from any PostgreSQL database",
	Long:  `Serve a RESTful API from any PostgreSQL database, start HTTP server`,
	Run: func(cmd *cobra.Command, args []string) {
		if config.PrestConf.Adapter == nil {
			nlog.Warningln("adapter is not set. Using the default (postgres)")
			postgres.Load()
		}
		startServer()
	},
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	upCmd.AddCommand(authUpCmd)
	downCmd.AddCommand(authDownCmd)
	migrateCmd.AddCommand(downCmd)
	migrateCmd.AddCommand(mversionCmd)
	migrateCmd.AddCommand(nextCmd)
	migrateCmd.AddCommand(redoCmd)
	migrateCmd.AddCommand(upCmd)
	migrateCmd.AddCommand(resetCmd)
	RootCmd.AddCommand(versionCmd)
	RootCmd.AddCommand(migrateCmd)
	migrateCmd.PersistentFlags().StringVar(&urlConn, "url", driverURL(), "Database driver url")
	migrateCmd.PersistentFlags().StringVar(&path, "path", config.PrestConf.MigrationsPath, "Migrations directory")

	if err := RootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}

// MakeHandler reagister all routes
func MakeHandler() http.Handler {
	n := middlewares.GetApp()
	r := router.Get()
	// if auth is enabled
	if config.PrestConf.AuthEnabled {
		r.HandleFunc("/auth", controllers.Auth).Methods("POST")
	}
	r.HandleFunc("/databases", controllers.GetDatabases).Methods("GET")
	r.HandleFunc("/schemas", controllers.GetSchemas).Methods("GET")
	r.HandleFunc("/tables", controllers.GetTables).Methods("GET")
	r.HandleFunc("/_QUERIES/{queriesLocation}/{script}", controllers.ExecuteFromScripts)
	r.HandleFunc("/{database}/{schema}", controllers.GetTablesByDatabaseAndSchema).Methods("GET")
	r.HandleFunc("/show/{database}/{schema}/{table}", controllers.ShowTable).Methods("GET")
	crudRoutes := mux.NewRouter().PathPrefix("/").Subrouter().StrictSlash(true)
	crudRoutes.HandleFunc("/{database}/{schema}/{table}", controllers.SelectFromTables).Methods("GET")
	crudRoutes.HandleFunc("/{database}/{schema}/{table}", controllers.InsertInTables).Methods("POST")
	crudRoutes.HandleFunc("/batch/{database}/{schema}/{table}", controllers.BatchInsertInTables).Methods("POST")
	crudRoutes.HandleFunc("/{database}/{schema}/{table}", controllers.DeleteFromTable).Methods("DELETE")
	crudRoutes.HandleFunc("/{database}/{schema}/{table}", controllers.UpdateTable).Methods("PUT", "PATCH")
	r.PathPrefix("/").Handler(negroni.New(
		middlewares.AccessControl(),
		negroni.Wrap(crudRoutes),
	))
	n.UseHandler(r)
	return n
}

func startServer() {
	http.Handle(config.PrestConf.ContextPath, MakeHandler())
	l := log.New(os.Stdout, "[prest] ", 0)

	if !config.PrestConf.AccessConf.Restrict {
		nlog.Warningln("You are running pREST in public mode.")
	}

	if config.PrestConf.Debug {
		nlog.DebugMode = config.PrestConf.Debug
		nlog.Warningln("You are running pREST in debug mode.")
	}
	addr := fmt.Sprintf("%s:%d", config.PrestConf.HTTPHost, config.PrestConf.HTTPPort)
	l.Printf("listening on %s and serving on %s", addr, config.PrestConf.ContextPath)
	if config.PrestConf.HTTPSMode {
		l.Fatal(http.ListenAndServeTLS(addr, config.PrestConf.HTTPSCert, config.PrestConf.HTTPSKey, nil))
	}
	l.Fatal(http.ListenAndServe(addr, nil))
}
<|endoftext|>