<commit_before>package cc1100

import (
	"fmt"
	"log"
	"time"
)

const (
	fifoSize     = 64
	usePolling   = false
	pollInterval = time.Millisecond

	// Approximate time for one byte to be transmitted, based on
	// the data rate.  It was determined empirically so that few
	// if any iterations are needed in drainTxFifo().
	byteDuration = time.Millisecond
)

func (r *Radio) startRadio() {
	if !r.radioStarted {
		r.radioStarted = true
		go r.radio()
		go r.awaitInterrupts()
	}
}

func (r *Radio) Incoming() <-chan Packet {
	return r.receivedPackets
}

func (r *Radio) Outgoing() chan<- Packet {
	return r.transmittedPackets
}

func (r *Radio) radio() {
	err := r.changeState(SRX, STATE_RX)
	if err != nil {
		log.Fatal(err)
	}
	for {
		select {
		case packet := <-r.transmittedPackets:
			err = r.transmit(packet.Data)
		case <-r.interrupt:
			err = r.receive()
		}
		if err != nil {
			log.Fatal(err)
		}
	}
}

func (r *Radio) awaitInterrupts() {
	for {
		if usePolling {
			n, _ := r.ReadNumRxBytes()
			if n == 0 {
				time.Sleep(pollInterval)
				continue
			}
		} else {
			r.interruptPin.Wait()
		}
		r.interrupt <- struct{}{}
	}
}

func (r *Radio) transmit(data []byte) error {
	// Terminate packet with zero byte,
	// and pad with another to ensure final bytes
	// are transmitted before leaving TX state.
	data = append(data, []byte{0, 0}...)
	if len(data) <= fifoSize {
		return r.transmitSmall(data)
	} else {
		return r.transmitLarge(data)
	}
}

func (r *Radio) transmitSmall(data []byte) error {
	err := r.WriteFifo(data)
	if err != nil {
		return err
	}
	err = r.changeState(STX, STATE_TX)
	if err != nil {
		return err
	}
	return r.drainTxFifo(len(data))
}

// Transmit a packet that is larger than the TXFIFO size.
// See TI Design Note DN500 (swra109c).
func (r *Radio) transmitLarge(data []byte) error {
	avail := fifoSize
	for {
		err := r.WriteFifo(data[:avail])
		if err != nil {
			return err
		}
		err = r.changeState(STX, STATE_TX)
		if err != nil {
			return err
		}
		data = data[avail:]
		if len(data) == 0 {
			break
		}
		// Err on the short side here to avoid TXFIFO underflow.
		time.Sleep(fifoSize/4 * byteDuration)
		for {
			n, err := r.ReadNumTxBytes()
			if err != nil {
				return err
			}
			if n < fifoSize {
				avail = fifoSize - int(n)
				if avail > len(data) {
					avail = len(data)
				}
				break
			}
		}
	}
	return r.drainTxFifo(len(data))
}

func (r *Radio) drainTxFifo(numBytes int) error {
	time.Sleep(time.Duration(numBytes) * byteDuration)
	for {
		n, err := r.ReadNumTxBytes()
		if err != nil && err != TxFifoUnderflow {
			return err
		}
		if n == 0 || err == TxFifoUnderflow {
			r.PacketsSent++
			break
		}
		s, err := r.ReadState()
		if err != nil {
			return err
		}
		if s != STATE_TX && s != STATE_TXFIFO_UNDERFLOW {
			return fmt.Errorf("unexpected %s state during TXFIFO drain", StateName(s))
		}
		if verbose {
			log.Printf("waiting to transmit %d bytes\n", n)
		}
	}
	return r.changeState(SIDLE, STATE_IDLE)
}

func (r *Radio) receive() error {
	err := r.changeState(SRX, STATE_RX)
	if err != nil {
		return err
	}
	waiting := false
	for {
		numBytes, err := r.ReadNumRxBytes()
		if err == RxFifoOverflow {
			r.changeState(SRX, STATE_RX)
			continue
		} else if err != nil {
			return err
		}
		// Don't read last byte of FIFO if packet is still
		// being received. See Section 20 of data sheet.
		if numBytes < 2 {
			if waiting {
				return nil
			}
			waiting = true
			time.Sleep(byteDuration)
			continue
		}
		waiting = false
		c, err := r.ReadRegister(RXFIFO)
		if err != nil {
			return err
		}
		if c != 0 {
			err = r.receiveBuffer.WriteByte(c)
			if err != nil {
				return err
			}
			continue
		}
		// End of packet.
		rssi, err := r.ReadRSSI()
		if err != nil {
			return err
		}
		size := r.receiveBuffer.Len()
		if size != 0 {
			r.PacketsReceived++
			p := make([]byte, size)
			_, err := r.receiveBuffer.Read(p)
			if err != nil {
				return err
			}
			r.receiveBuffer.Reset()
			r.receivedPackets <- Packet{Rssi: rssi, Data: p}
		}
		return nil
	}
}

func (r *Radio) changeState(strobe byte, desired byte) error {
	s, err := r.ReadState()
	if err != nil {
		return err
	}
	if verbose && s != desired {
		log.Printf("change from %s to %s\n", StateName(s), StateName(desired))
	}
	for {
		switch s {
		case desired:
			return nil
		case STATE_RXFIFO_OVERFLOW:
			s, err = r.Strobe(SFRX)
		case STATE_TXFIFO_UNDERFLOW:
			s, err = r.Strobe(SFTX)
		default:
			s, err = r.Strobe(strobe)
		}
		if err != nil {
			return err
		}
		s = (s >> STATE_SHIFT) & STATE_MASK
		if verbose {
			log.Printf("  %s\n", StateName(s))
		}
	}
}
<commit_msg>Snapshot: getting RXFIFO overflows and no large packets received<commit_after>package cc1100

import (
	"fmt"
	"log"
	"time"

	"github.com/ecc1/radio"
)

const (
	fifoSize      = 64
	maxPacketSize = 200
	usePolling    = false
	pollInterval  = time.Millisecond

	// Approximate time for one byte to be transmitted, based on
	// the data rate.  It was determined empirically so that few
	// if any iterations are needed in drainTxFifo().
	byteDuration = time.Millisecond
)

func (r *Radio) startRadio() {
	if !r.radioStarted {
		r.radioStarted = true
		go r.radio()
		go r.awaitInterrupts()
	}
}

func (r *Radio) Incoming() <-chan radio.Packet {
	return r.receivedPackets
}

func (r *Radio) Outgoing() chan<- radio.Packet {
	return r.transmittedPackets
}

func (r *Radio) radio() {
	err := r.changeState(SRX, STATE_RX)
	if err != nil {
		log.Fatal(err)
	}
	for {
		select {
		case packet := <-r.transmittedPackets:
			err = r.transmit(packet.Data)
		case <-r.interrupt:
			err = r.receive()
		}
		if err != nil {
			log.Fatal(err)
		}
	}
}

func (r *Radio) awaitInterrupts() {
	for {
		if usePolling {
			n, _ := r.ReadNumRxBytes()
			if n == 0 {
				time.Sleep(pollInterval)
				continue
			}
		} else {
			log.Printf("waiting for interrupt in %s state\n", r.State()) //XXX
			r.interruptPin.Wait()
		}
		r.interrupt <- struct{}{}
	}
}

// FIXME: move to per-radio struct
var packetBuffer [maxPacketSize + 2]byte

func (r *Radio) transmit(data []byte) error {
	if len(data) > maxPacketSize {
		log.Panicf("attempting to send %d-byte packet\n", len(data))
	}
	copy(packetBuffer[0:], data)
	// Terminate packet with zero byte,
	// and pad with another to ensure final bytes
	// are transmitted before leaving TX state.
	packetBuffer[len(data)] = 0
	packetBuffer[len(data)+1] = 0
	data = packetBuffer[:len(data)+2]
	var err error
	if len(data) <= fifoSize {
		err = r.transmitSmall(data)
	} else {
		err = r.transmitLarge(data)
	}
	if err != nil {
		r.stats.Packets.Sent++
		r.stats.Bytes.Sent += len(data)
	}
	return err
}

func (r *Radio) transmitSmall(data []byte) error {
	err := r.WriteBurst(TXFIFO, data)
	if err != nil {
		return err
	}
	err = r.changeState(STX, STATE_TX)
	if err != nil {
		return err
	}
	return r.drainTxFifo(len(data))
}

// Transmit a packet that is larger than the TXFIFO size.
// See TI Design Note DN500 (swra109c).
func (r *Radio) transmitLarge(data []byte) error {
	avail := fifoSize
	for {
		err := r.WriteBurst(TXFIFO, data[:avail])
		if err != nil {
			return err
		}
		err = r.changeState(STX, STATE_TX)
		if err != nil {
			return err
		}
		data = data[avail:]
		if len(data) == 0 {
			break
		}
		// Err on the short side here to avoid TXFIFO underflow.
		time.Sleep(fifoSize / 4 * byteDuration)
		for {
			n, err := r.ReadNumTxBytes()
			if err != nil {
				return err
			}
			if n < fifoSize {
				avail = fifoSize - int(n)
				if avail > len(data) {
					avail = len(data)
				}
				break
			}
		}
	}
	return r.drainTxFifo(avail)
}

func (r *Radio) drainTxFifo(numBytes int) error {
	time.Sleep(time.Duration(numBytes) * byteDuration)
	for {
		n, err := r.ReadNumTxBytes()
		if err != nil && err != TxFifoUnderflow {
			return err
		}
		if n == 0 || err == TxFifoUnderflow {
			break
		}
		s, err := r.ReadState()
		if err != nil {
			return err
		}
		if s != STATE_TX && s != STATE_TXFIFO_UNDERFLOW {
			return fmt.Errorf("unexpected %s state during TXFIFO drain", StateName(s))
		}
		if verbose {
			log.Printf("waiting to transmit %d bytes\n", n)
		}
	}
	return r.changeState(SIDLE, STATE_IDLE)
}

func (r *Radio) receive() error {
	err := r.changeState(SRX, STATE_RX)
	if err != nil {
		return err
	}
	waiting := false
	for {
		numBytes, err := r.ReadNumRxBytes()
		if err == RxFifoOverflow {
			r.changeState(SRX, STATE_RX)
			continue
		} else if err != nil {
			return err
		}
		// Don't read last byte of FIFO if packet is still
		// being received. See Section 20 of data sheet.
		if numBytes < 2 {
			if waiting {
				return nil
			}
			waiting = true
			time.Sleep(byteDuration)
			continue
		}
		waiting = false
		c, err := r.ReadRegister(RXFIFO)
		if err != nil {
			return err
		}
		if c != 0 {
			err = r.receiveBuffer.WriteByte(c)
			if err != nil {
				return err
			}
			continue
		}
		// End of packet.
		rssi, err := r.ReadRSSI()
		if err != nil {
			return err
		}
		size := r.receiveBuffer.Len()
		if size != 0 {
			r.stats.Packets.Received++
			r.stats.Bytes.Received += size
			p := make([]byte, size)
			_, err := r.receiveBuffer.Read(p)
			if err != nil {
				return err
			}
			r.receiveBuffer.Reset()
			r.receivedPackets <- radio.Packet{Rssi: rssi, Data: p}
		}
		return nil
//		return r.drainRxFifo()
//		return r.changeState(SIDLE, STATE_IDLE)
	}
}

func (r *Radio) drainRxFifo() error {
	n, err := r.ReadNumRxBytes()
	if err == RxFifoOverflow {
		// Flush RX FIFO and change back to RX.
		return r.changeState(SRX, STATE_RX)
	}
	if err != nil || n == 0 {
		return err
	}
	s, err := r.ReadState()
	if err != nil {
		return err
	}
	switch s {
	case STATE_RX:
		log.Printf("draining %d bytes from RXFIFO\n", n)
		_, err = r.ReadBurst(RXFIFO, int(n))
		if err != nil {
			return err
		}
	case STATE_RXFIFO_OVERFLOW:
		log.Printf("flushing RXFIFO\n")
	default:
		return fmt.Errorf("unexpected %s state during RXFIFO drain", StateName(s))
	}
	return r.changeState(SIDLE, STATE_IDLE)
}

func (r *Radio) changeState(strobe byte, desired byte) error {
	s, err := r.ReadState()
	if err != nil {
		return err
	}
	if verbose && s != desired {
		log.Printf("change from %s to %s\n", StateName(s), StateName(desired))
	}
	for {
		switch s {
		case desired:
			return nil
		case STATE_RXFIFO_OVERFLOW:
			s, err = r.Strobe(SFRX)
		case STATE_TXFIFO_UNDERFLOW:
			s, err = r.Strobe(SFTX)
		default:
			s, err = r.Strobe(strobe)
		}
		if err != nil {
			return err
		}
		s = (s >> STATE_SHIFT) & STATE_MASK
		if verbose {
			log.Printf("  %s\n", StateName(s))
		}
	}
}
<|endoftext|>


<commit_before>/*
Package r provides an interface to the R statistical programming language. It
can either be run as a single R session:

	session, err := r.NewSession()
	...
	key, err := session.Call("stats","rnorm","n=100")
	...
	res, err := session.Get(key, "json")
	...
	res, err = session.Rpc("stats", "rnorm", "n=100", "json")
	...

standalone server that accepts HTTP requests (e.g. from the Client):

	s, err := r.Server(4, "/tmp/kvik")
	...
	s.Start(":8181")

or a client if there is a server running somewhere:

	client := r.Client{"http://localhost.com:8181", "username","password"}
	key, err := client.Call("stats","rnorm","n=100")
	...
	res, err := client.Get(key, "json")
	...
	res, err := client.Rpc("stats","rnorm","n=100","json")
	...

The server starts up a number of R sessions that it communicates with to execute
R functions and retrieve results. The server exposes a HTTP interface with three
methods Call, Get and Rpc. Call executes an R function and returns a temporary key
that can be used in subsequent Get requests. Get requests can return json, csv,
png or pdf files. Rpc combines a call to Call following a Get to get the
results.

*/
package r
<commit_msg>words<commit_after>/*
Package r provides an interface to the R statistical programming language. Users
can interface with R through three methods: Call, Get or Rpc. Call executes a
function call and returns a unique key that can be used in subseqent calls to
Get to retrieve the results. Get can return results in json, csv, png or pdf.
Rpc can be used to bundle Call and Get in a single function call.

As a single R session:

	session, err := r.NewSession()
	...
	key, err := session.Call("stats","rnorm","n=100")
	...
	res, err := session.Get(key, "json")
	...
	res, err = session.Rpc("stats", "rnorm", "n=100", "json")
	...

standalone server that accepts HTTP requests (e.g. from the Client or curl)

	s, err := r.Server(4, "/tmp/kvik")
	...
	s.Start(":8181")

or a client if there is a server running somewhere:

	client := r.Client{"http://localhost.com:8181", "username","password"}
	key, err := client.Call("stats","rnorm","n=100")
	...
	res, err := client.Get(key, "json")
	...
	res, err := client.Rpc("stats","rnorm","n=100","json")
	...

*/
package r
<|endoftext|>


<commit_before>// Routines to read ~/.pstoprc [pstop configuration]
// - and to munge some table names based on the [munge] section (if present)
package rc

import (
	"log"
	"os"
	"regexp"
	"strings"

	go_ini "github.com/vaughan0/go-ini" // not sure what to do with dashes in names

	"github.com/sjmudd/ps-top/lib"
)

const (
	pstoprc = "~/.pstoprc" // location of the default pstop config file
)

// A single regexp expression from ~/.pstoprc
type munge_regexp struct {
	pattern string
	replace string
	re      *regexp.Regexp
	valid   bool
}

// A slice of regexp expressions
type munge_regexps []munge_regexp

var (
	regexps        munge_regexps
	loaded_regexps bool // Have we [attempted to] loaded data?
	have_regexps   bool // Do we have any valid data?
)

// There must be a better way of doing this. Fix me...
// Copied from github.com/sjmudd/mysql_defaults_file so I should share this common code or fix it.
// Return the environment value of a given name.
func get_environ(name string) string {
	for i := range os.Environ() {
		s := os.Environ()[i]
		k_v := strings.Split(s, "=")

		if k_v[0] == name {
			return k_v[1]
		}
	}
	return ""
}

// Convert ~ to $HOME
// Copied from github.com/sjmudd/mysql_defaults_file so I should share this common code or fix it.
func convert_filename(filename string) string {
	for i := range filename {
		if filename[i] == '~' {
			//                      fmt.Println("Filename before", filename )
			filename = filename[:i] + get_environ("HOME") + filename[i+1:]
			//                      fmt.Println("Filename after", filename )
			break
		}
	}

	return filename
}

// Load the ~/.pstoprc regexp expressions in section [munge]
func load_regexps() {
	if loaded_regexps {
		return
	}
	loaded_regexps = true

	lib.Logger.Println("rc.load_regexps()")

	have_regexps = false
	filename := convert_filename(pstoprc)

	// Is the file is there?
	f, err := os.Open(filename)
	if err != nil {
		lib.Logger.Println("- unable to open " + filename + ", nothing to munge")
		return // can't open file. This is not fatal. We just can't do anything useful.
	}
	// If we get here the file is readable, so close it again.
	err = f.Close()
	if err != nil {
		// Do nothing. What can we do? Do we care?
	}

	// Load and process the ini file.
	i, err := go_ini.LoadFile(filename)
	if err != nil {
		log.Fatal("Could not load ~/.pstoprc", filename, ":", err)
	}

	// Note: This is wrong if I want to have an _ordered_ list of regexps
	// as go-ini provides me a hash so I lose the ordering. This may not
	// be desirable but as a first step accept this is broken.
	section := i.Section("munge")

	regexps = make(munge_regexps, 0, len(section))

	// now look for regexps and load them in...
	for k, v := range section {
		var m munge_regexp
		var err error

		m.pattern, m.replace = k, v
		m.re, err = regexp.Compile(m.pattern)
		if err == nil {
			m.valid = true
		}
		regexps = append(regexps, m)
	}

	if len(regexps) > 0 {
		have_regexps = true
	}
	lib.Logger.Println("- found", len(regexps), "regexps to use to munge output")
}

// Optionally munge table names so they can be combined.
// - this reads ~/.pstoprc for configuration information.
// - e.g.
// [munge]
// <re_match> = <replace>
// _[0-9]{8}$ = _YYYYMMDD
// _[0-9]{6}$ = _YYYYMM
func Munge(name string) string {
	if !loaded_regexps {
		load_regexps()
	}
	if !have_regexps {
		return name // nothing to do so return what we were given.
	}

	munged := name

	for i := range regexps {
		if regexps[i].valid {
			if regexps[i].re.MatchString(munged) {
				munged = regexps[i].re.ReplaceAllLiteralString(munged, regexps[i].replace)
			}
		}
	}

	return munged
}
<commit_msg>Lint/Vet fixes<commit_after>// Package rc provides routines to read ~/.pstoprc [pstop configuration]
// - and to munge some table names based on the [munge] section (if present)
package rc

import (
	"log"
	"os"
	"regexp"
	"strings"

	go_ini "github.com/vaughan0/go-ini" // not sure what to do with dashes in names

	"github.com/sjmudd/ps-top/lib"
)

const (
	pstoprc = "~/.pstoprc" // location of the default pstop config file
)

// A single regexp expression from ~/.pstoprc
type mungeRegexp struct {
	pattern string
	replace string
	re      *regexp.Regexp
	valid   bool
}

// A slice of regexp expressions
type mungeRegexps []mungeRegexp

var (
	regexps       mungeRegexps
	loadedRegexps bool // Have we [attempted to] loaded data?
	haveRegexps   bool // Do we have any valid data?
)

// There must be a better way of doing this. Fix me...
// Copied from github.com/sjmudd/mysql_defaults_file so I should share this common code or fix it.
// Return the environment value of a given name.
func getEnviron(name string) string {
	for i := range os.Environ() {
		s := os.Environ()[i]
		keyValue := strings.Split(s, "=")

		if keyValue[0] == name {
			return keyValue[1]
		}
	}
	return ""
}

// Convert ~ to $HOME
// Copied from github.com/sjmudd/mysql_defaults_file so I should share this common code or fix it.
func convertFilename(filename string) string {
	for i := range filename {
		if filename[i] == '~' {
			//                      fmt.Println("Filename before", filename )
			filename = filename[:i] + getEnviron("HOME") + filename[i+1:]
			//                      fmt.Println("Filename after", filename )
			break
		}
	}

	return filename
}

// Load the ~/.pstoprc regexp expressions in section [munge]
func loadRegexps() {
	if loadedRegexps {
		return
	}
	loadedRegexps = true

	lib.Logger.Println("rc.loadRegexps()")

	haveRegexps = false
	filename := convertFilename(pstoprc)

	// Is the file is there?
	f, err := os.Open(filename)
	if err != nil {
		lib.Logger.Println("- unable to open " + filename + ", nothing to munge")
		return // can't open file. This is not fatal. We just can't do anything useful.
	}
	// If we get here the file is readable, so close it again.
	err = f.Close()
	if err != nil {
		// Do nothing. What can we do? Do we care?
	}

	// Load and process the ini file.
	i, err := go_ini.LoadFile(filename)
	if err != nil {
		log.Fatal("Could not load ~/.pstoprc", filename, ":", err)
	}

	// Note: This is wrong if I want to have an _ordered_ list of regexps
	// as go-ini provides me a hash so I lose the ordering. This may not
	// be desirable but as a first step accept this is broken.
	section := i.Section("munge")

	regexps = make(mungeRegexps, 0, len(section))

	// now look for regexps and load them in...
	for k, v := range section {
		var m mungeRegexp
		var err error

		m.pattern, m.replace = k, v
		m.re, err = regexp.Compile(m.pattern)
		if err == nil {
			m.valid = true
		}
		regexps = append(regexps, m)
	}

	if len(regexps) > 0 {
		haveRegexps = true
	}
	lib.Logger.Println("- found", len(regexps), "regexps to use to munge output")
}

// Munge Optionally munges table names so they can be combined.
// - this reads ~/.pstoprc for configuration information.
// - e.g.
// [munge]
// <re_match> = <replace>
// _[0-9]{8}$ = _YYYYMMDD
// _[0-9]{6}$ = _YYYYMM
func Munge(name string) string {
	if !loadedRegexps {
		loadRegexps()
	}
	if !haveRegexps {
		return name // nothing to do so return what we were given.
	}

	munged := name

	for i := range regexps {
		if regexps[i].valid {
			if regexps[i].re.MatchString(munged) {
				munged = regexps[i].re.ReplaceAllLiteralString(munged, regexps[i].replace)
			}
		}
	}

	return munged
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Zachary Klippenstein

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

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

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

/*
Package regen is a library for generating random strings from regular expressions.
The generated strings will match the expressions they were generated from.

E.g.
	regen.Generate("[a-z0-9]{1,64}")
will return a lowercase alphanumeric string
between 1 and 64 characters long.

Expressions are parsed using the Go standard library's parser: http://golang.org/pkg/regexp/syntax/.

Constraints

"." will generate any character, not necessarily a printable one.

"x{0,}", "x*", and "x+" will generate a random number of x's up to an arbitrary limit.
If you care about the maximum number, specify it explicitly in the expression,
e.g. "x{0,256}".

Flags

Flags can be passed to the parser by setting them in the GeneratorArgs struct.
Newline flags are respected, and newlines won't be generated unless the appropriate flags for
matching them are set.

E.g.
Generate(".|[^a]") will never generate newlines. To generate newlines, create a generator and pass
the flag syntax.MatchNL.

The Perl character class flag is supported, and required if the pattern contains them.

Unicode groups are not supported at this time. Support may be added in the future.

Concurrent Use

A generator can safely be used from multiple goroutines without locking.

A large bottleneck with running generators concurrently is actually the entropy source. Sources returned from
rand.NewSource() are slow to seed, and not safe for concurrent use. Instead, the source passed in GeneratorArgs
is used to seed an XorShift64 source (algorithm from the paper at http://vigna.di.unimi.it/ftp/papers/xorshift.pdf).
This source only uses a single variable internally, and is much faster to seed than the default source. One
source is created per call to NewGenerator. If no source is passed in, the default source is used to seed.

The source is not locked and does not use atomic operations, so there is a chance that multiple goroutines using
the same source may get the same output. While obviously not cryptographically secure, I think the simplicity and performance
benefit outweighs the risk of collisions. If you really care about preventing this, the solution is simple: don't
call a single Generator from multiple goroutines.

Benchmarks

Benchmarks are included for creating and running generators for limited-length,
complex regexes, and simple, highly-repetitive regexes.

	go test -bench .

The complex benchmarks generate fake HTTP messages with the following regex:
	POST (/[-a-zA-Z0-9_.]{3,12}){3,6}
	Content-Length: [0-9]{2,3}
	X-Auth-Token: [a-zA-Z0-9+/]{64}

	([A-Za-z0-9+/]{64}
	){3,15}[A-Za-z0-9+/]{60}([A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)

The repetitive benchmarks use the regex
	a{999}

See regen_benchmarks_test.go for more information.

On my mid-2014 MacBook Pro (2.6GHz Intel Core i5, 8GB 1600MHz DDR3),
the results of running the benchmarks with minimal load are:
	BenchmarkComplexCreation-4                       200	   8322160 ns/op
	BenchmarkComplexGeneration-4                   10000	    153625 ns/op
	BenchmarkLargeRepeatCreateSerial-4  	        3000	    411772 ns/op
	BenchmarkLargeRepeatGenerateSerial-4	        5000	    291416 ns/op
*/
package regen

import (
	"math/rand"
	"regexp/syntax"
)

// GeneratorArgs are arguments passed to NewGenerator that control how generators
// are created.
type GeneratorArgs struct {
	// Used to seed a custom RNG that is a lot faster than the default implementation.
	// See http://vigna.di.unimi.it/ftp/papers/xorshift.pdf.
	RngSource rand.Source

	// Default is 0 (syntax.POSIX).
	Flags syntax.Flags

	// Used by generators.
	rng *rand.Rand
}

// Generator generates random strings.
type Generator interface {
	Generate() string
}

/*
Generate a random string that matches the regular expression pattern.
If args is nil, default values are used.

This function does not seed the default RNG, so you must call rand.Seed() if you want
non-deterministic strings.
*/
func Generate(pattern string) (string, error) {
	generator, err := NewGenerator(pattern, nil)
	if err != nil {
		return "", err
	}
	return generator.Generate(), nil
}

// NewGenerator creates a generator that returns random strings that match the regular expression in pattern.
// If args is nil, default values are used.
func NewGenerator(pattern string, args *GeneratorArgs) (generator Generator, err error) {
	if nil == args {
		args = &GeneratorArgs{}
	}

	var seed int64
	if nil == args.RngSource {
		seed = rand.Int63()
	} else {
		seed = args.RngSource.Int63()
	}
	rngSource := xorShift64Source(seed)
	args.rng = rand.New(&rngSource)

	// unicode groups only allowed with Perl
	if (args.Flags&syntax.UnicodeGroups) == syntax.UnicodeGroups && (args.Flags&syntax.Perl) != syntax.Perl {
		return nil, generatorError(nil, "UnicodeGroups not supported")
	}

	var regexp *syntax.Regexp
	regexp, err = syntax.Parse(pattern, args.Flags)
	if err != nil {
		return
	}

	var gen *internalGenerator
	gen, err = newGenerator(regexp, args)
	if err != nil {
		return
	}

	return gen, nil
}
<commit_msg>Added comment about randexp to package doc.<commit_after>/*
Copyright 2014 Zachary Klippenstein

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

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

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

/*
Package regen is a library for generating random strings from regular expressions.
The generated strings will match the expressions they were generated from. Similar
to Ruby's randexp library.

E.g.
	regen.Generate("[a-z0-9]{1,64}")
will return a lowercase alphanumeric string
between 1 and 64 characters long.

Expressions are parsed using the Go standard library's parser: http://golang.org/pkg/regexp/syntax/.

Constraints

"." will generate any character, not necessarily a printable one.

"x{0,}", "x*", and "x+" will generate a random number of x's up to an arbitrary limit.
If you care about the maximum number, specify it explicitly in the expression,
e.g. "x{0,256}".

Flags

Flags can be passed to the parser by setting them in the GeneratorArgs struct.
Newline flags are respected, and newlines won't be generated unless the appropriate flags for
matching them are set.

E.g.
Generate(".|[^a]") will never generate newlines. To generate newlines, create a generator and pass
the flag syntax.MatchNL.

The Perl character class flag is supported, and required if the pattern contains them.

Unicode groups are not supported at this time. Support may be added in the future.

Concurrent Use

A generator can safely be used from multiple goroutines without locking.

A large bottleneck with running generators concurrently is actually the entropy source. Sources returned from
rand.NewSource() are slow to seed, and not safe for concurrent use. Instead, the source passed in GeneratorArgs
is used to seed an XorShift64 source (algorithm from the paper at http://vigna.di.unimi.it/ftp/papers/xorshift.pdf).
This source only uses a single variable internally, and is much faster to seed than the default source. One
source is created per call to NewGenerator. If no source is passed in, the default source is used to seed.

The source is not locked and does not use atomic operations, so there is a chance that multiple goroutines using
the same source may get the same output. While obviously not cryptographically secure, I think the simplicity and performance
benefit outweighs the risk of collisions. If you really care about preventing this, the solution is simple: don't
call a single Generator from multiple goroutines.

Benchmarks

Benchmarks are included for creating and running generators for limited-length,
complex regexes, and simple, highly-repetitive regexes.

	go test -bench .

The complex benchmarks generate fake HTTP messages with the following regex:
	POST (/[-a-zA-Z0-9_.]{3,12}){3,6}
	Content-Length: [0-9]{2,3}
	X-Auth-Token: [a-zA-Z0-9+/]{64}

	([A-Za-z0-9+/]{64}
	){3,15}[A-Za-z0-9+/]{60}([A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)

The repetitive benchmarks use the regex
	a{999}

See regen_benchmarks_test.go for more information.

On my mid-2014 MacBook Pro (2.6GHz Intel Core i5, 8GB 1600MHz DDR3),
the results of running the benchmarks with minimal load are:
	BenchmarkComplexCreation-4                       200	   8322160 ns/op
	BenchmarkComplexGeneration-4                   10000	    153625 ns/op
	BenchmarkLargeRepeatCreateSerial-4  	        3000	    411772 ns/op
	BenchmarkLargeRepeatGenerateSerial-4	        5000	    291416 ns/op
*/
package regen

import (
	"math/rand"
	"regexp/syntax"
)

// GeneratorArgs are arguments passed to NewGenerator that control how generators
// are created.
type GeneratorArgs struct {
	// Used to seed a custom RNG that is a lot faster than the default implementation.
	// See http://vigna.di.unimi.it/ftp/papers/xorshift.pdf.
	RngSource rand.Source

	// Default is 0 (syntax.POSIX).
	Flags syntax.Flags

	// Used by generators.
	rng *rand.Rand
}

// Generator generates random strings.
type Generator interface {
	Generate() string
}

/*
Generate a random string that matches the regular expression pattern.
If args is nil, default values are used.

This function does not seed the default RNG, so you must call rand.Seed() if you want
non-deterministic strings.
*/
func Generate(pattern string) (string, error) {
	generator, err := NewGenerator(pattern, nil)
	if err != nil {
		return "", err
	}
	return generator.Generate(), nil
}

// NewGenerator creates a generator that returns random strings that match the regular expression in pattern.
// If args is nil, default values are used.
func NewGenerator(pattern string, args *GeneratorArgs) (generator Generator, err error) {
	if nil == args {
		args = &GeneratorArgs{}
	}

	var seed int64
	if nil == args.RngSource {
		seed = rand.Int63()
	} else {
		seed = args.RngSource.Int63()
	}
	rngSource := xorShift64Source(seed)
	args.rng = rand.New(&rngSource)

	// unicode groups only allowed with Perl
	if (args.Flags&syntax.UnicodeGroups) == syntax.UnicodeGroups && (args.Flags&syntax.Perl) != syntax.Perl {
		return nil, generatorError(nil, "UnicodeGroups not supported")
	}

	var regexp *syntax.Regexp
	regexp, err = syntax.Parse(pattern, args.Flags)
	if err != nil {
		return
	}

	var gen *internalGenerator
	gen, err = newGenerator(regexp, args)
	if err != nil {
		return
	}

	return gen, nil
}
<|endoftext|>


<commit_before>// Package presence provides simple user presence system
package presence

import (
	"errors"
	"fmt"
	"strconv"
	"sync"
	"time"

	gredis "github.com/garyburd/redigo/redis"
	"github.com/koding/redis"
)

// Prefix for redisence package
const RedisencePrefix = "redisence"

// Redis holds the required connection data for redis
type Redis struct {
	// main redis connection
	redis *redis.RedisSession

	// inactiveDuration specifies no-probe allowance time
	inactiveDuration time.Duration

	// receiving offline events pattern
	becameOfflinePattern string

	// receiving online events pattern
	becameOnlinePattern string

	// errChan pipe all errors  the this channel
	errChan chan error

	// closed holds the status of connection
	closed bool

	//psc holds the pubsub channel if opened
	psc *gredis.PubSubConn

	// holds event channel
	events chan Event

	// lock for Redis struct
	mu sync.Mutex
}

// NewRedis creates a Redis for any broker system that is architected to use,
// communicate, forward events to the presence system
func NewRedis(server string, db int, inactiveDuration time.Duration) (Backend, error) {
	redis, err := redis.NewRedisSession(&redis.RedisConf{Server: server, DB: db})
	if err != nil {
		return nil, err
	}
	redis.SetPrefix(RedisencePrefix)

	return &Redis{
		redis:                redis,
		becameOfflinePattern: fmt.Sprintf("__keyevent@%d__:expired", db),
		becameOnlinePattern:  fmt.Sprintf("__keyevent@%d__:set", db),
		inactiveDuration:     inactiveDuration,
		errChan:              make(chan error, 1),
	}, nil
}

// Online resets the expiration time for any given key
// if key doesnt exists, it means user is now online and should be set as online
// Whenever application gets any prob from a client
// should call this function
func (s *Redis) Online(ids ...string) error {
	if len(ids) == 1 {
		// if member exits increase ttl
		if s.redis.Expire(ids[0], s.inactiveDuration) == nil {
			return nil
		}

		// if member doesnt exist set it
		return s.redis.Setex(ids[0], s.inactiveDuration, ids[0])
	}

	existance, err := s.sendMultiExpire(ids, s.inactiveDurationString())
	if err != nil {
		return err
	}

	return s.sendMultiSetIfRequired(ids, existance)
}

// Offline sets given ids as offline, ignores any error
func (s *Redis) Offline(ids ...string) error {
	if len(ids) == 1 {
		if s.redis.Expire(ids[0], time.Second*0) == nil {
			return nil
		}
	}

	s.sendMultiExpire(ids, "0")
	return nil
}

// sendMultiSetIfRequired accepts set of ids and their existtance status
// traverse over them and any key is not exists in db, set them in a multi/exec
// request
func (s *Redis) sendMultiSetIfRequired(ids []string, existance []int) error {
	if len(ids) != len(existance) {
		return fmt.Errorf("length is not same Ids: %d Existance: %d", len(ids), len(existance))
	}

	// cache inactive duration
	seconds := s.inactiveDurationString()

	// get one connection from pool
	c := s.redis.Pool().Get()

	// item count for non-existent members
	notExistsCount := 0

	for i, exists := range existance {
		// `0` means, member doesnt exists in presence system
		if exists != 0 {
			continue
		}

		// init multi command lazily
		if notExistsCount == 0 {
			c.Send("MULTI")
		}

		notExistsCount++
		c.Send("SETEX", s.redis.AddPrefix(ids[i]), seconds, ids[i])
	}

	// execute multi command if only we flushed some to connection
	if notExistsCount != 0 {
		// ignore values
		if _, err := c.Do("EXEC"); err != nil {
			return err
		}
	}

	// do not forget to close the connection
	if err := c.Close(); err != nil {
		return err
	}

	return nil
}

// sendMultiExpire if the system tries to update more than one key at a time
// inorder to leverage rtt, send multi expire
func (s *Redis) sendMultiExpire(ids []string, duration string) ([]int, error) {
	// get one connection from pool
	c := s.redis.Pool().Get()

	// init multi command
	c.Send("MULTI")

	// send expire command for all members
	for _, id := range ids {
		c.Send("EXPIRE", s.redis.AddPrefix(id), duration)
	}

	// execute command
	r, err := c.Do("EXEC")
	if err != nil {
		return nil, err
	}

	// close connection
	if err := c.Close(); err != nil {
		return nil, err
	}

	values, err := s.redis.Values(r)
	if err != nil {
		return nil, err
	}

	res := make([]int, len(values))
	for i, value := range values {
		res[i], err = s.redis.Int(value)
		if err != nil {
			// what about returning half-generated slice?
			// instead of an empty one
			return nil, err
		}

	}

	return res, nil
}

// Status returns the current status multiple keys from system
func (s *Redis) Status(ids ...string) ([]Event, error) {
	if len(ids) == 1 {
		return s.singleStatus(ids[0])
	}

	// get one connection from pool
	c := s.redis.Pool().Get()

	// init multi command
	c.Send("MULTI")

	// send expire command for all members
	for _, id := range ids {
		c.Send("EXISTS", s.redis.AddPrefix(id))
	}

	// execute command
	r, err := c.Do("EXEC")
	if err != nil {
		return nil, err
	}

	// close connection
	if err := c.Close(); err != nil {
		return nil, err
	}

	values, err := s.redis.Values(r)
	if err != nil {
		return nil, err
	}

	res := make([]Event, len(values))
	for i, value := range values {
		status, err := s.redis.Int(value)
		if err != nil {
			return nil, err
		}

		res[i] = Event{
			ID: ids[i],
			// cast redis response to Status
			Status: Status(status),
		}
	}

	return res, nil
}

// Close closes the redis connection gracefully
func (s *Redis) Close() error {
	return s.close()
}

func (s *Redis) close() error {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.closed {
		return errors.New("closing of already closed connection")
	}

	s.closed = true

	if s.events != nil {
		close(s.events)
	}

	if s.psc != nil {
		s.psc.PUnsubscribe()
	}

	return s.redis.Close()
}

// Status returns the current status a key from system
func (s *Redis) singleStatus(id string) ([]Event, error) {
	res := make([]Event, 1)
	res[0] = Event{
		ID:     id,
		Status: Offline,
	}

	if s.redis.Exists(id) {
		res[0].Status = Online
	}

	return res, nil
}

// ListenStatusChanges subscribes with a pattern to the redis and
// gets online and offline status changes from it
func (s *Redis) ListenStatusChanges() chan Event {
	s.psc = s.redis.CreatePubSubConn()
	s.psc.PSubscribe(s.becameOnlinePattern, s.becameOfflinePattern)

	s.events = make(chan Event)
	go s.listenEvents()
	return s.events
}

// createEvent Creates the event with the required properties
func (s *Redis) listenEvents() {
	for {
		s.mu.Lock()
		if s.closed {
			s.mu.Unlock()
			return
		}
		s.mu.Unlock()

		switch n := s.psc.Receive().(type) {
		case gredis.PMessage:
			s.events <- s.createEvent(n)
		case error:
			s.errChan <- n
			return
		}
	}
}

// createEvent Creates the event with the required properties
func (s *Redis) createEvent(n gredis.PMessage) Event {
	e := Event{}

	switch n.Pattern {
	case s.becameOfflinePattern:
		e.ID = string(n.Data[len(RedisencePrefix)+1:])
		e.Status = Offline
	case s.becameOnlinePattern:
		e.ID = string(n.Data[len(RedisencePrefix)+1:])
		e.Status = Online
	default:
		//ignore other events
	}

	return e
}

func (s *Redis) inactiveDurationString() string {
	return strconv.Itoa(int(s.inactiveDuration.Seconds()))
}

// Error returns error if it happens while listening  to status changes
func (s *Redis) Error() error {
	return <-s.errChan
}
<commit_msg>Presence:  change const to var<commit_after>// Package presence provides simple user presence system
package presence

import (
	"errors"
	"fmt"
	"strconv"
	"sync"
	"time"

	gredis "github.com/garyburd/redigo/redis"
	"github.com/koding/redis"
)

// Prefix for redisence package
var RedisencePrefix = "redisence"

// Redis holds the required connection data for redis
type Redis struct {
	// main redis connection
	redis *redis.RedisSession

	// inactiveDuration specifies no-probe allowance time
	inactiveDuration time.Duration

	// receiving offline events pattern
	becameOfflinePattern string

	// receiving online events pattern
	becameOnlinePattern string

	// errChan pipe all errors  the this channel
	errChan chan error

	// closed holds the status of connection
	closed bool

	//psc holds the pubsub channel if opened
	psc *gredis.PubSubConn

	// holds event channel
	events chan Event

	// lock for Redis struct
	mu sync.Mutex
}

// NewRedis creates a Redis for any broker system that is architected to use,
// communicate, forward events to the presence system
func NewRedis(server string, db int, inactiveDuration time.Duration) (Backend, error) {
	redis, err := redis.NewRedisSession(&redis.RedisConf{Server: server, DB: db})
	if err != nil {
		return nil, err
	}
	redis.SetPrefix(RedisencePrefix)

	return &Redis{
		redis:                redis,
		becameOfflinePattern: fmt.Sprintf("__keyevent@%d__:expired", db),
		becameOnlinePattern:  fmt.Sprintf("__keyevent@%d__:set", db),
		inactiveDuration:     inactiveDuration,
		errChan:              make(chan error, 1),
	}, nil
}

// Online resets the expiration time for any given key
// if key doesnt exists, it means user is now online and should be set as online
// Whenever application gets any prob from a client
// should call this function
func (s *Redis) Online(ids ...string) error {
	if len(ids) == 1 {
		// if member exits increase ttl
		if s.redis.Expire(ids[0], s.inactiveDuration) == nil {
			return nil
		}

		// if member doesnt exist set it
		return s.redis.Setex(ids[0], s.inactiveDuration, ids[0])
	}

	existance, err := s.sendMultiExpire(ids, s.inactiveDurationString())
	if err != nil {
		return err
	}

	return s.sendMultiSetIfRequired(ids, existance)
}

// Offline sets given ids as offline, ignores any error
func (s *Redis) Offline(ids ...string) error {
	if len(ids) == 1 {
		if s.redis.Expire(ids[0], time.Second*0) == nil {
			return nil
		}
	}

	s.sendMultiExpire(ids, "0")
	return nil
}

// sendMultiSetIfRequired accepts set of ids and their existtance status
// traverse over them and any key is not exists in db, set them in a multi/exec
// request
func (s *Redis) sendMultiSetIfRequired(ids []string, existance []int) error {
	if len(ids) != len(existance) {
		return fmt.Errorf("length is not same Ids: %d Existance: %d", len(ids), len(existance))
	}

	// cache inactive duration
	seconds := s.inactiveDurationString()

	// get one connection from pool
	c := s.redis.Pool().Get()

	// item count for non-existent members
	notExistsCount := 0

	for i, exists := range existance {
		// `0` means, member doesnt exists in presence system
		if exists != 0 {
			continue
		}

		// init multi command lazily
		if notExistsCount == 0 {
			c.Send("MULTI")
		}

		notExistsCount++
		c.Send("SETEX", s.redis.AddPrefix(ids[i]), seconds, ids[i])
	}

	// execute multi command if only we flushed some to connection
	if notExistsCount != 0 {
		// ignore values
		if _, err := c.Do("EXEC"); err != nil {
			return err
		}
	}

	// do not forget to close the connection
	if err := c.Close(); err != nil {
		return err
	}

	return nil
}

// sendMultiExpire if the system tries to update more than one key at a time
// inorder to leverage rtt, send multi expire
func (s *Redis) sendMultiExpire(ids []string, duration string) ([]int, error) {
	// get one connection from pool
	c := s.redis.Pool().Get()

	// init multi command
	c.Send("MULTI")

	// send expire command for all members
	for _, id := range ids {
		c.Send("EXPIRE", s.redis.AddPrefix(id), duration)
	}

	// execute command
	r, err := c.Do("EXEC")
	if err != nil {
		return nil, err
	}

	// close connection
	if err := c.Close(); err != nil {
		return nil, err
	}

	values, err := s.redis.Values(r)
	if err != nil {
		return nil, err
	}

	res := make([]int, len(values))
	for i, value := range values {
		res[i], err = s.redis.Int(value)
		if err != nil {
			// what about returning half-generated slice?
			// instead of an empty one
			return nil, err
		}

	}

	return res, nil
}

// Status returns the current status multiple keys from system
func (s *Redis) Status(ids ...string) ([]Event, error) {
	if len(ids) == 1 {
		return s.singleStatus(ids[0])
	}

	// get one connection from pool
	c := s.redis.Pool().Get()

	// init multi command
	c.Send("MULTI")

	// send expire command for all members
	for _, id := range ids {
		c.Send("EXISTS", s.redis.AddPrefix(id))
	}

	// execute command
	r, err := c.Do("EXEC")
	if err != nil {
		return nil, err
	}

	// close connection
	if err := c.Close(); err != nil {
		return nil, err
	}

	values, err := s.redis.Values(r)
	if err != nil {
		return nil, err
	}

	res := make([]Event, len(values))
	for i, value := range values {
		status, err := s.redis.Int(value)
		if err != nil {
			return nil, err
		}

		res[i] = Event{
			ID: ids[i],
			// cast redis response to Status
			Status: Status(status),
		}
	}

	return res, nil
}

// Close closes the redis connection gracefully
func (s *Redis) Close() error {
	return s.close()
}

func (s *Redis) close() error {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.closed {
		return errors.New("closing of already closed connection")
	}

	s.closed = true

	if s.events != nil {
		close(s.events)
	}

	if s.psc != nil {
		s.psc.PUnsubscribe()
	}

	return s.redis.Close()
}

// Status returns the current status a key from system
func (s *Redis) singleStatus(id string) ([]Event, error) {
	res := make([]Event, 1)
	res[0] = Event{
		ID:     id,
		Status: Offline,
	}

	if s.redis.Exists(id) {
		res[0].Status = Online
	}

	return res, nil
}

// ListenStatusChanges subscribes with a pattern to the redis and
// gets online and offline status changes from it
func (s *Redis) ListenStatusChanges() chan Event {
	s.psc = s.redis.CreatePubSubConn()
	s.psc.PSubscribe(s.becameOnlinePattern, s.becameOfflinePattern)

	s.events = make(chan Event)
	go s.listenEvents()
	return s.events
}

// createEvent Creates the event with the required properties
func (s *Redis) listenEvents() {
	for {
		s.mu.Lock()
		if s.closed {
			s.mu.Unlock()
			return
		}
		s.mu.Unlock()

		switch n := s.psc.Receive().(type) {
		case gredis.PMessage:
			s.events <- s.createEvent(n)
		case error:
			s.errChan <- n
			return
		}
	}
}

// createEvent Creates the event with the required properties
func (s *Redis) createEvent(n gredis.PMessage) Event {
	e := Event{}

	switch n.Pattern {
	case s.becameOfflinePattern:
		e.ID = string(n.Data[len(RedisencePrefix)+1:])
		e.Status = Offline
	case s.becameOnlinePattern:
		e.ID = string(n.Data[len(RedisencePrefix)+1:])
		e.Status = Online
	default:
		//ignore other events
	}

	return e
}

func (s *Redis) inactiveDurationString() string {
	return strconv.Itoa(int(s.inactiveDuration.Seconds()))
}

// Error returns error if it happens while listening  to status changes
func (s *Redis) Error() error {
	return <-s.errChan
}
<|endoftext|>


<commit_before>// Redis implements basic connections and pooling to redis servers.
package redis

import (
	"errors"
	"fmt"
	"io"
	"net"
	"sync"
	"time"

	"bytes"

	"bitbucket.org/shipwire/redis/resp"
)

var CommandInProgress = errors.New("command in progress")

// Conn represents an open connection to a redis server.
type Conn struct {
	net.Conn
	whence       *Pool
	openCommands int
	commandLock  *sync.Mutex
	reply        *resp.RESP
}

// Dial connects to the redis server.
func Dial(network, address string) (*Conn, error) {
	c, err := net.Dial(network, address)
	conn := &Conn{Conn: c, commandLock: &sync.Mutex{}}
	conn.reply = resp.New(c)
	return conn, err
}

// DialTimeout acts like Dial but takes a timeout. The timeout includes name resolution, if required.
func DialTimeout(network, address string, timeout time.Duration) (*Conn, error) {
	c, err := net.DialTimeout(network, address, timeout)
	conn := &Conn{Conn: c, commandLock: &sync.Mutex{}}
	conn.reply = resp.New(c)
	return conn, err
}

// RawCmd sends a raw command to the redis server
func (c *Conn) RawCmd(command string, args ...string) error {
	c.commandLock.Lock()
	defer c.commandLock.Unlock()

	c.openCommands += 1

	cmd, err := c.Command(command, len(args))
	if err != nil {
		return err
	}

	for _, arg := range args {
		cmd.WriteArgumentString(arg)
	}
	return cmd.Close()
}

// Command initializes a command with the given number of arguments. The connection
// only allows one open command at a time and will block callers to prevent jumbled queues.
func (c *Conn) Command(command string, args int) (*Cmd, error) {
	c.commandLock.Lock()

	fmt.Fprintf(c, "*%d\r\n", args+1)
	cmd := &Cmd{c, c}
	cmd.WriteArgumentString(command)
	return cmd, nil
}

// Resp reads a RESP from the connection
func (c *Conn) Resp() *resp.RESP {
	defer func() {
		c.openCommands -= 1
	}()
	return c.reply
}

// Cmd is a command that is currently being written to a connection.
type Cmd struct {
	io.Writer
	conn *Conn
}

// WriteArgumentString writes a static string to the connection as a command argument.
func (c Cmd) WriteArgumentString(arg string) (int, error) {
	return fmt.Fprintf(c, "$%d\r\n%s\r\n", len(arg), arg)
}

// WriteArgument is a shortcut method to write a reader to a command. If at all possible,
// WriteArgumentLength should be used instead.
func (c Cmd) WriteArgument(r io.Reader) (int, error) {
	buf := &bytes.Buffer{}
	io.Copy(buf, r)
	return fmt.Fprintf(c, "$%d\r\n%s\r\n", buf.Len(), buf.String())
}

// WriteArgumentLength copies a reader as an argument to a command. It expects the reader
// to be of the given length.
func (c Cmd) WriteArgumentLength(r io.Reader, l int64) (written int, err error) {
	r = io.LimitReader(r, l)
	w, err := fmt.Fprintf(c, "$%d\r\n", l)
	if err != nil {
		return
	}
	written += w
	ww, err := io.Copy(c, r)
	if err != nil {
		return
	}
	written += int(ww)
	w, err = fmt.Fprint(c, "\r\n")
	if err != nil {
		return
	}
	written += w
	return
}

// Close closes the command with a CRLF.
func (c Cmd) Close() error {
	defer c.conn.commandLock.Unlock()
	_, err := io.WriteString(c, "\r\n")
	return err
}
<commit_msg>Fixing command locking<commit_after>// Redis implements basic connections and pooling to redis servers.
package redis

import (
	"errors"
	"fmt"
	"io"
	"net"
	"sync"
	"time"

	"bytes"

	"bitbucket.org/shipwire/redis/resp"
)

var CommandInProgress = errors.New("command in progress")

// Conn represents an open connection to a redis server.
type Conn struct {
	net.Conn
	whence       *Pool
	openCommands int
	commandLock  *sync.Mutex
	reply        *resp.RESP
}

// Dial connects to the redis server.
func Dial(network, address string) (*Conn, error) {
	c, err := net.Dial(network, address)
	conn := &Conn{Conn: c, commandLock: &sync.Mutex{}}
	conn.reply = resp.New(c)
	return conn, err
}

// DialTimeout acts like Dial but takes a timeout. The timeout includes name resolution, if required.
func DialTimeout(network, address string, timeout time.Duration) (*Conn, error) {
	c, err := net.DialTimeout(network, address, timeout)
	conn := &Conn{Conn: c, commandLock: &sync.Mutex{}}
	conn.reply = resp.New(c)
	return conn, err
}

// RawCmd sends a raw command to the redis server
func (c *Conn) RawCmd(command string, args ...string) error {

	cmd, err := c.Command(command, len(args))
	if err != nil {
		return err
	}

	for _, arg := range args {
		cmd.WriteArgumentString(arg)
	}
	return cmd.Close()
}

// Command initializes a command with the given number of arguments. The connection
// only allows one open command at a time and will block callers to prevent jumbled queues.
func (c *Conn) Command(command string, args int) (*Cmd, error) {
	c.commandLock.Lock()
	c.openCommands += 1

	fmt.Fprintf(c, "*%d\r\n", args+1)
	cmd := &Cmd{c, c}
	cmd.WriteArgumentString(command)
	return cmd, nil
}

// Resp reads a RESP from the connection
func (c *Conn) Resp() *resp.RESP {
	defer func() {
		c.openCommands -= 1
	}()
	return c.reply
}

// Cmd is a command that is currently being written to a connection.
type Cmd struct {
	io.Writer
	conn *Conn
}

// WriteArgumentString writes a static string to the connection as a command argument.
func (c Cmd) WriteArgumentString(arg string) (int, error) {
	return fmt.Fprintf(c, "$%d\r\n%s\r\n", len(arg), arg)
}

// WriteArgument is a shortcut method to write a reader to a command. If at all possible,
// WriteArgumentLength should be used instead.
func (c Cmd) WriteArgument(r io.Reader) (int, error) {
	buf := &bytes.Buffer{}
	io.Copy(buf, r)
	return fmt.Fprintf(c, "$%d\r\n%s\r\n", buf.Len(), buf.String())
}

// WriteArgumentLength copies a reader as an argument to a command. It expects the reader
// to be of the given length.
func (c Cmd) WriteArgumentLength(r io.Reader, l int64) (written int, err error) {
	r = io.LimitReader(r, l)
	w, err := fmt.Fprintf(c, "$%d\r\n", l)
	if err != nil {
		return
	}
	written += w
	ww, err := io.Copy(c, r)
	if err != nil {
		return
	}
	written += int(ww)
	w, err = fmt.Fprint(c, "\r\n")
	if err != nil {
		return
	}
	written += w
	return
}

// Close closes the command with a CRLF.
func (c Cmd) Close() error {
	defer c.conn.commandLock.Unlock()
	_, err := io.WriteString(c, "\r\n")
	return err
}
<|endoftext|>


<commit_before>// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.

package revel

import (
	"go/build"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/agtorre/gocolorize"
	"github.com/revel/config"
)

const (
	// RevelImportPath Revel framework import path
	RevelImportPath = "github.com/revel/revel"
)

type revelLogs struct {
	c gocolorize.Colorize
	w io.Writer
}

func (r *revelLogs) Write(p []byte) (n int, err error) {
	return r.w.Write([]byte(r.c.Paint(string(p))))
}

// App details
var (
	AppName    string // e.g. "sample"
	AppRoot    string // e.g. "/app1"
	BasePath   string // e.g. "$GOPATH/src/corp/sample"
	AppPath    string // e.g. "$GOPATH/src/corp/sample/app"
	ViewsPath  string // e.g. "$GOPATH/src/corp/sample/app/views"
	ImportPath string // e.g. "corp/sample"
	SourcePath string // e.g. "$GOPATH/src"

	Config  *config.Context
	RunMode string // Application-defined (by default, "dev" or "prod")
	DevMode bool   // if true, RunMode is a development mode.

	// Revel installation details
	RevelPath string // e.g. "$GOPATH/src/github.com/revel/revel"

	// Where to look for templates
	// Ordered by priority. (Earlier paths take precedence over later paths.)
	CodePaths     []string
	TemplatePaths []string

	// ConfPaths where to look for configurations
	// Config load order
	// 1. framework (revel/conf/*)
	// 2. application (conf/*)
	// 3. user supplied configs (...) - User configs can override/add any from above
	ConfPaths []string

	Modules []Module

	// Server config.
	//
	// Alert: This is how the app is configured, which may be different from
	// the current process reality.  For example, if the app is configured for
	// port 9000, HTTPPort will always be 9000, even though in dev mode it is
	// run on a random port and proxied.
	HTTPPort    int    // e.g. 9000
	HTTPAddr    string // e.g. "", "127.0.0.1"
	HTTPSsl     bool   // e.g. true if using ssl
	HTTPSslCert string // e.g. "/path/to/cert.pem"
	HTTPSslKey  string // e.g. "/path/to/key.pem"

	// All cookies dropped by the framework begin with this prefix.
	CookiePrefix string
	// Cookie domain
	CookieDomain string
	// Cookie flags
	CookieSecure bool

	// Delimiters to use when rendering templates
	TemplateDelims string

	//Logger colors
	colors = map[string]gocolorize.Colorize{
		"trace": gocolorize.NewColor("magenta"),
		"info":  gocolorize.NewColor("white"),
		"warn":  gocolorize.NewColor("yellow"),
		"error": gocolorize.NewColor("red"),
	}

	errorLog = revelLogs{c: colors["error"], w: os.Stderr}

	// Loggers
	TRACE = log.New(ioutil.Discard, "TRACE ", log.Ldate|log.Ltime|log.Lshortfile)
	INFO  = log.New(ioutil.Discard, "INFO ", log.Ldate|log.Ltime|log.Lshortfile)
	WARN  = log.New(ioutil.Discard, "WARN ", log.Ldate|log.Ltime|log.Lshortfile)
	ERROR = log.New(&errorLog, "ERROR ", log.Ldate|log.Ltime|log.Lshortfile)

	// Revel request access log, not exposed from package.
	// However output settings can be controlled from app.conf
	requestLog           = log.New(ioutil.Discard, "", 0)
	requestLogTimeFormat = "2006/01/02 15:04:05.000"

	Initialized bool

	// Private
	secretKey []byte // Key used to sign cookies. An empty key disables signing.
	packaged  bool   // If true, this is running from a pre-built package.
)

// Init initializes Revel -- it provides paths for getting around the app.
//
// Params:
//   mode - the run mode, which determines which app.conf settings are used.
//   importPath - the Go import path of the application.
//   srcPath - the path to the source directory, containing Revel and the app.
//     If not specified (""), then a functioning Go installation is required.
func Init(mode, importPath, srcPath string) {
	// Ignore trailing slashes.
	ImportPath = strings.TrimRight(importPath, "/")
	SourcePath = srcPath
	RunMode = mode

	if runtime.GOOS == "windows" {
		gocolorize.SetPlain(true)
	}

	// If the SourcePath is not specified, find it using build.Import.
	var revelSourcePath string // may be different from the app source path
	if SourcePath == "" {
		revelSourcePath, SourcePath = findSrcPaths(importPath)
	} else {
		// If the SourcePath was specified, assume both Revel and the app are within it.
		SourcePath = filepath.Clean(SourcePath)
		revelSourcePath = SourcePath
		packaged = true
	}

	RevelPath = filepath.Join(revelSourcePath, filepath.FromSlash(RevelImportPath))
	BasePath = filepath.Join(SourcePath, filepath.FromSlash(importPath))
	AppPath = filepath.Join(BasePath, "app")
	ViewsPath = filepath.Join(AppPath, "views")

	CodePaths = []string{AppPath}

	if ConfPaths == nil {
		ConfPaths = []string{}
	}

	// Config load order
	// 1. framework (revel/conf/*)
	// 2. application (conf/*)
	// 3. user supplied configs (...) - User configs can override/add any from above
	ConfPaths = append(
		[]string{
			filepath.Join(RevelPath, "conf"),
			filepath.Join(BasePath, "conf"),
		},
		ConfPaths...)

	TemplatePaths = []string{
		ViewsPath,
		filepath.Join(RevelPath, "templates"),
	}

	// Load app.conf
	var err error
	Config, err = config.LoadContext("app.conf", ConfPaths)
	if err != nil || Config == nil {
		log.Fatalln("Failed to load app.conf:", err)
	}
	// Ensure that the selected runmode appears in app.conf.
	// If empty string is passed as the mode, treat it as "DEFAULT"
	if mode == "" {
		mode = config.DefaultSection
	}
	if !Config.HasSection(mode) {
		log.Fatalln("app.conf: No mode found:", mode)
	}
	Config.SetSection(mode)

	// Configure properties from app.conf
	DevMode = Config.BoolDefault("mode.dev", false)
	HTTPPort = Config.IntDefault("http.port", 9000)
	HTTPAddr = Config.StringDefault("http.addr", "")
	HTTPSsl = Config.BoolDefault("http.ssl", false)
	HTTPSslCert = Config.StringDefault("http.sslcert", "")
	HTTPSslKey = Config.StringDefault("http.sslkey", "")
	if HTTPSsl {
		if HTTPSslCert == "" {
			log.Fatalln("No http.sslcert provided.")
		}
		if HTTPSslKey == "" {
			log.Fatalln("No http.sslkey provided.")
		}
	}

	AppName = Config.StringDefault("app.name", "(not set)")
	AppRoot = Config.StringDefault("app.root", "")
	CookiePrefix = Config.StringDefault("cookie.prefix", "REVEL")
	CookieDomain = Config.StringDefault("cookie.domain", "")
	CookieSecure = Config.BoolDefault("cookie.secure", HTTPSsl)
	TemplateDelims = Config.StringDefault("template.delimiters", "")
	if secretStr := Config.StringDefault("app.secret", ""); secretStr != "" {
		secretKey = []byte(secretStr)
	}

	// Configure logging
	if !Config.BoolDefault("log.colorize", true) {
		gocolorize.SetPlain(true)
	}

	TRACE = getLogger("trace")
	INFO = getLogger("info")
	WARN = getLogger("warn")
	ERROR = getLogger("error")

	// Revel request access logger, not exposed from package.
	// However output settings can be controlled from app.conf
	requestLog = getLogger("request")

	loadModules()

	Initialized = true
	INFO.Printf("Initialized Revel v%s (%s) for %s", Version, BuildDate, MinimumGoVersion)
}

// Create a logger using log.* directives in app.conf plus the current settings
// on the default logger.
func getLogger(name string) *log.Logger {
	var logger *log.Logger

	// Create a logger with the requested output. (default to stderr)
	output := Config.StringDefault("log."+name+".output", "stderr")
	var newlog revelLogs

	switch output {
	case "stdout":
		newlog = revelLogs{c: colors[name], w: os.Stdout}
		logger = newLogger(&newlog)
	case "stderr":
		newlog = revelLogs{c: colors[name], w: os.Stderr}
		logger = newLogger(&newlog)
	case "off":
		return newLogger(ioutil.Discard)
	default:
		if !filepath.IsAbs(output) {
			output = filepath.Join(BasePath, output)
		}

		logPath := filepath.Dir(output)
		if err := createDir(logPath); err != nil {
			log.Fatalln(err)
		}

		file, err := os.OpenFile(output, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
		if err != nil {
			log.Fatalln("Failed to open log file", output, ":", err)
		}
		logger = newLogger(file)
	}

	if strings.EqualFold(name, "request") {
		logger.SetFlags(0)
		return logger
	}

	// Set the prefix / flags.
	flags, found := Config.Int("log." + name + ".flags")
	if found {
		logger.SetFlags(flags)
	}

	prefix, found := Config.String("log." + name + ".prefix")
	if found {
		logger.SetPrefix(prefix)
	}

	return logger
}

func newLogger(wr io.Writer) *log.Logger {
	return log.New(wr, "", INFO.Flags())
}

// findSrcPaths uses the "go/build" package to find the source root for Revel
// and the app.
func findSrcPaths(importPath string) (revelSourcePath, appSourcePath string) {
	var (
		gopaths = filepath.SplitList(build.Default.GOPATH)
		goroot  = build.Default.GOROOT
	)

	if len(gopaths) == 0 {
		ERROR.Fatalln("GOPATH environment variable is not set. ",
			"Please refer to http://golang.org/doc/code.html to configure your Go environment.")
	}

	if ContainsString(gopaths, goroot) {
		ERROR.Fatalf("GOPATH (%s) must not include your GOROOT (%s). "+
			"Please refer to http://golang.org/doc/code.html to configure your Go environment.",
			gopaths, goroot)
	}

	appPkg, err := build.Import(importPath, "", build.FindOnly)
	if err != nil {
		ERROR.Fatalln("Failed to import", importPath, "with error:", err)
	}

	revelPkg, err := build.Import(RevelImportPath, "", build.FindOnly)
	if err != nil {
		ERROR.Fatalln("Failed to find Revel with error:", err)
	}

	return revelPkg.SrcRoot, appPkg.SrcRoot
}

type Module struct {
	Name, ImportPath, Path string
}

func loadModules() {
	for _, key := range Config.Options("module.") {
		moduleImportPath := Config.StringDefault(key, "")
		if moduleImportPath == "" {
			continue
		}

		modulePath, err := ResolveImportPath(moduleImportPath)
		if err != nil {
			log.Fatalln("Failed to load module.  Import of", moduleImportPath, "failed:", err)
		}
		addModule(key[len("module."):], moduleImportPath, modulePath)
	}
}

// ResolveImportPath returns the filesystem path for the given import path.
// Returns an error if the import path could not be found.
func ResolveImportPath(importPath string) (string, error) {
	if packaged {
		return filepath.Join(SourcePath, importPath), nil
	}

	modPkg, err := build.Import(importPath, "", build.FindOnly)
	if err != nil {
		return "", err
	}
	return modPkg.Dir, nil
}

func addModule(name, importPath, modulePath string) {
	Modules = append(Modules, Module{Name: name, ImportPath: importPath, Path: modulePath})
	if codePath := filepath.Join(modulePath, "app"); DirExists(codePath) {
		CodePaths = append(CodePaths, codePath)
		if viewsPath := filepath.Join(modulePath, "app", "views"); DirExists(viewsPath) {
			TemplatePaths = append(TemplatePaths, viewsPath)
		}
	}

	INFO.Print("Loaded module ", filepath.Base(modulePath))

	// Hack: There is presently no way for the testrunner module to add the
	// "test" subdirectory to the CodePaths.  So this does it instead.
	if importPath == Config.StringDefault("module.testrunner", "github.com/revel/modules/testrunner") {
		CodePaths = append(CodePaths, filepath.Join(BasePath, "tests"))
	}
}

// ModuleByName returns the module of the given name, if loaded.
func ModuleByName(name string) (m Module, found bool) {
	for _, module := range Modules {
		if module.Name == name {
			return module, true
		}
	}
	return Module{}, false
}

// CheckInit method checks `revel.Initialized` if not initialized it panics
func CheckInit() {
	if !Initialized {
		panic("Revel has not been initialized!")
	}
}

func init() {
	log.SetFlags(INFO.Flags())
}
<commit_msg>Adds  path detection when loading modules<commit_after>// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.

package revel

import (
	"go/build"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/agtorre/gocolorize"
	"github.com/revel/config"
)

const (
	// RevelImportPath Revel framework import path
	RevelImportPath = "github.com/revel/revel"
)

type revelLogs struct {
	c gocolorize.Colorize
	w io.Writer
}

func (r *revelLogs) Write(p []byte) (n int, err error) {
	return r.w.Write([]byte(r.c.Paint(string(p))))
}

// App details
var (
	AppName    string // e.g. "sample"
	AppRoot    string // e.g. "/app1"
	BasePath   string // e.g. "$GOPATH/src/corp/sample"
	AppPath    string // e.g. "$GOPATH/src/corp/sample/app"
	ViewsPath  string // e.g. "$GOPATH/src/corp/sample/app/views"
	ImportPath string // e.g. "corp/sample"
	SourcePath string // e.g. "$GOPATH/src"

	Config  *config.Context
	RunMode string // Application-defined (by default, "dev" or "prod")
	DevMode bool   // if true, RunMode is a development mode.

	// Revel installation details
	RevelPath string // e.g. "$GOPATH/src/github.com/revel/revel"

	// Where to look for templates
	// Ordered by priority. (Earlier paths take precedence over later paths.)
	CodePaths     []string
	TemplatePaths []string

	// ConfPaths where to look for configurations
	// Config load order
	// 1. framework (revel/conf/*)
	// 2. application (conf/*)
	// 3. user supplied configs (...) - User configs can override/add any from above
	ConfPaths []string

	Modules []Module

	// Server config.
	//
	// Alert: This is how the app is configured, which may be different from
	// the current process reality.  For example, if the app is configured for
	// port 9000, HTTPPort will always be 9000, even though in dev mode it is
	// run on a random port and proxied.
	HTTPPort    int    // e.g. 9000
	HTTPAddr    string // e.g. "", "127.0.0.1"
	HTTPSsl     bool   // e.g. true if using ssl
	HTTPSslCert string // e.g. "/path/to/cert.pem"
	HTTPSslKey  string // e.g. "/path/to/key.pem"

	// All cookies dropped by the framework begin with this prefix.
	CookiePrefix string
	// Cookie domain
	CookieDomain string
	// Cookie flags
	CookieSecure bool

	// Delimiters to use when rendering templates
	TemplateDelims string

	//Logger colors
	colors = map[string]gocolorize.Colorize{
		"trace": gocolorize.NewColor("magenta"),
		"info":  gocolorize.NewColor("white"),
		"warn":  gocolorize.NewColor("yellow"),
		"error": gocolorize.NewColor("red"),
	}

	errorLog = revelLogs{c: colors["error"], w: os.Stderr}

	// Loggers
	TRACE = log.New(ioutil.Discard, "TRACE ", log.Ldate|log.Ltime|log.Lshortfile)
	INFO  = log.New(ioutil.Discard, "INFO ", log.Ldate|log.Ltime|log.Lshortfile)
	WARN  = log.New(ioutil.Discard, "WARN ", log.Ldate|log.Ltime|log.Lshortfile)
	ERROR = log.New(&errorLog, "ERROR ", log.Ldate|log.Ltime|log.Lshortfile)

	// Revel request access log, not exposed from package.
	// However output settings can be controlled from app.conf
	requestLog           = log.New(ioutil.Discard, "", 0)
	requestLogTimeFormat = "2006/01/02 15:04:05.000"

	Initialized bool

	// Private
	secretKey []byte // Key used to sign cookies. An empty key disables signing.
	packaged  bool   // If true, this is running from a pre-built package.
)

// Init initializes Revel -- it provides paths for getting around the app.
//
// Params:
//   mode - the run mode, which determines which app.conf settings are used.
//   importPath - the Go import path of the application.
//   srcPath - the path to the source directory, containing Revel and the app.
//     If not specified (""), then a functioning Go installation is required.
func Init(mode, importPath, srcPath string) {
	// Ignore trailing slashes.
	ImportPath = strings.TrimRight(importPath, "/")
	SourcePath = srcPath
	RunMode = mode

	if runtime.GOOS == "windows" {
		gocolorize.SetPlain(true)
	}

	// If the SourcePath is not specified, find it using build.Import.
	var revelSourcePath string // may be different from the app source path
	if SourcePath == "" {
		revelSourcePath, SourcePath = findSrcPaths(importPath)
	} else {
		// If the SourcePath was specified, assume both Revel and the app are within it.
		SourcePath = filepath.Clean(SourcePath)
		revelSourcePath = SourcePath
		packaged = true
	}

	RevelPath = filepath.Join(revelSourcePath, filepath.FromSlash(RevelImportPath))
	BasePath = filepath.Join(SourcePath, filepath.FromSlash(importPath))
	AppPath = filepath.Join(BasePath, "app")
	ViewsPath = filepath.Join(AppPath, "views")

	CodePaths = []string{AppPath}

	if ConfPaths == nil {
		ConfPaths = []string{}
	}

	// Config load order
	// 1. framework (revel/conf/*)
	// 2. application (conf/*)
	// 3. user supplied configs (...) - User configs can override/add any from above
	ConfPaths = append(
		[]string{
			filepath.Join(RevelPath, "conf"),
			filepath.Join(BasePath, "conf"),
		},
		ConfPaths...)

	TemplatePaths = []string{
		ViewsPath,
		filepath.Join(RevelPath, "templates"),
	}

	// Load app.conf
	var err error
	Config, err = config.LoadContext("app.conf", ConfPaths)
	if err != nil || Config == nil {
		log.Fatalln("Failed to load app.conf:", err)
	}
	// Ensure that the selected runmode appears in app.conf.
	// If empty string is passed as the mode, treat it as "DEFAULT"
	if mode == "" {
		mode = config.DefaultSection
	}
	if !Config.HasSection(mode) {
		log.Fatalln("app.conf: No mode found:", mode)
	}
	Config.SetSection(mode)

	// Configure properties from app.conf
	DevMode = Config.BoolDefault("mode.dev", false)
	HTTPPort = Config.IntDefault("http.port", 9000)
	HTTPAddr = Config.StringDefault("http.addr", "")
	HTTPSsl = Config.BoolDefault("http.ssl", false)
	HTTPSslCert = Config.StringDefault("http.sslcert", "")
	HTTPSslKey = Config.StringDefault("http.sslkey", "")
	if HTTPSsl {
		if HTTPSslCert == "" {
			log.Fatalln("No http.sslcert provided.")
		}
		if HTTPSslKey == "" {
			log.Fatalln("No http.sslkey provided.")
		}
	}

	AppName = Config.StringDefault("app.name", "(not set)")
	AppRoot = Config.StringDefault("app.root", "")
	CookiePrefix = Config.StringDefault("cookie.prefix", "REVEL")
	CookieDomain = Config.StringDefault("cookie.domain", "")
	CookieSecure = Config.BoolDefault("cookie.secure", HTTPSsl)
	TemplateDelims = Config.StringDefault("template.delimiters", "")
	if secretStr := Config.StringDefault("app.secret", ""); secretStr != "" {
		secretKey = []byte(secretStr)
	}

	// Configure logging
	if !Config.BoolDefault("log.colorize", true) {
		gocolorize.SetPlain(true)
	}

	TRACE = getLogger("trace")
	INFO = getLogger("info")
	WARN = getLogger("warn")
	ERROR = getLogger("error")

	// Revel request access logger, not exposed from package.
	// However output settings can be controlled from app.conf
	requestLog = getLogger("request")

	loadModules()

	Initialized = true
	INFO.Printf("Initialized Revel v%s (%s) for %s", Version, BuildDate, MinimumGoVersion)
}

// Create a logger using log.* directives in app.conf plus the current settings
// on the default logger.
func getLogger(name string) *log.Logger {
	var logger *log.Logger

	// Create a logger with the requested output. (default to stderr)
	output := Config.StringDefault("log."+name+".output", "stderr")
	var newlog revelLogs

	switch output {
	case "stdout":
		newlog = revelLogs{c: colors[name], w: os.Stdout}
		logger = newLogger(&newlog)
	case "stderr":
		newlog = revelLogs{c: colors[name], w: os.Stderr}
		logger = newLogger(&newlog)
	case "off":
		return newLogger(ioutil.Discard)
	default:
		if !filepath.IsAbs(output) {
			output = filepath.Join(BasePath, output)
		}

		logPath := filepath.Dir(output)
		if err := createDir(logPath); err != nil {
			log.Fatalln(err)
		}

		file, err := os.OpenFile(output, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
		if err != nil {
			log.Fatalln("Failed to open log file", output, ":", err)
		}
		logger = newLogger(file)
	}

	if strings.EqualFold(name, "request") {
		logger.SetFlags(0)
		return logger
	}

	// Set the prefix / flags.
	flags, found := Config.Int("log." + name + ".flags")
	if found {
		logger.SetFlags(flags)
	}

	prefix, found := Config.String("log." + name + ".prefix")
	if found {
		logger.SetPrefix(prefix)
	}

	return logger
}

func newLogger(wr io.Writer) *log.Logger {
	return log.New(wr, "", INFO.Flags())
}

// findSrcPaths uses the "go/build" package to find the source root for Revel
// and the app.
func findSrcPaths(importPath string) (revelSourcePath, appSourcePath string) {
	var (
		gopaths = filepath.SplitList(build.Default.GOPATH)
		goroot  = build.Default.GOROOT
	)

	if len(gopaths) == 0 {
		ERROR.Fatalln("GOPATH environment variable is not set. ",
			"Please refer to http://golang.org/doc/code.html to configure your Go environment.")
	}

	if ContainsString(gopaths, goroot) {
		ERROR.Fatalf("GOPATH (%s) must not include your GOROOT (%s). "+
			"Please refer to http://golang.org/doc/code.html to configure your Go environment.",
			gopaths, goroot)
	}

	appPkg, err := build.Import(importPath, "", build.FindOnly)
	if err != nil {
		ERROR.Fatalln("Failed to import", importPath, "with error:", err)
	}

	revelPkg, err := build.Import(RevelImportPath, "", build.FindOnly)
	if err != nil {
		ERROR.Fatalln("Failed to find Revel with error:", err)
	}

	return revelPkg.SrcRoot, appPkg.SrcRoot
}

type Module struct {
	Name, ImportPath, Path string
}

func loadModules() {
	for _, key := range Config.Options("module.") {
		moduleImportPath := Config.StringDefault(key, "")
		if moduleImportPath == "" {
			continue
		}

		modulePath, err := ResolveImportPath(moduleImportPath)
		if err != nil {
			log.Fatalln("Failed to load module.  Import of", moduleImportPath, "failed:", err)
		}
		addModule(key[len("module."):], moduleImportPath, modulePath)
	}
}

// ResolveImportPath returns the filesystem path for the given import path.
// Returns an error if the import path could not be found.
func ResolveImportPath(importPath string) (string, error) {
	if packaged {
		return filepath.Join(SourcePath, importPath), nil
	}

	modPkg, err := build.Import(importPath, "", build.FindOnly)
	if err != nil {
		return "", err
	}
	return modPkg.Dir, nil
}

func addModule(name, importPath, modulePath string) {
	Modules = append(Modules, Module{Name: name, ImportPath: importPath, Path: modulePath})
	if codePath := filepath.Join(modulePath, "app"); DirExists(codePath) {
		CodePaths = append(CodePaths, codePath)
		if viewsPath := filepath.Join(modulePath, "app", "views"); DirExists(viewsPath) {
			TemplatePaths = append(TemplatePaths, viewsPath)
		}
	}

	INFO.Print("Loaded module ", filepath.Base(modulePath))

	// Hack: There is presently no way for the testrunner module to add the
	// "test" subdirectory to the CodePaths.  So this does it instead.
	if importPath == Config.StringDefault("module.testrunner", "github.com/revel/modules/testrunner") {
		INFO.Print("Found testrunner module, adding `tests` path ", filepath.Join(BasePath, "tests"))
		CodePaths = append(CodePaths, filepath.Join(BasePath, "tests"))
	}
	if testsPath := filepath.Join(modulePath, "tests"); DirExists(testsPath) {
		INFO.Print("Found tests path ", testsPath)
		CodePaths = append(CodePaths, testsPath)
	}
}

// ModuleByName returns the module of the given name, if loaded.
func ModuleByName(name string) (m Module, found bool) {
	for _, module := range Modules {
		if module.Name == name {
			return module, true
		}
	}
	return Module{}, false
}

// CheckInit method checks `revel.Initialized` if not initialized it panics
func CheckInit() {
	if !Initialized {
		panic("Revel has not been initialized!")
	}
}

func init() {
	log.SetFlags(INFO.Flags())
}
<|endoftext|>