<commit_before>package conn

import (
	"context"
	"errors"
	"net/http"
	"time"

	"github.com/gorilla/websocket"
)

var (
	// ErrMessageChannelFull indicates that the connection's message channel is full.
	ErrMessageChannelFull = errors.New("websocket-conn: Message channel is full")

	closeMessage = Message{websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")}
	pingMessage  = Message{websocket.PingMessage, []byte{}}
	pongMessage  = Message{websocket.PongMessage, []byte{}}
)

// Connect to the peer. the requestHeader argument may be nil.
func Connect(ctx context.Context, settings Settings, url string, requestHeader http.Header) (*Conn, *http.Response, error) {
	dialer := new(websocket.Dialer)
	dialer.ReadBufferSize = settings.ReadBufferSize
	dialer.WriteBufferSize = settings.WriteBufferSize
	dialer.HandshakeTimeout = settings.HandshakeTimeout
	dialer.Subprotocols = settings.Subprotocols
	dialer.NetDial = settings.DialerSettings.NetDial
	dialer.TLSClientConfig = settings.DialerSettings.TLSClientConfig

	conn, response, err := dialer.Dial(url, requestHeader)
	if err != nil {
		return nil, response, err
	}
	c := &Conn{conn: conn}
	c.start(ctx, settings)
	return c, response, nil
}

// UpgradeFromHTTP upgrades HTTP to WebSocket.
func UpgradeFromHTTP(ctx context.Context, settings Settings, w http.ResponseWriter, r *http.Request) (*Conn, error) {
	upgrader := new(websocket.Upgrader)
	upgrader.ReadBufferSize = settings.ReadBufferSize
	upgrader.WriteBufferSize = settings.WriteBufferSize
	upgrader.HandshakeTimeout = settings.HandshakeTimeout
	upgrader.Subprotocols = settings.Subprotocols
	upgrader.Error = settings.UpgraderSettings.Error
	upgrader.CheckOrigin = settings.UpgraderSettings.CheckOrigin

	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return nil, err
	}
	c := &Conn{conn: conn}
	c.start(ctx, settings)
	return c, nil
}

// Conn represents a WebSocket connection.
type Conn struct {
	ctx  context.Context
	conn *websocket.Conn
	err  error

	pingPeriod time.Duration
	writeWait  time.Duration

	streamDataReceived   chan Data
	errored              chan error
	readPumpFinished     chan struct{}
	writePumpFinished    chan struct{}
	sendMessageRequested chan Message
}

// Stream retrieve the peer's message data from the stream channel.
// If the connection closed, it returns data with true of EOS flag at last.
func (c *Conn) Stream() <-chan Data {
	return c.streamDataReceived
}

// Err returns the disconnection error if the connection closed.
func (c *Conn) Err() error {
	return c.err
}

// SendBinaryMessage to the peer. This method is goroutine safe.
func (c *Conn) SendBinaryMessage(data []byte) error {
	return c.sendMessage(Message{websocket.BinaryMessage, data})
}

// SendTextMessage to the peer. This method is goroutine safe.
func (c *Conn) SendTextMessage(text string) error {
	return c.sendMessage(Message{websocket.TextMessage, []byte(text)})
}

func (c *Conn) start(ctx context.Context, settings Settings) {
	c.ctx = ctx
	c.conn.SetReadLimit(settings.MaxMessageSize)
	c.conn.SetReadDeadline(time.Now().Add(settings.PongWait))
	c.conn.SetPingHandler(func(string) error {
		return c.sendMessage(pongMessage)
	})
	c.conn.SetPongHandler(func(string) error {
		return c.conn.SetReadDeadline(time.Now().Add(settings.PongWait))
	})

	c.pingPeriod = settings.PingPeriod
	c.writeWait = settings.WriteWait

	c.streamDataReceived = make(chan Data)
	c.errored = make(chan error, 2)
	c.readPumpFinished = make(chan struct{})
	c.writePumpFinished = make(chan struct{})
	c.sendMessageRequested = make(chan Message, settings.MessageChannelBufferSize)

	go c.writePump()
	go c.readPump()
}

func (c *Conn) sendMessage(m Message) error {
	select {
	case c.sendMessageRequested <- m:
		return nil
	default:
		return ErrMessageChannelFull
	}
}

func (c *Conn) writeMessage(m Message) error {
	if err := c.conn.SetWriteDeadline(time.Now().Add(c.writeWait)); err != nil {
		return err
	}
	if err := c.conn.WriteMessage(int(m.MessageType), m.Data); err != nil {
		return err
	}
	return nil
}

func (c *Conn) writePump() {
	defer c.conn.Close()

	ticker := time.NewTicker(c.pingPeriod)
	defer ticker.Stop()

loop:
	for {
		select {
		case <-c.ctx.Done():
			close(c.sendMessageRequested)
			for m := range c.sendMessageRequested {
				if err := c.writeMessage(m); err != nil {
					c.errored <- err
					break loop
				}
			}
			if err := c.writeMessage(closeMessage); err != nil {
				c.errored <- err
				break loop
			}
			c.errored <- c.ctx.Err()
			break loop
		case <-c.readPumpFinished:
			break loop
		case m := <-c.sendMessageRequested:
			if err := c.writeMessage(m); err != nil {
				c.errored <- err
				break loop
			}
		case <-ticker.C:
			if err := c.writeMessage(pingMessage); err != nil {
				c.errored <- err
				break loop
			}
		}
	}
	close(c.writePumpFinished)
}

func (c *Conn) readPump() {
	defer c.conn.Close()

loop:
	for {
		messageType, data, err := c.conn.ReadMessage()
		if err != nil {
			c.errored <- err
			break loop
		}
		var d Data
		switch messageType {
		case websocket.TextMessage:
			d = Data{Message: Message{TextMessageType, data}}
		case websocket.BinaryMessage:
			d = Data{Message: Message{BinaryMessageType, data}}
		default:
			continue
		}

	streamDataLoop:
		for {
			select {
			case <-c.ctx.Done():
				c.errored <- c.ctx.Err()
				break loop
			case <-c.writePumpFinished:
				break loop
			case c.streamDataReceived <- d:
				break streamDataLoop
			}
		}
	}
	close(c.readPumpFinished)
	<-c.writePumpFinished

	c.err = <-c.errored
	c.streamDataReceived <- Data{EOS: true}
	close(c.streamDataReceived)
}
<commit_msg>Remove unnecessary loop<commit_after>package conn

import (
	"context"
	"errors"
	"net/http"
	"time"

	"github.com/gorilla/websocket"
)

var (
	// ErrMessageChannelFull indicates that the connection's message channel is full.
	ErrMessageChannelFull = errors.New("websocket-conn: Message channel is full")

	closeMessage = Message{websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")}
	pingMessage  = Message{websocket.PingMessage, []byte{}}
	pongMessage  = Message{websocket.PongMessage, []byte{}}
)

// Connect to the peer. the requestHeader argument may be nil.
func Connect(ctx context.Context, settings Settings, url string, requestHeader http.Header) (*Conn, *http.Response, error) {
	dialer := new(websocket.Dialer)
	dialer.ReadBufferSize = settings.ReadBufferSize
	dialer.WriteBufferSize = settings.WriteBufferSize
	dialer.HandshakeTimeout = settings.HandshakeTimeout
	dialer.Subprotocols = settings.Subprotocols
	dialer.NetDial = settings.DialerSettings.NetDial
	dialer.TLSClientConfig = settings.DialerSettings.TLSClientConfig

	conn, response, err := dialer.Dial(url, requestHeader)
	if err != nil {
		return nil, response, err
	}
	c := &Conn{conn: conn}
	c.start(ctx, settings)
	return c, response, nil
}

// UpgradeFromHTTP upgrades HTTP to WebSocket.
func UpgradeFromHTTP(ctx context.Context, settings Settings, w http.ResponseWriter, r *http.Request) (*Conn, error) {
	upgrader := new(websocket.Upgrader)
	upgrader.ReadBufferSize = settings.ReadBufferSize
	upgrader.WriteBufferSize = settings.WriteBufferSize
	upgrader.HandshakeTimeout = settings.HandshakeTimeout
	upgrader.Subprotocols = settings.Subprotocols
	upgrader.Error = settings.UpgraderSettings.Error
	upgrader.CheckOrigin = settings.UpgraderSettings.CheckOrigin

	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return nil, err
	}
	c := &Conn{conn: conn}
	c.start(ctx, settings)
	return c, nil
}

// Conn represents a WebSocket connection.
type Conn struct {
	ctx  context.Context
	conn *websocket.Conn
	err  error

	pingPeriod time.Duration
	writeWait  time.Duration

	streamDataReceived   chan Data
	errored              chan error
	readPumpFinished     chan struct{}
	writePumpFinished    chan struct{}
	sendMessageRequested chan Message
}

// Stream retrieve the peer's message data from the stream channel.
// If the connection closed, it returns data with true of EOS flag at last.
func (c *Conn) Stream() <-chan Data {
	return c.streamDataReceived
}

// Err returns the disconnection error if the connection closed.
func (c *Conn) Err() error {
	return c.err
}

// SendBinaryMessage to the peer. This method is goroutine safe.
func (c *Conn) SendBinaryMessage(data []byte) error {
	return c.sendMessage(Message{websocket.BinaryMessage, data})
}

// SendTextMessage to the peer. This method is goroutine safe.
func (c *Conn) SendTextMessage(text string) error {
	return c.sendMessage(Message{websocket.TextMessage, []byte(text)})
}

func (c *Conn) start(ctx context.Context, settings Settings) {
	c.ctx = ctx
	c.conn.SetReadLimit(settings.MaxMessageSize)
	c.conn.SetReadDeadline(time.Now().Add(settings.PongWait))
	c.conn.SetPingHandler(func(string) error {
		return c.sendMessage(pongMessage)
	})
	c.conn.SetPongHandler(func(string) error {
		return c.conn.SetReadDeadline(time.Now().Add(settings.PongWait))
	})

	c.pingPeriod = settings.PingPeriod
	c.writeWait = settings.WriteWait

	c.streamDataReceived = make(chan Data)
	c.errored = make(chan error, 2)
	c.readPumpFinished = make(chan struct{})
	c.writePumpFinished = make(chan struct{})
	c.sendMessageRequested = make(chan Message, settings.MessageChannelBufferSize)

	go c.writePump()
	go c.readPump()
}

func (c *Conn) sendMessage(m Message) error {
	select {
	case c.sendMessageRequested <- m:
		return nil
	default:
		return ErrMessageChannelFull
	}
}

func (c *Conn) writeMessage(m Message) error {
	if err := c.conn.SetWriteDeadline(time.Now().Add(c.writeWait)); err != nil {
		return err
	}
	if err := c.conn.WriteMessage(int(m.MessageType), m.Data); err != nil {
		return err
	}
	return nil
}

func (c *Conn) writePump() {
	defer c.conn.Close()

	ticker := time.NewTicker(c.pingPeriod)
	defer ticker.Stop()

loop:
	for {
		select {
		case <-c.ctx.Done():
			close(c.sendMessageRequested)
			for m := range c.sendMessageRequested {
				if err := c.writeMessage(m); err != nil {
					c.errored <- err
					break loop
				}
			}
			if err := c.writeMessage(closeMessage); err != nil {
				c.errored <- err
				break loop
			}
			c.errored <- c.ctx.Err()
			break loop
		case <-c.readPumpFinished:
			break loop
		case m := <-c.sendMessageRequested:
			if err := c.writeMessage(m); err != nil {
				c.errored <- err
				break loop
			}
		case <-ticker.C:
			if err := c.writeMessage(pingMessage); err != nil {
				c.errored <- err
				break loop
			}
		}
	}
	close(c.writePumpFinished)
}

func (c *Conn) readPump() {
	defer c.conn.Close()

loop:
	for {
		messageType, data, err := c.conn.ReadMessage()
		if err != nil {
			c.errored <- err
			break loop
		}

		var d Data
		switch messageType {
		case websocket.TextMessage:
			d = Data{Message: Message{TextMessageType, data}}
		case websocket.BinaryMessage:
			d = Data{Message: Message{BinaryMessageType, data}}
		default:
			continue
		}

		select {
		case <-c.ctx.Done():
			c.errored <- c.ctx.Err()
			break loop
		case <-c.writePumpFinished:
			break loop
		case c.streamDataReceived <- d:
			break
		}
	}
	close(c.readPumpFinished)
	<-c.writePumpFinished

	c.err = <-c.errored
	c.streamDataReceived <- Data{EOS: true}
	close(c.streamDataReceived)
}
<|endoftext|>


<commit_before>// Core for web framework
package tori

import "log"
import "net/http"
import "github.com/shiroyuki/re"
import yotsuba "github.com/shiroyuki/yotsuba-go"

serverSignature = "shiroyuki/tori-go/1.0"

type Core struct { // implements http.Handler
    Router     *Router
    Cache      *yotsuba.CacheDriver
    Enigma     *yotsuba.Enigma
    Internal   *http.Server
    Compressed bool
}

// Create a core of the web framework with everything pre-configured for development.
func NewSimpleCore() *Core {
    var enigma = yotsuba.Enigma{}
    var router = NewRouter()
    var actualCacheDriver = yotsuba.NewInMemoryCacheDriver(&enigma, false)
    var castedCacheDriver = yotsuba.CacheDriver(actualCacheDriver)

    return NewCore(
        router,
        &castedCacheDriver,
        &enigma,
        false,
    )
}

func NewCore(
    router     *Router,
    cache      *yotsuba.CacheDriver,
    enigma     *yotsuba.Enigma,
    compressed bool,
) *Core {
    appCore := Core{
        Router:     router,
        Cache:      cache,
        Enigma:     enigma,
        Compressed: compressed,
    }

    internalServer := &http.Server{
        Addr:    "0.0.0.0:8000",
        Handler: &appCore,
    }

    appCore.Internal = internalServer

    return &appCore
}

// Handle the request and delegate the request to a proper handler.
func (self *Core) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    var routingRecord *Record
    var parameters    *re.MultipleResult

    var method string = r.Method
    var path   string = r.URL.Path

    routingRecord, parameters = self.Router.Find(method, path)

    w.Header().Set("Server", serverSignature)

    if routingRecord == nil {
        w.WriteHeader(http.StatusNotFound)
        w.Write([]byte("Not Found"))

        // TODO Event "web.core.error.404@default": allow flexible error handling for HTTP 404.

        return
    }

    handler := NewHandler(routingRecord.Route, &w, r, parameters)
    action  := routingRecord.Action

    // TODO Event "web.handler.pre.<route_id>": allow flexible interceptions before processing requests.

    (*action)(handler)

    // TODO Event "web.handler.post.<route_id>": allow flexible interceptions before processing requests.

    self.response(handler)
}

func (self *Core) response(handler *Handler) {
    var content []byte = handler.Content()

    if !self.Compressed {
        handler.SetContentLength(len(content))
        (*handler.Response).Write(content)

        return
    }

    compressed := self.Enigma.Compress(content)

    handler.SetContentEncoding("gzip")
    handler.SetContentLength(len(compressed))
    (*handler.Response).Write(compressed)
}

func (self *Core) Listen(address *string) {
    if address != nil {
        self.Internal.Addr = *address
    }

    log.Println("Listening at:", self.Internal.Addr)
    log.Fatal("Terminated due to:", self.Internal.ListenAndServe())
}
<commit_msg>Updated according to the sandbox project.<commit_after>// Core for web framework
package tori

import "log"
import "net/http"
import "github.com/shiroyuki/re"
import yotsuba "github.com/shiroyuki/yotsuba-go"

const serverSignature string = "shiroyuki/tori-go/1.0"

type Core struct { // implements http.Handler
    Router     *Router
    Cache      *yotsuba.CacheDriver
    Enigma     *yotsuba.Enigma
    Internal   *http.Server
    Compressed bool
}

// Create a core of the web framework with everything pre-configured for development.
func NewSimpleCore() *Core {
    var enigma = yotsuba.Enigma{}
    var router = NewRouter()
    var actualCacheDriver = yotsuba.NewInMemoryCacheDriver(&enigma, false)
    var castedCacheDriver = yotsuba.CacheDriver(actualCacheDriver)

    return NewCore(
        router,
        &castedCacheDriver,
        &enigma,
        false,
    )
}

func NewCore(
    router     *Router,
    cache      *yotsuba.CacheDriver,
    enigma     *yotsuba.Enigma,
    compressed bool,
) *Core {
    appCore := Core{
        Router:     router,
        Cache:      cache,
        Enigma:     enigma,
        Compressed: compressed,
    }

    internalServer := &http.Server{
        Addr:    "0.0.0.0:8000",
        Handler: &appCore,
    }

    appCore.Internal = internalServer

    return &appCore
}

// Handle the request and delegate the request to a proper handler.
func (self *Core) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    var routingRecord *Record
    var parameters    *re.MultipleResult

    var method string = r.Method
    var path   string = r.URL.Path

    routingRecord, parameters = self.Router.Find(method, path)

    w.Header().Set("Server", serverSignature)

    if routingRecord == nil {
        w.WriteHeader(http.StatusNotFound)
        w.Write([]byte("Not Found"))

        // TODO Event "web.core.error.404@default": allow flexible error handling for HTTP 404.

        return
    }

    handler := NewHandler(routingRecord.Route, &w, r, parameters)
    action  := routingRecord.Action

    // TODO Event "web.handler.pre.<route_id>": allow flexible interceptions before processing requests.

    (*action)(handler)

    // TODO Event "web.handler.post.<route_id>": allow flexible interceptions before processing requests.

    self.response(handler)
}

func (self *Core) response(handler *Handler) {
    var content []byte = handler.Content()

    if !self.Compressed {
        handler.SetContentLength(len(content))
        (*handler.Response).Write(content)

        return
    }

    compressed := self.Enigma.Compress(content)

    handler.SetContentEncoding("gzip")
    handler.SetContentLength(len(compressed))
    (*handler.Response).Write(compressed)
}

func (self *Core) Listen(address *string) {
    if address != nil {
        self.Internal.Addr = *address
    }

    log.Println("Listening at:", self.Internal.Addr)
    log.Fatal("Terminated due to:", self.Internal.ListenAndServe())
}
<|endoftext|>


<commit_before>/*
This code implements the flow chart that can be found here.
http://www.html5rocks.com/static/images/cors_server_flowchart.png

A Default Config for example is below:

	cors.Config{
		Origins:        "*",
		Methods:        "GET, PUT, POST, DELETE",
		RequestHeaders: "Origin, Authorization, Content-Type",
		ExposedHeaders: "",
		MaxAge: 1 * time.Minute,
		Credentials: true,
		ValidateHeaders: false,
	}
*/
package cors

import (
	"fmt"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

const (
	AllowOriginKey      string = "Access-Control-Allow-Origin"
	AllowCredentialsKey        = "Access-Control-Allow-Credentials"
	AllowHeadersKey            = "Access-Control-Allow-Headers"
	AllowMethodsKey            = "Access-Control-Allow-Methods"
	MaxAgeKey                  = "Access-Control-Max-Age"

	OriginKey         = "Origin"
	RequestMethodKey  = "Access-Control-Request-Method"
	RequestHeadersKey = "Access-Control-Request-Headers"
	ExposeHeadersKey  = "Access-Control-Expose-Headers"
)

const (
	optionsMethod = "OPTIONS"
)

/*
Config defines the configuration options available to control how the CORS middleware should function.
*/
type Config struct {
	// Enabling this causes us to compare Request-Method and Request-Headers to confirm they contain a subset of the Allowed Methods and Allowed Headers
	// The spec however allows for the server to always match, and simply return the allowed methods and headers. Either is supported in this middleware.
	ValidateHeaders bool

	// Comma delimited list of origin domains. Wildcard "*" is also allowed, and matches all origins.
	// If the origin does not match an item in the list, then the request is denied.
	Origins string
	origins []string

	// This are the headers that the resource supports, and will accept in the request.
	// Default is "Authorization".
	RequestHeaders string
	requestHeaders []string

	// These are headers that should be accessable by the CORS client, they are in addition to those defined by the spec as "simple response headers"
	//	 Cache-Control
	//	 Content-Language
	//	 Content-Type
	//	 Expires
	//	 Last-Modified
	//	 Pragma
	ExposedHeaders string

	// Comma delimited list of acceptable HTTP methods.
	Methods string
	methods []string

	// The amount of time in seconds that the client should cache the Preflight request
	MaxAge time.Duration
	maxAge string

	// If true, then cookies and Authorization headers are allowed along with the request.  This
	// is passed to the browser, but is not enforced.
	Credentials bool
	credentials string
}

// One time, do the conversion from our the public facing Configuration,
// to all the formats we use internally strings for headers.. slices for looping
func (config *Config) prepare() {
	config.origins = strings.Split(config.Origins, ", ")
	config.methods = strings.Split(config.Methods, ", ")
	config.requestHeaders = strings.Split(config.RequestHeaders, ", ")
	config.maxAge = fmt.Sprintf("%.f", config.MaxAge.Seconds())

	// Generates a boolean of value "true".
	config.credentials = fmt.Sprintf("%t", config.Credentials)

	// Convert to lower-case once as request headers are supposed to be a case-insensitive match
	for idx, header := range config.requestHeaders {
		config.requestHeaders[idx] = strings.ToLower(header)
	}
}

/*
Middleware generates a middleware handler function that works inside of a Gin request
to set the correct CORS headers.  It accepts a cors.Options struct for configuration.
*/
func Middleware(config Config) gin.HandlerFunc {
	forceOriginMatch := false

	if config.Origins == "" {
		panic("You must set at least a single valid origin. If you don't want CORS, to apply, simply remove the middleware.")
	}

	if config.Origins == "*" {
		forceOriginMatch = true
	}

	config.prepare()

	// Create the Middleware function
	return func(context *gin.Context) {
		// Read the Origin header from the HTTP request
		currentOrigin := context.Request.Header.Get(OriginKey)
		context.Writer.Header().Add("Vary", OriginKey)

		// CORS headers are added whenever the browser request includes an "Origin" header
		// However, if no Origin is supplied, they should never be added.
		if currentOrigin == "" {
			return
		}

		originMatch := false
		if !forceOriginMatch {
			originMatch = matchOrigin(currentOrigin, config)
		}

		if forceOriginMatch || originMatch {
			valid := false
			preflight := false

			if context.Request.Method == optionsMethod {
				requestMethod := context.Request.Header.Get(RequestMethodKey)
				if requestMethod != "" {
					preflight = true
					valid = handlePreflight(context, config, requestMethod)
				}
			}

			if !preflight {
				valid = handleRequest(context, config)
			}

			if valid {

				if config.Credentials {
					context.Writer.Header().Set(AllowCredentialsKey, config.credentials)
					// Allowed origins cannot be the string "*" cannot be used for a resource that supports credentials.
					context.Writer.Header().Set(AllowOriginKey, currentOrigin)
				} else if forceOriginMatch {
					context.Writer.Header().Set(AllowOriginKey, "*")
				} else {
					context.Writer.Header().Set(AllowOriginKey, currentOrigin)
				}

				//If this is a preflight request, we are finished, quit.
				//Otherwise this is a normal request and operations should proceed at normal
				if preflight {
					context.AbortWithStatus(200)
				}
				return
			}
		}

		//If it reaches here, it was not a valid request
		context.Abort()
	}
}

func handlePreflight(context *gin.Context, config Config, requestMethod string) bool {
	if ok := validateRequestMethod(requestMethod, config); ok == false {
		return false
	}

	if ok := validateRequestHeaders(context.Request.Header.Get(RequestHeadersKey), config); ok == true {
		context.Writer.Header().Set(AllowMethodsKey, config.Methods)
		context.Writer.Header().Set(AllowHeadersKey, config.RequestHeaders)

		if config.maxAge != "0" {
			context.Writer.Header().Set(MaxAgeKey, config.maxAge)
		}

		return true
	}

	return false
}

func handleRequest(context *gin.Context, config Config) bool {
	if config.ExposedHeaders != "" {
		context.Writer.Header().Set(ExposeHeadersKey, config.ExposedHeaders)
	}

	return true
}

// Case-sensitive match of origin header
func matchOrigin(origin string, config Config) bool {
	for _, value := range config.origins {
		if value == origin {
			return true
		}
	}
	return false
}

// Case-sensitive match of request method
func validateRequestMethod(requestMethod string, config Config) bool {
	if !config.ValidateHeaders {
		return true
	}

	if requestMethod != "" {
		for _, value := range config.methods {
			if value == requestMethod {
				return true
			}
		}
	}

	return false
}

// Case-insensitive match of request headers
func validateRequestHeaders(requestHeaders string, config Config) bool {
	if !config.ValidateHeaders {
		return true
	}

	headers := strings.Split(requestHeaders, ",")

	for _, header := range headers {
		match := false
		header = strings.ToLower(strings.Trim(header, " \t\r\n"))

		for _, value := range config.requestHeaders {
			if value == header {
				match = true
				break
			}
		}

		if !match {
			return false
		}
	}

	return true
}
<commit_msg>Prevent reflection of Origin header<commit_after>/*
This code implements the flow chart that can be found here.
http://www.html5rocks.com/static/images/cors_server_flowchart.png

A Default Config for example is below:

	cors.Config{
		Origins:        "*",
		Methods:        "GET, PUT, POST, DELETE",
		RequestHeaders: "Origin, Authorization, Content-Type",
		ExposedHeaders: "",
		MaxAge: 1 * time.Minute,
		Credentials: true,
		ValidateHeaders: false,
	}
*/
package cors

import (
	"fmt"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

const (
	AllowOriginKey      string = "Access-Control-Allow-Origin"
	AllowCredentialsKey        = "Access-Control-Allow-Credentials"
	AllowHeadersKey            = "Access-Control-Allow-Headers"
	AllowMethodsKey            = "Access-Control-Allow-Methods"
	MaxAgeKey                  = "Access-Control-Max-Age"

	OriginKey         = "Origin"
	RequestMethodKey  = "Access-Control-Request-Method"
	RequestHeadersKey = "Access-Control-Request-Headers"
	ExposeHeadersKey  = "Access-Control-Expose-Headers"
)

const (
	optionsMethod = "OPTIONS"
)

/*
Config defines the configuration options available to control how the CORS middleware should function.
*/
type Config struct {
	// Enabling this causes us to compare Request-Method and Request-Headers to confirm they contain a subset of the Allowed Methods and Allowed Headers
	// The spec however allows for the server to always match, and simply return the allowed methods and headers. Either is supported in this middleware.
	ValidateHeaders bool

	// Comma delimited list of origin domains. Wildcard "*" is also allowed, and matches all origins.
	// If the origin does not match an item in the list, then the request is denied.
	Origins string
	origins []string

	// This are the headers that the resource supports, and will accept in the request.
	// Default is "Authorization".
	RequestHeaders string
	requestHeaders []string

	// These are headers that should be accessable by the CORS client, they are in addition to those defined by the spec as "simple response headers"
	//	 Cache-Control
	//	 Content-Language
	//	 Content-Type
	//	 Expires
	//	 Last-Modified
	//	 Pragma
	ExposedHeaders string

	// Comma delimited list of acceptable HTTP methods.
	Methods string
	methods []string

	// The amount of time in seconds that the client should cache the Preflight request
	MaxAge time.Duration
	maxAge string

	// If true, then cookies and Authorization headers are allowed along with the request.  This
	// is passed to the browser, but is not enforced.
	Credentials bool
	credentials string
}

// One time, do the conversion from our the public facing Configuration,
// to all the formats we use internally strings for headers.. slices for looping
func (config *Config) prepare() {
	if (config.Origins == "*" && config.Credentials == true) {
		panic("Do not use Origins = \"*\" and Credentials = true together.")
	}

	config.origins = strings.Split(config.Origins, ", ")
	config.methods = strings.Split(config.Methods, ", ")
	config.requestHeaders = strings.Split(config.RequestHeaders, ", ")
	config.maxAge = fmt.Sprintf("%.f", config.MaxAge.Seconds())

	// Generates a boolean of value "true".
	config.credentials = fmt.Sprintf("%t", config.Credentials)

	// Convert to lower-case once as request headers are supposed to be a case-insensitive match
	for idx, header := range config.requestHeaders {
		config.requestHeaders[idx] = strings.ToLower(header)
	}
}

/*
Middleware generates a middleware handler function that works inside of a Gin request
to set the correct CORS headers.  It accepts a cors.Options struct for configuration.
*/
func Middleware(config Config) gin.HandlerFunc {
	forceOriginMatch := false

	if config.Origins == "" {
		panic("You must set at least a single valid origin. If you don't want CORS, to apply, simply remove the middleware.")
	}

	if config.Origins == "*" {
		forceOriginMatch = true
	}

	config.prepare()

	// Create the Middleware function
	return func(context *gin.Context) {
		// Read the Origin header from the HTTP request
		currentOrigin := context.Request.Header.Get(OriginKey)
		context.Writer.Header().Add("Vary", OriginKey)

		// CORS headers are added whenever the browser request includes an "Origin" header
		// However, if no Origin is supplied, they should never be added.
		if currentOrigin == "" {
			return
		}

		originMatch := false
		if !forceOriginMatch {
			originMatch = matchOrigin(currentOrigin, config)
		}

		if forceOriginMatch || originMatch {
			valid := false
			preflight := false

			if context.Request.Method == optionsMethod {
				requestMethod := context.Request.Header.Get(RequestMethodKey)
				if requestMethod != "" {
					preflight = true
					valid = handlePreflight(context, config, requestMethod)
				}
			}

			if !preflight {
				valid = handleRequest(context, config)
			}

			if valid {

				if config.Credentials {
					context.Writer.Header().Set(AllowCredentialsKey, config.credentials)
					// Allowed origins cannot be the string "*" cannot be used for a resource that supports credentials.
					context.Writer.Header().Set(AllowOriginKey, currentOrigin)
				} else if forceOriginMatch {
					context.Writer.Header().Set(AllowOriginKey, "*")
				} else {
					context.Writer.Header().Set(AllowOriginKey, currentOrigin)
				}

				//If this is a preflight request, we are finished, quit.
				//Otherwise this is a normal request and operations should proceed at normal
				if preflight {
					context.AbortWithStatus(200)
				}
				return
			}
		}

		//If it reaches here, it was not a valid request
		context.Abort()
	}
}

func handlePreflight(context *gin.Context, config Config, requestMethod string) bool {
	if ok := validateRequestMethod(requestMethod, config); ok == false {
		return false
	}

	if ok := validateRequestHeaders(context.Request.Header.Get(RequestHeadersKey), config); ok == true {
		context.Writer.Header().Set(AllowMethodsKey, config.Methods)
		context.Writer.Header().Set(AllowHeadersKey, config.RequestHeaders)

		if config.maxAge != "0" {
			context.Writer.Header().Set(MaxAgeKey, config.maxAge)
		}

		return true
	}

	return false
}

func handleRequest(context *gin.Context, config Config) bool {
	if config.ExposedHeaders != "" {
		context.Writer.Header().Set(ExposeHeadersKey, config.ExposedHeaders)
	}

	return true
}

// Case-sensitive match of origin header
func matchOrigin(origin string, config Config) bool {
	for _, value := range config.origins {
		if value == origin {
			return true
		}
	}
	return false
}

// Case-sensitive match of request method
func validateRequestMethod(requestMethod string, config Config) bool {
	if !config.ValidateHeaders {
		return true
	}

	if requestMethod != "" {
		for _, value := range config.methods {
			if value == requestMethod {
				return true
			}
		}
	}

	return false
}

// Case-insensitive match of request headers
func validateRequestHeaders(requestHeaders string, config Config) bool {
	if !config.ValidateHeaders {
		return true
	}

	headers := strings.Split(requestHeaders, ",")

	for _, header := range headers {
		match := false
		header = strings.ToLower(strings.Trim(header, " \t\r\n"))

		for _, value := range config.requestHeaders {
			if value == header {
				match = true
				break
			}
		}

		if !match {
			return false
		}
	}

	return true
}
<|endoftext|>


<commit_before>// Copyright 2015 Hajime Hoshi
//
// 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 audio provides audio players.
//
// The stream format must be 16-bit little endian and 2 channels. The format is as follows:
//   [data]      = [sample 1] [sample 2] [sample 3] ...
//   [sample *]  = [channel 1] ...
//   [channel *] = [byte 1] [byte 2] ...
//
// An audio context (audio.Context object) has a sample rate you can specify and all streams you want to play must have the same
// sample rate. However, decoders in e.g. audio/mp3 package adjust sample rate automatically,
// and you don't have to care about it as long as you use those decoders.
//
// An audio context can generate 'players' (audio.Player objects),
// and you can play sound by calling Play function of players.
// When multiple players play, mixing is automatically done.
// Note that too many players may cause distortion.
//
// For the simplest example to play sound, see wav package in the examples.
package audio

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"runtime"
	"sync"
	"time"

	"github.com/hajimehoshi/ebiten/v2/internal/hooks"
)

const (
	channelNum      = 2
	bitDepthInBytes = 2
	bytesPerSample  = bitDepthInBytes * channelNum
)

type newPlayerImpler interface {
	newPlayerImpl(context *Context, src io.Reader) (playerImpl, error)
}

// A Context represents a current state of audio.
//
// At most one Context object can exist in one process.
// This means only one constant sample rate is valid in your one application.
//
// For a typical usage example, see examples/wav/main.go.
type Context struct {
	np newPlayerImpler

	// inited represents whether the audio device is initialized and available or not.
	// On Android, audio loop cannot be started unless JVM is accessible. After updating one frame, JVM should exist.
	inited     chan struct{}
	initedOnce sync.Once

	sampleRate int
	err        error
	ready      bool

	players map[playerImpl]struct{}

	m         sync.Mutex
	semaphore chan struct{}
}

var (
	theContext     *Context
	theContextLock sync.Mutex
)

// NewContext creates a new audio context with the given sample rate.
//
// The sample rate is also used for decoding MP3 with audio/mp3 package
// or other formats as the target sample rate.
//
// sampleRate should be 44100 or 48000.
// Other values might not work.
// For example, 22050 causes error on Safari when decoding MP3.
//
// NewContext panics when an audio context is already created.
func NewContext(sampleRate int) *Context {
	theContextLock.Lock()
	defer theContextLock.Unlock()

	if theContext != nil {
		panic("audio: context is already created")
	}

	var np newPlayerImpler
	if isReaderContextAvailable() {
		// 'Reader players' are players that implement io.Reader. This is the new way and
		// not all the environments support reader players. Reader players can have enough
		// buffers so that clicking noises can be avoided compared to writer players.
		// Reder players will replace writer players in any platforms in the future.
		np = newReaderPlayerFactory(sampleRate)
	} else {
		// 'Writer players' are players that implement io.Writer. This is the old way but
		// all the environments support writer players. Writer players cannot have enough
		// buffers and clicking noises are sometimes problematic (#1356, #1458).
		np = newWriterPlayerFactory(sampleRate)
	}

	c := &Context{
		sampleRate: sampleRate,
		np:         np,
		players:    map[playerImpl]struct{}{},
		inited:     make(chan struct{}),
		semaphore:  make(chan struct{}, 1),
	}
	theContext = c

	h := getHook()
	h.OnSuspendAudio(func() {
		c.semaphore <- struct{}{}
	})
	h.OnResumeAudio(func() {
		<-c.semaphore
	})

	h.AppendHookOnBeforeUpdate(func() error {
		c.initedOnce.Do(func() {
			close(c.inited)
		})

		var err error
		theContextLock.Lock()
		if theContext != nil {
			theContext.m.Lock()
			err = theContext.err
			theContext.m.Unlock()
		}
		theContextLock.Unlock()
		if err != nil {
			return err
		}

		// Now reader players cannot call removePlayers from themselves in the current implementation.
		// Underlying playering can be the pause state after fishing its playing,
		// but there is no way to notify this to readerPlayers so far.
		// Instead, let's check the states proactively every frame.
		for p := range c.players {
			rp, ok := p.(*readerPlayer)
			if !ok {
				break
			}
			if !rp.IsPlaying() {
				delete(c.players, p)
			}
		}
		return nil
	})

	return c
}

// CurrentContext returns the current context or nil if there is no context.
func CurrentContext() *Context {
	theContextLock.Lock()
	c := theContext
	theContextLock.Unlock()
	return c
}

func (c *Context) hasError() bool {
	c.m.Lock()
	r := c.err != nil
	c.m.Unlock()
	return r
}

func (c *Context) setError(err error) {
	// TODO: What if c.err already exists?
	c.m.Lock()
	c.err = err
	c.m.Unlock()
}

func (c *Context) setReady() {
	c.m.Lock()
	c.ready = true
	c.m.Unlock()
}

func (c *Context) addPlayer(p playerImpl) {
	c.m.Lock()
	defer c.m.Unlock()
	c.players[p] = struct{}{}

	// Check the source duplication
	srcs := map[io.Reader]struct{}{}
	for p := range c.players {
		if _, ok := srcs[p.source()]; ok {
			c.err = errors.New("audio: a same source is used by multiple Player")
			return
		}
		srcs[p.source()] = struct{}{}
	}
}

func (c *Context) removePlayer(p playerImpl) {
	c.m.Lock()
	delete(c.players, p)
	c.m.Unlock()
}

// IsReady returns a boolean value indicating whether the audio is ready or not.
//
// On some browsers, user interaction like click or pressing keys is required to start audio.
func (c *Context) IsReady() bool {
	c.m.Lock()
	defer c.m.Unlock()

	r := c.ready
	if r {
		return r
	}
	if len(c.players) != 0 {
		return r
	}

	// Create another goroutine since (*Player).Play can lock the context's mutex.
	go func() {
		// The audio context is never ready unless there is a player. This is
		// problematic when a user tries to play audio after the context is ready.
		// Play a dummy player to avoid the blocking (#969).
		// Use a long enough buffer so that writing doesn't finish immediately (#970).
		p := NewPlayerFromBytes(c, make([]byte, bufferSize()*2))
		p.Play()
	}()

	return r
}

// SampleRate returns the sample rate.
func (c *Context) SampleRate() int {
	return c.sampleRate
}

func (c *Context) acquireSemaphore() {
	c.semaphore <- struct{}{}
}

func (c *Context) releaseSemaphore() {
	<-c.semaphore
}

func (c *Context) waitUntilInited() {
	<-c.inited
}

// Player is an audio player which has one stream.
//
// Even when all references to a Player object is gone,
// the object is not GCed until the player finishes playing.
// This means that if a Player plays an infinite stream,
// the object is never GCed unless Close is called.
type Player struct {
	p playerImpl
}

type playerImpl interface {
	io.Closer

	Play()
	IsPlaying() bool
	Pause()
	Volume() float64
	SetVolume(volume float64)
	Current() time.Duration
	Rewind() error
	Seek(offset time.Duration) error

	source() io.Reader
}

// NewPlayer creates a new player with the given stream.
//
// src's format must be linear PCM (16bits little endian, 2 channel stereo)
// without a header (e.g. RIFF header).
// The sample rate must be same as that of the audio context.
//
// The player is seekable when src is io.Seeker.
// Attempt to seek the player that is not io.Seeker causes panic.
//
// Note that the given src can't be shared with other Player objects.
//
// NewPlayer tries to call Seek of src to get the current position.
// NewPlayer returns error when the Seek returns error.
//
// A Player doesn't close src even if src implements io.Closer.
// Closing the source is src owner's responsibility.
func NewPlayer(context *Context, src io.Reader) (*Player, error) {
	pi, err := context.np.newPlayerImpl(context, src)
	if err != nil {
		return nil, err
	}

	p := &Player{pi}

	runtime.SetFinalizer(p, (*Player).finalize)

	return p, nil
}

// NewPlayerFromBytes creates a new player with the given bytes.
//
// As opposed to NewPlayer, you don't have to care if src is already used by another player or not.
// src can be shared by multiple players.
//
// The format of src should be same as noted at NewPlayer.
func NewPlayerFromBytes(context *Context, src []byte) *Player {
	b := bytes.NewReader(src)
	p, err := NewPlayer(context, b)
	if err != nil {
		// Errors should never happen.
		panic(fmt.Sprintf("audio: %v at NewPlayerFromBytes", err))
	}
	return p
}

func (p *Player) finalize() {
	runtime.SetFinalizer(p, nil)
	if !p.IsPlaying() {
		p.Close()
	}
}

// Close closes the stream.
//
// When Close is called, the stream owned by the player is NOT closed,
// even if the stream implements io.Closer.
//
// Close returns error when the player is already closed.
func (p *Player) Close() error {
	return p.p.Close()
}

// Play plays the stream.
func (p *Player) Play() {
	p.p.Play()
}

// IsPlaying returns boolean indicating whether the player is playing.
func (p *Player) IsPlaying() bool {
	return p.p.IsPlaying()
}

// Rewind rewinds the current position to the start.
//
// The passed source to NewPlayer must be io.Seeker, or Rewind panics.
//
// Rewind returns error when seeking the source stream returns error.
func (p *Player) Rewind() error {
	return p.p.Rewind()
}

// Seek seeks the position with the given offset.
//
// The passed source to NewPlayer must be io.Seeker, or Seek panics.
//
// Seek returns error when seeking the source stream returns error.
func (p *Player) Seek(offset time.Duration) error {
	return p.p.Seek(offset)
}

// Pause pauses the playing.
func (p *Player) Pause() {
	p.p.Pause()
}

// Current returns the current position in time.
func (p *Player) Current() time.Duration {
	return p.p.Current()
}

// Volume returns the current volume of this player [0-1].
func (p *Player) Volume() float64 {
	return p.p.Volume()
}

// SetVolume sets the volume of this player.
// volume must be in between 0 and 1. SetVolume panics otherwise.
func (p *Player) SetVolume(volume float64) {
	p.p.SetVolume(volume)
}

type hook interface {
	OnSuspendAudio(f func())
	OnResumeAudio(f func())
	AppendHookOnBeforeUpdate(f func() error)
}

var hookForTesting hook

func getHook() hook {
	if hookForTesting != nil {
		return hookForTesting
	}
	return &hookImpl{}
}

type hookImpl struct{}

func (h *hookImpl) OnSuspendAudio(f func()) {
	hooks.OnSuspendAudio(f)
}

func (h *hookImpl) OnResumeAudio(f func()) {
	hooks.OnResumeAudio(f)
}

func (h *hookImpl) AppendHookOnBeforeUpdate(f func() error) {
	hooks.AppendHookOnBeforeUpdate(f)
}
<commit_msg>audio: Bug fix: Race condition on accessing the set of players<commit_after>// Copyright 2015 Hajime Hoshi
//
// 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 audio provides audio players.
//
// The stream format must be 16-bit little endian and 2 channels. The format is as follows:
//   [data]      = [sample 1] [sample 2] [sample 3] ...
//   [sample *]  = [channel 1] ...
//   [channel *] = [byte 1] [byte 2] ...
//
// An audio context (audio.Context object) has a sample rate you can specify and all streams you want to play must have the same
// sample rate. However, decoders in e.g. audio/mp3 package adjust sample rate automatically,
// and you don't have to care about it as long as you use those decoders.
//
// An audio context can generate 'players' (audio.Player objects),
// and you can play sound by calling Play function of players.
// When multiple players play, mixing is automatically done.
// Note that too many players may cause distortion.
//
// For the simplest example to play sound, see wav package in the examples.
package audio

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"runtime"
	"sync"
	"time"

	"github.com/hajimehoshi/ebiten/v2/internal/hooks"
)

const (
	channelNum      = 2
	bitDepthInBytes = 2
	bytesPerSample  = bitDepthInBytes * channelNum
)

type newPlayerImpler interface {
	newPlayerImpl(context *Context, src io.Reader) (playerImpl, error)
}

// A Context represents a current state of audio.
//
// At most one Context object can exist in one process.
// This means only one constant sample rate is valid in your one application.
//
// For a typical usage example, see examples/wav/main.go.
type Context struct {
	np newPlayerImpler

	// inited represents whether the audio device is initialized and available or not.
	// On Android, audio loop cannot be started unless JVM is accessible. After updating one frame, JVM should exist.
	inited     chan struct{}
	initedOnce sync.Once

	sampleRate int
	err        error
	ready      bool

	players map[playerImpl]struct{}

	m         sync.Mutex
	semaphore chan struct{}
}

var (
	theContext     *Context
	theContextLock sync.Mutex
)

// NewContext creates a new audio context with the given sample rate.
//
// The sample rate is also used for decoding MP3 with audio/mp3 package
// or other formats as the target sample rate.
//
// sampleRate should be 44100 or 48000.
// Other values might not work.
// For example, 22050 causes error on Safari when decoding MP3.
//
// NewContext panics when an audio context is already created.
func NewContext(sampleRate int) *Context {
	theContextLock.Lock()
	defer theContextLock.Unlock()

	if theContext != nil {
		panic("audio: context is already created")
	}

	var np newPlayerImpler
	if isReaderContextAvailable() {
		// 'Reader players' are players that implement io.Reader. This is the new way and
		// not all the environments support reader players. Reader players can have enough
		// buffers so that clicking noises can be avoided compared to writer players.
		// Reder players will replace writer players in any platforms in the future.
		np = newReaderPlayerFactory(sampleRate)
	} else {
		// 'Writer players' are players that implement io.Writer. This is the old way but
		// all the environments support writer players. Writer players cannot have enough
		// buffers and clicking noises are sometimes problematic (#1356, #1458).
		np = newWriterPlayerFactory(sampleRate)
	}

	c := &Context{
		sampleRate: sampleRate,
		np:         np,
		players:    map[playerImpl]struct{}{},
		inited:     make(chan struct{}),
		semaphore:  make(chan struct{}, 1),
	}
	theContext = c

	h := getHook()
	h.OnSuspendAudio(func() {
		c.semaphore <- struct{}{}
	})
	h.OnResumeAudio(func() {
		<-c.semaphore
	})

	h.AppendHookOnBeforeUpdate(func() error {
		c.initedOnce.Do(func() {
			close(c.inited)
		})

		var err error
		theContextLock.Lock()
		if theContext != nil {
			theContext.m.Lock()
			err = theContext.err
			theContext.m.Unlock()
		}
		theContextLock.Unlock()
		if err != nil {
			return err
		}

		c.gcPlayers()
		return nil
	})

	return c
}

// CurrentContext returns the current context or nil if there is no context.
func CurrentContext() *Context {
	theContextLock.Lock()
	c := theContext
	theContextLock.Unlock()
	return c
}

func (c *Context) hasError() bool {
	c.m.Lock()
	r := c.err != nil
	c.m.Unlock()
	return r
}

func (c *Context) setError(err error) {
	// TODO: What if c.err already exists?
	c.m.Lock()
	c.err = err
	c.m.Unlock()
}

func (c *Context) setReady() {
	c.m.Lock()
	c.ready = true
	c.m.Unlock()
}

func (c *Context) addPlayer(p playerImpl) {
	c.m.Lock()
	defer c.m.Unlock()
	c.players[p] = struct{}{}

	// Check the source duplication
	srcs := map[io.Reader]struct{}{}
	for p := range c.players {
		if _, ok := srcs[p.source()]; ok {
			c.err = errors.New("audio: a same source is used by multiple Player")
			return
		}
		srcs[p.source()] = struct{}{}
	}
}

func (c *Context) removePlayer(p playerImpl) {
	c.m.Lock()
	delete(c.players, p)
	c.m.Unlock()
}

func (c *Context) gcPlayers() {
	c.m.Lock()
	defer c.m.Unlock()

	// Now reader players cannot call removePlayers from themselves in the current implementation.
	// Underlying playering can be the pause state after fishing its playing,
	// but there is no way to notify this to readerPlayers so far.
	// Instead, let's check the states proactively every frame.
	for p := range c.players {
		rp, ok := p.(*readerPlayer)
		if !ok {
			return
		}
		if !rp.IsPlaying() {
			delete(c.players, p)
		}
	}
}

// IsReady returns a boolean value indicating whether the audio is ready or not.
//
// On some browsers, user interaction like click or pressing keys is required to start audio.
func (c *Context) IsReady() bool {
	c.m.Lock()
	defer c.m.Unlock()

	r := c.ready
	if r {
		return r
	}
	if len(c.players) != 0 {
		return r
	}

	// Create another goroutine since (*Player).Play can lock the context's mutex.
	go func() {
		// The audio context is never ready unless there is a player. This is
		// problematic when a user tries to play audio after the context is ready.
		// Play a dummy player to avoid the blocking (#969).
		// Use a long enough buffer so that writing doesn't finish immediately (#970).
		p := NewPlayerFromBytes(c, make([]byte, bufferSize()*2))
		p.Play()
	}()

	return r
}

// SampleRate returns the sample rate.
func (c *Context) SampleRate() int {
	return c.sampleRate
}

func (c *Context) acquireSemaphore() {
	c.semaphore <- struct{}{}
}

func (c *Context) releaseSemaphore() {
	<-c.semaphore
}

func (c *Context) waitUntilInited() {
	<-c.inited
}

// Player is an audio player which has one stream.
//
// Even when all references to a Player object is gone,
// the object is not GCed until the player finishes playing.
// This means that if a Player plays an infinite stream,
// the object is never GCed unless Close is called.
type Player struct {
	p playerImpl
}

type playerImpl interface {
	io.Closer

	Play()
	IsPlaying() bool
	Pause()
	Volume() float64
	SetVolume(volume float64)
	Current() time.Duration
	Rewind() error
	Seek(offset time.Duration) error

	source() io.Reader
}

// NewPlayer creates a new player with the given stream.
//
// src's format must be linear PCM (16bits little endian, 2 channel stereo)
// without a header (e.g. RIFF header).
// The sample rate must be same as that of the audio context.
//
// The player is seekable when src is io.Seeker.
// Attempt to seek the player that is not io.Seeker causes panic.
//
// Note that the given src can't be shared with other Player objects.
//
// NewPlayer tries to call Seek of src to get the current position.
// NewPlayer returns error when the Seek returns error.
//
// A Player doesn't close src even if src implements io.Closer.
// Closing the source is src owner's responsibility.
func NewPlayer(context *Context, src io.Reader) (*Player, error) {
	pi, err := context.np.newPlayerImpl(context, src)
	if err != nil {
		return nil, err
	}

	p := &Player{pi}

	runtime.SetFinalizer(p, (*Player).finalize)

	return p, nil
}

// NewPlayerFromBytes creates a new player with the given bytes.
//
// As opposed to NewPlayer, you don't have to care if src is already used by another player or not.
// src can be shared by multiple players.
//
// The format of src should be same as noted at NewPlayer.
func NewPlayerFromBytes(context *Context, src []byte) *Player {
	b := bytes.NewReader(src)
	p, err := NewPlayer(context, b)
	if err != nil {
		// Errors should never happen.
		panic(fmt.Sprintf("audio: %v at NewPlayerFromBytes", err))
	}
	return p
}

func (p *Player) finalize() {
	runtime.SetFinalizer(p, nil)
	if !p.IsPlaying() {
		p.Close()
	}
}

// Close closes the stream.
//
// When Close is called, the stream owned by the player is NOT closed,
// even if the stream implements io.Closer.
//
// Close returns error when the player is already closed.
func (p *Player) Close() error {
	return p.p.Close()
}

// Play plays the stream.
func (p *Player) Play() {
	p.p.Play()
}

// IsPlaying returns boolean indicating whether the player is playing.
func (p *Player) IsPlaying() bool {
	return p.p.IsPlaying()
}

// Rewind rewinds the current position to the start.
//
// The passed source to NewPlayer must be io.Seeker, or Rewind panics.
//
// Rewind returns error when seeking the source stream returns error.
func (p *Player) Rewind() error {
	return p.p.Rewind()
}

// Seek seeks the position with the given offset.
//
// The passed source to NewPlayer must be io.Seeker, or Seek panics.
//
// Seek returns error when seeking the source stream returns error.
func (p *Player) Seek(offset time.Duration) error {
	return p.p.Seek(offset)
}

// Pause pauses the playing.
func (p *Player) Pause() {
	p.p.Pause()
}

// Current returns the current position in time.
func (p *Player) Current() time.Duration {
	return p.p.Current()
}

// Volume returns the current volume of this player [0-1].
func (p *Player) Volume() float64 {
	return p.p.Volume()
}

// SetVolume sets the volume of this player.
// volume must be in between 0 and 1. SetVolume panics otherwise.
func (p *Player) SetVolume(volume float64) {
	p.p.SetVolume(volume)
}

type hook interface {
	OnSuspendAudio(f func())
	OnResumeAudio(f func())
	AppendHookOnBeforeUpdate(f func() error)
}

var hookForTesting hook

func getHook() hook {
	if hookForTesting != nil {
		return hookForTesting
	}
	return &hookImpl{}
}

type hookImpl struct{}

func (h *hookImpl) OnSuspendAudio(f func()) {
	hooks.OnSuspendAudio(f)
}

func (h *hookImpl) OnResumeAudio(f func()) {
	hooks.OnResumeAudio(f)
}

func (h *hookImpl) AppendHookOnBeforeUpdate(f func() error) {
	hooks.AppendHookOnBeforeUpdate(f)
}
<|endoftext|>


<commit_before>package scenario

import (
	"fmt"
	"time"

	"encoding/json"

	"github.com/catatsuy/isucon6-final/bench/session"
	"github.com/catatsuy/isucon6-final/bench/sse"
)

type RoomWatcher struct {
	EndCh  chan struct{}
	Logs   []StrokeLog
	Errors []string

	es     *sse.EventSource
	isLeft bool
}

func NewRoomWatcher(target string, roomID int64) *RoomWatcher {
	w := &RoomWatcher{
		EndCh:  make(chan struct{}, 1),
		Logs:   make([]StrokeLog, 0),
		Errors: make([]string, 0),
		isLeft: false,
	}

	go w.watch(target, roomID)

	return w
}

// 描いたstrokeがこの時間以上経ってから届いたら、ユーザーがストレスに感じてタブを閉じる、という設定にした。
const thresholdResponseTime = 5 * time.Second

func (w *RoomWatcher) watch(target string, roomID int64) {

	// TODO:用途がだいぶ特殊なので普通のベンチマークと同じsessionを使うべきか悩ましい
	s := session.New(target)
	s.Client.Timeout = 3 * time.Second

	path := fmt.Sprintf("/rooms/%d", roomID)

	token, ok := fetchCSRFToken(s, path)
	if !ok {
		w.EndCh <- struct{}{}
		return
	}

	startTime := time.Now()
	path = "/api/stream" + path

	if w.isLeft {
		w.EndCh <- struct{}{}
		return
	}
	w.es = sse.NewEventSource(s.Client, target+path+"?csrf_token="+token)
	w.es.AddHeader("User-Agent", s.UserAgent)

	w.es.On("stroke", func(data string) {
		var stroke Stroke
		err := json.Unmarshal([]byte(data), &stroke)
		if err != nil {
			fmt.Println(err)
			w.addError(path + ", jsonのデコードに失敗しました")
			w.es.Close()
		}
		now := time.Now()
		// strokes APIには最初はLast-Event-IDをつけずに送るので、これまでに描かれたstrokeが全部降ってくるが、それは無視する。
		if stroke.CreatedAt.After(startTime) && now.Sub(stroke.CreatedAt) > thresholdResponseTime {
			fmt.Println("response too late")
			w.es.Close()
		}
		w.Logs = append(w.Logs, StrokeLog{
			ReceivedTime: now,
			RoomID:       roomID,
			StrokeID:     stroke.ID,
		})
	})
	w.es.On("bad_request", func(data string) {
		w.addError(path + " bad_request: " + data)
		w.es.Close()
	})
	//w.es.On("watcher_count", func(data string) {
	//	fmt.Println("watcher_count")
	//	fmt.Println(data)
	//})
	w.es.OnError(func(err error) {
		if e, ok := err.(*sse.BadContentType); ok {
			w.addError(path + " Content-Typeが正しくありません: " + e.ContentType)
			return
		}
		if e, ok := err.(*sse.BadStatusCode); ok {
			w.addError(fmt.Sprintf("%s ステータスコードが正しくありません: %d\n", path, e.StatusCode))
			w.es.Close()
			return
		}
		//fmt.Println(err)
		w.addError(path + " 予期せぬエラー")
	})
	w.es.OnEnd(func() {
		w.EndCh <- struct{}{}
	})

	w.es.Start()
}

func (w *RoomWatcher) addError(msg string) {
	w.Errors = append(w.Errors, msg)
}

func (w *RoomWatcher) Leave() {
	w.isLeft = true
	if w.es != nil {
		w.es.Close()
	}
}
<commit_msg>Write errors directly by fails.Add<commit_after>package scenario

import (
	"fmt"
	"time"

	"encoding/json"

	"github.com/catatsuy/isucon6-final/bench/fails"
	"github.com/catatsuy/isucon6-final/bench/session"
	"github.com/catatsuy/isucon6-final/bench/sse"
)

type RoomWatcher struct {
	EndCh chan struct{}
	Logs  []StrokeLog

	es     *sse.EventSource
	isLeft bool
}

func NewRoomWatcher(target string, roomID int64) *RoomWatcher {
	w := &RoomWatcher{
		EndCh:  make(chan struct{}, 1),
		Logs:   make([]StrokeLog, 0),
		isLeft: false,
	}

	go w.watch(target, roomID)

	return w
}

// 描いたstrokeがこの時間以上経ってから届いたら、ユーザーがストレスに感じてタブを閉じる、という設定にした。
const thresholdResponseTime = 5 * time.Second

func (w *RoomWatcher) watch(target string, roomID int64) {

	// TODO:用途がだいぶ特殊なので普通のベンチマークと同じsessionを使うべきか悩ましい
	s := session.New(target)
	s.Client.Timeout = 3 * time.Second

	path := fmt.Sprintf("/rooms/%d", roomID)
	l := &fails.Logger{Prefix: "[" + path + "] "}

	token, ok := fetchCSRFToken(s, path)
	if !ok {
		w.EndCh <- struct{}{}
		return
	}

	startTime := time.Now()
	path = "/api/stream" + path

	if w.isLeft {
		w.EndCh <- struct{}{}
		return
	}
	w.es = sse.NewEventSource(s.Client, target+path+"?csrf_token="+token)
	w.es.AddHeader("User-Agent", s.UserAgent)

	w.es.On("stroke", func(data string) {
		var stroke Stroke
		err := json.Unmarshal([]byte(data), &stroke)
		if err != nil {
			l.Add("jsonのデコードに失敗しました", err)
			w.es.Close()
		}
		now := time.Now()
		// strokes APIには最初はLast-Event-IDをつけずに送るので、これまでに描かれたstrokeが全部降ってくるが、それは無視する。
		if stroke.CreatedAt.After(startTime) && now.Sub(stroke.CreatedAt) > thresholdResponseTime {
			l.Add("strokeが届くまでに時間がかかりすぎています", nil)
			w.es.Close()
		}
		w.Logs = append(w.Logs, StrokeLog{
			ReceivedTime: now,
			RoomID:       roomID,
			StrokeID:     stroke.ID,
		})
	})
	w.es.On("bad_request", func(data string) {
		l.Add("bad_request: "+data, nil)
		w.es.Close()
	})
	//w.es.On("watcher_count", func(data string) {
	//	fmt.Println("watcher_count")
	//	fmt.Println(data)
	//})
	w.es.OnError(func(err error) {
		if e, ok := err.(*sse.BadContentType); ok {
			l.Add(path+" Content-Typeが正しくありません: "+e.ContentType, err)
			return
		}
		if e, ok := err.(*sse.BadStatusCode); ok {
			l.Add(fmt.Sprintf("ステータスコードが正しくありません: %d", e.StatusCode), err)
			w.es.Close()
			return
		}
		l.Add("予期せぬエラー（主催者に連絡してください）", err)
	})
	w.es.OnEnd(func() {
		w.EndCh <- struct{}{}
	})

	w.es.Start()
}

func (w *RoomWatcher) Leave() {
	w.isLeft = true
	if w.es != nil {
		w.es.Close()
	}
}
<|endoftext|>


<commit_before>package smartcrop

import (
	"errors"
	"fmt"
	"image"
	"image/color"
	"image/jpeg"
	_ "image/png"
	"math"
	"os"
	"time"
)

var (
	aspect                  = 0
	cropWidth               = 0.0
	cropHeight              = 0.0
	detailWeight            = 0.2
	skinColor               = [3]float64{0.78, 0.57, 0.44}
	skinBias                = 0.01
	skinBrightnessMin       = 0.2
	skinBrightnessMax       = 1.0
	skinThreshold           = 0.8
	skinWeight              = 1.8
	saturationBrightnessMin = 0.25
	saturationBrightnessMax = 0.9
	saturationThreshold     = 0.4
	saturationBias          = 0.2
	saturationWeight        = 0.3
	// step * minscale rounded down to the next power of two should be good
	scoreDownSample   = 8
	step              = 8
	scaleStep         = 0.1
	minScale          = 0.9
	maxScale          = 1.0
	edgeRadius        = 0.4
	edgeWeight        = -20.0
	outsideImportance = -0.5
	ruleOfThirds      = true
	prescale          = true
	debug             = false
)

type Score struct {
	Detail     float64
	Saturation float64
	Skin       float64
	Total      float64
}

type Crop struct {
	X      int
	Y      int
	Width  int
	Height int
	Score  Score
}

func SmartCrop(img *image.Image, width, height int) (Crop, error) {
	if width == 0 && height == 0 {
		return Crop{}, errors.New("Expect either a height or width")
	}

	scale := math.Min(float64((*img).Bounds().Size().X)/float64(width), float64((*img).Bounds().Size().Y)/float64(height))
	cropWidth, cropHeight = math.Floor(float64(width)*scale), math.Floor(float64(height)*scale)
	minScale = math.Min(maxScale, math.Max(1.0/scale, minScale))

	fmt.Printf("original resolution: %dx%d\n", (*img).Bounds().Size().X, (*img).Bounds().Size().Y)
	fmt.Printf("scale: %f, cropw: %f, croph: %f, minscale: %f\n", scale, cropWidth, cropHeight, minScale)

	topCrop := analyse(img)
	return topCrop, nil
}

func thirds(x float64) float64 {
	x1 := int(x - (1.0 / 3.0) + 1.0)
	res := (float64(x1%2.0) * 0.5) - 0.5
	return res * 16.0
}

func importance(crop *Crop, x, y int) float64 {
	if crop.X > x || x >= crop.X+crop.Width || crop.Y > y || y >= crop.Y+crop.Height {
		return outsideImportance
	}

	xf := float64(x-crop.X) / float64(crop.Width)
	yf := float64(y-crop.Y) / float64(crop.Height)

	px := math.Abs(0.5-xf) * 2.0
	py := math.Abs(0.5-yf) * 2.0

	dx := math.Max(px-1.0+edgeRadius, 0.0)
	dy := math.Max(py-1.0+edgeRadius, 0.0)
	d := (math.Pow(dx, 2) + math.Pow(dy, 2)) * edgeWeight

	s := 1.41 - math.Sqrt(math.Pow(px, 2)+math.Pow(py, 2))
	if ruleOfThirds {
		s += (math.Max(0.0, s+d+0.5) * 1.2) * (thirds(px) + thirds(py))
	}

	return s + d
}

func score(output *image.Image, crop *Crop) Score {
	o := (*output).(*image.RGBA)
	height := (*output).Bounds().Size().Y
	width := (*output).Bounds().Size().X
	score := Score{}

	for y := 0; y < height; y++ {
		yoffset := y * width
		ydownSample := y * scoreDownSample
		for x := 0; x < width; x++ {
			//			now := time.Now()
			imp := importance(crop, x*scoreDownSample, ydownSample)
			//			fmt.Println("Time elapsed single-imp:", time.Since(now))

			p := yoffset + x * 4

			r8 := float64(o.Pix[p]) / 255.0
			g8 := float64(o.Pix[p+1]) / 255.0
			b8 := float64(o.Pix[p+2]) / 255.0

			score.Skin += r8 * (g8 + skinBias) * imp
			score.Detail += g8 * imp
			score.Saturation += b8 * (g8 + saturationBias) * imp
		}
	}

	score.Total = (score.Detail*detailWeight + score.Skin*skinWeight + score.Saturation*saturationWeight) / float64(crop.Width) / float64(crop.Height)
	return score
}

func writeImage(img *image.Image, name string) {
	fso, err := os.Create(name)
	if err != nil {
		panic(err)
	}
	defer fso.Close()

	jpeg.Encode(fso, (*img), &jpeg.Options{Quality: 90})
	fso.Close()
}

func analyse(img *image.Image) Crop {
	o := image.Image(image.NewRGBA((*img).Bounds()))

	now := time.Now()
	edgeDetect(img, &o)
	fmt.Println("Time elapsed edge:", time.Since(now))
//	writeImage(&o, "/tmp/foo_step1.jpg")

	now = time.Now()
	skinDetect(img, &o)
	fmt.Println("Time elapsed skin:", time.Since(now))
//	writeImage(&o, "/tmp/foo_step2.jpg")

	now = time.Now()
	saturationDetect(img, &o)
	fmt.Println("Time elapsed sat:", time.Since(now))
//	writeImage(&o, "/tmp/foo_step3.jpg")

	now = time.Now()
	var topCrop Crop
	topScore := -1.0
	cs := crops(&o)
	fmt.Println("Time elapsed crops:", time.Since(now), len(cs))

	now = time.Now()
	for _, crop := range cs {
		//		nowIn := time.Now()
		crop.Score = score(&o, &crop)
		//		fmt.Println("Time elapsed single-score:", time.Since(nowIn))
		if crop.Score.Total > topScore {
			topCrop = crop
			topScore = crop.Score.Total
		}
	}
	fmt.Println("Time elapsed score:", time.Since(now))

	return topCrop
}

func saturation(c color.Color) float64 {
	r, g, b, _ := c.RGBA()
	r8 := float64(r >> 8)
	g8 := float64(g >> 8)
	b8 := float64(b >> 8)

	maximum := math.Max(math.Max(r8/255.0, g8/255.0), b8/255.0)
	minimum := math.Min(math.Min(r8/255.0, g8/255.0), b8/255.0)

	if maximum == minimum {
		return 0
	}

	l := (maximum + minimum) / 2.0
	d := maximum - minimum

	if l > 0.5 {
		return d / (2.0 - maximum - minimum)
	} else {
		return d / (maximum + minimum)
	}
}

func cie(c color.Color) float64 {
	r, g, b, _ := c.RGBA()
	r8 := float64(r >> 8)
	g8 := float64(g >> 8)
	b8 := float64(b >> 8)

	return 0.5126*b8 + 0.7152*g8 + 0.0722*r8
}

func skinCol(c color.Color) float64 {
	r, g, b, _ := c.RGBA()
	r8 := float64(r >> 8)
	g8 := float64(g >> 8)
	b8 := float64(b >> 8)

	mag := math.Sqrt(math.Pow(r8, 2) + math.Pow(g8, 2) + math.Pow(b8, 2))
	rd := r8/mag - skinColor[0]
	gd := g8/mag - skinColor[1]
	bd := b8/mag - skinColor[2]

	d := math.Sqrt(math.Pow(rd, 2) + math.Pow(gd, 2) + math.Pow(bd, 2))
	return 1.0 - d
}

func edgeDetect(i *image.Image, o *image.Image) {
	w := (*i).Bounds().Size().X
	h := (*i).Bounds().Size().Y

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			var lightness float64

			if x == 0 || x >= w-1 || y == 0 || y >= h-1 {
				lightness = cie((*i).At(x, y))
			} else {
				lightness = cie((*i).At(x, y))*4.0 -
					cie((*i).At(x, y-1)) -
					cie((*i).At(x-1, y)) -
					cie((*i).At(x+1, y)) -
					cie((*i).At(x, y+1))
			}

			if lightness < 0 {
				continue
			}

			nc := color.RGBA{uint8(lightness), uint8(lightness), uint8(lightness), 255}
			(*o).(*image.RGBA).Set(x, y, nc)
		}
	}
}

func skinDetect(i *image.Image, o *image.Image) {
	w := (*i).Bounds().Size().X
	h := (*i).Bounds().Size().Y

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			lightness := cie((*i).At(x, y)) / 255.0
			skin := skinCol((*i).At(x, y))

			if skin > skinThreshold && lightness >= skinBrightnessMin && lightness <= skinBrightnessMax {
				r := (skin - skinThreshold) * (255.0 / (1.0 - skinThreshold))
				_, g, b, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{uint8(r), uint8(g >> 8), uint8(b >> 8), 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			} else {
				_, g, b, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{0, uint8(g >> 8), uint8(b >> 8), 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			}
		}
	}
}

func saturationDetect(i *image.Image, o *image.Image) {
	w := (*i).Bounds().Size().X
	h := (*i).Bounds().Size().Y

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			lightness := cie((*i).At(x, y)) / 255.0
			saturation := saturation((*i).At(x, y))

			if saturation > saturationThreshold && lightness >= saturationBrightnessMin && lightness <= saturationBrightnessMax {
				b := (saturation - saturationThreshold) * (255.0 / (1.0 - saturationThreshold))
				r, g, _, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b), 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			} else {
				r, g, _, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{uint8(r >> 8), uint8(g >> 8), 0, 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			}
		}
	}
}

func crops(i *image.Image) []Crop {
	res := []Crop{}
	width := (*i).Bounds().Size().X
	height := (*i).Bounds().Size().Y

	//minDimension := math.Min(float64(width), float64(height))
	cropW := cropWidth  //|| minDimension
	cropH := cropHeight //|| minDimension

	for scale := maxScale; scale >= minScale; scale -= scaleStep {
		for y := 0; float64(y)+cropH*scale <= float64(height); y += step {
			for x := 0; float64(x)+cropW*scale <= float64(width); x += step {
				res = append(res, Crop{
					X:      x,
					Y:      y,
					Width:  int(cropW * scale),
					Height: int(cropH * scale),
				})
			}
		}
	}

	return res
}
<commit_msg>* Obsolete Close()<commit_after>package smartcrop

import (
	"errors"
	"fmt"
	"image"
	"image/color"
	"image/jpeg"
	_ "image/png"
	"math"
	"os"
	"time"
)

var (
	aspect                  = 0
	cropWidth               = 0.0
	cropHeight              = 0.0
	detailWeight            = 0.2
	skinColor               = [3]float64{0.78, 0.57, 0.44}
	skinBias                = 0.01
	skinBrightnessMin       = 0.2
	skinBrightnessMax       = 1.0
	skinThreshold           = 0.8
	skinWeight              = 1.8
	saturationBrightnessMin = 0.25
	saturationBrightnessMax = 0.9
	saturationThreshold     = 0.4
	saturationBias          = 0.2
	saturationWeight        = 0.3
	// step * minscale rounded down to the next power of two should be good
	scoreDownSample   = 8
	step              = 8
	scaleStep         = 0.1
	minScale          = 0.9
	maxScale          = 1.0
	edgeRadius        = 0.4
	edgeWeight        = -20.0
	outsideImportance = -0.5
	ruleOfThirds      = true
	prescale          = true
	debug             = false
)

type Score struct {
	Detail     float64
	Saturation float64
	Skin       float64
	Total      float64
}

type Crop struct {
	X      int
	Y      int
	Width  int
	Height int
	Score  Score
}

func SmartCrop(img *image.Image, width, height int) (Crop, error) {
	if width == 0 && height == 0 {
		return Crop{}, errors.New("Expect either a height or width")
	}

	scale := math.Min(float64((*img).Bounds().Size().X)/float64(width), float64((*img).Bounds().Size().Y)/float64(height))
	cropWidth, cropHeight = math.Floor(float64(width)*scale), math.Floor(float64(height)*scale)
	minScale = math.Min(maxScale, math.Max(1.0/scale, minScale))

	fmt.Printf("original resolution: %dx%d\n", (*img).Bounds().Size().X, (*img).Bounds().Size().Y)
	fmt.Printf("scale: %f, cropw: %f, croph: %f, minscale: %f\n", scale, cropWidth, cropHeight, minScale)

	topCrop := analyse(img)
	return topCrop, nil
}

func thirds(x float64) float64 {
	x1 := int(x - (1.0 / 3.0) + 1.0)
	res := (float64(x1%2.0) * 0.5) - 0.5
	return res * 16.0
}

func importance(crop *Crop, x, y int) float64 {
	if crop.X > x || x >= crop.X+crop.Width || crop.Y > y || y >= crop.Y+crop.Height {
		return outsideImportance
	}

	xf := float64(x-crop.X) / float64(crop.Width)
	yf := float64(y-crop.Y) / float64(crop.Height)

	px := math.Abs(0.5-xf) * 2.0
	py := math.Abs(0.5-yf) * 2.0

	dx := math.Max(px-1.0+edgeRadius, 0.0)
	dy := math.Max(py-1.0+edgeRadius, 0.0)
	d := (math.Pow(dx, 2) + math.Pow(dy, 2)) * edgeWeight

	s := 1.41 - math.Sqrt(math.Pow(px, 2)+math.Pow(py, 2))
	if ruleOfThirds {
		s += (math.Max(0.0, s+d+0.5) * 1.2) * (thirds(px) + thirds(py))
	}

	return s + d
}

func score(output *image.Image, crop *Crop) Score {
	o := (*output).(*image.RGBA)
	height := (*output).Bounds().Size().Y
	width := (*output).Bounds().Size().X
	score := Score{}

	for y := 0; y < height; y++ {
		yoffset := y * width
		ydownSample := y * scoreDownSample
		for x := 0; x < width; x++ {
			//			now := time.Now()
			imp := importance(crop, x*scoreDownSample, ydownSample)
			//			fmt.Println("Time elapsed single-imp:", time.Since(now))

			p := yoffset + x * 4

			r8 := float64(o.Pix[p]) / 255.0
			g8 := float64(o.Pix[p+1]) / 255.0
			b8 := float64(o.Pix[p+2]) / 255.0

			score.Skin += r8 * (g8 + skinBias) * imp
			score.Detail += g8 * imp
			score.Saturation += b8 * (g8 + saturationBias) * imp
		}
	}

	score.Total = (score.Detail*detailWeight + score.Skin*skinWeight + score.Saturation*saturationWeight) / float64(crop.Width) / float64(crop.Height)
	return score
}

func writeImage(img *image.Image, name string) {
	fso, err := os.Create(name)
	if err != nil {
		panic(err)
	}
	defer fso.Close()

	jpeg.Encode(fso, (*img), &jpeg.Options{Quality: 90})
}

func analyse(img *image.Image) Crop {
	o := image.Image(image.NewRGBA((*img).Bounds()))

	now := time.Now()
	edgeDetect(img, &o)
	fmt.Println("Time elapsed edge:", time.Since(now))
//	writeImage(&o, "/tmp/foo_step1.jpg")

	now = time.Now()
	skinDetect(img, &o)
	fmt.Println("Time elapsed skin:", time.Since(now))
//	writeImage(&o, "/tmp/foo_step2.jpg")

	now = time.Now()
	saturationDetect(img, &o)
	fmt.Println("Time elapsed sat:", time.Since(now))
//	writeImage(&o, "/tmp/foo_step3.jpg")

	now = time.Now()
	var topCrop Crop
	topScore := -1.0
	cs := crops(&o)
	fmt.Println("Time elapsed crops:", time.Since(now), len(cs))

	now = time.Now()
	for _, crop := range cs {
		//		nowIn := time.Now()
		crop.Score = score(&o, &crop)
		//		fmt.Println("Time elapsed single-score:", time.Since(nowIn))
		if crop.Score.Total > topScore {
			topCrop = crop
			topScore = crop.Score.Total
		}
	}
	fmt.Println("Time elapsed score:", time.Since(now))

	return topCrop
}

func saturation(c color.Color) float64 {
	r, g, b, _ := c.RGBA()
	r8 := float64(r >> 8)
	g8 := float64(g >> 8)
	b8 := float64(b >> 8)

	maximum := math.Max(math.Max(r8/255.0, g8/255.0), b8/255.0)
	minimum := math.Min(math.Min(r8/255.0, g8/255.0), b8/255.0)

	if maximum == minimum {
		return 0
	}

	l := (maximum + minimum) / 2.0
	d := maximum - minimum

	if l > 0.5 {
		return d / (2.0 - maximum - minimum)
	} else {
		return d / (maximum + minimum)
	}
}

func cie(c color.Color) float64 {
	r, g, b, _ := c.RGBA()
	r8 := float64(r >> 8)
	g8 := float64(g >> 8)
	b8 := float64(b >> 8)

	return 0.5126*b8 + 0.7152*g8 + 0.0722*r8
}

func skinCol(c color.Color) float64 {
	r, g, b, _ := c.RGBA()
	r8 := float64(r >> 8)
	g8 := float64(g >> 8)
	b8 := float64(b >> 8)

	mag := math.Sqrt(math.Pow(r8, 2) + math.Pow(g8, 2) + math.Pow(b8, 2))
	rd := r8/mag - skinColor[0]
	gd := g8/mag - skinColor[1]
	bd := b8/mag - skinColor[2]

	d := math.Sqrt(math.Pow(rd, 2) + math.Pow(gd, 2) + math.Pow(bd, 2))
	return 1.0 - d
}

func edgeDetect(i *image.Image, o *image.Image) {
	w := (*i).Bounds().Size().X
	h := (*i).Bounds().Size().Y

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			var lightness float64

			if x == 0 || x >= w-1 || y == 0 || y >= h-1 {
				lightness = cie((*i).At(x, y))
			} else {
				lightness = cie((*i).At(x, y))*4.0 -
					cie((*i).At(x, y-1)) -
					cie((*i).At(x-1, y)) -
					cie((*i).At(x+1, y)) -
					cie((*i).At(x, y+1))
			}

			if lightness < 0 {
				continue
			}

			nc := color.RGBA{uint8(lightness), uint8(lightness), uint8(lightness), 255}
			(*o).(*image.RGBA).Set(x, y, nc)
		}
	}
}

func skinDetect(i *image.Image, o *image.Image) {
	w := (*i).Bounds().Size().X
	h := (*i).Bounds().Size().Y

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			lightness := cie((*i).At(x, y)) / 255.0
			skin := skinCol((*i).At(x, y))

			if skin > skinThreshold && lightness >= skinBrightnessMin && lightness <= skinBrightnessMax {
				r := (skin - skinThreshold) * (255.0 / (1.0 - skinThreshold))
				_, g, b, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{uint8(r), uint8(g >> 8), uint8(b >> 8), 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			} else {
				_, g, b, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{0, uint8(g >> 8), uint8(b >> 8), 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			}
		}
	}
}

func saturationDetect(i *image.Image, o *image.Image) {
	w := (*i).Bounds().Size().X
	h := (*i).Bounds().Size().Y

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			lightness := cie((*i).At(x, y)) / 255.0
			saturation := saturation((*i).At(x, y))

			if saturation > saturationThreshold && lightness >= saturationBrightnessMin && lightness <= saturationBrightnessMax {
				b := (saturation - saturationThreshold) * (255.0 / (1.0 - saturationThreshold))
				r, g, _, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b), 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			} else {
				r, g, _, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{uint8(r >> 8), uint8(g >> 8), 0, 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			}
		}
	}
}

func crops(i *image.Image) []Crop {
	res := []Crop{}
	width := (*i).Bounds().Size().X
	height := (*i).Bounds().Size().Y

	//minDimension := math.Min(float64(width), float64(height))
	cropW := cropWidth  //|| minDimension
	cropH := cropHeight //|| minDimension

	for scale := maxScale; scale >= minScale; scale -= scaleStep {
		for y := 0; float64(y)+cropH*scale <= float64(height); y += step {
			for x := 0; float64(x)+cropW*scale <= float64(width); x += step {
				res = append(res, Crop{
					X:      x,
					Y:      y,
					Width:  int(cropW * scale),
					Height: int(cropH * scale),
				})
			}
		}
	}

	return res
}
<|endoftext|>


<commit_before>package bech32

import (
	"fmt"
	"strings"
)

// charset is the sequence of ascii characters that make up the bech32
// alphabet.  Each character represents a 5-bit squashed byte.
// q = 0b00000, p = 0b00001, z = 0b00010, and so on.
const charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"

// inverseCharset is a mapping of 8-bit ascii characters to the charset
// positions.  Both uppercase and lowercase ascii are mapped to the 5-bit
// position values.
var inverseCharset = [256]int8{
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
	-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
	1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
	-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
	1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1}

// Bytes8to5 extends a byte slice into a longer, padded byte slice of 5-bit elements
// where the high 3 bits are all 0.
func Bytes8to5(input []byte) []byte {
	// no way to triger an error going from 8 to 5
	output, _ := ByteSquasher(input, 8, 5)
	return output
}

// Bytes5to8 goes from squashed bytes to full height bytes
func Bytes5to8(input []byte) ([]byte, error) {
	return ByteSquasher(input, 5, 8)
}

// ByteSquasher squashes full-width (8-bit) bytes into "squashed" 5-bit bytes,
// and vice versa.  It can operate on other widths but in this backage only
// goes 5 to 8 and back again.  It can return an error if the squashed input
// you give it isn't actually squashed, or if there is padding (trailing q characters)
// when going from 5 to 8
func ByteSquasher(input []byte, inputWidth, outputWidth uint32) ([]byte, error) {
	var bitstash, accumulator uint32
	var output []byte
	maxOutputValue := uint32((1 << outputWidth) - 1)
	for i, c := range input {
		if c>>inputWidth != 0 {
			return nil, fmt.Errorf("byte %d (%x) high bits set", i, c)
		}
		accumulator = (accumulator << inputWidth) | uint32(c)
		bitstash += inputWidth
		for bitstash >= outputWidth {
			bitstash -= outputWidth
			output = append(output,
				byte((accumulator>>bitstash)&maxOutputValue))
		}
	}
	// pad if going from 8 to 5
	if inputWidth == 8 && outputWidth == 5 {
		if bitstash != 0 {
			output = append(output,
				byte((accumulator << (outputWidth - bitstash) & maxOutputValue)))
		}
	} else if bitstash >= inputWidth ||
		((accumulator<<(outputWidth-bitstash))&maxOutputValue) != 0 {
		// no pad from 5 to 8 allowed
		return nil, fmt.Errorf(
			"invalid padding from %d to %d bits", inputWidth, outputWidth)
	}
	return output, nil
}

// SquashedBytesToString swaps 5-bit bytes with a string of the corresponding letters
func SquashedBytesToString(input []byte) (string, error) {
	var s string
	for i, c := range input {
		if c&0xe0 != 0 {
			return "", fmt.Errorf("high bits set at position %d: %x", i, c)
		}
		s += string(charset[c])
	}
	return s, nil
}

// StringToSquashedBytes uses the inverseCharset to switch from the characters
// back to 5-bit squashed bytes.
func StringToSquashedBytes(input string) ([]byte, error) {
	b := make([]byte, len(input))
	for i, c := range input {
		if inverseCharset[c] == -1 {
			return nil, fmt.Errorf("contains invalid character %s", string(c))
		}
		b[i] = byte(inverseCharset[c])
	}
	return b, nil
}

// PolyMod takes a byte slice and returns the 32-bit BCH checksum.
// Note that the input bytes to PolyMod need to be squashed to 5-bits tall
// before being used in this function.  And this function will not error,
// but instead return an unsuable checksum, if you give it full-height bytes.
func PolyMod(values []byte) uint32 {

	// magic generator uint32s
	gen := []uint32{
		0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3,
	}

	// start with 1
	chk := uint32(1)

	for _, v := range values {
		top := chk >> 25
		chk = (chk&0x1ffffff)<<5 ^ uint32(v)
		for i, g := range gen {
			if (top>>uint8(i))&1 == 1 {
				chk ^= g
			}
		}
	}

	return chk
}

// HRPExpand turns the human redable part into 5bit-bytes for later processing
func HRPExpand(input string) []byte {
	output := make([]byte, (len(input)*2)+1)

	// first half is the input string shifted down 5 bits.
	// not much is going on there in terms of data / entropy
	for i, c := range input {
		output[i] = uint8(c) >> 5
	}
	// then there's a 0 byte separator
	// don't need to set 0 byte in the middle, as it starts out that way

	// second half is the input string, with the top 3 bits zeroed.
	// most of the data / entropy will live here.
	for i, c := range input {
		output[i+len(input)+1] = uint8(c) & 0x1f
	}
	return output
}

// create checksum makes a 6-shortbyte checksum from the HRP and data parts
func CreateChecksum(hrp string, data []byte) []byte {
	values := append(HRPExpand(hrp), data...)
	// put 6 zero bytes on at the end
	values = append(values, make([]byte, 6)...)
	//get checksum for whole slice

	// flip the LSB of the checksum data after creating it
	checksum := PolyMod(values) ^ 1

	for i := 0; i < 6; i++ {
		// note that this is NOT the same as converting 8 to 5
		// this is it's own expansion to 6 bytes from 4, chopping
		// off the MSBs.
		values[(len(values)-6)+i] = byte(checksum>>(5*(5-uint32(i)))) & 0x1f
	}

	return values[len(values)-6:]
}

func VerifyChecksum(hrp string, data []byte) bool {
	values := append(HRPExpand(hrp), data...)
	checksum := PolyMod(values)
	// make sure it's 1 (from the LSB flip in CreateChecksum
	return checksum == 1
}

// Encode takes regular bytes of data, and an hrp prefix, and returns the
// bech32 encoded string.  It doesn't do any segwit specific encoding.
func Encode(hrp string, data []byte) string {
	fiveData := Bytes8to5(data)
	return EncodeSquashed(hrp, fiveData)
}

// EncodeSquashed takes the hrp prefix, as well as byte data that has already
// been squashed to 5-bits high, and returns the bech32 encoded string.
// It does not return an error; if you give it non-squashed data it will return
// an empty string.
func EncodeSquashed(hrp string, data []byte) string {
	combined := append(data, CreateChecksum(hrp, data)...)

	// Should be squashed, return empty string if it's not.
	dataString, err := SquashedBytesToString(combined)
	if err != nil {
		return ""
	}
	return hrp + "1" + dataString
}

// Decode takes a bech32 encoded string and returns the hrp and the full-height
// data.  Can error out for various reasons, mostly problems in the string given.
// Doesn't do anything segwit specific.
func Decode(adr string) (string, []byte, error) {
	hrp, squashedData, err := DecodeSquashed(adr)
	if err != nil {
		return "", nil, err
	}
	data, err := Bytes5to8(squashedData)
	if err != nil {
		return "", nil, err
	}
	return hrp, data, nil
}

// DecodeSquashed is the same as Decode, but will return squashed 5-bit high
// data.
func DecodeSquashed(adr string) (string, []byte, error) {

	// make an all lowercase and all uppercase version of the input string
	lowAdr := strings.ToLower(adr)
	highAdr := strings.ToUpper(adr)

	// if there's mixed case, that's not OK
	if adr != lowAdr && adr != highAdr {
		return "", nil, fmt.Errorf("mixed case address")
	}

	// defualt to lowercase
	adr = lowAdr

	// find the last "1" and split there
	splitLoc := strings.LastIndex(adr, "1")
	if splitLoc == -1 {
		return "", nil, fmt.Errorf("1 separator not present in address")
	}

	// hrp comes before the split
	hrp := adr[0:splitLoc]

	// get squashed data
	data, err := StringToSquashedBytes(adr[splitLoc+1:])
	if err != nil {
		return "", nil, err
	}

	// make sure checksum works
	sumOK := VerifyChecksum(hrp, data)
	if !sumOK {
		return "", nil, fmt.Errorf("Checksum invalid")
	}

	// chop off checksum to return only payload
	data = data[:len(data)-6]

	return hrp, data, nil
}

// SegWitAddressEncode takes an hrp and data and gives back a segwit address.
// The data that goes in should be the full pkscript from the txout, including the
// version byte and the pushdata byte.
func SegWitAddressEncode(hrp string, data []byte) (string, error) {

	if len(data) < 4 {
		return "", fmt.Errorf("data too short (%d bytes)", len(data))
	}
	// first byte is the version number.  that shouldn't be more than
	// 16, so only 4 bits, doesn't need to be squashed
	version := data[0]
	// the next byte is the length.  make sure it's right
	length := data[1]

	// the rest of the data is real data and needs to be squashed
	data = data[2:]

	if int(length) != len(data) {
		return "", fmt.Errorf(
			"push byte / payload length mismatch: %d, %d", length, len(data))
	}

	// only 2 networks currently supported: mainnet and testnet3
	if hrp != "bc" && hrp != "tb" {
		return "", fmt.Errorf("prefix %s is not bitcoin or testnet", hrp)
	}
	// 1 byte programs are not ok.  Also 40 bytes should be enough for anyone.
	if len(data) < 2 || len(data) > 40 {
		return "", fmt.Errorf("Data length %d out of bounds", len(data))
	}
	// Better get all your features in soon; only 16 possible script versions.
	if version > 16 {
		return "", fmt.Errorf("Invalid witness program version %d", data[0])
	}
	// version 0 scripts can only be 20 bytes (p2wpkh) or 32 bytes (p2wsh)
	if version == 0 && len(data) != 20 && len(data) != 32 {
		return "", fmt.Errorf("expect 20 or 32 byte v0 witprog, got %d", len(data))
	}

	// squash payload data
	squashedData := Bytes8to5(data)
	// prepend version byte
	squashedData = append([]byte{version}, squashedData...)

	address := EncodeSquashed(hrp, squashedData)

	return address, nil
}

// SegWitAddressDecode takes a segwit address and returns the pkscript that
// can go directly into the txout.  (includes version byte and data push byte)
func SegWitAddressDecode(adr string) ([]byte, error) {
	hrp, squashedData, err := DecodeSquashed(adr)
	if err != nil {
		return nil, err
	}
	// the segwit version byte is directly put into a 5bit squashed byte
	// since it maxes out at 16, wasting ~1 byte instead of 4.

	version := squashedData[0]
	data, err := Bytes5to8(squashedData[1:])
	if err != nil {
		return nil, err
	}
	if hrp != "bc" && hrp != "tb" {
		return nil, fmt.Errorf("prefix %s is not bitcoin or testnet", hrp)
	}
	if len(data) < 2 || len(data) > 40 {
		return nil, fmt.Errorf("Data length %d out of bounds", len(data))
	}

	if version > 16 {
		return nil, fmt.Errorf("Invalid witness program version %d", data[0])
	}
	if version == 0 && len(data) != 20 && len(data) != 32 {
		return nil, fmt.Errorf("expect 20 or 32 byte v0 witprog, got %d", len(data))
	}

	// first give version byte, then push length
	if version > 0 {
		version |= 0x80
	}
	outputScript := append([]byte{version}, byte(len(data)))
	outputScript = append(outputScript, data...)

	return outputScript, nil
}
<commit_msg>typo<commit_after>package bech32

import (
	"fmt"
	"strings"
)

// charset is the sequence of ascii characters that make up the bech32
// alphabet.  Each character represents a 5-bit squashed byte.
// q = 0b00000, p = 0b00001, z = 0b00010, and so on.
const charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"

// inverseCharset is a mapping of 8-bit ascii characters to the charset
// positions.  Both uppercase and lowercase ascii are mapped to the 5-bit
// position values.
var inverseCharset = [256]int8{
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
	-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
	1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
	-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
	1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1}

// Bytes8to5 extends a byte slice into a longer, padded byte slice of 5-bit elements
// where the high 3 bits are all 0.
func Bytes8to5(input []byte) []byte {
	// no way to triger an error going from 8 to 5
	output, _ := ByteSquasher(input, 8, 5)
	return output
}

// Bytes5to8 goes from squashed bytes to full height bytes
func Bytes5to8(input []byte) ([]byte, error) {
	return ByteSquasher(input, 5, 8)
}

// ByteSquasher squashes full-width (8-bit) bytes into "squashed" 5-bit bytes,
// and vice versa.  It can operate on other widths but in this package only
// goes 5 to 8 and back again.  It can return an error if the squashed input
// you give it isn't actually squashed, or if there is padding (trailing q characters)
// when going from 5 to 8
func ByteSquasher(input []byte, inputWidth, outputWidth uint32) ([]byte, error) {
	var bitstash, accumulator uint32
	var output []byte
	maxOutputValue := uint32((1 << outputWidth) - 1)
	for i, c := range input {
		if c>>inputWidth != 0 {
			return nil, fmt.Errorf("byte %d (%x) high bits set", i, c)
		}
		accumulator = (accumulator << inputWidth) | uint32(c)
		bitstash += inputWidth
		for bitstash >= outputWidth {
			bitstash -= outputWidth
			output = append(output,
				byte((accumulator>>bitstash)&maxOutputValue))
		}
	}
	// pad if going from 8 to 5
	if inputWidth == 8 && outputWidth == 5 {
		if bitstash != 0 {
			output = append(output,
				byte((accumulator << (outputWidth - bitstash) & maxOutputValue)))
		}
	} else if bitstash >= inputWidth ||
		((accumulator<<(outputWidth-bitstash))&maxOutputValue) != 0 {
		// no pad from 5 to 8 allowed
		return nil, fmt.Errorf(
			"invalid padding from %d to %d bits", inputWidth, outputWidth)
	}
	return output, nil
}

// SquashedBytesToString swaps 5-bit bytes with a string of the corresponding letters
func SquashedBytesToString(input []byte) (string, error) {
	var s string
	for i, c := range input {
		if c&0xe0 != 0 {
			return "", fmt.Errorf("high bits set at position %d: %x", i, c)
		}
		s += string(charset[c])
	}
	return s, nil
}

// StringToSquashedBytes uses the inverseCharset to switch from the characters
// back to 5-bit squashed bytes.
func StringToSquashedBytes(input string) ([]byte, error) {
	b := make([]byte, len(input))
	for i, c := range input {
		if inverseCharset[c] == -1 {
			return nil, fmt.Errorf("contains invalid character %s", string(c))
		}
		b[i] = byte(inverseCharset[c])
	}
	return b, nil
}

// PolyMod takes a byte slice and returns the 32-bit BCH checksum.
// Note that the input bytes to PolyMod need to be squashed to 5-bits tall
// before being used in this function.  And this function will not error,
// but instead return an unsuable checksum, if you give it full-height bytes.
func PolyMod(values []byte) uint32 {

	// magic generator uint32s
	gen := []uint32{
		0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3,
	}

	// start with 1
	chk := uint32(1)

	for _, v := range values {
		top := chk >> 25
		chk = (chk&0x1ffffff)<<5 ^ uint32(v)
		for i, g := range gen {
			if (top>>uint8(i))&1 == 1 {
				chk ^= g
			}
		}
	}

	return chk
}

// HRPExpand turns the human redable part into 5bit-bytes for later processing
func HRPExpand(input string) []byte {
	output := make([]byte, (len(input)*2)+1)

	// first half is the input string shifted down 5 bits.
	// not much is going on there in terms of data / entropy
	for i, c := range input {
		output[i] = uint8(c) >> 5
	}
	// then there's a 0 byte separator
	// don't need to set 0 byte in the middle, as it starts out that way

	// second half is the input string, with the top 3 bits zeroed.
	// most of the data / entropy will live here.
	for i, c := range input {
		output[i+len(input)+1] = uint8(c) & 0x1f
	}
	return output
}

// create checksum makes a 6-shortbyte checksum from the HRP and data parts
func CreateChecksum(hrp string, data []byte) []byte {
	values := append(HRPExpand(hrp), data...)
	// put 6 zero bytes on at the end
	values = append(values, make([]byte, 6)...)
	//get checksum for whole slice

	// flip the LSB of the checksum data after creating it
	checksum := PolyMod(values) ^ 1

	for i := 0; i < 6; i++ {
		// note that this is NOT the same as converting 8 to 5
		// this is it's own expansion to 6 bytes from 4, chopping
		// off the MSBs.
		values[(len(values)-6)+i] = byte(checksum>>(5*(5-uint32(i)))) & 0x1f
	}

	return values[len(values)-6:]
}

func VerifyChecksum(hrp string, data []byte) bool {
	values := append(HRPExpand(hrp), data...)
	checksum := PolyMod(values)
	// make sure it's 1 (from the LSB flip in CreateChecksum
	return checksum == 1
}

// Encode takes regular bytes of data, and an hrp prefix, and returns the
// bech32 encoded string.  It doesn't do any segwit specific encoding.
func Encode(hrp string, data []byte) string {
	fiveData := Bytes8to5(data)
	return EncodeSquashed(hrp, fiveData)
}

// EncodeSquashed takes the hrp prefix, as well as byte data that has already
// been squashed to 5-bits high, and returns the bech32 encoded string.
// It does not return an error; if you give it non-squashed data it will return
// an empty string.
func EncodeSquashed(hrp string, data []byte) string {
	combined := append(data, CreateChecksum(hrp, data)...)

	// Should be squashed, return empty string if it's not.
	dataString, err := SquashedBytesToString(combined)
	if err != nil {
		return ""
	}
	return hrp + "1" + dataString
}

// Decode takes a bech32 encoded string and returns the hrp and the full-height
// data.  Can error out for various reasons, mostly problems in the string given.
// Doesn't do anything segwit specific.
func Decode(adr string) (string, []byte, error) {
	hrp, squashedData, err := DecodeSquashed(adr)
	if err != nil {
		return "", nil, err
	}
	data, err := Bytes5to8(squashedData)
	if err != nil {
		return "", nil, err
	}
	return hrp, data, nil
}

// DecodeSquashed is the same as Decode, but will return squashed 5-bit high
// data.
func DecodeSquashed(adr string) (string, []byte, error) {

	// make an all lowercase and all uppercase version of the input string
	lowAdr := strings.ToLower(adr)
	highAdr := strings.ToUpper(adr)

	// if there's mixed case, that's not OK
	if adr != lowAdr && adr != highAdr {
		return "", nil, fmt.Errorf("mixed case address")
	}

	// defualt to lowercase
	adr = lowAdr

	// find the last "1" and split there
	splitLoc := strings.LastIndex(adr, "1")
	if splitLoc == -1 {
		return "", nil, fmt.Errorf("1 separator not present in address")
	}

	// hrp comes before the split
	hrp := adr[0:splitLoc]

	// get squashed data
	data, err := StringToSquashedBytes(adr[splitLoc+1:])
	if err != nil {
		return "", nil, err
	}

	// make sure checksum works
	sumOK := VerifyChecksum(hrp, data)
	if !sumOK {
		return "", nil, fmt.Errorf("Checksum invalid")
	}

	// chop off checksum to return only payload
	data = data[:len(data)-6]

	return hrp, data, nil
}

// SegWitAddressEncode takes an hrp and data and gives back a segwit address.
// The data that goes in should be the full pkscript from the txout, including the
// version byte and the pushdata byte.
func SegWitAddressEncode(hrp string, data []byte) (string, error) {

	if len(data) < 4 {
		return "", fmt.Errorf("data too short (%d bytes)", len(data))
	}
	// first byte is the version number.  that shouldn't be more than
	// 16, so only 4 bits, doesn't need to be squashed
	version := data[0]
	// the next byte is the length.  make sure it's right
	length := data[1]

	// the rest of the data is real data and needs to be squashed
	data = data[2:]

	if int(length) != len(data) {
		return "", fmt.Errorf(
			"push byte / payload length mismatch: %d, %d", length, len(data))
	}

	// only 2 networks currently supported: mainnet and testnet3
	if hrp != "bc" && hrp != "tb" {
		return "", fmt.Errorf("prefix %s is not bitcoin or testnet", hrp)
	}
	// 1 byte programs are not ok.  Also 40 bytes should be enough for anyone.
	if len(data) < 2 || len(data) > 40 {
		return "", fmt.Errorf("Data length %d out of bounds", len(data))
	}
	// Better get all your features in soon; only 16 possible script versions.
	if version > 16 {
		return "", fmt.Errorf("Invalid witness program version %d", data[0])
	}
	// version 0 scripts can only be 20 bytes (p2wpkh) or 32 bytes (p2wsh)
	if version == 0 && len(data) != 20 && len(data) != 32 {
		return "", fmt.Errorf("expect 20 or 32 byte v0 witprog, got %d", len(data))
	}

	// squash payload data
	squashedData := Bytes8to5(data)
	// prepend version byte
	squashedData = append([]byte{version}, squashedData...)

	address := EncodeSquashed(hrp, squashedData)

	return address, nil
}

// SegWitAddressDecode takes a segwit address and returns the pkscript that
// can go directly into the txout.  (includes version byte and data push byte)
func SegWitAddressDecode(adr string) ([]byte, error) {
	hrp, squashedData, err := DecodeSquashed(adr)
	if err != nil {
		return nil, err
	}
	// the segwit version byte is directly put into a 5bit squashed byte
	// since it maxes out at 16, wasting ~1 byte instead of 4.

	version := squashedData[0]
	data, err := Bytes5to8(squashedData[1:])
	if err != nil {
		return nil, err
	}
	if hrp != "bc" && hrp != "tb" {
		return nil, fmt.Errorf("prefix %s is not bitcoin or testnet", hrp)
	}
	if len(data) < 2 || len(data) > 40 {
		return nil, fmt.Errorf("Data length %d out of bounds", len(data))
	}

	if version > 16 {
		return nil, fmt.Errorf("Invalid witness program version %d", data[0])
	}
	if version == 0 && len(data) != 20 && len(data) != 32 {
		return nil, fmt.Errorf("expect 20 or 32 byte v0 witprog, got %d", len(data))
	}

	// first give version byte, then push length
	if version > 0 {
		version |= 0x80
	}
	outputScript := append([]byte{version}, byte(len(data)))
	outputScript = append(outputScript, data...)

	return outputScript, nil
}
<|endoftext|>