<commit_before>package rhynock

import (
	"github.com/gorilla/websocket"
	"net/http"
	"time"
	"log"
)

// Connection handles our websocket
type Connection struct {
	Ws      *websocket.Conn
	Send    chan []byte
	Authed  chan bool
	Dst     BottleDst
}

const (
	writeWait = 10 * time.Second
	pongWait = 60 * time.Second
	pingPeriod = (pongWait * 9) / 10
	maxMessageSize = 512
)

//
// Used to write a single message to the client and report any errors
//
func (c *Connection) write(t int, payload []byte) error {
	c.Ws.SetWriteDeadline(time.Now().Add(writeWait))
	return c.Ws.WriteMessage(t, payload)
}

//
// Maintains both a reader and a writer, cleans up both if one fails
//
func (c *Connection) read_write() {
	// Ping timer
	ticker := time.NewTicker(pingPeriod)

	// Clean up Connection and Connection resources
	defer func() {
		ticker.Stop()
		c.Ws.Close()
	}()

	// Config websocket settings
	c.Ws.SetReadLimit(maxMessageSize)
	c.Ws.SetReadDeadline(time.Now().Add(pongWait))
	c.Ws.SetPongHandler(func(string) error {
		// Give each client pongWait seconds after the ping to respond
		c.Ws.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})

	// Start a reading goroutine
	// The reader will stop when the c.Ws.Close is called at
	// in the defered cleanup function, so we do not manually
	// have to close the reader
	go func() {
		for {
			// This blcoks until it reads EOF or an error
			// occurs trying to read, the error can be
			// used to detect when the client closes the Connection
			_, message, err := c.Ws.ReadMessage()
			if err != nil {
				break // If we get an error escape the loop
			}

			// Bottle the message with its sender
			bottle := &Bottle{
				Sender: c,
				Message: message,
			}

			// Send to the destination for processing
			c.Dst.GetBottleChan() <- bottle
		}
		// The reader has been terminated

	}()

	// Main handling loop
	for {
		select {
		case message, ok := <- c.Send:
			// Our send channel has something in it or the channel closed
			if !ok {
				// Our channel was closed, gracefully close socket Connection
				c.write(websocket.CloseMessage, []byte{})
				return
			}
			// Attempt to write the message to the websocket
			if err := c.write(websocket.TextMessage, message); err != nil {
				// If we get an error we can no longer communcate with client
				// return, no need to send CloseMessage since that would
				// just yield another error
				return
			}

		case <- ticker.C:
			// Ping ticker went off. We need to ping to check for connectivity.
			if err := c.write(websocket.PingMessage, []byte{}); err != nil {
				// We got an error pinging, return and call defer
				// defer will close the socket which will kill the reader
				return
			}
		}
	}

}

var upgrader = &websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r* http.Request) bool { return true }}

func ConnectionHandler(w http.ResponseWriter, r *http.Request, dst BottleDst) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	// Create new connection object
	c := &Connection{
		Send: make(chan []byte, 256),
		Ws: ws,
		Authed: make(chan bool),
		Dst: dst,
	}

	dst.ConnectionOpened(c)

	// Start infinite read/write loop
	c.read_write()
}
<commit_msg>A few changes to the channel and closing system<commit_after>package rhynock

import (
	"github.com/gorilla/websocket"
	"net/http"
	"time"
	"log"
)

// Connection handles our websocket
type Connection struct {
	// Exported
	Ws      *websocket.Conn
	Send    chan []byte
	Dst     BottleDst

	// Private
	quit    chan bool
}

const (
	writeWait = 10 * time.Second
	pongWait = 60 * time.Second
	pingPeriod = (pongWait * 9) / 10
	maxMessageSize = 512
)

//
// Used to write a single message to the client and report any errors
//
func (c *Connection) write(t int, payload []byte) error {
	c.Ws.SetWriteDeadline(time.Now().Add(writeWait))
	return c.Ws.WriteMessage(t, payload)
}

//
// Maintains both a reader and a writer, cleans up both if one fails
//
func (c *Connection) read_write() {
	// Ping timer
	ticker := time.NewTicker(pingPeriod)

	// Clean up Connection and Connection resources
	defer func() {
		ticker.Stop()
		c.Ws.Close()
	}()

	// Config websocket settings
	c.Ws.SetReadLimit(maxMessageSize)
	c.Ws.SetReadDeadline(time.Now().Add(pongWait))
	c.Ws.SetPongHandler(func(string) error {
		// Give each client pongWait seconds after the ping to respond
		c.Ws.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})

	// Start a reading goroutine
	// The reader will stop when the c.Ws.Close is called at
	// in the defered cleanup function, so we do not manually
	// have to close the reader
	go func() {
		for {
			// This blcoks until it reads EOF or an error
			// occurs trying to read, the error can be
			// used to detect when the client closes the Connection
			_, message, err := c.Ws.ReadMessage()
			if err != nil {
				break // If we get an error escape the loop
			}

			// Bottle the message with its sender
			bottle := &Bottle{
				Sender: c,
				Message: message,
			}

			// Send to the destination for processing
			c.Dst.GetBottleChan() <- bottle
		}
		// The reader has been terminated

	}()

	// Main handling loop
	for {
		select {
		case message, ok := <- c.Send:
			// Our send channel has something in it or the channel closed
			if !ok {
				// Our channel was closed, gracefully close socket Connection
				c.write(websocket.CloseMessage, []byte{})
				return
			}
			// Attempt to write the message to the websocket
			if err := c.write(websocket.TextMessage, message); err != nil {
				// If we get an error we can no longer communcate with client
				// return, no need to send CloseMessage since that would
				// just yield another error
				return
			}

		case <- ticker.C:
			// Ping ticker went off. We need to ping to check for connectivity.
			if err := c.write(websocket.PingMessage, []byte{}); err != nil {
				// We got an error pinging, return and call defer
				// defer will close the socket which will kill the reader
				return
			}

		case <- quit:
			// Quit signal was invoked by our Close function
			// The bottle destination wants this connection closed
			c.write(websocket.CloseMessage. []byte{})
			return
		}
	}

}

func (c *Connection) Close() {
	// Send ourself the quit signal provided by a function
	c.quit <- true
}

var upgrader = &websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r* http.Request) bool { return true }}

func ConnectionHandler(w http.ResponseWriter, r *http.Request, dst BottleDst) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	// Create new connection object
	c := &Connection{
		Send: make(chan []byte, 256),
		Ws: ws,
		Dst: dst,
		quit: make(chan bool)
	}

	dst.ConnectionOpened(c)

	// Start infinite read/write loop
	c.read_write()
}
<|endoftext|>


<commit_before>package rhynock

import (
	"github.com/gorilla/websocket"
	"net/http"
	"time"
	"log"
)

// Some defaults for pinging
// Needs to be settable from outside
const (
	writeWait = 10 * time.Second
	pongWait = 60 * time.Second
	pingPeriod = (pongWait * 9) / 10
	maxMessageSize = 512
)

// Conn encapsulates our websocket
type Conn struct {
	// Exported so everything can be messed with from outside
	Ws      *websocket.Conn
	Send    chan []byte
	Dst     BottleDst
	Quit    chan []byte
	wait    chan bool
	valid   bool
}

//
// Convenience function so you dont have to use the Send channel
//
func (c *Conn) SendMsg(message string) {
	// Basically just typecasting for convenience
	if c.valid {
		c.Send <- []byte(message)
	} else {
		log.Println("rhynock.Conn.SendMsg: Socket closed.")
	}
}

//
// Convenience function to call the quit channel with a message
//
func (c *Conn) CloseMsg(message string) {
	if c.valid {
		c.Quit <- []byte(message)
	} else {
		log.Println("rhynock.Conn.CloseMsg: Socket already closed.")
	}
}

//
// This function chews through the power cables
//
func (c *Conn) Close() {
	// Send ourself the quit signal with no message
	if c.valid {
		c.Quit <- []byte("")
	} else {
		log.Println("rhynock.Conn.Close: Socket already closed.")
	}
}

//
// Used to write a single message to the client and report any errors
//
func (c *Conn) write(t int, payload []byte) error {
	c.Ws.SetWriteDeadline(time.Now().Add(writeWait))
	return c.Ws.WriteMessage(t, payload)
}

//
// Maintains both a reader and a writer, cleans up both if one fails
//
func (c *Conn) read_write() {
	// Ping timer
	ticker := time.NewTicker(pingPeriod)

	// Clean up Connection and Connection resources
	defer func() {
		ticker.Stop()
		c.valid = false
		close(c.Send)
		close(c.Quit)
		c.Ws.Close()
		close(c.wait) // Unblocks the ConnectionHandler
	}()

	// Config websocket settings
	c.Ws.SetReadLimit(maxMessageSize)
	c.Ws.SetReadDeadline(time.Now().Add(pongWait))
	c.Ws.SetPongHandler(func(string) error {
		// Give each client pongWait seconds after the ping to respond
		c.Ws.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})

	// Start a reading goroutine
	// The reader will stop when the c.Ws.Close is called at
	// in the defered cleanup function, so we do not manually
	// have to close the reader
	go func() {
		for {
			// This blocks until it reads EOF or an error
			// occurs trying to read, the error can be
			// used to detect when the client closes the Connection
			_, message, err := c.Ws.ReadMessage()
			if err != nil {
				break // If we get an error escape the loop
			}

			// Bottle the message with its sender
			bottle := &Bottle{
				Sender: c,
				Message: message,
			}

			// Send to the destination for processing
			c.Dst.GetBottleChan() <- bottle
		}
		// The reader has been terminated, alert the writer if needed
		if c.valid {
			c.Close()
		}
	}()

	// Main writing loop
	for {
		select {
		case message, ok := <- c.Send:
			// Our send channel has something in it or the channel closed
			if !ok {
				// Our channel was closed, gracefully close socket Conn
				c.write(websocket.CloseMessage, []byte{})
				return
			}
			// Attempt to write the message to the websocket
			if err := c.write(websocket.TextMessage, message); err != nil {
				// If we get an error we can no longer communcate with client
				// return, no need to send CloseMessage since that would
				// just yield another error
				return
			}

		case <- ticker.C:
			// Ping ticker went off. We need to ping to check for connectivity.
			if err := c.write(websocket.PingMessage, []byte{}); err != nil {
				// We got an error pinging, return and call defer
				// defer will close the socket which will kill the reader
				return
			}

		case bytes := <- c.Quit:
			// Close connection and send a final message
			c.write(websocket.TextMessage, bytes)
			c.write(websocket.CloseMessage, []byte{})
			return
		}
	}
}

var upgrader = &websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r* http.Request) bool { return true }}

//
// Hanlder function to start a websocket connection
//
func ConnectionHandler(w http.ResponseWriter, r *http.Request, dst BottleDst) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	// Create new connection object
	c := &Conn{
		Send: make(chan []byte, 256),
		Ws: ws,
		Dst: dst,
		Quit: make(chan []byte),
		valid: true,
		wait: make(chan bool),
	}

	// Alert the destination that a new connection has opened
	dst.ConnectionOpened(c)

	// Make sure connections that are opened will get closed
	defer func() {
		dst.ConnectionClosed(c)
	}()

	// Start read write loop blocking
	c.read_write()
}
<commit_msg>Calculate and set ping<commit_after>package rhynock

import (
	"github.com/gorilla/websocket"
	"net/http"
	"time"
	"sync"
	"log"
)

// Some defaults for pinging
// Needs to be settable from outside
const (
	writeWait = 10 * time.Second
	pongWait = 60 * time.Second
	pingPeriod = 20 * time.Second
	maxMessageSize = 512
)

// Conn encapsulates our websocket
type Conn struct {
	// Exported actual webscoket so can be messed with from outside
	Ws        *websocket.Conn
	Send      chan []byte
	Dst       BottleDst
	Quit      chan []byte
	wait      chan bool
	valid     bool

	// Ping Calculation
	lastping  int64
	ping      int64
	pinglock  sync.Mutex
}

//
// Convenience function so you dont have to use the Send channel
//
func (c *Conn) SendMsg(message string) {
	// Basically just typecasting for convenience
	if c.valid {
		c.Send <- []byte(message)
	} else {
		log.Println("rhynock.Conn.SendMsg: Socket closed.")
	}
}

//
// Convenience function to call the quit channel with a message
//
func (c *Conn) CloseMsg(message string) {
	if c.valid {
		c.Quit <- []byte(message)
	} else {
		log.Println("rhynock.Conn.CloseMsg: Socket already closed.")
	}
}

//
// This function chews through the power cables
//
func (c *Conn) Close() {
	// Send ourself the quit signal with no message
	if c.valid {
		c.Quit <- []byte("")
	} else {
		log.Println("rhynock.Conn.Close: Socket already closed.")
	}
}

//
// Get the current ping of this connection
//
func (c *Conn) GetPing() int64 {
	c.pinglock.Lock()
	defer c.pinglock.Unlock()
	return c.ping
}

//
// Used to write a single message to the client and report any errors
//
func (c *Conn) write(t int, payload []byte) error {
	c.Ws.SetWriteDeadline(time.Now().Add(writeWait))
	return c.Ws.WriteMessage(t, payload)
}

//
// Maintains both a reader and a writer, cleans up both if one fails
//
func (c *Conn) read_write() {
	// Ping timer
	ticker := time.NewTicker(pingPeriod)

	// Clean up Connection and Connection resources
	defer func() {
		ticker.Stop()
		c.valid = false
		close(c.Send)
		close(c.Quit)
		c.Ws.Close()
		close(c.wait) // Unblocks the ConnectionHandler
	}()

	// Config websocket settings
	c.Ws.SetReadLimit(maxMessageSize)
	c.Ws.SetReadDeadline(time.Now().Add(pongWait))
	c.Ws.SetPongHandler(func(string) error {
		// Give each client pongWait seconds after the ping to respond
		c.Ws.SetReadDeadline(time.Now().Add(pongWait))
		c.pinglock.Lock()
		c.ping = (time.Now().UnixNano() - c.lastping) / 2000000
		c.pinglock.Unlock()
		return nil
	})

	// Start a reading goroutine
	// The reader will stop when the c.Ws.Close is called at
	// in the defered cleanup function, so we do not manually
	// have to close the reader
	go func() {
		for {
			// This blocks until it reads EOF or an error
			// occurs trying to read, the error can be
			// used to detect when the client closes the Connection
			_, message, err := c.Ws.ReadMessage()
			if err != nil {
				break // If we get an error escape the loop
			}

			// Bottle the message with its sender
			bottle := &Bottle{
				Sender: c,
				Message: message,
			}

			// Send to the destination for processing
			c.Dst.GetBottleChan() <- bottle
		}
		// The reader has been terminated, alert the writer if needed
		if c.valid {
			c.Close()
		}
	}()

	// Main writing loop
	for {
		select {
		case message, ok := <- c.Send:
			// Our send channel has something in it or the channel closed
			if !ok {
				// Our channel was closed, gracefully close socket Conn
				c.write(websocket.CloseMessage, []byte{})
				return
			}
			// Attempt to write the message to the websocket
			if err := c.write(websocket.TextMessage, message); err != nil {
				// If we get an error we can no longer communcate with client
				// return, no need to send CloseMessage since that would
				// just yield another error
				return
			}

		case <- ticker.C:
			// Record timestamp of ping
			c.lastping = time.Now().UnixNano()

			// Ping ticker went off. We need to ping to check for connectivity.
			if err := c.write(websocket.PingMessage, []byte{}); err != nil {
				// We got an error pinging, return and call defer
				// defer will close the socket which will kill the reader
				return
			}

		case bytes := <- c.Quit:
			// Close connection and send a final message
			c.write(websocket.TextMessage, bytes)
			c.write(websocket.CloseMessage, []byte{})
			return
		}
	}
}

var upgrader = &websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r* http.Request) bool { return true }}

//
// Hanlder function to start a websocket connection
//
func ConnectionHandler(w http.ResponseWriter, r *http.Request, dst BottleDst) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	// Create new connection object
	c := &Conn{
		Send: make(chan []byte, 256),
		Ws:       ws,
		Dst:      dst,
		Quit:     make(chan []byte),
		valid:    true,
		wait:     make(chan bool),
		lastping: 0,
		ping:     0,
	}

	// Alert the destination that a new connection has opened
	dst.ConnectionOpened(c)

	// Make sure connections that are opened will get closed
	defer func() {
		dst.ConnectionClosed(c)
	}()

	// Start read write loop blocking
	c.read_write()
}
<|endoftext|>


<commit_before>package epp

import (
	"encoding/binary"
	"encoding/xml"
	"fmt"
	"io"
	"net"
	"sync/atomic"
)

// Conn represents a single connection to an EPP server.
// This implementation is not safe for concurrent use.
type Conn struct {
	// Greeting holds the last received greeting message from the server,
	// indicating server name, status, data policy and capabilities.
	Greeting *Greeting

	net.Conn
	txnID uint64
}

// NewConn initializes an epp.Conn from a net.Conn and performs the EPP
// handshake. It reads and stores the initial EPP <greeting> message.
// https://tools.ietf.org/html/rfc5730#section-2.4
func NewConn(conn net.Conn) (*Conn, error) {
	c := &Conn{Conn: conn}
	var err error
	c.Greeting, err = c.ReadGreeting()
	return c, err
}

// writeMessage serializes msg into XML and writes it to c.
func (c *Conn) writeMessage(msg *message) error {
	data, err := marshal(msg)
	if err != nil {
		return err
	}
	logRequest(data)
	return c.writeDataUnit(data)
}

// writeDataUnit writes a slice of bytes to c.
// Bytes written are prefixed with 32-bit header specifying the total size
// of the data unit (message + 4 byte header), in network (big-endian) order.
// http://www.ietf.org/rfc/rfc4934.txt
func (c *Conn) writeDataUnit(p []byte) error {
	s := uint32(4 + len(xmlHeader) + len(p))
	err := binary.Write(c.Conn, binary.BigEndian, s)
	if err != nil {
		return err
	}
	_, err = c.Conn.Write(xmlHeader)
	if err != nil {
		return err
	}
	_, err = c.Conn.Write(p)
	return err
}

// xmlHeader is a byte-slice representation of the
// standard XML header. Declared as a global to relieve GC pressure.
var xmlHeader = []byte(xml.Header)

// readMessage reads a single EPP response from c and parses the XML into req.
// It returns an error if the EPP response contains an error result.
func (c *Conn) readMessage(msg *message) error {
	data, err := c.readDataUnit()
	if err != nil {
		return err
	}
	logResponse(data)
	return unmarshal(data, msg)
}

// readDataUnit reads a single EPP message from c.
// It returns the bytes read and/or an error.
// FIXME: allocate a single buffer per Conn to reduce GC pressure?
func (c *Conn) readDataUnit() (data []byte, err error) {
	var s uint32
	err = binary.Read(c.Conn, binary.BigEndian, &s)
	if err != nil {
		return
	}
	data = make([]byte, s-4)
	n, err := c.Conn.Read(data)
	if err != nil {
		return
	}
	if n != len(data) {
		return data, io.ErrNoProgress
	}
	return data, nil
}

// id returns a zero-padded 16-character hex uint64 transaction ID.
func (c *Conn) id() string {
	return fmt.Sprintf("%016x", atomic.AddUint64(&c.txnID, 1))
}

// TxnID returns the current client transaction ID for c.
// This generally corresponds to the number of commands performed.
func (c *Conn) TxnID() uint64 {
	return c.txnID
}
<commit_msg>use io.ReadFull<commit_after>package epp

import (
	"encoding/binary"
	"encoding/xml"
	"fmt"
	"io"
	"net"
	"sync/atomic"
)

// Conn represents a single connection to an EPP server.
// This implementation is not safe for concurrent use.
type Conn struct {
	// Greeting holds the last received greeting message from the server,
	// indicating server name, status, data policy and capabilities.
	Greeting *Greeting

	net.Conn
	txnID uint64
}

// NewConn initializes an epp.Conn from a net.Conn and performs the EPP
// handshake. It reads and stores the initial EPP <greeting> message.
// https://tools.ietf.org/html/rfc5730#section-2.4
func NewConn(conn net.Conn) (*Conn, error) {
	c := &Conn{Conn: conn}
	var err error
	c.Greeting, err = c.ReadGreeting()
	return c, err
}

// writeMessage serializes msg into XML and writes it to c.
func (c *Conn) writeMessage(msg *message) error {
	data, err := marshal(msg)
	if err != nil {
		return err
	}
	logRequest(data)
	return c.writeDataUnit(data)
}

// writeDataUnit writes a slice of bytes to c.
// Bytes written are prefixed with 32-bit header specifying the total size
// of the data unit (message + 4 byte header), in network (big-endian) order.
// http://www.ietf.org/rfc/rfc4934.txt
func (c *Conn) writeDataUnit(p []byte) error {
	s := uint32(4 + len(xmlHeader) + len(p))
	err := binary.Write(c.Conn, binary.BigEndian, s)
	if err != nil {
		return err
	}
	_, err = c.Conn.Write(xmlHeader)
	if err != nil {
		return err
	}
	_, err = c.Conn.Write(p)
	return err
}

// xmlHeader is a byte-slice representation of the
// standard XML header. Declared as a global to relieve GC pressure.
var xmlHeader = []byte(xml.Header)

// readMessage reads a single EPP response from c and parses the XML into req.
// It returns an error if the EPP response contains an error result.
func (c *Conn) readMessage(msg *message) error {
	data, err := c.readDataUnit()
	if err != nil {
		return err
	}
	logResponse(data)
	return unmarshal(data, msg)
}

// readDataUnit reads a single EPP message from c.
// It returns the bytes read and/or an error.
// FIXME: allocate a single buffer per Conn to reduce GC pressure?
func (c *Conn) readDataUnit() (data []byte, err error) {
	var s uint32
	err = binary.Read(c.Conn, binary.BigEndian, &s)
	if err != nil {
		return
	}
	data = make([]byte, s-4)
	_, err = io.ReadFull(c.Conn, data)
	if err != nil {
		return
	}
	return data, nil
}

// id returns a zero-padded 16-character hex uint64 transaction ID.
func (c *Conn) id() string {
	return fmt.Sprintf("%016x", atomic.AddUint64(&c.txnID, 1))
}

// TxnID returns the current client transaction ID for c.
// This generally corresponds to the number of commands performed.
func (c *Conn) TxnID() uint64 {
	return c.txnID
}
<|endoftext|>


<commit_before>package proxy

import (
	"database/sql/driver"
)

type Conn struct {
	Conn  driver.Conn
	Proxy *Proxy
}

func (conn *Conn) Prepare(query string) (driver.Stmt, error) {
	stmt, err := conn.Conn.Prepare(query)
	if err != nil {
		return nil, err
	}
	return &Stmt{
		Stmt:        stmt,
		QueryString: query,
		Proxy:       conn.Proxy,
	}, nil
}

func (conn *Conn) Close() error {
	return conn.Conn.Close()
}

func (conn *Conn) Begin() (driver.Tx, error) {
	var err error
	var ctx interface{}

	var tx driver.Tx
	if h := conn.Proxy.Hooks.PostBegin; h != nil {
		defer func() { h(ctx, conn) }()
	}

	if h := conn.Proxy.Hooks.PreBegin; h != nil {
		if ctx, err = h(conn); err != nil {
			return nil, err
		}
	}

	tx, err = conn.Conn.Begin()
	if err != nil {
		return nil, err
	}

	if hook := conn.Proxy.Hooks.Begin; hook != nil {
		if err = hook(ctx, conn); err != nil {
			tx.Rollback()
			return nil, err
		}
	}

	return &Tx{
		Tx:    tx,
		Proxy: conn.Proxy,
	}, nil
}
<commit_msg>support Execer and Queryer<commit_after>package proxy

import (
	"database/sql/driver"
)

type Conn struct {
	Conn  driver.Conn
	Proxy *Proxy
}

func (conn *Conn) Prepare(query string) (driver.Stmt, error) {
	stmt, err := conn.Conn.Prepare(query)
	if err != nil {
		return nil, err
	}
	return &Stmt{
		Stmt:        stmt,
		QueryString: query,
		Proxy:       conn.Proxy,
	}, nil
}

func (conn *Conn) Close() error {
	return conn.Conn.Close()
}

func (conn *Conn) Begin() (driver.Tx, error) {
	var err error
	var ctx interface{}

	var tx driver.Tx
	if h := conn.Proxy.Hooks.PostBegin; h != nil {
		defer func() { h(ctx, conn) }()
	}

	if h := conn.Proxy.Hooks.PreBegin; h != nil {
		if ctx, err = h(conn); err != nil {
			return nil, err
		}
	}

	tx, err = conn.Conn.Begin()
	if err != nil {
		return nil, err
	}

	if hook := conn.Proxy.Hooks.Begin; hook != nil {
		if err = hook(ctx, conn); err != nil {
			tx.Rollback()
			return nil, err
		}
	}

	return &Tx{
		Tx:    tx,
		Proxy: conn.Proxy,
	}, nil
}

func (conn *Conn) Exec(query string, args []driver.Value) (driver.Result, error) {
	execer, ok := conn.Conn.(driver.Execer)
	if !ok {
		return nil, driver.ErrSkip
	}

	stmt := &Stmt{
		QueryString: query,
		Proxy:       conn.Proxy,
	}

	var ctx interface{}
	var err error
	var result driver.Result

	if h := stmt.Proxy.Hooks.PostExec; h != nil {
		defer func() { h(ctx, stmt, args, result) }()
	}
	if h := stmt.Proxy.Hooks.PreExec; h != nil {
		if ctx, err = h(stmt, args); err != nil {
			return nil, err
		}
	}

	result, err = execer.Exec(query, args)
	if err != nil {
		return nil, err
	}

	if h := stmt.Proxy.Hooks.Exec; h != nil {
		if err := h(ctx, stmt, args, result); err != nil {
			return nil, err
		}
	}

	return result, nil
}

func (conn *Conn) Query(query string, args []driver.Value) (driver.Rows, error) {
	queryer, ok := conn.Conn.(driver.Queryer)
	if !ok {
		return nil, driver.ErrSkip
	}

	stmt := &Stmt{
		QueryString: query,
		Proxy:       conn.Proxy,
	}

	var ctx interface{}
	var err error
	var rows driver.Rows

	if h := stmt.Proxy.Hooks.PostQuery; h != nil {
		defer func() { h(ctx, stmt, args, rows) }()
	}

	if h := stmt.Proxy.Hooks.PreQuery; h != nil {
		if ctx, err = h(stmt, args); err != nil {
			return nil, err
		}
	}

	rows, err = queryer.Query(query, args)
	if err != nil {
		return nil, err
	}

	if h := stmt.Proxy.Hooks.Query; h != nil {
		if err := h(ctx, stmt, args, rows); err != nil {
			return nil, err
		}
	}

	return rows, nil
}
<|endoftext|>


<commit_before>// (c) 2021, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package index

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"

	"github.com/prometheus/client_golang/prometheus"

	"github.com/ava-labs/avalanchego/utils/logging"
	"github.com/ava-labs/avalanchego/vms/components/avax"

	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/database/prefixdb"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/utils/wrappers"
)

var (
	idxKey                         = []byte("idx")
	idxCompleteKey                 = []byte("complete")
	errIndexingRequiredFromGenesis = errors.New("running would create incomplete index. Allow incomplete indices or re-sync from genesis with indexing enabled")
	errCausesIncompleteIndex       = errors.New("running would create incomplete index. Allow incomplete indices or enable indexing")
)

// AddressTxsIndexer maintains information about which transactions changed
// the balances of which addresses. This includes both transactions that
// increase and decrease an address's balance.
// A transaction is said to change an address's balance if either is true:
// 1) A UTXO that the transaction consumes was at least partially owned by the address.
// 2) A UTXO that the transaction produces is at least partially owned by the address.
type AddressTxsIndexer interface {
	// Accept is called when [txID] is accepted.
	// Persists data about [txID] and what balances it changed.
	// [inputUTXOs] are the UTXOs [txID] consumes.
	// [outputUTXOs] are the UTXOs [txID] creates.
	// If the error is non-nil, do not persist [txID] to disk as accepted in the VM
	Accept(
		txID ids.ID,
		inputUTXOs []*avax.UTXO,
		outputUTXOs []*avax.UTXO,
	) error

	// Read returns the IDs of transactions that changed [address]'s balance of [assetID].
	// The returned transactions are in order of increasing acceptance time.
	// The length of the returned slice <= [pageSize].
	// [cursor] is the offset to start reading from.
	Read(address []byte, assetID ids.ID, cursor, pageSize uint64) ([]ids.ID, error)
}

// indexer implements AddressTxsIndexer
type indexer struct {
	log     logging.Logger
	metrics metrics
	db      database.Database
	// txID -> Address -> AssetID --> exists if the address's balance
	// of the asset is changed by processing tx [txID]
	balanceChanges map[ids.ID]map[ids.ShortID]map[ids.ID]struct{}
}

// NewIndexer Returns a new AddressTxsIndexer.
// The returned indexer ignores UTXOs that are not type secp256k1fx.TransferOutput.
func NewIndexer(
	db database.Database,
	log logging.Logger,
	metricsNamespace string,
	metricsRegisterer prometheus.Registerer,
	allowIncompleteIndices bool,
) (AddressTxsIndexer, error) {
	i := &indexer{
		balanceChanges: make(map[ids.ID]map[ids.ShortID]map[ids.ID]struct{}),
		db:             db,
		log:            log,
	}
	// initialize the indexer
	if err := checkIndexStatus(i.db, true, allowIncompleteIndices); err != nil {
		return nil, err
	}
	// initialize the metrics
	if err := i.metrics.initialize(metricsNamespace, metricsRegisterer); err != nil {
		return nil, err
	}
	return i, nil
}

// Accept persists which balances [txID] changed.
// Associates all UTXOs in [i.balanceChanges] with transaction [txID].
// The database structure is:
// [address]
// |  [assetID]
// |  |
// |  | "idx" => 2 		Running transaction index key, represents the next index
// |  | "0"   => txID1
// |  | "1"   => txID1
// See interface documentation AddressTxsIndexer.Accept
func (i *indexer) Accept(txID ids.ID, inputUTXOs []*avax.UTXO, outputUTXOs []*avax.UTXO) error {
	utxos := inputUTXOs
	// Fetch and add the output UTXOs
	utxos = append(utxos, outputUTXOs...)

	// convert UTXOs into balance changes
	// Address -> AssetID --> exists if the address's balance
	// of the asset is changed by processing tx [txID]
	// we do this step separately to simplify the write process later
	balanceChanges := make(map[string]map[ids.ID]struct{})
	for _, utxo := range utxos {
		out, ok := utxo.Out.(avax.Addressable)
		if !ok {
			i.log.Verbo("skipping UTXO %s for indexing", utxo.InputID())
			continue
		}

		for _, addressBytes := range out.Addresses() {
			address := string(addressBytes)

			if _, exists := balanceChanges[address]; !exists {
				balanceChanges[address] = make(map[ids.ID]struct{})
			}
			balanceChanges[address][utxo.AssetID()] = struct{}{}
		}
	}

	// Process the balance changes
	for address, assetIDs := range balanceChanges {
		addressPrefixDB := prefixdb.New([]byte(address), i.db)
		for assetID := range assetIDs {
			assetPrefixDB := prefixdb.New(assetID[:], addressPrefixDB)

			var idx uint64
			idxBytes, err := assetPrefixDB.Get(idxKey)
			switch err {
			case nil:
				// index is found, parse stored [idxBytes]
				idx = binary.BigEndian.Uint64(idxBytes)
			case database.ErrNotFound:
				// idx not found; this must be the first entry.
				idx = 0
				idxBytes = make([]byte, wrappers.LongLen)
				binary.BigEndian.PutUint64(idxBytes, idx)
			default:
				// Unexpected error
				return fmt.Errorf("unexpected error when indexing txID %s: %s", txID, err)
			}

			// write the [txID] at the index
			i.log.Verbo("writing address/assetID/index/txID %s/%s/%d/%s", address, assetID, idx, txID)
			if err := assetPrefixDB.Put(idxBytes, txID[:]); err != nil {
				return fmt.Errorf("failed to write txID while indexing %s: %s", txID, err)
			}

			// increment and store the index for next use
			idx++
			binary.BigEndian.PutUint64(idxBytes, idx)

			if err := assetPrefixDB.Put(idxKey, idxBytes); err != nil {
				return fmt.Errorf("failed to write index txID while indexing %s: %s", txID, err)
			}
		}
	}
	i.metrics.numTxsIndexed.Inc()
	return nil
}

// Read returns IDs of transactions that changed [address]'s balance of [assetID],
// starting at [cursor], in order of transaction acceptance. e.g. if [cursor] == 1, does
// not return the first transaction that changed the balance. (This is for for pagination.)
// Returns at most [pageSize] elements.
// See AddressTxsIndexer
func (i *indexer) Read(address []byte, assetID ids.ID, cursor, pageSize uint64) ([]ids.ID, error) {
	// setup prefix DBs
	addressTxDB := prefixdb.New(address, i.db)
	assetPrefixDB := prefixdb.New(assetID[:], addressTxDB)

	// get cursor in bytes
	cursorBytes := make([]byte, wrappers.LongLen)
	binary.BigEndian.PutUint64(cursorBytes, cursor)

	// start reading from the cursor bytes, numeric keys maintain the order (see Accept)
	iter := assetPrefixDB.NewIteratorWithStart(cursorBytes)
	defer iter.Release()

	var txIDs []ids.ID
	for uint64(len(txIDs)) < pageSize && iter.Next() {
		if bytes.Equal(idxKey, iter.Key()) {
			// This key has the next index to use, not a tx ID
			continue
		}

		// get the value and try to convert it to ID
		txIDBytes := iter.Value()
		txID, err := ids.ToID(txIDBytes)
		if err != nil {
			return nil, err
		}

		txIDs = append(txIDs, txID)
	}
	return txIDs, nil
}

// checkIndexStatus checks the indexing status in the database, returning error if the state
// with respect to provided parameters is invalid
func checkIndexStatus(db database.KeyValueReaderWriter, enableIndexing, allowIncomplete bool) error {
	// verify whether the index is complete.
	idxComplete, err := database.GetBool(db, idxCompleteKey)
	if err == database.ErrNotFound {
		// We've not run before. Mark whether indexing is enabled this run.
		return database.PutBool(db, idxCompleteKey, enableIndexing)
	} else if err != nil {
		return err
	}

	if idxComplete && enableIndexing {
		// indexing has been enabled in the past and we're enabling it now
		return nil
	}

	if !idxComplete && enableIndexing && !allowIncomplete {
		// In a previous run, we did not index so it's incomplete.
		// indexing was disabled before but now we want to index.
		return errIndexingRequiredFromGenesis
	} else if !idxComplete {
		// either indexing is disabled, or incomplete indices are ok, so we don't care that index is incomplete
		return nil
	}

	// the index is complete
	if !enableIndexing && !allowIncomplete { // indexing is disabled this run
		return errCausesIncompleteIndex
	} else if !enableIndexing {
		// running without indexing makes it incomplete
		return database.PutBool(db, idxCompleteKey, false)
	}

	return nil
}

type noIndexer struct{}

func NewNoIndexer(db database.Database, allowIncomplete bool) (AddressTxsIndexer, error) {
	return &noIndexer{}, checkIndexStatus(db, false, allowIncomplete)
}

func (i *noIndexer) Accept(ids.ID, []*avax.UTXO, []*avax.UTXO) error {
	return nil
}

func (i *noIndexer) Read([]byte, ids.ID, uint64, uint64) ([]ids.ID, error) {
	return nil, nil
}
<commit_msg>remove unnecessary assignment<commit_after>// (c) 2021, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package index

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"

	"github.com/prometheus/client_golang/prometheus"

	"github.com/ava-labs/avalanchego/utils/logging"
	"github.com/ava-labs/avalanchego/vms/components/avax"

	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/database/prefixdb"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/utils/wrappers"
)

var (
	idxKey                         = []byte("idx")
	idxCompleteKey                 = []byte("complete")
	errIndexingRequiredFromGenesis = errors.New("running would create incomplete index. Allow incomplete indices or re-sync from genesis with indexing enabled")
	errCausesIncompleteIndex       = errors.New("running would create incomplete index. Allow incomplete indices or enable indexing")
)

// AddressTxsIndexer maintains information about which transactions changed
// the balances of which addresses. This includes both transactions that
// increase and decrease an address's balance.
// A transaction is said to change an address's balance if either is true:
// 1) A UTXO that the transaction consumes was at least partially owned by the address.
// 2) A UTXO that the transaction produces is at least partially owned by the address.
type AddressTxsIndexer interface {
	// Accept is called when [txID] is accepted.
	// Persists data about [txID] and what balances it changed.
	// [inputUTXOs] are the UTXOs [txID] consumes.
	// [outputUTXOs] are the UTXOs [txID] creates.
	// If the error is non-nil, do not persist [txID] to disk as accepted in the VM
	Accept(
		txID ids.ID,
		inputUTXOs []*avax.UTXO,
		outputUTXOs []*avax.UTXO,
	) error

	// Read returns the IDs of transactions that changed [address]'s balance of [assetID].
	// The returned transactions are in order of increasing acceptance time.
	// The length of the returned slice <= [pageSize].
	// [cursor] is the offset to start reading from.
	Read(address []byte, assetID ids.ID, cursor, pageSize uint64) ([]ids.ID, error)
}

// indexer implements AddressTxsIndexer
type indexer struct {
	log     logging.Logger
	metrics metrics
	db      database.Database
	// txID -> Address -> AssetID --> exists if the address's balance
	// of the asset is changed by processing tx [txID]
	balanceChanges map[ids.ID]map[ids.ShortID]map[ids.ID]struct{}
}

// NewIndexer Returns a new AddressTxsIndexer.
// The returned indexer ignores UTXOs that are not type secp256k1fx.TransferOutput.
func NewIndexer(
	db database.Database,
	log logging.Logger,
	metricsNamespace string,
	metricsRegisterer prometheus.Registerer,
	allowIncompleteIndices bool,
) (AddressTxsIndexer, error) {
	i := &indexer{
		balanceChanges: make(map[ids.ID]map[ids.ShortID]map[ids.ID]struct{}),
		db:             db,
		log:            log,
	}
	// initialize the indexer
	if err := checkIndexStatus(i.db, true, allowIncompleteIndices); err != nil {
		return nil, err
	}
	// initialize the metrics
	if err := i.metrics.initialize(metricsNamespace, metricsRegisterer); err != nil {
		return nil, err
	}
	return i, nil
}

// Accept persists which balances [txID] changed.
// Associates all UTXOs in [i.balanceChanges] with transaction [txID].
// The database structure is:
// [address]
// |  [assetID]
// |  |
// |  | "idx" => 2 		Running transaction index key, represents the next index
// |  | "0"   => txID1
// |  | "1"   => txID1
// See interface documentation AddressTxsIndexer.Accept
func (i *indexer) Accept(txID ids.ID, inputUTXOs []*avax.UTXO, outputUTXOs []*avax.UTXO) error {
	utxos := inputUTXOs
	// Fetch and add the output UTXOs
	utxos = append(utxos, outputUTXOs...)

	// convert UTXOs into balance changes
	// Address -> AssetID --> exists if the address's balance
	// of the asset is changed by processing tx [txID]
	// we do this step separately to simplify the write process later
	balanceChanges := make(map[string]map[ids.ID]struct{})
	for _, utxo := range utxos {
		out, ok := utxo.Out.(avax.Addressable)
		if !ok {
			i.log.Verbo("skipping UTXO %s for indexing", utxo.InputID())
			continue
		}

		for _, addressBytes := range out.Addresses() {
			address := string(addressBytes)

			if _, exists := balanceChanges[address]; !exists {
				balanceChanges[address] = make(map[ids.ID]struct{})
			}
			balanceChanges[address][utxo.AssetID()] = struct{}{}
		}
	}

	// Process the balance changes
	for address, assetIDs := range balanceChanges {
		addressPrefixDB := prefixdb.New([]byte(address), i.db)
		for assetID := range assetIDs {
			assetPrefixDB := prefixdb.New(assetID[:], addressPrefixDB)

			var idx uint64
			idxBytes, err := assetPrefixDB.Get(idxKey)
			switch err {
			case nil:
				// index is found, parse stored [idxBytes]
				idx = binary.BigEndian.Uint64(idxBytes)
			case database.ErrNotFound:
				// idx not found; this must be the first entry.
				idxBytes = make([]byte, wrappers.LongLen)
				binary.BigEndian.PutUint64(idxBytes, idx)
			default:
				// Unexpected error
				return fmt.Errorf("unexpected error when indexing txID %s: %s", txID, err)
			}

			// write the [txID] at the index
			i.log.Verbo("writing address/assetID/index/txID %s/%s/%d/%s", address, assetID, idx, txID)
			if err := assetPrefixDB.Put(idxBytes, txID[:]); err != nil {
				return fmt.Errorf("failed to write txID while indexing %s: %s", txID, err)
			}

			// increment and store the index for next use
			idx++
			binary.BigEndian.PutUint64(idxBytes, idx)

			if err := assetPrefixDB.Put(idxKey, idxBytes); err != nil {
				return fmt.Errorf("failed to write index txID while indexing %s: %s", txID, err)
			}
		}
	}
	i.metrics.numTxsIndexed.Inc()
	return nil
}

// Read returns IDs of transactions that changed [address]'s balance of [assetID],
// starting at [cursor], in order of transaction acceptance. e.g. if [cursor] == 1, does
// not return the first transaction that changed the balance. (This is for for pagination.)
// Returns at most [pageSize] elements.
// See AddressTxsIndexer
func (i *indexer) Read(address []byte, assetID ids.ID, cursor, pageSize uint64) ([]ids.ID, error) {
	// setup prefix DBs
	addressTxDB := prefixdb.New(address, i.db)
	assetPrefixDB := prefixdb.New(assetID[:], addressTxDB)

	// get cursor in bytes
	cursorBytes := make([]byte, wrappers.LongLen)
	binary.BigEndian.PutUint64(cursorBytes, cursor)

	// start reading from the cursor bytes, numeric keys maintain the order (see Accept)
	iter := assetPrefixDB.NewIteratorWithStart(cursorBytes)
	defer iter.Release()

	var txIDs []ids.ID
	for uint64(len(txIDs)) < pageSize && iter.Next() {
		if bytes.Equal(idxKey, iter.Key()) {
			// This key has the next index to use, not a tx ID
			continue
		}

		// get the value and try to convert it to ID
		txIDBytes := iter.Value()
		txID, err := ids.ToID(txIDBytes)
		if err != nil {
			return nil, err
		}

		txIDs = append(txIDs, txID)
	}
	return txIDs, nil
}

// checkIndexStatus checks the indexing status in the database, returning error if the state
// with respect to provided parameters is invalid
func checkIndexStatus(db database.KeyValueReaderWriter, enableIndexing, allowIncomplete bool) error {
	// verify whether the index is complete.
	idxComplete, err := database.GetBool(db, idxCompleteKey)
	if err == database.ErrNotFound {
		// We've not run before. Mark whether indexing is enabled this run.
		return database.PutBool(db, idxCompleteKey, enableIndexing)
	} else if err != nil {
		return err
	}

	if idxComplete && enableIndexing {
		// indexing has been enabled in the past and we're enabling it now
		return nil
	}

	if !idxComplete && enableIndexing && !allowIncomplete {
		// In a previous run, we did not index so it's incomplete.
		// indexing was disabled before but now we want to index.
		return errIndexingRequiredFromGenesis
	} else if !idxComplete {
		// either indexing is disabled, or incomplete indices are ok, so we don't care that index is incomplete
		return nil
	}

	// the index is complete
	if !enableIndexing && !allowIncomplete { // indexing is disabled this run
		return errCausesIncompleteIndex
	} else if !enableIndexing {
		// running without indexing makes it incomplete
		return database.PutBool(db, idxCompleteKey, false)
	}

	return nil
}

type noIndexer struct{}

func NewNoIndexer(db database.Database, allowIncomplete bool) (AddressTxsIndexer, error) {
	return &noIndexer{}, checkIndexStatus(db, false, allowIncomplete)
}

func (i *noIndexer) Accept(ids.ID, []*avax.UTXO, []*avax.UTXO) error {
	return nil
}

func (i *noIndexer) Read([]byte, ids.ID, uint64, uint64) ([]ids.ID, error) {
	return nil, nil
}
<|endoftext|>


<commit_before>package goboots

import (
	"bytes"
	"fmt"
	"github.com/gabstv/i18ngo"
	"labix.org/v2/mgo/bson"
	"log"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"text/template"
	"time"
)

const (
	ErrNil = 0
	// TLS
	ErrTLSNil = 100
)

type AppError struct {
	Id      int
	Message string
}

func (e *AppError) Error() string {
	return e.Message
}

type env struct {
	path       string
	controller string
	action     string
	params     []string
	request    *http.Request
	body       string
	form       map[string][]string
}

type Session struct {
	SID  string
	Data map[string]interface{}
	Time time.Time
}

func (s *Session) Flush() {
	FlushSession(s)
}

func (s *Session) GetBool(key string) (bool, bool) {
	iface, ok := s.Data[key]
	if !ok {
		return false, false
	}
	return bool(reflect.ValueOf(iface).Bool()), true
}

func (s *Session) GetBoolD(key string, defaultValue bool) bool {
	iface, ok := s.Data[key]
	if !ok {
		return defaultValue
	}
	return bool(reflect.ValueOf(iface).Bool())
}

func (s *Session) GetInt32(key string) (int, bool) {
	iface, ok := s.Data[key]
	if !ok {
		return 0, false
	}
	return int(reflect.ValueOf(iface).Int()), true
}

func (s *Session) GetInt32D(key string, defaultValue int) int {
	iface, ok := s.Data[key]
	if !ok {
		return defaultValue
	}
	return int(reflect.ValueOf(iface).Int())
}

func (s *Session) GetString(key string) (string, bool) {
	iface, ok := s.Data[key]
	if !ok {
		return "", false
	}
	return reflect.ValueOf(iface).String(), true
}

func (s *Session) GetStringD(key string, defaultValue string) string {
	iface, ok := s.Data[key]
	if !ok {
		return defaultValue
	}
	return reflect.ValueOf(iface).String()
}

func GetSession(w http.ResponseWriter, r *http.Request) *Session {
	var cookie *http.Cookie
	var sid string
	var err error
	//LOAD
	cookie, err = r.Cookie("goboots_sessid")
	if err == nil {
		sid = cookie.Value
		if _validateSidString(sid) {
			var msession *Session
			// try get from cache
			msession = APP.SessionCache.GetSession(sid)
			if msession != nil {
				cache := APP.SessionCache.GetCache(sid)
				mu_session.Lock()
				cache.UpdateTime()
				mu_session.Unlock()
				return msession
			}
			// get from mongo!
			msession = &Session{}
			err = DB.C("goboots_sessid").Find(bson.M{"sid": sid}).One(&msession)
			if err == nil {
				APP.SessionCache.SetCache(msession)
				return msession
			}
			fmt.Printf("SESSION ERROR :( [%s] %s\n", sid, err.Error())
			// not found, generate a new sid
		} else {
			fmt.Printf("COULD NOT VALIDATE :( [%s]\n", sid)
		}
	}
	// gen session
	rand.Seed(time.Now().UnixNano())
	var uuid [16]byte
	for i := 0; i < 16; i++ {
		uuid[i] = byte(rand.Intn(255))
	}
	// secrets
	//uuid[6] = (4 << 4) | (uuid[6] & 15)
	//uuid[8] = (2 << 4) | (uuid[8] & 15)
	//sid = fmt.Sprintf("%x%x%x%x%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:])
	sid = fmt.Sprintf("%x", uuid)
	session := &Session{
		SID:  sid,
		Data: make(map[string]interface{}),
		Time: time.Now(),
	}
	err = DB.C("goboots_sessid").Insert(session)
	__panic(err)
	SetCookieAdv(w, "goboots_sessid", sid, "/", "", time.Now().AddDate(0, 1, 0), 0, false, true)
	return session
}

func GetUserLang(w http.ResponseWriter, r *http.Request) string {
	var l string
	// get lang from cookies
	cookie, err := r.Cookie("lang")
	if err != nil && cookie != nil {
		l = cookie.Value
		return l
	}
	// get lang from http request
	alh := r.Header.Get("Accept-Language")
	// 2013-07-29 : not all clients actually send this header (googlebot/wget etc)
	if len(alh) > 1 {
		validlangs := i18ngo.GetLanguageCodes()
		// break alh
		alh0 := strings.Split(alh, ",")
		for _, v := range alh0 {
			lc := strings.Split(v, ";")[0][0:2]
			if StringIndexOf(validlangs, lc) != -1 {
				l = lc
				break
			}
		}
		if len(l) > 0 {
			// just update the cookies
			SetCookieSimple(w, "lang", l)
		} else {
			l = i18ngo.GetDefaultLanguageCode()
		}
		return l
	}
	return i18ngo.GetDefaultLanguageCode()
}

func SetUserLang(w http.ResponseWriter, r *http.Request, langcode string) {
	SetCookieSimple(w, "lang", langcode)
}

func FlushSession(s *Session) error {
	return DB.C("goboots_sessid").Update(bson.M{"sid": s.SID}, s)
}

func DestroySession(w http.ResponseWriter, r *http.Request, s *Session) {
	DB.C("goboots_sessid").Remove(bson.M{"sid": s.SID})
	APP.SessionCache.DeleteCache(s.SID)
	SetCookieAdv(w, "goboots_sessid", "", "/", "", time.Now(), 1, false, true)
}

func _validateSidString(sid string) bool {
	if len(sid) != 32 {
		return false
	}
	for _, c := range sid {
		// check if sid contains invalid chars
		if !((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) {
			return false
		}
	}
	return true
}

//func _parseCookies(r *http.Request) (map[string]string, error) {
//cookies := make(map[string]string)
//c := r.Cookies()
//c[0].
//}

type templateInfo struct {
	path       string
	data       *template.Template
	lastUpdate time.Time
}

func SetCookieAdv(w http.ResponseWriter, name string, value string, path string, domain string, expires time.Time, maxage int, secure bool, httpOnly bool) {
	cookie := &http.Cookie{
		Name:     name,
		Value:    value,
		Path:     path,
		Domain:   domain,
		Expires:  expires,
		MaxAge:   maxage,
		Secure:   secure,
		HttpOnly: httpOnly,
	}
	http.SetCookie(w, cookie)
}

func SetCookieSimple(w http.ResponseWriter, key string, value string) {
	SetCookieAdv(w, key, value, "/", "", time.Now().AddDate(100, 0, 0), 0, false, false)
}

func StrConcat(strings ...string) string {
	var buffer bytes.Buffer
	for _, str := range strings {
		buffer.WriteString(str)
	}
	return buffer.String()
}

func DeleteItem(slice interface{}, i int) {
	v := reflect.ValueOf(slice).Elem()
	v.Set(reflect.AppendSlice(v.Slice(0, i), v.Slice(i+1, v.Len())))
}

func InsertItem(slice interface{}, i int, val interface{}) {
	v := reflect.ValueOf(slice).Elem()
	v.Set(reflect.AppendSlice(v.Slice(0, i+1), v.Slice(i, v.Len())))
	v.Index(i).Set(reflect.ValueOf(val))
}

func StringIndexOf(haystack []string, needle string) int {
	for i, v := range haystack {
		if v == needle {
			return i
		}
	}
	return -1
}

func IntMax(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func __panic(err error) {
	if err != nil {
		log.Panicln(err)
	}
}

func FormatPath(rawpath string) string {
	cwd, _ := os.Getwd()
	if strings.HasPrefix(rawpath, "./") {
		return filepath.Clean(cwd + rawpath[1:])
	} else if strings.HasPrefix(rawpath, "../") {
		return filepath.Clean(cwd + "/" + rawpath)
	}
	return filepath.Clean(rawpath)
}
<commit_msg>Started session interface<commit_after>package goboots

import (
	"bytes"
	"fmt"
	"github.com/gabstv/i18ngo"
	"labix.org/v2/mgo/bson"
	"log"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"text/template"
	"time"
)

const (
	ErrNil = 0
	// TLS
	ErrTLSNil = 100
)

type AppError struct {
	Id      int
	Message string
}

func (e *AppError) Error() string {
	return e.Message
}

type env struct {
	path       string
	controller string
	action     string
	params     []string
	request    *http.Request
	body       string
	form       map[string][]string
}

type ISession interface {
	GetData() (string, map[string]interface{}, time.Time)
}

type Session struct {
	SID  string
	Data map[string]interface{}
	Time time.Time
}

func (s *Session) GetData() (string, map[string]interface{}, time.Time) {
	return s.SID, s.Data, s.Time
}

func (s *Session) Flush() {
	FlushSession(s)
}

func (s *Session) GetBool(key string) (bool, bool) {
	iface, ok := s.Data[key]
	if !ok {
		return false, false
	}
	return bool(reflect.ValueOf(iface).Bool()), true
}

func (s *Session) GetBoolD(key string, defaultValue bool) bool {
	iface, ok := s.Data[key]
	if !ok {
		return defaultValue
	}
	return bool(reflect.ValueOf(iface).Bool())
}

func (s *Session) GetInt32(key string) (int, bool) {
	iface, ok := s.Data[key]
	if !ok {
		return 0, false
	}
	return int(reflect.ValueOf(iface).Int()), true
}

func (s *Session) GetInt32D(key string, defaultValue int) int {
	iface, ok := s.Data[key]
	if !ok {
		return defaultValue
	}
	return int(reflect.ValueOf(iface).Int())
}

func (s *Session) GetString(key string) (string, bool) {
	iface, ok := s.Data[key]
	if !ok {
		return "", false
	}
	return reflect.ValueOf(iface).String(), true
}

func (s *Session) GetStringD(key string, defaultValue string) string {
	iface, ok := s.Data[key]
	if !ok {
		return defaultValue
	}
	return reflect.ValueOf(iface).String()
}

func GetSession(w http.ResponseWriter, r *http.Request) *Session {
	var cookie *http.Cookie
	var sid string
	var err error
	//LOAD
	cookie, err = r.Cookie("goboots_sessid")
	if err == nil {
		sid = cookie.Value
		if _validateSidString(sid) {
			var msession *Session
			// try get from cache
			msession = APP.SessionCache.GetSession(sid)
			if msession != nil {
				cache := APP.SessionCache.GetCache(sid)
				mu_session.Lock()
				cache.UpdateTime()
				mu_session.Unlock()
				return msession
			}
			// get from mongo!
			msession = &Session{}
			err = DB.C("goboots_sessid").Find(bson.M{"sid": sid}).One(&msession)
			if err == nil {
				APP.SessionCache.SetCache(msession)
				return msession
			}
			fmt.Printf("SESSION ERROR :( [%s] %s\n", sid, err.Error())
			// not found, generate a new sid
		} else {
			fmt.Printf("COULD NOT VALIDATE :( [%s]\n", sid)
		}
	}
	// gen session
	rand.Seed(time.Now().UnixNano())
	var uuid [16]byte
	for i := 0; i < 16; i++ {
		uuid[i] = byte(rand.Intn(255))
	}
	// secrets
	//uuid[6] = (4 << 4) | (uuid[6] & 15)
	//uuid[8] = (2 << 4) | (uuid[8] & 15)
	//sid = fmt.Sprintf("%x%x%x%x%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:])
	sid = fmt.Sprintf("%x", uuid)
	session := &Session{
		SID:  sid,
		Data: make(map[string]interface{}),
		Time: time.Now(),
	}
	err = DB.C("goboots_sessid").Insert(session)
	__panic(err)
	SetCookieAdv(w, "goboots_sessid", sid, "/", "", time.Now().AddDate(0, 1, 0), 0, false, true)
	return session
}

func GetUserLang(w http.ResponseWriter, r *http.Request) string {
	var l string
	// get lang from cookies
	cookie, err := r.Cookie("lang")
	if err != nil && cookie != nil {
		l = cookie.Value
		return l
	}
	// get lang from http request
	alh := r.Header.Get("Accept-Language")
	// 2013-07-29 : not all clients actually send this header (googlebot/wget etc)
	if len(alh) > 1 {
		validlangs := i18ngo.GetLanguageCodes()
		// break alh
		alh0 := strings.Split(alh, ",")
		for _, v := range alh0 {
			lc := strings.Split(v, ";")[0][0:2]
			if StringIndexOf(validlangs, lc) != -1 {
				l = lc
				break
			}
		}
		if len(l) > 0 {
			// just update the cookies
			SetCookieSimple(w, "lang", l)
		} else {
			l = i18ngo.GetDefaultLanguageCode()
		}
		return l
	}
	return i18ngo.GetDefaultLanguageCode()
}

func SetUserLang(w http.ResponseWriter, r *http.Request, langcode string) {
	SetCookieSimple(w, "lang", langcode)
}

func FlushSession(s *Session) error {
	return DB.C("goboots_sessid").Update(bson.M{"sid": s.SID}, s)
}

func DestroySession(w http.ResponseWriter, r *http.Request, s *Session) {
	DB.C("goboots_sessid").Remove(bson.M{"sid": s.SID})
	APP.SessionCache.DeleteCache(s.SID)
	SetCookieAdv(w, "goboots_sessid", "", "/", "", time.Now(), 1, false, true)
}

func _validateSidString(sid string) bool {
	if len(sid) != 32 {
		return false
	}
	for _, c := range sid {
		// check if sid contains invalid chars
		if !((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) {
			return false
		}
	}
	return true
}

//func _parseCookies(r *http.Request) (map[string]string, error) {
//cookies := make(map[string]string)
//c := r.Cookies()
//c[0].
//}

type templateInfo struct {
	path       string
	data       *template.Template
	lastUpdate time.Time
}

func SetCookieAdv(w http.ResponseWriter, name string, value string, path string, domain string, expires time.Time, maxage int, secure bool, httpOnly bool) {
	cookie := &http.Cookie{
		Name:     name,
		Value:    value,
		Path:     path,
		Domain:   domain,
		Expires:  expires,
		MaxAge:   maxage,
		Secure:   secure,
		HttpOnly: httpOnly,
	}
	http.SetCookie(w, cookie)
}

func SetCookieSimple(w http.ResponseWriter, key string, value string) {
	SetCookieAdv(w, key, value, "/", "", time.Now().AddDate(100, 0, 0), 0, false, false)
}

func StrConcat(strings ...string) string {
	var buffer bytes.Buffer
	for _, str := range strings {
		buffer.WriteString(str)
	}
	return buffer.String()
}

func DeleteItem(slice interface{}, i int) {
	v := reflect.ValueOf(slice).Elem()
	v.Set(reflect.AppendSlice(v.Slice(0, i), v.Slice(i+1, v.Len())))
}

func InsertItem(slice interface{}, i int, val interface{}) {
	v := reflect.ValueOf(slice).Elem()
	v.Set(reflect.AppendSlice(v.Slice(0, i+1), v.Slice(i, v.Len())))
	v.Index(i).Set(reflect.ValueOf(val))
}

func StringIndexOf(haystack []string, needle string) int {
	for i, v := range haystack {
		if v == needle {
			return i
		}
	}
	return -1
}

func IntMax(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func __panic(err error) {
	if err != nil {
		log.Panicln(err)
	}
}

func FormatPath(rawpath string) string {
	cwd, _ := os.Getwd()
	if strings.HasPrefix(rawpath, "./") {
		return filepath.Clean(cwd + rawpath[1:])
	} else if strings.HasPrefix(rawpath, "../") {
		return filepath.Clean(cwd + "/" + rawpath)
	}
	return filepath.Clean(rawpath)
}
<|endoftext|>


<commit_before>package wav

import (
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"io"
	"time"

	"github.com/faiface/beep"
	"github.com/pkg/errors"
)

// Decode takes a ReadCloser containing audio data in WAVE format and returns a StreamSeekCloser,
// which streams that audio. The Seek method will panic if rc is not io.Seeker.
//
// Do not close the supplied ReadSeekCloser, instead, use the Close method of the returned
// StreamSeekCloser when you want to release the resources.
func Decode(rc io.ReadCloser) (s beep.StreamSeekCloser, format beep.Format, err error) {
	d := decoder{rc: rc}
	defer func() { // hacky way to always close rsc if an error occured
		if err != nil {
			d.rc.Close()
		}
	}()

	// READ "RIFF" header
	if err := binary.Read(rc, binary.LittleEndian, d.h.RiffMark[:]); err != nil {
		return nil, beep.Format{}, errors.Wrap(err, "wav")
	}

	if string(d.h.RiffMark[:]) != "RIFF" {
		return nil, beep.Format{}, errors.New(fmt.Sprintf("wav: missing RIFF at the beginning > %s", string(d.h.RiffMark[:])))
	}

	// READ Total file size
	if err := binary.Read(rc, binary.LittleEndian, &d.h.FileSize); err != nil {
		return nil, beep.Format{}, errors.Wrap(err, "wav: missing RIFF file size")
	}

	if err := binary.Read(rc, binary.LittleEndian, d.h.WaveMark[:]); err != nil {
		return nil, beep.Format{}, errors.Wrap(err, "wav: missing RIFF file type")
	}

	if string(d.h.WaveMark[:]) != "WAVE" {
		return nil, beep.Format{}, errors.New("wav: unsupported file type")
	}

	// check each formtypes
	ft := [4]byte{0, 0, 0, 0}

	var fs int32 = 0

	for string(ft[:]) != "data" {

		if err = binary.Read(rc, binary.LittleEndian, ft[:]); err != nil {
			return nil, beep.Format{}, errors.Wrap(err, "wav: missing chunk type")
		}

		switch {
		case string(ft[:]) == "fmt ":
			{
				d.h.FmtMark = ft
				if err := binary.Read(rc, binary.LittleEndian, &d.h.FormatSize); err != nil {
					return nil, beep.Format{}, errors.New("wav: missing format chunk size")
				}

				if err := binary.Read(rc, binary.LittleEndian, &d.h.FormatType); err != nil {
					return nil, beep.Format{}, errors.New("wav: missing format type")
				}

				if d.h.FormatType == -2 {
					// WAVEFORMATEXTENSIBLE
					fmtchunk := formatchunkextensible{
						formatchunk{0, 0, 0, 0, 0}, 0, 0, 0,
						guid{0, 0, 0, [8]byte{0, 0, 0, 0, 0, 0, 0, 0}},
					}
					if err := binary.Read(rc, binary.LittleEndian, &fmtchunk); err != nil {
						return nil, beep.Format{}, errors.New("wav: missing format chunk body")
					} else {
						d.h.NumChans = fmtchunk.NumChans
						d.h.SampleRate = fmtchunk.SampleRate
						d.h.ByteRate = fmtchunk.ByteRate
						d.h.BytesPerFrame = fmtchunk.BytesPerFrame
						d.h.BitsPerSample = fmtchunk.BitsPerSample
					}

					// SubFormat is represented by GUID. Plain PCM is KSDATAFORMAT_SUBTYPE_PCM GUID.
					// See https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-waveformatextensible
					pcmguid := guid{
						0x00000001, 0x0000, 0x0010,
						[8]byte{0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71},
					}
					if fmtchunk.SubFormat != pcmguid {
						return nil, beep.Format{}, errors.New(
							fmt.Sprintf(
								"wav: unsupported sub format type - %04x-%02x-%02x-%s",
								fmtchunk.SubFormat.Data1, fmtchunk.SubFormat.Data2, fmtchunk.SubFormat.Data3,
								hex.EncodeToString(fmtchunk.SubFormat.Data4[:]),
							),
						)
					}
				} else {
					// WAVEFORMAT or WAVEFORMATEX
					fmtchunk := formatchunk{0, 0, 0, 0, 0}
					if err := binary.Read(rc, binary.LittleEndian, &fmtchunk); err != nil {
						return nil, beep.Format{}, errors.New("wav: missing format chunk body")
					} else {
						d.h.NumChans = fmtchunk.NumChans
						d.h.SampleRate = fmtchunk.SampleRate
						d.h.ByteRate = fmtchunk.ByteRate
						d.h.BytesPerFrame = fmtchunk.BytesPerFrame
						d.h.BitsPerSample = fmtchunk.BitsPerSample
					}
					// it would be skipping cbSize (WAVEFORMATEX's last member).
					if d.h.FormatSize > 16 {
						trash := make([]byte, d.h.FormatSize-16)
						if err := binary.Read(rc, binary.LittleEndian, trash); err != nil {
							return nil, beep.Format{}, errors.Wrap(err, "wav: missing extended format chunk body")
						}
					}
				}

			}

		case string(ft[:]) == "data":
			{
				d.h.DataMark = ft
				if err := binary.Read(rc, binary.LittleEndian, &d.h.DataSize); err != nil {
					return nil, beep.Format{}, errors.Wrap(err, "wav: missing data chunk size")
				}
			}

		default:
			{
				if err := binary.Read(rc, binary.LittleEndian, &fs); err != nil {
					return nil, beep.Format{}, errors.Wrap(err, "wav: missing unknown chunk size")
				}
				trash := make([]byte, fs)
				if err := binary.Read(rc, binary.LittleEndian, trash); err != nil {
					return nil, beep.Format{}, errors.Wrap(err, "wav: missing unknown chunk body")
				}
			}
		}
	}

	if string(d.h.FmtMark[:]) != "fmt " {
		return nil, beep.Format{}, errors.New("wav: missing format chunk marker")
	}
	if string(d.h.DataMark[:]) != "data" {
		return nil, beep.Format{}, errors.New("wav: missing data chunk marker")
	}
	if d.h.FormatType != 1 && d.h.FormatType != -2 {
		return nil, beep.Format{}, errors.New(fmt.Sprintf("wav: unsupported format type - %d", d.h.FormatType))
	}
	if d.h.NumChans <= 0 {
		return nil, beep.Format{}, errors.New("wav: invalid number of channels (less than 1)")
	}
	if d.h.BitsPerSample != 8 && d.h.BitsPerSample != 16 {
		return nil, beep.Format{}, errors.New("wav: unsupported number of bits per sample, 8 or 16 are supported")
	}
	format = beep.Format{
		SampleRate:  beep.SampleRate(d.h.SampleRate),
		NumChannels: int(d.h.NumChans),
		Precision:   int(d.h.BitsPerSample / 8),
	}
	return &d, format, nil
}

type guid struct {
	Data1 int32
	Data2 int16
	Data3 int16
	Data4 [8]byte
}

type formatchunk struct {
	NumChans      int16
	SampleRate    int32
	ByteRate      int32
	BytesPerFrame int16
	BitsPerSample int16
}

type formatchunkextensible struct {
	formatchunk
	SubFormatSize int16
	Samples       int16 // original: union 3 types of WORD member (wValidBisPerSample, wSamplesPerBlock, wReserved)
	ChannelMask   int32
	SubFormat     guid
}

type header struct {
	RiffMark [4]byte
	FileSize int32

	WaveMark [4]byte

	FmtMark    [4]byte
	FormatSize int32
	FormatType int16

	NumChans      int16
	SampleRate    int32
	ByteRate      int32
	BytesPerFrame int16
	BitsPerSample int16

	DataMark [4]byte
	DataSize int32
}

type decoder struct {
	rc  io.ReadCloser
	h   header
	pos int32
	err error
}

func (d *decoder) Stream(samples [][2]float64) (n int, ok bool) {
	if d.err != nil || d.pos >= d.h.DataSize {
		return 0, false
	}
	bytesPerFrame := int(d.h.BytesPerFrame)
	numBytes := int32(len(samples) * bytesPerFrame)
	if numBytes > d.h.DataSize-d.pos {
		numBytes = d.h.DataSize - d.pos
	}
	p := make([]byte, numBytes)
	n, err := d.rc.Read(p)
	if err != nil && err != io.EOF {
		d.err = err
	}
	switch {
	case d.h.BitsPerSample == 8 && d.h.NumChans == 1:
		for i, j := 0, 0; i <= n-bytesPerFrame; i, j = i+bytesPerFrame, j+1 {
			val := float64(p[i])/(1<<8-1)*2 - 1
			samples[j][0] = val
			samples[j][1] = val
		}
	case d.h.BitsPerSample == 8 && d.h.NumChans >= 2:
		for i, j := 0, 0; i <= n-bytesPerFrame; i, j = i+bytesPerFrame, j+1 {
			samples[j][0] = float64(p[i+0])/(1<<8-1)*2 - 1
			samples[j][1] = float64(p[i+1])/(1<<8-1)*2 - 1
		}
	case d.h.BitsPerSample == 16 && d.h.NumChans == 1:
		for i, j := 0, 0; i <= n-bytesPerFrame; i, j = i+bytesPerFrame, j+1 {
			val := float64(int16(p[i+0])+int16(p[i+1])*(1<<8)) / (1<<15 - 1)
			samples[j][0] = val
			samples[j][1] = val
		}
	case d.h.BitsPerSample == 16 && d.h.NumChans >= 2:
		for i, j := 0, 0; i <= n-bytesPerFrame; i, j = i+bytesPerFrame, j+1 {
			samples[j][0] = float64(int16(p[i+0])+int16(p[i+1])*(1<<8)) / (1<<15 - 1)
			samples[j][1] = float64(int16(p[i+2])+int16(p[i+3])*(1<<8)) / (1<<15 - 1)
		}
	}
	d.pos += int32(n)
	return n / bytesPerFrame, true
}

func (d *decoder) Err() error {
	return d.err
}

func (d *decoder) Len() int {
	numBytes := time.Duration(d.h.DataSize)
	perFrame := time.Duration(d.h.BytesPerFrame)
	return int(numBytes / perFrame)
}

func (d *decoder) Position() int {
	return int(d.pos / int32(d.h.BytesPerFrame))
}

func (d *decoder) Seek(p int) error {
	seeker, ok := d.rc.(io.Seeker)
	if !ok {
		panic(fmt.Errorf("wav: seek: resource is not io.Seeker"))
	}
	if p < 0 || d.Len() < p {
		return fmt.Errorf("wav: seek position %v out of range [%v, %v]", p, 0, d.Len())
	}
	pos := int32(p) * int32(d.h.BytesPerFrame)
	_, err := seeker.Seek(int64(pos)+44, io.SeekStart) // 44 is the size of the header
	if err != nil {
		return errors.Wrap(err, "wav: seek error")
	}
	d.pos = pos
	return nil
}

func (d *decoder) Close() error {
	err := d.rc.Close()
	if err != nil {
		return errors.Wrap(err, "wav")
	}
	return nil
}
<commit_msg>fixed error message (hex string width)<commit_after>package wav

import (
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"io"
	"time"

	"github.com/faiface/beep"
	"github.com/pkg/errors"
)

// Decode takes a ReadCloser containing audio data in WAVE format and returns a StreamSeekCloser,
// which streams that audio. The Seek method will panic if rc is not io.Seeker.
//
// Do not close the supplied ReadSeekCloser, instead, use the Close method of the returned
// StreamSeekCloser when you want to release the resources.
func Decode(rc io.ReadCloser) (s beep.StreamSeekCloser, format beep.Format, err error) {
	d := decoder{rc: rc}
	defer func() { // hacky way to always close rsc if an error occured
		if err != nil {
			d.rc.Close()
		}
	}()

	// READ "RIFF" header
	if err := binary.Read(rc, binary.LittleEndian, d.h.RiffMark[:]); err != nil {
		return nil, beep.Format{}, errors.Wrap(err, "wav")
	}

	if string(d.h.RiffMark[:]) != "RIFF" {
		return nil, beep.Format{}, errors.New(fmt.Sprintf("wav: missing RIFF at the beginning > %s", string(d.h.RiffMark[:])))
	}

	// READ Total file size
	if err := binary.Read(rc, binary.LittleEndian, &d.h.FileSize); err != nil {
		return nil, beep.Format{}, errors.Wrap(err, "wav: missing RIFF file size")
	}

	if err := binary.Read(rc, binary.LittleEndian, d.h.WaveMark[:]); err != nil {
		return nil, beep.Format{}, errors.Wrap(err, "wav: missing RIFF file type")
	}

	if string(d.h.WaveMark[:]) != "WAVE" {
		return nil, beep.Format{}, errors.New("wav: unsupported file type")
	}

	// check each formtypes
	ft := [4]byte{0, 0, 0, 0}

	var fs int32 = 0

	for string(ft[:]) != "data" {

		if err = binary.Read(rc, binary.LittleEndian, ft[:]); err != nil {
			return nil, beep.Format{}, errors.Wrap(err, "wav: missing chunk type")
		}

		switch {
		case string(ft[:]) == "fmt ":
			{
				d.h.FmtMark = ft
				if err := binary.Read(rc, binary.LittleEndian, &d.h.FormatSize); err != nil {
					return nil, beep.Format{}, errors.New("wav: missing format chunk size")
				}

				if err := binary.Read(rc, binary.LittleEndian, &d.h.FormatType); err != nil {
					return nil, beep.Format{}, errors.New("wav: missing format type")
				}

				if d.h.FormatType == -2 {
					// WAVEFORMATEXTENSIBLE
					fmtchunk := formatchunkextensible{
						formatchunk{0, 0, 0, 0, 0}, 0, 0, 0,
						guid{0, 0, 0, [8]byte{0, 0, 0, 0, 0, 0, 0, 0}},
					}
					if err := binary.Read(rc, binary.LittleEndian, &fmtchunk); err != nil {
						return nil, beep.Format{}, errors.New("wav: missing format chunk body")
					} else {
						d.h.NumChans = fmtchunk.NumChans
						d.h.SampleRate = fmtchunk.SampleRate
						d.h.ByteRate = fmtchunk.ByteRate
						d.h.BytesPerFrame = fmtchunk.BytesPerFrame
						d.h.BitsPerSample = fmtchunk.BitsPerSample
					}

					// SubFormat is represented by GUID. Plain PCM is KSDATAFORMAT_SUBTYPE_PCM GUID.
					// See https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-waveformatextensible
					pcmguid := guid{
						0x00000001, 0x0000, 0x0010,
						[8]byte{0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71},
					}
					if fmtchunk.SubFormat != pcmguid {
						return nil, beep.Format{}, errors.New(
							fmt.Sprintf(
								"wav: unsupported sub format type - %08x-%04x-%04x-%s",
								fmtchunk.SubFormat.Data1, fmtchunk.SubFormat.Data2, fmtchunk.SubFormat.Data3,
								hex.EncodeToString(fmtchunk.SubFormat.Data4[:]),
							),
						)
					}
				} else {
					// WAVEFORMAT or WAVEFORMATEX
					fmtchunk := formatchunk{0, 0, 0, 0, 0}
					if err := binary.Read(rc, binary.LittleEndian, &fmtchunk); err != nil {
						return nil, beep.Format{}, errors.New("wav: missing format chunk body")
					} else {
						d.h.NumChans = fmtchunk.NumChans
						d.h.SampleRate = fmtchunk.SampleRate
						d.h.ByteRate = fmtchunk.ByteRate
						d.h.BytesPerFrame = fmtchunk.BytesPerFrame
						d.h.BitsPerSample = fmtchunk.BitsPerSample
					}
					// it would be skipping cbSize (WAVEFORMATEX's last member).
					if d.h.FormatSize > 16 {
						trash := make([]byte, d.h.FormatSize-16)
						if err := binary.Read(rc, binary.LittleEndian, trash); err != nil {
							return nil, beep.Format{}, errors.Wrap(err, "wav: missing extended format chunk body")
						}
					}
				}

			}

		case string(ft[:]) == "data":
			{
				d.h.DataMark = ft
				if err := binary.Read(rc, binary.LittleEndian, &d.h.DataSize); err != nil {
					return nil, beep.Format{}, errors.Wrap(err, "wav: missing data chunk size")
				}
			}

		default:
			{
				if err := binary.Read(rc, binary.LittleEndian, &fs); err != nil {
					return nil, beep.Format{}, errors.Wrap(err, "wav: missing unknown chunk size")
				}
				trash := make([]byte, fs)
				if err := binary.Read(rc, binary.LittleEndian, trash); err != nil {
					return nil, beep.Format{}, errors.Wrap(err, "wav: missing unknown chunk body")
				}
			}
		}
	}

	if string(d.h.FmtMark[:]) != "fmt " {
		return nil, beep.Format{}, errors.New("wav: missing format chunk marker")
	}
	if string(d.h.DataMark[:]) != "data" {
		return nil, beep.Format{}, errors.New("wav: missing data chunk marker")
	}
	if d.h.FormatType != 1 && d.h.FormatType != -2 {
		return nil, beep.Format{}, errors.New(fmt.Sprintf("wav: unsupported format type - %d", d.h.FormatType))
	}
	if d.h.NumChans <= 0 {
		return nil, beep.Format{}, errors.New("wav: invalid number of channels (less than 1)")
	}
	if d.h.BitsPerSample != 8 && d.h.BitsPerSample != 16 {
		return nil, beep.Format{}, errors.New("wav: unsupported number of bits per sample, 8 or 16 are supported")
	}
	format = beep.Format{
		SampleRate:  beep.SampleRate(d.h.SampleRate),
		NumChannels: int(d.h.NumChans),
		Precision:   int(d.h.BitsPerSample / 8),
	}
	return &d, format, nil
}

type guid struct {
	Data1 int32
	Data2 int16
	Data3 int16
	Data4 [8]byte
}

type formatchunk struct {
	NumChans      int16
	SampleRate    int32
	ByteRate      int32
	BytesPerFrame int16
	BitsPerSample int16
}

type formatchunkextensible struct {
	formatchunk
	SubFormatSize int16
	Samples       int16 // original: union 3 types of WORD member (wValidBisPerSample, wSamplesPerBlock, wReserved)
	ChannelMask   int32
	SubFormat     guid
}

type header struct {
	RiffMark [4]byte
	FileSize int32

	WaveMark [4]byte

	FmtMark    [4]byte
	FormatSize int32
	FormatType int16

	NumChans      int16
	SampleRate    int32
	ByteRate      int32
	BytesPerFrame int16
	BitsPerSample int16

	DataMark [4]byte
	DataSize int32
}

type decoder struct {
	rc  io.ReadCloser
	h   header
	pos int32
	err error
}

func (d *decoder) Stream(samples [][2]float64) (n int, ok bool) {
	if d.err != nil || d.pos >= d.h.DataSize {
		return 0, false
	}
	bytesPerFrame := int(d.h.BytesPerFrame)
	numBytes := int32(len(samples) * bytesPerFrame)
	if numBytes > d.h.DataSize-d.pos {
		numBytes = d.h.DataSize - d.pos
	}
	p := make([]byte, numBytes)
	n, err := d.rc.Read(p)
	if err != nil && err != io.EOF {
		d.err = err
	}
	switch {
	case d.h.BitsPerSample == 8 && d.h.NumChans == 1:
		for i, j := 0, 0; i <= n-bytesPerFrame; i, j = i+bytesPerFrame, j+1 {
			val := float64(p[i])/(1<<8-1)*2 - 1
			samples[j][0] = val
			samples[j][1] = val
		}
	case d.h.BitsPerSample == 8 && d.h.NumChans >= 2:
		for i, j := 0, 0; i <= n-bytesPerFrame; i, j = i+bytesPerFrame, j+1 {
			samples[j][0] = float64(p[i+0])/(1<<8-1)*2 - 1
			samples[j][1] = float64(p[i+1])/(1<<8-1)*2 - 1
		}
	case d.h.BitsPerSample == 16 && d.h.NumChans == 1:
		for i, j := 0, 0; i <= n-bytesPerFrame; i, j = i+bytesPerFrame, j+1 {
			val := float64(int16(p[i+0])+int16(p[i+1])*(1<<8)) / (1<<15 - 1)
			samples[j][0] = val
			samples[j][1] = val
		}
	case d.h.BitsPerSample == 16 && d.h.NumChans >= 2:
		for i, j := 0, 0; i <= n-bytesPerFrame; i, j = i+bytesPerFrame, j+1 {
			samples[j][0] = float64(int16(p[i+0])+int16(p[i+1])*(1<<8)) / (1<<15 - 1)
			samples[j][1] = float64(int16(p[i+2])+int16(p[i+3])*(1<<8)) / (1<<15 - 1)
		}
	}
	d.pos += int32(n)
	return n / bytesPerFrame, true
}

func (d *decoder) Err() error {
	return d.err
}

func (d *decoder) Len() int {
	numBytes := time.Duration(d.h.DataSize)
	perFrame := time.Duration(d.h.BytesPerFrame)
	return int(numBytes / perFrame)
}

func (d *decoder) Position() int {
	return int(d.pos / int32(d.h.BytesPerFrame))
}

func (d *decoder) Seek(p int) error {
	seeker, ok := d.rc.(io.Seeker)
	if !ok {
		panic(fmt.Errorf("wav: seek: resource is not io.Seeker"))
	}
	if p < 0 || d.Len() < p {
		return fmt.Errorf("wav: seek position %v out of range [%v, %v]", p, 0, d.Len())
	}
	pos := int32(p) * int32(d.h.BytesPerFrame)
	_, err := seeker.Seek(int64(pos)+44, io.SeekStart) // 44 is the size of the header
	if err != nil {
		return errors.Wrap(err, "wav: seek error")
	}
	d.pos = pos
	return nil
}

func (d *decoder) Close() error {
	err := d.rc.Close()
	if err != nil {
		return errors.Wrap(err, "wav")
	}
	return nil
}
<|endoftext|>


<commit_before>package sdcard

import (
	"bits"
)

type Command uint16

// All Command constants are defined to be friendly to use with ARM PrimeCell
// Multimedia Card Interface (used by STM32, LPC and probably more MCUs). Do
// not add, delete, modify command fields without checking the stm32/hal/sdmmc
// and lpc/hal/sdmmc.
const (
	// Command fields
	CmdIdx   Command = 63 << 0 // Command index.
	HasResp  Command = 1 << 6  // Response expected.
	LongResp Command = 1 << 7  // Long response.
	RespIdx  Command = 15 << 8 // Response index.
	Busy     Command = 1 << 14 // Command can set D0 low to signal busy state.
	App      Command = 1 << 15 // Application command (hint, APP_CMD required).

	// Response types
	RespType = RespIdx | HasResp | LongResp
	R1       = 1<<10 | HasResp
	R2       = 2<<10 | HasResp | LongResp
	R3       = 3<<10 | HasResp
	R4       = 4<<10 | HasResp
	R5       = 5<<10 | HasResp
	R6       = 6<<10 | HasResp
	R7       = 7<<10 | HasResp

	cmd0  = 0              // GO_IDLE_STATE
	cmd2  = 2 | R2         // ALL_SEND_CID
	cmd3  = 3 | R6         // SEND_RELATIVE_ADDR
	cmd4  = 4              // SET_DSR
	cmd5  = 5 | R4         // IO_SEND_OP_COND
	cmd6  = 6 | R1         // SWITCH_FUNC
	cmd7  = 7 | R1 | Busy  // SELECT_CARD/DESELECT_CARD
	cmd8  = 8 | R7         // SEND_IF_COND
	cmd9  = 9 | R2         // SEND_CSD
	cmd10 = 10 | R2        // SEND_CID
	cmd11 = 11 | R1        // VOLTAGE_SWITCH
	cmd12 = 12 | R1 | Busy // STOP_TRANSMISSION
	cmd13 = 13 | R1        // SEND_STATUS/SEND_TASK_STATUS
	cmd15 = 15             // GO_INACTIVE_STATE
	cmd16 = 16 | R1        // SET_BLOCKLEN
	cmd17 = 17 | R1        // READ_SINGLE_BLOCK
	cmd18 = 18 | R1        // READ_MULTIPLE_BLOCK
	cmd19 = 19 | R1        // SEND_TUNING_BLOCK
	cmd20 = 20 | R1 | Busy // SPEED_CLASS_CONTROL
	cmd23 = 23 | R1        // SET_BLOCK_COUNT
	cmd24 = 24 | R1        // WRITE_BLOCK
	cmd25 = 25 | R1        // WRITE_MULTIPLE_BLOCK
	cmd27 = 27 | R1        // PROGRAM_CSD
	cmd28 = 28 | R1 | Busy // SET_WRITE_PROT
	cmd29 = 29 | R1 | Busy // CLR_WRITE_PROT
	cmd30 = 30 | R1        // SEND_WRITE_PROT
	cmd32 = 30 | R1        // ERASE_WR_BLK_START
	cmd33 = 33 | R1        // ERASE_WR_BLK_END
	cmd38 = 38 | R1 | Busy // ERASE
	cmd40 = 40 | R1        // TODO: See DPS spec.
	cmd42 = 42 | R1        // LOCK_UNLOCK
	cmd52 = 52 | R5        // IO_RW_DIRECT
	cmd53 = 53 | R5        // IO_RW_EXTENDED
	cmd55 = 55 | R1        // APP_CMD
	cmd56 = 56 | R1        // GEN_CMD

	acmd6  = 6 | R1 | App  // SET_BUS_WIDTH
	acmd13 = 13 | R1 | App // SD_STATUS
	acmd22 = 22 | R1 | App // SEND_NUM_WR_BLOCKS
	acmd23 = 23 | R1 | App // SET_WR_BLK_ERASE_COUNT
	acmd41 = 41 | R3 | App // SD_SEND_OP_COND
	acmd42 = 42 | R1 | App // SET_CLR_CARD_DETECT
	acmd51 = 51 | R1 | App // SEND_SCR
)

// Use command numbers (eg. CMD17) as function names instead of abbreviated
// command names (eg. READ_SINGLE_BLOCK) because the SD Card protocol
// specification uses numbers instead of names when describes state machines
// and algorithms. It turned out to be difficult to follow the source code side
// by side with the protocol specification when code uses command names.

// CMD0 (GO_IDLE_STATE) performs software reset and sets the card into Idle
// State.
func CMD0() (Command, uint32) {
	return cmd0, 0
}

// CMD2 (ALL_SEND_CID, R2) gets Card Identification Data.
func CMD2() (Command, uint32) {
	return cmd2, 0
}

// CMD3 (SEND_RELATIVE_ADDR, R6) asks the card to publishets a new Relative
// Card Address (RCA) and Card Status bits 23,22,19,12:0
func CMD3() (Command, uint32) {
	return cmd3, 0
}

// CMD5 (IO_SEND_OP_COND, R4) inquires about the voltage range needed by the
// I/O card.
func CMD5(ocr OCR) (Command, uint32) {
	return cmd5, uint32(ocr)
}

type SwitchFunc uint32

const (
	AccessMode   SwitchFunc = 0x00000F // Access mode (keep current).
	DefaultSpeed SwitchFunc = 0x000000 // Default Speed or SDR12.
	HighSpeed    SwitchFunc = 0x000001 // High Speed or SDR25.
	SDR50        SwitchFunc = 0x000002 // SDR50.
	SDR104       SwitchFunc = 0x000003 // SDR104.
	DDR50        SwitchFunc = 0x000004 // DDR50.

	CommandSystem SwitchFunc = 0x0000F0 // Command system (keep current).
	DefaultSystem SwitchFunc = 0x000000 // Default Command System.
	OTP           SwitchFunc = 0x000030
	ASSD          SwitchFunc = 0x000040
	VendorSpec    SwitchFunc = 0x0000E0

	Driver       SwitchFunc = 0x000F00 // UHS-I driver strength (keep current).
	DefaultTypeB SwitchFunc = 0x000000 // Default Type B driver.
	TypeA        SwitchFunc = 0x000100 // Type A driver.
	TypeC        SwitchFunc = 0x000200 // Type C driver.
	TypeD        SwitchFunc = 0x000300 // Type D driver.

	PowerLimit SwitchFunc = 0x00F000 // Power limit (keep current).
	Default720 SwitchFunc = 0x000000 // Default limit: 720 mW.
	Power1440  SwitchFunc = 0x001000 // Limit: 1440 mW.
	Power2160  SwitchFunc = 0x002000 // Limit: 2160 mW.
	Power2880  SwitchFunc = 0x003000 // Limit: 2880 mW.
	Power1800  SwitchFunc = 0x004000 // Limit: 1800 mW.

	ModeCheck  SwitchFunc = 0 << 31 // Checks switchable function.
	ModeSwitch SwitchFunc = 1 << 31 // Switch card function.
)

// CMD6 (SWITCH_FUNC, R1) switches or expands memory card functions.
func CMD6(sf SwitchFunc) (Command, uint32) {
	return cmd6, uint32(sf)
}

// CMD7 (SELECT_CARD/DESELECT_CARD, R1b) selects card with rca address (puts
// into Transfer State) and deselects all other (puts into Stand-by State).
func CMD7(rca uint16) (Command, uint32) {
	return cmd7, uint32(rca) << 16
}

type VHS byte

const (
	V27_36 VHS = 1 << 0
	LVR    VHS = 1 << 1
)

// CMD8 (SEND_IF_COND, R7) initializes SD Memory Cards compliant to the Physical
// Layer Specification Version 2.00 or later.
func CMD8(vhs VHS, checkPattern byte) (Command, uint32) {
	return cmd8, uint32(vhs)&0xF<<8 | uint32(checkPattern)
}

// CMD9 (SEND_CSD, R2) reads Card Specific Data from card indentified by rca.
func CMD9(rca uint16) (Command, uint32) {
	return cmd9, uint32(rca) << 16
}

// CMD12 (STOP_TRANSMISSION, R1b) forces the card to stop transmission in
// Multiple Block Read Operation.
func CMD12() (Command, uint32) {
	return cmd12, 0
}

type StatusReg byte

const (
	Status     StatusReg = 0
	TaskStatus StatusReg = 1
)

// CMD13 (SEND_STATUS/SEND_TASK_STATUS, R1) reads Status or TaskStatus register.
func CMD13(rca uint16, reg StatusReg) (Command, uint32) {
	return cmd13, uint32(rca)<<16 | uint32(reg)<<15
}

// CMD16 (SET_BLOCKLEN, R1) sets the block length (in bytes) for block commands.
func CMD16(blen int) (Command, uint32) {
	return cmd16, uint32(blen)
}

// CMD17 (READ_SINGLE_BLOCK, R1) reads a block of the size selected by CMD16.
func CMD17(addr uint) (Command, uint32) {
	return cmd17, uint32(addr)
}

// CMD18 (READ_MULTIPLE_BLOCK, R1) works like CMD17 but does not stop the
// transmision after first data block. Instead the card continuously transfers
// data blocks until it receives CMD12 (STOP_TRANSMISSION) command.
func CMD18(addr uint) (Command, uint32) {
	return cmd18, uint32(addr)
}

// CMD24 (WRITE_BLOCK, R1) writes a block of the size selected by CMD16.
func CMD24(addr uint) (Command, uint32) {
	return cmd24, uint32(addr)
}

// CMD25 (WRITE_MULTIPLE_BLOCK, R1) works like CMD24 but allows to transmit
// multiple block to the card until. To signal the end of transfer host have to
// send CMD12 (STOP_TRANSMISSION) command.
func CMD25(addr uint) (Command, uint32) {
	return cmd25, uint32(addr)
}

type IORWFlags byte

const (
	IncAddr IORWFlags = 1 << 0 // OP Code (CMD53)
	RAW     IORWFlags = 1 << 1 // Read after write (CMD52)
	Block   IORWFlags = 1 << 1 // Block mode (CMD53)
	Read    IORWFlags = 0 << 5 // Read data (CMD52, CMD53)
	Write   IORWFlags = 1 << 5 // Write data (CMD52, CMD53)

	WriteRead = Write | RAW
)

func panicIOFunc() {
	panic("sdcard: IO func")
}

func panicIOAddr() {
	panic("sdcard: IO addr")
}

// CMD52 (IO_RW_DIRECT, R5)
func CMD52(fn, addr int, flags IORWFlags, val byte) (Command, uint32) {
	if uint(fn) > 7 {
		panicIOFunc()
	}
	if uint(addr) > 0x1FFFF {
		panicIOAddr()
	}
	return cmd52,
		uint32(val) | uint32(addr)<<9 | uint32(flags)<<26 | uint32(fn)<<28
}

// CMD53 (IO_RW_EXTENDED, R5)
func CMD53(fn, addr int, flags IORWFlags, n int) (Command, uint32) {
	if uint(fn) > 7 {
		panicIOFunc()
	}
	if uint(addr) > 0x1FFFF {
		panicIOAddr()
	}
	if uint(n) > 0x1F {
		panic("sdcard: IO count")
	}
	return cmd52,
		uint32(n) | uint32(addr)<<9 | uint32(flags)<<26 | uint32(fn)<<28
}

// CMD55 (APP_CMD, R1) indicates to the card that the next command is an
// application specific command.
func CMD55(rca uint16) (Command, uint32) {
	return cmd55, uint32(rca) << 16
}

// ACMD6 (SET_BUS_WIDTH, R1) sets the data bus width.
func ACMD6(bw BusWidth) (Command, uint32) {
	return acmd6, uint32(bw)
}

// ACMD41 (SD_SEND_OP_COND, R3) starts initialization/identification process.
func ACMD41(ocr OCR) (Command, uint32) {
	return acmd41, uint32(ocr)
}

// ACMD42 (SET_CLR_CARD_DETECT, R1) enables/disables pull-up resistor on D3/CD.
func ACMD42(pullUp bool) (Command, uint32) {
	return acmd42, uint32(bits.One(pullUp))
}

// ACMD51 (SEND_SCR, R1) reads SD Configuration Register.
func ACMD51() (Command, uint32) {
	return acmd51, 0
}
<commit_msg>sdcard: Fix bug in RespIdx<commit_after>package sdcard

import (
	"bits"
)

type Command uint16

// All Command constants are defined to be friendly to use with ARM PrimeCell
// Multimedia Card Interface (used by STM32, LPC and probably more MCUs). Do
// not add, delete, modify command fields without checking the stm32/hal/sdmmc
// and lpc/hal/sdmmc.
const (
	// Command fields
	CmdIdx   Command = 63 << 0  // Command index.
	HasResp  Command = 1 << 6   // Response expected.
	LongResp Command = 1 << 7   // Long response.
	RespIdx  Command = 15 << 10 // Response index.
	Busy     Command = 1 << 14  // Command can set D0 low to signal busy state.
	App      Command = 1 << 15  // Application command (hint, APP_CMD required).

	// Response types
	RespType = RespIdx | HasResp | LongResp
	R1       = 1<<10 | HasResp
	R2       = 2<<10 | HasResp | LongResp
	R3       = 3<<10 | HasResp
	R4       = 4<<10 | HasResp
	R5       = 5<<10 | HasResp
	R6       = 6<<10 | HasResp
	R7       = 7<<10 | HasResp

	cmd0  = 0              // GO_IDLE_STATE
	cmd2  = 2 | R2         // ALL_SEND_CID
	cmd3  = 3 | R6         // SEND_RELATIVE_ADDR
	cmd4  = 4              // SET_DSR
	cmd5  = 5 | R4         // IO_SEND_OP_COND
	cmd6  = 6 | R1         // SWITCH_FUNC
	cmd7  = 7 | R1 | Busy  // SELECT_CARD/DESELECT_CARD
	cmd8  = 8 | R7         // SEND_IF_COND
	cmd9  = 9 | R2         // SEND_CSD
	cmd10 = 10 | R2        // SEND_CID
	cmd11 = 11 | R1        // VOLTAGE_SWITCH
	cmd12 = 12 | R1 | Busy // STOP_TRANSMISSION
	cmd13 = 13 | R1        // SEND_STATUS/SEND_TASK_STATUS
	cmd15 = 15             // GO_INACTIVE_STATE
	cmd16 = 16 | R1        // SET_BLOCKLEN
	cmd17 = 17 | R1        // READ_SINGLE_BLOCK
	cmd18 = 18 | R1        // READ_MULTIPLE_BLOCK
	cmd19 = 19 | R1        // SEND_TUNING_BLOCK
	cmd20 = 20 | R1 | Busy // SPEED_CLASS_CONTROL
	cmd23 = 23 | R1        // SET_BLOCK_COUNT
	cmd24 = 24 | R1        // WRITE_BLOCK
	cmd25 = 25 | R1        // WRITE_MULTIPLE_BLOCK
	cmd27 = 27 | R1        // PROGRAM_CSD
	cmd28 = 28 | R1 | Busy // SET_WRITE_PROT
	cmd29 = 29 | R1 | Busy // CLR_WRITE_PROT
	cmd30 = 30 | R1        // SEND_WRITE_PROT
	cmd32 = 30 | R1        // ERASE_WR_BLK_START
	cmd33 = 33 | R1        // ERASE_WR_BLK_END
	cmd38 = 38 | R1 | Busy // ERASE
	cmd40 = 40 | R1        // TODO: See DPS spec.
	cmd42 = 42 | R1        // LOCK_UNLOCK
	cmd52 = 52 | R5        // IO_RW_DIRECT
	cmd53 = 53 | R5        // IO_RW_EXTENDED
	cmd55 = 55 | R1        // APP_CMD
	cmd56 = 56 | R1        // GEN_CMD

	acmd6  = 6 | R1 | App  // SET_BUS_WIDTH
	acmd13 = 13 | R1 | App // SD_STATUS
	acmd22 = 22 | R1 | App // SEND_NUM_WR_BLOCKS
	acmd23 = 23 | R1 | App // SET_WR_BLK_ERASE_COUNT
	acmd41 = 41 | R3 | App // SD_SEND_OP_COND
	acmd42 = 42 | R1 | App // SET_CLR_CARD_DETECT
	acmd51 = 51 | R1 | App // SEND_SCR
)

// Use command numbers (eg. CMD17) as function names instead of abbreviated
// command names (eg. READ_SINGLE_BLOCK) because the SD Card protocol
// specification uses numbers instead of names when describes state machines
// and algorithms. It turned out to be difficult to follow the source code side
// by side with the protocol specification when code uses command names.

// CMD0 (GO_IDLE_STATE) performs software reset and sets the card into Idle
// State.
func CMD0() (Command, uint32) {
	return cmd0, 0
}

// CMD2 (ALL_SEND_CID, R2) gets Card Identification Data.
func CMD2() (Command, uint32) {
	return cmd2, 0
}

// CMD3 (SEND_RELATIVE_ADDR, R6) asks the card to publishets a new Relative
// Card Address (RCA) and Card Status bits 23,22,19,12:0
func CMD3() (Command, uint32) {
	return cmd3, 0
}

// CMD5 (IO_SEND_OP_COND, R4) inquires about the voltage range needed by the
// I/O card.
func CMD5(ocr OCR) (Command, uint32) {
	return cmd5, uint32(ocr)
}

type SwitchFunc uint32

const (
	AccessMode   SwitchFunc = 0x00000F // Access mode (keep current).
	DefaultSpeed SwitchFunc = 0x000000 // Default Speed or SDR12.
	HighSpeed    SwitchFunc = 0x000001 // High Speed or SDR25.
	SDR50        SwitchFunc = 0x000002 // SDR50.
	SDR104       SwitchFunc = 0x000003 // SDR104.
	DDR50        SwitchFunc = 0x000004 // DDR50.

	CommandSystem SwitchFunc = 0x0000F0 // Command system (keep current).
	DefaultSystem SwitchFunc = 0x000000 // Default Command System.
	OTP           SwitchFunc = 0x000030
	ASSD          SwitchFunc = 0x000040
	VendorSpec    SwitchFunc = 0x0000E0

	Driver       SwitchFunc = 0x000F00 // UHS-I driver strength (keep current).
	DefaultTypeB SwitchFunc = 0x000000 // Default Type B driver.
	TypeA        SwitchFunc = 0x000100 // Type A driver.
	TypeC        SwitchFunc = 0x000200 // Type C driver.
	TypeD        SwitchFunc = 0x000300 // Type D driver.

	PowerLimit SwitchFunc = 0x00F000 // Power limit (keep current).
	Default720 SwitchFunc = 0x000000 // Default limit: 720 mW.
	Power1440  SwitchFunc = 0x001000 // Limit: 1440 mW.
	Power2160  SwitchFunc = 0x002000 // Limit: 2160 mW.
	Power2880  SwitchFunc = 0x003000 // Limit: 2880 mW.
	Power1800  SwitchFunc = 0x004000 // Limit: 1800 mW.

	ModeCheck  SwitchFunc = 0 << 31 // Checks switchable function.
	ModeSwitch SwitchFunc = 1 << 31 // Switch card function.
)

// CMD6 (SWITCH_FUNC, R1) switches or expands memory card functions.
func CMD6(sf SwitchFunc) (Command, uint32) {
	return cmd6, uint32(sf)
}

// CMD7 (SELECT_CARD/DESELECT_CARD, R1b) selects card with rca address (puts
// into Transfer State) and deselects all other (puts into Stand-by State).
func CMD7(rca uint16) (Command, uint32) {
	return cmd7, uint32(rca) << 16
}

type VHS byte

const (
	V27_36 VHS = 1 << 0
	LVR    VHS = 1 << 1
)

// CMD8 (SEND_IF_COND, R7) initializes SD Memory Cards compliant to the Physical
// Layer Specification Version 2.00 or later.
func CMD8(vhs VHS, checkPattern byte) (Command, uint32) {
	return cmd8, uint32(vhs)&0xF<<8 | uint32(checkPattern)
}

// CMD9 (SEND_CSD, R2) reads Card Specific Data from card indentified by rca.
func CMD9(rca uint16) (Command, uint32) {
	return cmd9, uint32(rca) << 16
}

// CMD12 (STOP_TRANSMISSION, R1b) forces the card to stop transmission in
// Multiple Block Read Operation.
func CMD12() (Command, uint32) {
	return cmd12, 0
}

type StatusReg byte

const (
	Status     StatusReg = 0
	TaskStatus StatusReg = 1
)

// CMD13 (SEND_STATUS/SEND_TASK_STATUS, R1) reads Status or TaskStatus register.
func CMD13(rca uint16, reg StatusReg) (Command, uint32) {
	return cmd13, uint32(rca)<<16 | uint32(reg)<<15
}

// CMD16 (SET_BLOCKLEN, R1) sets the block length (in bytes) for block commands.
func CMD16(blen int) (Command, uint32) {
	return cmd16, uint32(blen)
}

// CMD17 (READ_SINGLE_BLOCK, R1) reads a block of the size selected by CMD16.
func CMD17(addr uint) (Command, uint32) {
	return cmd17, uint32(addr)
}

// CMD18 (READ_MULTIPLE_BLOCK, R1) works like CMD17 but does not stop the
// transmision after first data block. Instead the card continuously transfers
// data blocks until it receives CMD12 (STOP_TRANSMISSION) command.
func CMD18(addr uint) (Command, uint32) {
	return cmd18, uint32(addr)
}

// CMD24 (WRITE_BLOCK, R1) writes a block of the size selected by CMD16.
func CMD24(addr uint) (Command, uint32) {
	return cmd24, uint32(addr)
}

// CMD25 (WRITE_MULTIPLE_BLOCK, R1) works like CMD24 but allows to transmit
// multiple block to the card until. To signal the end of transfer host have to
// send CMD12 (STOP_TRANSMISSION) command.
func CMD25(addr uint) (Command, uint32) {
	return cmd25, uint32(addr)
}

type IORWFlags byte

const (
	IncAddr IORWFlags = 1 << 0 // OP Code (CMD53)
	RAW     IORWFlags = 1 << 1 // Read after write (CMD52)
	Block   IORWFlags = 1 << 1 // Block mode (CMD53)
	Read    IORWFlags = 0 << 5 // Read data (CMD52, CMD53)
	Write   IORWFlags = 1 << 5 // Write data (CMD52, CMD53)

	WriteRead = Write | RAW
)

func panicIOFunc() {
	panic("sdcard: IO func")
}

func panicIOAddr() {
	panic("sdcard: IO addr")
}

// CMD52 (IO_RW_DIRECT, R5)
func CMD52(fn, addr int, flags IORWFlags, val byte) (Command, uint32) {
	if uint(fn) > 7 {
		panicIOFunc()
	}
	if uint(addr) > 0x1FFFF {
		panicIOAddr()
	}
	return cmd52,
		uint32(val) | uint32(addr)<<9 | uint32(flags)<<26 | uint32(fn)<<28
}

// CMD53 (IO_RW_EXTENDED, R5)
func CMD53(fn, addr int, flags IORWFlags, n int) (Command, uint32) {
	if uint(fn) > 7 {
		panicIOFunc()
	}
	if uint(addr) > 0x1FFFF {
		panicIOAddr()
	}
	if uint(n) > 0x1F {
		panic("sdcard: IO count")
	}
	return cmd52,
		uint32(n) | uint32(addr)<<9 | uint32(flags)<<26 | uint32(fn)<<28
}

// CMD55 (APP_CMD, R1) indicates to the card that the next command is an
// application specific command.
func CMD55(rca uint16) (Command, uint32) {
	return cmd55, uint32(rca) << 16
}

// ACMD6 (SET_BUS_WIDTH, R1) sets the data bus width.
func ACMD6(bw BusWidth) (Command, uint32) {
	return acmd6, uint32(bw)
}

// ACMD41 (SD_SEND_OP_COND, R3) starts initialization/identification process.
func ACMD41(ocr OCR) (Command, uint32) {
	return acmd41, uint32(ocr)
}

// ACMD42 (SET_CLR_CARD_DETECT, R1) enables/disables pull-up resistor on D3/CD.
func ACMD42(pullUp bool) (Command, uint32) {
	return acmd42, uint32(bits.One(pullUp))
}

// ACMD51 (SEND_SCR, R1) reads SD Configuration Register.
func ACMD51() (Command, uint32) {
	return acmd51, 0
}
<|endoftext|>