<commit_before>package ebpf

import (
	"encoding/binary"
	"io"
	"os"
	"runtime"
	"sync"
	"sync/atomic"
	"syscall"
	"unsafe"

	"github.com/pkg/errors"
)

const (
	perfTypeSoftware     = 1
	perfCountSWBPFOutput = 10
	perfSampleRaw        = 1 << 10
)

type perfEventMeta struct {
	_          [128]uint64 /* Pad to 1 k, ignore fields */
	dataHead   uint64      /* head in the data section */
	dataTail   uint64      /* user-space written tail */
	dataOffset uint64      /* where the buffer starts */
	dataSize   uint64      /* data buffer size */
}

type perfEventHeader struct {
	Type uint32
	Misc uint16
	Size uint16
}

// perfEventRing is a page of metadata followed by
// a variable number of pages which form a ring buffer.
type perfEventRing struct {
	fd   int
	meta *perfEventMeta
	mmap []byte
	ring []byte
}

func newPerfEventRing(cpu int, opts PerfReaderOptions) (*perfEventRing, error) {
	const flagWakeupWatermark = 1 << 14

	if opts.Watermark >= opts.PerCPUBuffer {
		return nil, errors.Errorf("Watermark must be smaller than PerCPUBuffer")
	}

	// Round to nearest page boundary and allocate
	// an extra page for meta data
	pageSize := os.Getpagesize()
	nPages := (opts.PerCPUBuffer + pageSize - 1) / pageSize
	size := (1 + nPages) * pageSize

	attr := perfEventAttr{
		perfType:                perfTypeSoftware,
		config:                  perfCountSWBPFOutput,
		flags:                   flagWakeupWatermark,
		sampleType:              perfSampleRaw,
		wakeupEventsOrWatermark: uint32(opts.Watermark),
	}

	fd, err := perfEventOpen(&attr, -1, cpu, -1, 0)
	if err != nil {
		return nil, err
	}

	if err := syscall.SetNonblock(fd, true); err != nil {
		syscall.Close(fd)
		return nil, err
	}

	mmap, err := syscall.Mmap(fd, 0, size, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
	if err != nil {
		syscall.Close(fd)
		return nil, err
	}

	// This relies on the fact that we allocate an extra metadata page,
	// and that the struct is smaller than an OS page.
	// This use of unsafe.Pointer isn't explicitly sanctioned by the
	// documentation, since a byte is smaller than sampledPerfEvent.
	meta := (*perfEventMeta)(unsafe.Pointer(&mmap[0]))

	return &perfEventRing{
		fd:   fd,
		meta: meta,
		mmap: mmap,
		ring: mmap[meta.dataOffset : meta.dataOffset+meta.dataSize],
	}, nil
}

func (ring *perfEventRing) Close() {
	syscall.Close(ring.fd)
	syscall.Munmap(ring.mmap)
}

func readRecord(rd io.Reader) (*PerfSample, uint64, error) {
	const (
		perfRecordLost   = 2
		perfRecordSample = 9
	)

	var header perfEventHeader
	err := binary.Read(rd, nativeEndian, &header)
	if err == io.EOF {
		return nil, 0, nil
	}

	if err != nil {
		return nil, 0, errors.Wrap(err, "can't read event header")
	}

	switch header.Type {
	case perfRecordLost:
		lost, err := readLostRecords(rd)
		if err != nil {
			return nil, 0, err
		}

		return nil, lost, nil

	case perfRecordSample:
		sample, err := readSample(rd)
		if err != nil {
			return nil, 0, err
		}

		return sample, 0, nil

	default:
		return nil, 0, errors.Errorf("unknown event type %d", header.Type)
	}
}

func readLostRecords(rd io.Reader) (uint64, error) {
	var lostHeader struct {
		ID   uint64
		Lost uint64
	}

	err := binary.Read(rd, nativeEndian, &lostHeader)
	if err != nil {
		return 0, errors.Wrap(err, "can't read lost records header")
	}

	return lostHeader.Lost, nil
}

func readSample(rd io.Reader) (*PerfSample, error) {
	var size uint32
	if err := binary.Read(rd, nativeEndian, &size); err != nil {
		return nil, errors.Wrap(err, "can't read sample size")
	}

	data := make([]byte, int(size))
	_, err := io.ReadFull(rd, data)
	return &PerfSample{data}, errors.Wrap(err, "can't read sample")
}

// PerfSample is read from the kernel by PerfReader.
type PerfSample struct {
	// Data are padded with 0 to have a 64-bit alignment.
	// If you are using variable length samples you need to take
	// this into account.
	Data []byte
}

// PerfReader allows reading bpf_perf_event_output
// from user space.
type PerfReader struct {
	lostSamples uint64
	// Closing a PERF_EVENT_ARRAY removes all event fds
	// stored in it, so we keep a reference alive.
	array *Map

	closeOnce sync.Once
	closeFile *os.File
	close     chan struct{}

	// Error receives a write if the reader exits
	// due to an error.
	Error <-chan error

	// Samples is closed when the Reader exits.
	Samples <-chan *PerfSample
}

// PerfReaderOptions control the behaviour of the user
// space reader.
type PerfReaderOptions struct {
	// A map of type PerfEventArray. The reader takes ownership of the
	// map and takes care of closing it.
	Map *Map
	// Controls the size of the per CPU buffer in bytes. LostSamples() will
	// increase if the buffer is too small.
	PerCPUBuffer int
	// The reader will start processing samples once the per CPU buffer
	// exceeds this value. Must be smaller than PerCPUBuffer.
	Watermark int
}

// NewPerfReader creates a new reader with the given options.
//
// The value returned by LostSamples() will increase if the buffer
// isn't large enough to contain all incoming samples.
func NewPerfReader(opts PerfReaderOptions) (out *PerfReader, err error) {
	if opts.PerCPUBuffer < 1 {
		return nil, errors.New("PerCPUBuffer must be larger than 0")
	}

	nCPU, err := possibleCPUs()
	if err != nil {
		opts.Map.Close()
		return nil, errors.Wrap(err, "sampled perf event")
	}

	closeFd, err := newEventFd()
	if err != nil {
		opts.Map.Close()
		return nil, err
	}

	samples := make(chan *PerfSample, nCPU)
	errs := make(chan error, 1)

	out = &PerfReader{
		array:     opts.Map,
		closeFile: os.NewFile(uintptr(closeFd), "event fd"),
		close:     make(chan struct{}),
		Error:     errs,
		Samples:   samples,
	}
	runtime.SetFinalizer(out, (*PerfReader).Close)

	var (
		fds   = []int{closeFd}
		rings = make(map[int]*perfEventRing)
	)

	defer func() {
		if err != nil {
			for _, mmap := range rings {
				mmap.Close()
			}
		}
	}()

	// bpf_perf_event_output checks which CPU an event is enabled on,
	// but doesn't allow using a wildcard like -1 to specify "all CPUs".
	// Hence we have to create a ring for each CPU.
	for i := 0; i < nCPU; i++ {
		ring, err := newPerfEventRing(i, opts)
		if err != nil {
			return nil, errors.Wrapf(err, "failed to create perf ring for CPU %d", i)
		}

		if err := opts.Map.Put(uint32(i), uint32(ring.fd)); err != nil {
			ring.Close()
			return nil, errors.Wrapf(err, "could't put event fd for CPU %d", i)
		}

		fds = append(fds, ring.fd)
		rings[ring.fd] = ring
	}

	epollfd, err := newEpollFd(fds...)
	if err != nil {
		return nil, err
	}

	go out.poll(epollfd, rings, samples, errs)
	return out, nil
}

// LostSamples returns the number of samples dropped
// by the perf subsystem.
func (pr *PerfReader) LostSamples() uint64 {
	return atomic.LoadUint64(&pr.lostSamples)
}

// Close stops the reader.
//
// Calls to perf_event_output from eBPF programs will return
// ENOENT after calling this method.
func (pr *PerfReader) Close() (err error) {
	pr.closeOnce.Do(func() {
		runtime.SetFinalizer(pr, nil)

		// Indicate that we want to shut down
		close(pr.close)
		pr.array.Close()

		// Signal poll() via the event fd
		var value [8]byte
		nativeEndian.PutUint64(value[:], 1)
		_, err = pr.closeFile.Write(value[:])
	})

	return errors.Wrap(err, "can't write to event fd")
}

func (pr *PerfReader) poll(epollFd int, rings map[int]*perfEventRing, samples chan<- *PerfSample, errs chan<- error) {
	defer close(samples)
	defer syscall.Close(epollFd)
	defer func() {
		for _, ring := range rings {
			ring.Close()
		}
	}()

	epollEvents := make([]syscall.EpollEvent, len(rings))

	for {
		nEvents, err := syscall.EpollWait(epollFd, epollEvents, -1)
		if err != nil {
			// Handle EINTR
			if temp, ok := err.(temporaryError); ok && temp.Temporary() {
				continue
			}

			errs <- err
			return
		}

		select {
		case <-pr.close:
			// We were woken by Close via the event fd
			return
		default:
		}

		for _, event := range epollEvents[:nEvents] {
			ring := rings[int(event.Fd)]
			err := pr.flushRing(ring, samples)
			if err != nil {
				errs <- err
				return
			}
		}
	}
}

func (pr *PerfReader) flushRing(ring *perfEventRing, samples chan<- *PerfSample) error {
	rd := newRingReader(ring.meta, ring.ring)
	defer rd.Close()

	var totalLost uint64

	for {
		sample, lost, err := readRecord(rd)
		if err != nil {
			return err
		}

		if lost > 0 {
			totalLost += lost
			continue
		}

		if sample == nil {
			break
		}

		select {
		case samples <- sample:
		case <-pr.close:
			break
		}
	}

	if totalLost > 0 {
		atomic.AddUint64(&pr.lostSamples, totalLost)
	}
	return nil
}

type ringReader struct {
	meta       *perfEventMeta
	head, tail uint64
	mask       uint64
	ring       []byte
}

func newRingReader(meta *perfEventMeta, ring []byte) *ringReader {
	return &ringReader{
		meta: meta,
		head: atomic.LoadUint64(&meta.dataHead),
		tail: atomic.LoadUint64(&meta.dataTail),
		// cap is always a power of two
		mask: uint64(cap(ring) - 1),
		ring: ring,
	}
}

func (rb *ringReader) Close() error {
	// Commit the new tail. This lets the kernel know that
	// the ring buffer has been consumed.
	atomic.StoreUint64(&rb.meta.dataTail, rb.tail)
	return nil
}

func (rb *ringReader) Read(p []byte) (int, error) {
	start := int(rb.tail & rb.mask)

	n := len(p)
	// Truncate if the read wraps in the ring buffer
	if remainder := cap(rb.ring) - start; n > remainder {
		n = remainder
	}

	// Truncate if there isn't enough data
	if remainder := int(rb.head - rb.tail); n > remainder {
		n = remainder
	}

	copy(p, rb.ring[start:start+n])
	rb.tail += uint64(n)

	if rb.tail == rb.head {
		return n, io.EOF
	}

	return n, nil
}

type temporaryError interface {
	Temporary() bool
}
<commit_msg>Stop leaking the closeFd in PerfReader<commit_after>package ebpf

import (
	"encoding/binary"
	"io"
	"os"
	"runtime"
	"sync"
	"sync/atomic"
	"syscall"
	"unsafe"

	"github.com/pkg/errors"
)

const (
	perfTypeSoftware     = 1
	perfCountSWBPFOutput = 10
	perfSampleRaw        = 1 << 10
)

type perfEventMeta struct {
	_          [128]uint64 /* Pad to 1 k, ignore fields */
	dataHead   uint64      /* head in the data section */
	dataTail   uint64      /* user-space written tail */
	dataOffset uint64      /* where the buffer starts */
	dataSize   uint64      /* data buffer size */
}

type perfEventHeader struct {
	Type uint32
	Misc uint16
	Size uint16
}

// perfEventRing is a page of metadata followed by
// a variable number of pages which form a ring buffer.
type perfEventRing struct {
	fd   int
	meta *perfEventMeta
	mmap []byte
	ring []byte
}

func newPerfEventRing(cpu int, opts PerfReaderOptions) (*perfEventRing, error) {
	const flagWakeupWatermark = 1 << 14

	if opts.Watermark >= opts.PerCPUBuffer {
		return nil, errors.Errorf("Watermark must be smaller than PerCPUBuffer")
	}

	// Round to nearest page boundary and allocate
	// an extra page for meta data
	pageSize := os.Getpagesize()
	nPages := (opts.PerCPUBuffer + pageSize - 1) / pageSize
	size := (1 + nPages) * pageSize

	attr := perfEventAttr{
		perfType:                perfTypeSoftware,
		config:                  perfCountSWBPFOutput,
		flags:                   flagWakeupWatermark,
		sampleType:              perfSampleRaw,
		wakeupEventsOrWatermark: uint32(opts.Watermark),
	}

	fd, err := perfEventOpen(&attr, -1, cpu, -1, 0)
	if err != nil {
		return nil, err
	}

	if err := syscall.SetNonblock(fd, true); err != nil {
		syscall.Close(fd)
		return nil, err
	}

	mmap, err := syscall.Mmap(fd, 0, size, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
	if err != nil {
		syscall.Close(fd)
		return nil, err
	}

	// This relies on the fact that we allocate an extra metadata page,
	// and that the struct is smaller than an OS page.
	// This use of unsafe.Pointer isn't explicitly sanctioned by the
	// documentation, since a byte is smaller than sampledPerfEvent.
	meta := (*perfEventMeta)(unsafe.Pointer(&mmap[0]))

	return &perfEventRing{
		fd:   fd,
		meta: meta,
		mmap: mmap,
		ring: mmap[meta.dataOffset : meta.dataOffset+meta.dataSize],
	}, nil
}

func (ring *perfEventRing) Close() {
	syscall.Close(ring.fd)
	syscall.Munmap(ring.mmap)
}

func readRecord(rd io.Reader) (*PerfSample, uint64, error) {
	const (
		perfRecordLost   = 2
		perfRecordSample = 9
	)

	var header perfEventHeader
	err := binary.Read(rd, nativeEndian, &header)
	if err == io.EOF {
		return nil, 0, nil
	}

	if err != nil {
		return nil, 0, errors.Wrap(err, "can't read event header")
	}

	switch header.Type {
	case perfRecordLost:
		lost, err := readLostRecords(rd)
		if err != nil {
			return nil, 0, err
		}

		return nil, lost, nil

	case perfRecordSample:
		sample, err := readSample(rd)
		if err != nil {
			return nil, 0, err
		}

		return sample, 0, nil

	default:
		return nil, 0, errors.Errorf("unknown event type %d", header.Type)
	}
}

func readLostRecords(rd io.Reader) (uint64, error) {
	var lostHeader struct {
		ID   uint64
		Lost uint64
	}

	err := binary.Read(rd, nativeEndian, &lostHeader)
	if err != nil {
		return 0, errors.Wrap(err, "can't read lost records header")
	}

	return lostHeader.Lost, nil
}

func readSample(rd io.Reader) (*PerfSample, error) {
	var size uint32
	if err := binary.Read(rd, nativeEndian, &size); err != nil {
		return nil, errors.Wrap(err, "can't read sample size")
	}

	data := make([]byte, int(size))
	_, err := io.ReadFull(rd, data)
	return &PerfSample{data}, errors.Wrap(err, "can't read sample")
}

// PerfSample is read from the kernel by PerfReader.
type PerfSample struct {
	// Data are padded with 0 to have a 64-bit alignment.
	// If you are using variable length samples you need to take
	// this into account.
	Data []byte
}

// PerfReader allows reading bpf_perf_event_output
// from user space.
type PerfReader struct {
	lostSamples uint64
	// Closing a PERF_EVENT_ARRAY removes all event fds
	// stored in it, so we keep a reference alive.
	array *Map

	closeOnce sync.Once
	closeFd   int
	close     chan struct{}

	// Error receives a write if the reader exits
	// due to an error.
	Error <-chan error

	// Samples is closed when the Reader exits.
	Samples <-chan *PerfSample
}

// PerfReaderOptions control the behaviour of the user
// space reader.
type PerfReaderOptions struct {
	// A map of type PerfEventArray. The reader takes ownership of the
	// map and takes care of closing it.
	Map *Map
	// Controls the size of the per CPU buffer in bytes. LostSamples() will
	// increase if the buffer is too small.
	PerCPUBuffer int
	// The reader will start processing samples once the per CPU buffer
	// exceeds this value. Must be smaller than PerCPUBuffer.
	Watermark int
}

// NewPerfReader creates a new reader with the given options.
//
// The value returned by LostSamples() will increase if the buffer
// isn't large enough to contain all incoming samples.
func NewPerfReader(opts PerfReaderOptions) (out *PerfReader, err error) {
	if opts.PerCPUBuffer < 1 {
		return nil, errors.New("PerCPUBuffer must be larger than 0")
	}

	nCPU, err := possibleCPUs()
	if err != nil {
		return nil, errors.Wrap(err, "sampled perf event")
	}

	var (
		fds   []int
		rings = make(map[int]*perfEventRing)
	)

	defer func() {
		if err != nil {
			for _, ring := range rings {
				ring.Close()
			}
		}
	}()

	// bpf_perf_event_output checks which CPU an event is enabled on,
	// but doesn't allow using a wildcard like -1 to specify "all CPUs".
	// Hence we have to create a ring for each CPU.
	for i := 0; i < nCPU; i++ {
		ring, err := newPerfEventRing(i, opts)
		if err != nil {
			return nil, errors.Wrapf(err, "failed to create perf ring for CPU %d", i)
		}

		if err := opts.Map.Put(uint32(i), uint32(ring.fd)); err != nil {
			ring.Close()
			return nil, errors.Wrapf(err, "could't put event fd for CPU %d", i)
		}

		fds = append(fds, ring.fd)
		rings[ring.fd] = ring
	}

	closeFd, err := newEventFd()
	if err != nil {
		return nil, err
	}
	fds = append(fds, closeFd)

	epollFd, err := newEpollFd(fds...)
	if err != nil {
		syscall.Close(closeFd)
		return nil, err
	}

	samples := make(chan *PerfSample, nCPU)
	errs := make(chan error, 1)

	out = &PerfReader{
		array:   opts.Map,
		closeFd: closeFd,
		close:   make(chan struct{}),
		Error:   errs,
		Samples: samples,
	}
	runtime.SetFinalizer(out, (*PerfReader).Close)

	go out.poll(epollFd, rings, samples, errs)
	return out, nil
}

// LostSamples returns the number of samples dropped
// by the perf subsystem.
func (pr *PerfReader) LostSamples() uint64 {
	return atomic.LoadUint64(&pr.lostSamples)
}

// Close stops the reader.
//
// Calls to perf_event_output from eBPF programs will return
// ENOENT after calling this method.
func (pr *PerfReader) Close() (err error) {
	pr.closeOnce.Do(func() {
		runtime.SetFinalizer(pr, nil)

		// Indicate that we want to shut down
		close(pr.close)
		pr.array.Close()

		// Signal poll() via the event fd. Ignore the
		// write error since poll() may have exited
		// and closed the fd already
		var value [8]byte
		nativeEndian.PutUint64(value[:], 1)
		_, _ = syscall.Write(pr.closeFd, value[:])
	})

	return nil
}

func (pr *PerfReader) poll(epollFd int, rings map[int]*perfEventRing, samples chan<- *PerfSample, errs chan<- error) {
	defer close(samples)
	defer syscall.Close(epollFd)
	defer syscall.Close(pr.closeFd)
	defer func() {
		for _, ring := range rings {
			ring.Close()
		}
	}()

	epollEvents := make([]syscall.EpollEvent, len(rings)+1)

	for {
		nEvents, err := syscall.EpollWait(epollFd, epollEvents, -1)
		if err != nil {
			// Handle EINTR
			if temp, ok := err.(temporaryError); ok && temp.Temporary() {
				continue
			}

			errs <- err
			return
		}

		for _, event := range epollEvents[:nEvents] {
			fd := int(event.Fd)
			if fd == pr.closeFd {
				// We were woken by Close via the close fd
				return
			}

			err := pr.flushRing(rings[fd], samples)
			if err != nil {
				errs <- err
				return
			}
		}
	}
}

func (pr *PerfReader) flushRing(ring *perfEventRing, samples chan<- *PerfSample) error {
	rd := newRingReader(ring.meta, ring.ring)
	defer rd.Close()

	var totalLost uint64

	for {
		sample, lost, err := readRecord(rd)
		if err != nil {
			return err
		}

		if lost > 0 {
			totalLost += lost
			continue
		}

		if sample == nil {
			break
		}

		select {
		case samples <- sample:
		case <-pr.close:
			break
		}
	}

	if totalLost > 0 {
		atomic.AddUint64(&pr.lostSamples, totalLost)
	}
	return nil
}

type ringReader struct {
	meta       *perfEventMeta
	head, tail uint64
	mask       uint64
	ring       []byte
}

func newRingReader(meta *perfEventMeta, ring []byte) *ringReader {
	return &ringReader{
		meta: meta,
		head: atomic.LoadUint64(&meta.dataHead),
		tail: atomic.LoadUint64(&meta.dataTail),
		// cap is always a power of two
		mask: uint64(cap(ring) - 1),
		ring: ring,
	}
}

func (rb *ringReader) Close() error {
	// Commit the new tail. This lets the kernel know that
	// the ring buffer has been consumed.
	atomic.StoreUint64(&rb.meta.dataTail, rb.tail)
	return nil
}

func (rb *ringReader) Read(p []byte) (int, error) {
	start := int(rb.tail & rb.mask)

	n := len(p)
	// Truncate if the read wraps in the ring buffer
	if remainder := cap(rb.ring) - start; n > remainder {
		n = remainder
	}

	// Truncate if there isn't enough data
	if remainder := int(rb.head - rb.tail); n > remainder {
		n = remainder
	}

	copy(p, rb.ring[start:start+n])
	rb.tail += uint64(n)

	if rb.tail == rb.head {
		return n, io.EOF
	}

	return n, nil
}

type temporaryError interface {
	Temporary() bool
}
<|endoftext|>


<commit_before>package goplex

import (
	"encoding/xml"
	"errors"
	"io/ioutil"
	"net/http"
	"strconv"
)

const plexTVURL = "https://plex.tv"
const clientIdentifier = "plextrack"

type devicesResp struct {
	XMLName       xml.Name `xml:"MediaContainer"`
	PublicAddress HTTPURL  `xml:"publicAddress,attr"`
	Devices       []Device `xml:"Device"`
}

type sessionsResp struct {
	XMLName xml.Name `xml:"MediaContainer"`
	Videos  []Video  `xml:"Video"`
}

// Hook to override for tests
var client = http.DefaultClient

func GetUser(username, password string) (User, error) {
	req, err := http.NewRequest("POST", plexTVURL+"/users/sign_in.xml", nil)
	if err != nil {
		return User{}, err
	}
	req.SetBasicAuth(username, password)
	req.Header.Add("X-Plex-Client-Identifier", clientIdentifier)

	resp, err := fetchContent(req, http.StatusCreated)
	if err != nil {
		return User{}, err
	}

	user := User{}
	err = xml.Unmarshal(resp, &user)
	if err != nil {
		return User{}, err
	}

	return user, nil
}

func (user User) GetDevices() ([]Device, error) {
	req, err := http.NewRequest("GET", plexTVURL+"/devices.xml", nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("X-Plex-Client-Identifier", clientIdentifier)
	req.Header.Add("X-Plex-Token", user.AuthToken)

	content, err := fetchContent(req, http.StatusOK)

	resp := &devicesResp{}

	err = xml.Unmarshal(content, resp)
	if err != nil {
		return nil, err
	}

	for i := range resp.Devices {
		resp.Devices[i].Owner = user
	}

	return resp.Devices, nil
}

func (user User) GetServers() ([]Server, error) {
	devices, err := user.GetDevices()
	if err != nil {
		return nil, err
	}

	servers := make([]Server, 0)
	for _, device := range devices {
		server, err := device.toServer()
		if err == nil {
			servers = append(servers, server)
		}
	}

	return servers, nil
}

func (server Server) GetActivity() ([]Video, error) {
	server.PublicAddress.Path = "/status/sessions"

	req, err := http.NewRequest("GET", server.PublicAddress.String(), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("X-Plex-Client-Identifier", clientIdentifier)
	req.Header.Add("X-Plex-Token", server.Owner.AuthToken)

	resp, err := fetchContent(req, http.StatusOK)
	if err != nil {
		return nil, err
	}

	container := &sessionsResp{}
	if err := xml.Unmarshal(resp, container); err != nil {
		return nil, err
	}

	return container.Videos, nil
}

func fetchContent(req *http.Request, expectedStatusCode int) ([]byte, error) {
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != expectedStatusCode {
		return nil, errors.New("Received status: " + strconv.Itoa(resp.StatusCode) +
			" expected status: " + strconv.Itoa(expectedStatusCode))
	}

	contents, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	return contents, nil
}
<commit_msg>Codestyle nit<commit_after>package goplex

import (
	"encoding/xml"
	"errors"
	"io/ioutil"
	"net/http"
	"strconv"
)

const plexTVURL = "https://plex.tv"
const clientIdentifier = "plextrack"

type devicesResp struct {
	XMLName       xml.Name `xml:"MediaContainer"`
	PublicAddress HTTPURL  `xml:"publicAddress,attr"`
	Devices       []Device `xml:"Device"`
}

type sessionsResp struct {
	XMLName xml.Name `xml:"MediaContainer"`
	Videos  []Video  `xml:"Video"`
}

// Hook to override for tests
var client = http.DefaultClient

func GetUser(username, password string) (User, error) {
	req, err := http.NewRequest("POST", plexTVURL+"/users/sign_in.xml", nil)
	if err != nil {
		return User{}, err
	}
	req.SetBasicAuth(username, password)
	req.Header.Add("X-Plex-Client-Identifier", clientIdentifier)

	resp, err := fetchContent(req, http.StatusCreated)
	if err != nil {
		return User{}, err
	}

	user := User{}
	err = xml.Unmarshal(resp, &user)
	if err != nil {
		return User{}, err
	}

	return user, nil
}

func (user User) GetDevices() ([]Device, error) {
	req, err := http.NewRequest("GET", plexTVURL+"/devices.xml", nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("X-Plex-Client-Identifier", clientIdentifier)
	req.Header.Add("X-Plex-Token", user.AuthToken)

	content, err := fetchContent(req, http.StatusOK)

	resp := &devicesResp{}

	err = xml.Unmarshal(content, resp)
	if err != nil {
		return nil, err
	}

	for i := range resp.Devices {
		resp.Devices[i].Owner = user
	}

	return resp.Devices, nil
}

func (user User) GetServers() ([]Server, error) {
	devices, err := user.GetDevices()
	if err != nil {
		return nil, err
	}

	var servers []Server
	for _, device := range devices {
		server, err := device.toServer()
		if err == nil {
			servers = append(servers, server)
		}
	}

	return servers, nil
}

func (server Server) GetActivity() ([]Video, error) {
	server.PublicAddress.Path = "/status/sessions"

	req, err := http.NewRequest("GET", server.PublicAddress.String(), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("X-Plex-Client-Identifier", clientIdentifier)
	req.Header.Add("X-Plex-Token", server.Owner.AuthToken)

	resp, err := fetchContent(req, http.StatusOK)
	if err != nil {
		return nil, err
	}

	container := &sessionsResp{}
	if err := xml.Unmarshal(resp, container); err != nil {
		return nil, err
	}

	return container.Videos, nil
}

func fetchContent(req *http.Request, expectedStatusCode int) ([]byte, error) {
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != expectedStatusCode {
		return nil, errors.New("Received status: " + strconv.Itoa(resp.StatusCode) +
			" expected status: " + strconv.Itoa(expectedStatusCode))
	}

	contents, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	return contents, nil
}
<|endoftext|>


<commit_before>package ssa_test

// This file runs the SSA builder in sanity-checking mode on all
// packages beneath $GOROOT and prints some summary information.
//
// Run test with GOMAXPROCS=8 and CGO_ENABLED=0.  The latter cannot be
// set from the test because it's too late to stop go/build.init()
// from picking up the value from the parent's environment.

import (
	"go/build"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
	"time"

	"code.google.com/p/go.tools/importer"
	"code.google.com/p/go.tools/ssa"
)

const debugMode = false

func allPackages() []string {
	var pkgs []string
	root := filepath.Join(runtime.GOROOT(), "src/pkg") + "/"
	filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		// Prune the search if we encounter any of these names:
		switch filepath.Base(path) {
		case "testdata", ".hg":
			return filepath.SkipDir
		}
		if info.IsDir() {
			pkg := strings.TrimPrefix(path, root)
			switch pkg {
			case "builtin", "pkg", "code.google.com":
				return filepath.SkipDir // skip these subtrees
			case "":
				return nil // ignore root of tree
			}
			pkgs = append(pkgs, pkg)
		}

		return nil
	})
	return pkgs
}

func TestStdlib(t *testing.T) {
	ctxt := build.Default
	ctxt.CgoEnabled = false
	impctx := importer.Config{Loader: importer.MakeGoBuildLoader(&ctxt)}

	// Load, parse and type-check the program.
	t0 := time.Now()

	var hasErrors bool
	imp := importer.New(&impctx)
	for _, importPath := range allPackages() {
		if _, err := imp.LoadPackage(importPath); err != nil {
			t.Errorf("LoadPackage(%s): %s", importPath, err)
			hasErrors = true
		}
	}

	t1 := time.Now()

	runtime.GC()
	var memstats runtime.MemStats
	runtime.ReadMemStats(&memstats)
	alloc := memstats.Alloc

	// Create SSA packages.
	prog := ssa.NewProgram(imp.Fset, ssa.SanityCheckFunctions)
	for _, info := range imp.Packages {
		if info.Err == nil {
			prog.CreatePackage(info).SetDebugMode(debugMode)
		}
	}

	t2 := time.Now()

	// Build SSA IR... if it's safe.
	if !hasErrors {
		prog.BuildAll()
	}

	t3 := time.Now()

	runtime.GC()
	runtime.ReadMemStats(&memstats)

	numPkgs := len(prog.PackagesByPath)
	if want := 140; numPkgs < want {
		t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want)
	}

	// Dump some statistics.
	allFuncs := ssa.AllFunctions(prog)
	var numInstrs int
	for fn := range allFuncs {
		for _, b := range fn.Blocks {
			numInstrs += len(b.Instrs)
		}
	}

	t.Log("GOMAXPROCS:           ", runtime.GOMAXPROCS(0))
	t.Log("Load/parse/typecheck: ", t1.Sub(t0))
	t.Log("SSA create:           ", t2.Sub(t1))
	if !hasErrors {
		t.Log("SSA build:            ", t3.Sub(t2))
	}

	// SSA stats:
	t.Log("#Packages:            ", numPkgs)
	t.Log("#Functions:           ", len(allFuncs))
	t.Log("#Instructions:        ", numInstrs)
	t.Log("#MB:                  ", (memstats.Alloc-alloc)/1000000)
}
<commit_msg>go.tools/ssa: fix windows build<commit_after>package ssa_test

// This file runs the SSA builder in sanity-checking mode on all
// packages beneath $GOROOT and prints some summary information.
//
// Run test with GOMAXPROCS=8 and CGO_ENABLED=0.  The latter cannot be
// set from the test because it's too late to stop go/build.init()
// from picking up the value from the parent's environment.

import (
	"go/build"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
	"time"

	"code.google.com/p/go.tools/importer"
	"code.google.com/p/go.tools/ssa"
)

const debugMode = false

func allPackages() []string {
	var pkgs []string
	root := filepath.Join(runtime.GOROOT(), "src/pkg") + string(os.PathSeparator)
	filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		// Prune the search if we encounter any of these names:
		switch filepath.Base(path) {
		case "testdata", ".hg":
			return filepath.SkipDir
		}
		if info.IsDir() {
			pkg := strings.TrimPrefix(path, root)
			switch pkg {
			case "builtin", "pkg", "code.google.com":
				return filepath.SkipDir // skip these subtrees
			case "":
				return nil // ignore root of tree
			}
			pkgs = append(pkgs, pkg)
		}

		return nil
	})
	return pkgs
}

func TestStdlib(t *testing.T) {
	ctxt := build.Default
	ctxt.CgoEnabled = false
	impctx := importer.Config{Loader: importer.MakeGoBuildLoader(&ctxt)}

	// Load, parse and type-check the program.
	t0 := time.Now()

	var hasErrors bool
	imp := importer.New(&impctx)
	for _, importPath := range allPackages() {
		if _, err := imp.LoadPackage(importPath); err != nil {
			t.Errorf("LoadPackage(%s): %s", importPath, err)
			hasErrors = true
		}
	}

	t1 := time.Now()

	runtime.GC()
	var memstats runtime.MemStats
	runtime.ReadMemStats(&memstats)
	alloc := memstats.Alloc

	// Create SSA packages.
	prog := ssa.NewProgram(imp.Fset, ssa.SanityCheckFunctions)
	for _, info := range imp.Packages {
		if info.Err == nil {
			prog.CreatePackage(info).SetDebugMode(debugMode)
		}
	}

	t2 := time.Now()

	// Build SSA IR... if it's safe.
	if !hasErrors {
		prog.BuildAll()
	}

	t3 := time.Now()

	runtime.GC()
	runtime.ReadMemStats(&memstats)

	numPkgs := len(prog.PackagesByPath)
	if want := 140; numPkgs < want {
		t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want)
	}

	// Dump some statistics.
	allFuncs := ssa.AllFunctions(prog)
	var numInstrs int
	for fn := range allFuncs {
		for _, b := range fn.Blocks {
			numInstrs += len(b.Instrs)
		}
	}

	t.Log("GOMAXPROCS:           ", runtime.GOMAXPROCS(0))
	t.Log("Load/parse/typecheck: ", t1.Sub(t0))
	t.Log("SSA create:           ", t2.Sub(t1))
	if !hasErrors {
		t.Log("SSA build:            ", t3.Sub(t2))
	}

	// SSA stats:
	t.Log("#Packages:            ", numPkgs)
	t.Log("#Functions:           ", len(allFuncs))
	t.Log("#Instructions:        ", numInstrs)
	t.Log("#MB:                  ", (memstats.Alloc-alloc)/1000000)
}
<|endoftext|>


<commit_before>package angularjs

import (
	"reflect"

	"github.com/gopherjs/gopherjs/js"
	"github.com/pkg/errors"
)

var (
	injectMap = make(map[reflect.Type]*Inject)
)

// Inject represents a resource that can be injected into an controller/directive/route-resolve func
type Inject struct {
	f           func(obj *js.Object) reflect.Value
	angularName string
}

// RegisterResource registers a resource that can be injected
func RegisterResource(resourceType reflect.Type, angularName string, f func(obj *js.Object) reflect.Value) {
	injectMap[resourceType] = &Inject{
		f:           f,
		angularName: angularName,
	}
}

// GetResource returns the resource for a given reflect.Type
func GetResource(resourceType reflect.Type) *Inject {
	return injectMap[resourceType]
}

// MakeFuncInjectable returns a func that transforms *js.Object's to
// the corresponding go types.
func MakeFuncInjectable(f interface{}) (jsFunc js.S, err error) {
	angularParamNames := make(js.S, 0)
	transFormFuncs := make([]func(obj *js.Object) reflect.Value, 0)

	injects, callable, err := GetFuncInjectables(f)
	if err != nil {
		return nil, err
	}

	for _, i := range injects {
		angularParamNames = append(angularParamNames, i.angularName)
		transFormFuncs = append(transFormFuncs, i.f)
	}

	// we return an optional *js.Object here if the function returns it
	return append(angularParamNames, func(objs ...*js.Object) *js.Object {
		args := make([]reflect.Value, 0)
		for i, obj := range objs {
			args = append(args, transFormFuncs[i](obj))
		}

		ret := callable.Call(args)
		if len(ret) == 0 {
			return nil
		}

		return ret[0].Interface().(*js.Object)
	}), nil
}

// GetFuncInjectables returns the reflect.Type's resources that have to be uses
// to call the given function correctly.
func GetFuncInjectables(f interface{}) (injects []*Inject, callable reflect.Value, err error) {
	callable = reflect.ValueOf(f)
	if callable.Kind() != reflect.Func {
		return nil, callable, errors.New("Only func's can be made injectable")
	}

	for i := 0; i < callable.Type().NumIn(); i++ {
		arg := callable.Type().In(i)

		if injector := GetResource(arg); injector != nil {
			injects = append(injects, injector)
		} else {
			return nil, callable, errors.Errorf("no resource found for type: %v", arg)
		}
	}

	return injects, callable, nil
}
<commit_msg>Improved comments for func<commit_after>package angularjs

import (
	"reflect"

	"github.com/gopherjs/gopherjs/js"
	"github.com/pkg/errors"
)

var (
	injectMap = make(map[reflect.Type]*Inject)
)

// Inject represents a resource that can be injected into an controller/directive/route-resolve func
type Inject struct {
	f           func(obj *js.Object) reflect.Value
	angularName string
}

// RegisterResource registers a resource that can be injected
func RegisterResource(resourceType reflect.Type, angularName string, f func(obj *js.Object) reflect.Value) {
	injectMap[resourceType] = &Inject{
		f:           f,
		angularName: angularName,
	}
}

// GetResource returns the resource for a given reflect.Type
func GetResource(resourceType reflect.Type) *Inject {
	return injectMap[resourceType]
}

// MakeFuncInjectable returns a func that transforms *js.Object's to
// the corresponding go types.
func MakeFuncInjectable(f interface{}) (jsFunc js.S, err error) {
	angularParamNames := make(js.S, 0)
	transFormFuncs := make([]func(obj *js.Object) reflect.Value, 0)

	injects, callable, err := GetFuncInjectables(f)
	if err != nil {
		return nil, err
	}

	for _, i := range injects {
		angularParamNames = append(angularParamNames, i.angularName)
		transFormFuncs = append(transFormFuncs, i.f)
	}

	// we return an optional *js.Object here if the function returns it
	return append(angularParamNames, func(objs ...*js.Object) *js.Object {
		args := make([]reflect.Value, 0)
		for i, obj := range objs {
			args = append(args, transFormFuncs[i](obj))
		}

		ret := callable.Call(args)
		if len(ret) == 0 {
			return nil
		}

		return ret[0].Interface().(*js.Object)
	}), nil
}

// GetFuncInjectables reads the function arguments and transforms them to *Inject resources.
// If an arguments is not found the returns an error.
func GetFuncInjectables(f interface{}) (injects []*Inject, callable reflect.Value, err error) {
	callable = reflect.ValueOf(f)
	if callable.Kind() != reflect.Func {
		return nil, callable, errors.New("Only func's can be made injectable")
	}

	for i := 0; i < callable.Type().NumIn(); i++ {
		arg := callable.Type().In(i)

		if injector := GetResource(arg); injector != nil {
			injects = append(injects, injector)
		} else {
			return nil, callable, errors.Errorf("no resource found for type: %v", arg)
		}
	}

	return injects, callable, nil
}
<|endoftext|>


<commit_before>package zenodb

import (
	"fmt"
	"hash"
	"reflect"
	"strings"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/getlantern/bytemap"
	"github.com/getlantern/errors"
	"github.com/getlantern/wal"
	"github.com/getlantern/zenodb/encoding"
)

func (db *DB) Insert(stream string, ts time.Time, dims map[string]interface{}, vals map[string]interface{}) error {
	return db.InsertRaw(stream, ts, bytemap.New(dims), bytemap.New(vals))
}

func (db *DB) InsertRaw(stream string, ts time.Time, dims bytemap.ByteMap, vals bytemap.ByteMap) error {
	if db.opts.Follow != nil {
		return errors.New("Declining to insert data directly to follower")
	}

	stream = strings.TrimSpace(strings.ToLower(stream))
	db.tablesMutex.Lock()
	w := db.streams[stream]
	db.tablesMutex.Unlock()
	if w == nil {
		return fmt.Errorf("No wal found for stream %v", stream)
	}

	if len(db.opts.WhitelistedDimensions) > 0 {
		dims = dims.Slice(db.opts.WhitelistedDimensions...)
	}

	tsd := make([]byte, encoding.Width64bits)
	encoding.EncodeTime(tsd, ts)
	dimsLen := make([]byte, encoding.Width32bits)
	encoding.WriteInt32(dimsLen, len(dims))
	valsLen := make([]byte, encoding.Width32bits)
	encoding.WriteInt32(valsLen, len(vals))
	if db.log.IsTraceEnabled() {
		db.log.Tracef("Writing to wal with dims: %v", bytemap.ByteMap(dims).AsMap())
	}
	err := w.Write(tsd, dimsLen, dims, valsLen, vals)
	if err != nil {
		db.log.Error(err)
	}
	return err
}

type walRead struct {
	data   []byte
	offset wal.Offset
	source int
}

func (t *table) processWALInserts() {
	in := make(chan *walRead)
	t.db.Go(func(stop <-chan interface{}) {
		t.processInserts(in, stop)
	})

	for {
		data, err := t.wal.Read()
		if err != nil {
			t.db.Panic(fmt.Errorf("Unable to read from WAL: %v", err))
		}
		in <- &walRead{data, t.wal.Offset(), 0}
	}
}

func (t *table) processInserts(in chan *walRead, stop <-chan interface{}) {
	isFollower := t.db.opts.Follow != nil
	start := time.Now()
	inserted := 0
	skipped := 0
	bytesRead := 0

	h := partitionHash()
loop:
	for {
		select {
		case <-stop:
			return
		case read := <-in:
			if read.data == nil {
				// Ignore empty data
				continue loop
			}
			bytesRead += len(read.data)
			if t.insert(read.data, isFollower, h, read.offset, read.source) {
				inserted++
			} else {
				// Did not insert (probably due to WHERE clause)
				t.skip(read.offset, read.source)
				skipped++
			}
			t.db.walBuffers.Put(read.data)
			delta := time.Now().Sub(start)
			if delta > 1*time.Minute {
				t.log.Debugf("Read %v at %v per second", humanize.Bytes(uint64(bytesRead)), humanize.Bytes(uint64(float64(bytesRead)/delta.Seconds())))
				t.log.Debugf("Inserted %v points at %v per second", humanize.Comma(int64(inserted)), humanize.Commaf(float64(inserted)/delta.Seconds()))
				t.log.Debugf("Skipped %v points at %v per second", humanize.Comma(int64(skipped)), humanize.Commaf(float64(skipped)/delta.Seconds()))
				inserted = 0
				skipped = 0
				bytesRead = 0
				start = time.Now()
			}
		}
	}
}

func (t *table) insert(data []byte, isFollower bool, h hash.Hash32, offset wal.Offset, source int) bool {
	defer func() {
		p := recover()
		if p != nil {
			t.log.Errorf("Panic in inserting: %v", p)
		}
	}()

	tsd, remain := encoding.Read(data, encoding.Width64bits)
	ts := encoding.TimeFromBytes(tsd)
	if ts.Before(t.truncateBefore()) {
		// Ignore old data
		return false
	}
	dimsLen, remain := encoding.ReadInt32(remain)
	dims, remain := encoding.Read(remain, dimsLen)
	if isFollower && !t.db.inPartition(h, dims, t.PartitionBy, t.db.opts.Partition) {
		// data not relevant to follower on this table
		return false
	}

	valsLen, remain := encoding.ReadInt32(remain)
	vals, _ := encoding.Read(remain, valsLen)
	// Split the dims and vals so that holding on to one doesn't force holding on
	// to the other. Also, we need copies for both because the WAL read buffer
	// will change on next call to wal.Read().
	dimsBM := make(bytemap.ByteMap, len(dims))
	if t.log.IsTraceEnabled() {
		t.log.Tracef("Dims are %v", dimsBM.AsMap())
	}
	valsBM := make(bytemap.ByteMap, len(vals))
	copy(dimsBM, dims)
	copy(valsBM, vals)
	return t.doInsert(ts, dimsBM, valsBM, offset, source)
}

// Skip informs the table of a new offset so that we can store it
func (t *table) skip(offset wal.Offset, source int) {
	t.rowStore.insert(&insert{nil, nil, nil, offset, source})
}

func (t *table) doInsert(ts time.Time, dims bytemap.ByteMap, vals bytemap.ByteMap, offset wal.Offset, source int) bool {
	where := t.getWhere()

	if where != nil {
		ok := where.Eval(dims)
		if !ok.(bool) {
			if t.log.IsTraceEnabled() {
				t.log.Tracef("Filtering out inbound point at %v due to %v: %v", ts, where, dims.AsMap())
			}
			t.statsMutex.Lock()
			t.stats.FilteredPoints++
			t.statsMutex.Unlock()
			return false
		}
	}
	t.db.clock.Advance(ts)

	if t.log.IsTraceEnabled() {
		t.log.Tracef("Including inbound point at %v: %v", ts, dims.AsMap())
	}

	var key bytemap.ByteMap
	if len(t.GroupBy) == 0 {
		key = dims
	} else {
		// Reslice dimensions
		names := make([]string, 0, len(t.GroupBy))
		values := make([]interface{}, 0, len(t.GroupBy))
		for _, groupBy := range t.GroupBy {
			val := groupBy.Expr.Eval(dims)
			if val != nil {
				names = append(names, groupBy.Name)
				values = append(values, val)
			}
		}
		key = bytemap.FromSortedKeysAndValues(names, values)
	}

	// Do separate inserts rows for array values if necessary
	var additionalVals []bytemap.ByteMap
	hasMainValue := false
	mainVals := bytemap.Build(func(_include func(string, interface{})) {
		include := func(key string, val float64) {
			_include(key, val)
			hasMainValue = true
		}
		vals.IterateValues(func(key string, value interface{}) bool {
			switch v := value.(type) {
			case float64:
				include(key, v)
			case int:
				include(key, float64(v))
			case []float64:
				// include first value with main vals
				include(key, v[0])
				// do separate inserts for additional values
				for i := 1; i < len(v); i++ {
					subVals := bytemap.Build(func(subInclude func(string, interface{})) {
						subInclude(key, v[i])
					}, nil, true)
					additionalVals = append(additionalVals, subVals)
				}
			case []int:
				// include first value with main vals
				include(key, float64(v[0]))
				// do separate inserts for additional values
				for i := 1; i < len(v); i++ {
					subVals := bytemap.Build(func(subInclude func(string, interface{})) {
						subInclude(key, float64(v[i]))
					}, nil, true)
					additionalVals = append(additionalVals, subVals)
				}
			default:
				t.log.Errorf("Key %v contained value '%v' of unsupported type %v, ignoring", key, value, reflect.TypeOf(value))
			}
			return true
		})
	}, nil, true)

	t.db.capMemorySize(true)
	inserted := len(additionalVals)
	if hasMainValue {
		t.rowStore.insert(&insert{key, encoding.NewTSParams(ts, mainVals), dims, offset, source})
		inserted++
	}
	for _, subVals := range additionalVals {
		t.rowStore.insert(&insert{key, encoding.NewTSParams(ts, subVals), dims, offset, source})
	}
	t.statsMutex.Lock()
	t.stats.InsertedPoints += int64(inserted)
	t.statsMutex.Unlock()

	return true
}

func (t *table) recordQueued() {
	t.statsMutex.Lock()
	t.stats.QueuedPoints++
	t.statsMutex.Unlock()
}
<commit_msg>More trace logging<commit_after>package zenodb

import (
	"fmt"
	"hash"
	"reflect"
	"strings"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/getlantern/bytemap"
	"github.com/getlantern/errors"
	"github.com/getlantern/wal"
	"github.com/getlantern/zenodb/encoding"
)

func (db *DB) Insert(stream string, ts time.Time, dims map[string]interface{}, vals map[string]interface{}) error {
	return db.InsertRaw(stream, ts, bytemap.New(dims), bytemap.New(vals))
}

func (db *DB) InsertRaw(stream string, ts time.Time, dims bytemap.ByteMap, vals bytemap.ByteMap) error {
	if db.opts.Follow != nil {
		return errors.New("Declining to insert data directly to follower")
	}

	stream = strings.TrimSpace(strings.ToLower(stream))
	db.tablesMutex.Lock()
	w := db.streams[stream]
	db.tablesMutex.Unlock()
	if w == nil {
		return fmt.Errorf("No wal found for stream %v", stream)
	}

	if len(db.opts.WhitelistedDimensions) > 0 {
		dims = dims.Slice(db.opts.WhitelistedDimensions...)
	}

	tsd := make([]byte, encoding.Width64bits)
	encoding.EncodeTime(tsd, ts)
	dimsLen := make([]byte, encoding.Width32bits)
	encoding.WriteInt32(dimsLen, len(dims))
	valsLen := make([]byte, encoding.Width32bits)
	encoding.WriteInt32(valsLen, len(vals))
	if db.log.IsTraceEnabled() {
		db.log.Tracef("Writing to wal with dims: %v", bytemap.ByteMap(dims).AsMap())
	}
	err := w.Write(tsd, dimsLen, dims, valsLen, vals)
	if err != nil {
		db.log.Error(err)
	}
	return err
}

type walRead struct {
	data   []byte
	offset wal.Offset
	source int
}

func (t *table) processWALInserts() {
	in := make(chan *walRead)
	t.db.Go(func(stop <-chan interface{}) {
		t.processInserts(in, stop)
	})

	for {
		data, err := t.wal.Read()
		if err != nil {
			t.db.Panic(fmt.Errorf("Unable to read from WAL: %v", err))
		}
		in <- &walRead{data, t.wal.Offset(), 0}
	}
}

func (t *table) processInserts(in chan *walRead, stop <-chan interface{}) {
	isFollower := t.db.opts.Follow != nil
	start := time.Now()
	inserted := 0
	skipped := 0
	bytesRead := 0

	h := partitionHash()
loop:
	for {
		select {
		case <-stop:
			return
		case read := <-in:
			if read.data == nil {
				// Ignore empty data
				continue loop
			}
			bytesRead += len(read.data)
			if t.insert(read.data, isFollower, h, read.offset, read.source) {
				inserted++
			} else {
				// Did not insert (probably due to WHERE clause)
				t.skip(read.offset, read.source)
				skipped++
			}
			t.db.walBuffers.Put(read.data)
			delta := time.Now().Sub(start)
			if delta > 1*time.Minute {
				t.log.Debugf("Read %v at %v per second", humanize.Bytes(uint64(bytesRead)), humanize.Bytes(uint64(float64(bytesRead)/delta.Seconds())))
				t.log.Debugf("Inserted %v points at %v per second", humanize.Comma(int64(inserted)), humanize.Commaf(float64(inserted)/delta.Seconds()))
				t.log.Debugf("Skipped %v points at %v per second", humanize.Comma(int64(skipped)), humanize.Commaf(float64(skipped)/delta.Seconds()))
				inserted = 0
				skipped = 0
				bytesRead = 0
				start = time.Now()
			}
		}
	}
}

func (t *table) insert(data []byte, isFollower bool, h hash.Hash32, offset wal.Offset, source int) bool {
	defer func() {
		p := recover()
		if p != nil {
			t.log.Errorf("Panic in inserting: %v", p)
		}
	}()

	tsd, remain := encoding.Read(data, encoding.Width64bits)
	ts := encoding.TimeFromBytes(tsd)
	if ts.Before(t.truncateBefore()) {
		// Ignore old data
		return false
	}
	dimsLen, remain := encoding.ReadInt32(remain)
	dims, remain := encoding.Read(remain, dimsLen)
	if isFollower && !t.db.inPartition(h, dims, t.PartitionBy, t.db.opts.Partition) {
		// data not relevant to follower on this table
		return false
	}

	valsLen, remain := encoding.ReadInt32(remain)
	vals, _ := encoding.Read(remain, valsLen)
	// Split the dims and vals so that holding on to one doesn't force holding on
	// to the other. Also, we need copies for both because the WAL read buffer
	// will change on next call to wal.Read().
	dimsBM := make(bytemap.ByteMap, len(dims))
	valsBM := make(bytemap.ByteMap, len(vals))
	if t.log.IsTraceEnabled() {
		t.log.Tracef("Vals are %v", valsBM.AsMap())
		if len(dims) > 0 {
			t.log.Tracef("Dims are %v", dimsBM.AsMap())
		}
	}
	copy(dimsBM, dims)
	copy(valsBM, vals)
	return t.doInsert(ts, dimsBM, valsBM, offset, source)
}

// Skip informs the table of a new offset so that we can store it
func (t *table) skip(offset wal.Offset, source int) {
	t.rowStore.insert(&insert{nil, nil, nil, offset, source})
}

func (t *table) doInsert(ts time.Time, dims bytemap.ByteMap, vals bytemap.ByteMap, offset wal.Offset, source int) bool {
	where := t.getWhere()

	if where != nil {
		ok := where.Eval(dims)
		if !ok.(bool) {
			if t.log.IsTraceEnabled() {
				t.log.Tracef("Filtering out inbound point at %v due to %v: %v", ts, where, dims.AsMap())
			}
			t.statsMutex.Lock()
			t.stats.FilteredPoints++
			t.statsMutex.Unlock()
			return false
		}
	}
	t.db.clock.Advance(ts)

	if t.log.IsTraceEnabled() {
		t.log.Tracef("Including inbound point at %v: %v", ts, dims.AsMap())
	}

	var key bytemap.ByteMap
	if len(t.GroupBy) == 0 {
		key = dims
	} else {
		// Reslice dimensions
		names := make([]string, 0, len(t.GroupBy))
		values := make([]interface{}, 0, len(t.GroupBy))
		for _, groupBy := range t.GroupBy {
			val := groupBy.Expr.Eval(dims)
			if val != nil {
				names = append(names, groupBy.Name)
				values = append(values, val)
			}
		}
		key = bytemap.FromSortedKeysAndValues(names, values)
	}

	// Do separate inserts rows for array values if necessary
	var additionalVals []bytemap.ByteMap
	hasMainValue := false
	mainVals := bytemap.Build(func(_include func(string, interface{})) {
		include := func(key string, val float64) {
			_include(key, val)
			hasMainValue = true
		}
		vals.IterateValues(func(key string, value interface{}) bool {
			switch v := value.(type) {
			case float64:
				include(key, v)
			case int:
				include(key, float64(v))
			case []float64:
				// include first value with main vals
				include(key, v[0])
				// do separate inserts for additional values
				for i := 1; i < len(v); i++ {
					subVals := bytemap.Build(func(subInclude func(string, interface{})) {
						subInclude(key, v[i])
					}, nil, true)
					additionalVals = append(additionalVals, subVals)
				}
			case []int:
				// include first value with main vals
				include(key, float64(v[0]))
				// do separate inserts for additional values
				for i := 1; i < len(v); i++ {
					subVals := bytemap.Build(func(subInclude func(string, interface{})) {
						subInclude(key, float64(v[i]))
					}, nil, true)
					additionalVals = append(additionalVals, subVals)
				}
			default:
				t.log.Errorf("Key %v contained value '%v' of unsupported type %v, ignoring", key, value, reflect.TypeOf(value))
			}
			return true
		})
	}, nil, true)

	t.db.capMemorySize(true)
	inserted := len(additionalVals)
	if hasMainValue {
		t.rowStore.insert(&insert{key, encoding.NewTSParams(ts, mainVals), dims, offset, source})
		inserted++
	}
	for _, subVals := range additionalVals {
		t.rowStore.insert(&insert{key, encoding.NewTSParams(ts, subVals), dims, offset, source})
	}
	t.statsMutex.Lock()
	t.stats.InsertedPoints += int64(inserted)
	t.statsMutex.Unlock()

	return true
}

func (t *table) recordQueued() {
	t.statsMutex.Lock()
	t.stats.QueuedPoints++
	t.statsMutex.Unlock()
}
<|endoftext|>


<commit_before>// Copyright 2015-2016, Cyrill @ Schumacher.fm and the CoreStore contributors
//
// 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 dbr

//import (
//	"fmt"
//	"strings"
//	// "github.com/ugorji/go/codec"
//)
//
//// removed because the binary grows for 5MB. will be added once needed.
//// var _ codec.Selfer = (*NullString)(nil)
//
//// GoString satisfies the interface fmt.GoStringer when using %#v in Printf methods.
//// Returns
//// 		dbr.NewNullString(`...`,bool)
//func (ns NullString) GoString() string {
//	if ns.Valid && strings.ContainsRune(ns.String, '`') {
//		// `This is my`string`
//		ns.String = strings.Join(strings.Split(ns.String, "`"), "`+\"`\"+`")
//		// `This is my`+"`"+`string`
//	}
//
//	ns.String = "`" + ns.String + "`"
//	if !ns.Valid {
//		ns.String = "nil"
//	}
//	return fmt.Sprintf("dbr.NewNullString(%s)", ns.String)
//}

//// CodecEncodeSelf for ugorji.go codec package
//func (n NullString) CodecEncodeSelf(e *codec.Encoder) {
//	if err := e.Encode(n.String); err != nil {
//		PkgLog.Debug("dbr.NullString.CodecEncodeSelf", "err", err, "n", n)
//	}
//}
//
//// CodecDecodeSelf  for ugorji.go codec package @todo write test ... not sure if ok
//func (n *NullString) CodecDecodeSelf(d *codec.Decoder) {
//	if err := d.Decode(&n.String); err != nil {
//		PkgLog.Debug("dbr.NullString.CodecDecodeSelf", "err", err, "n", n)
//	}
//	// think about empty string and Valid value ...
//}
//
//// CodecEncodeSelf for ugorji.go codec package
//func (n *NullInt64) CodecEncodeSelf(e *codec.Encoder) {
//	if err := e.Encode(n.Int64); err != nil {
//		PkgLog.Debug("dbr.NullInt64.CodecEncodeSelf", "err", err, "n", n)
//	}
//}
//
//// CodecDecodeSelf  for ugorji.go codec package @todo write test ... not sure if ok
//func (n *NullInt64) CodecDecodeSelf(d *codec.Decoder) {
//	if err := d.Decode(&n.Int64); err != nil {
//		PkgLog.Debug("dbr.NullInt64.CodecDecodeSelf", "err", err, "n", n)
//	}
//}
//
//// CodecEncodeSelf for ugorji.go codec package
//func (n NullFloat64) CodecEncodeSelf(e *codec.Encoder) {
//	if err := e.Encode(n.Float64); err != nil {
//		PkgLog.Debug("dbr.NullFloat64.CodecEncodeSelf", "err", err, "n", n)
//	}
//}
//
//// CodecDecodeSelf  for ugorji.go codec package @todo write test ... not sure if ok
//func (n *NullFloat64) CodecDecodeSelf(d *codec.Decoder) {
//	if err := d.Decode(&n.Float64); err != nil {
//		PkgLog.Debug("dbr.NullFloat64.CodecDecodeSelf", "err", err, "n", n)
//	}
//}
//
//// CodecEncodeSelf for ugorji.go codec package
//func (n NullTime) CodecEncodeSelf(e *codec.Encoder) {
//	if err := e.Encode(n.Time); err != nil {
//		PkgLog.Debug("dbr.NullTime.CodecEncodeSelf", "err", err, "n", n)
//	}
//}
//
//// CodecDecodeSelf  for ugorji.go codec package @todo write test ... not sure if ok
//func (n *NullTime) CodecDecodeSelf(d *codec.Decoder) {
//	if err := d.Decode(&n.Time); err != nil {
//		PkgLog.Debug("dbr.NullTime.CodecDecodeSelf", "err", err, "n", n)
//	}
//}
//
//// CodecEncodeSelf for ugorji.go codec package
//func (n NullBool) CodecEncodeSelf(e *codec.Encoder) {
//	if err := e.Encode(n.Bool); err != nil {
//		PkgLog.Debug("dbr.NullBool.CodecEncodeSelf", "err", err, "n", n)
//	}
//}
//
//// CodecDecodeSelf  for ugorji.go codec package @todo write test ... not sure if ok
//func (n *NullBool) CodecDecodeSelf(d *codec.Decoder) {
//	if err := d.Decode(&n.Bool); err != nil {
//		PkgLog.Debug("dbr.NullBool.CodecDecodeSelf", "err", err, "n", n)
//	}
//}
<commit_msg>storage/dbr: Remove custom corestore types and move them into util/null<commit_after><|endoftext|>


<commit_before>package storage

import (
	"archive/zip"
	"fmt"
	"github.com/viant/toolbox"
	"io"
	"path"
	"strings"
)

type CopyHandler func(sourceObject Object, source io.Reader, destinationService Service, destinationURL string) error
type ModificationHandler func(reader io.Reader) (io.Reader, error)

func urlPath(URL string) string {
	var result = URL
	schemaPosition := strings.Index(URL, "://")
	if schemaPosition != -1 {
		result = string(URL[schemaPosition+3:])
	}
	pathRoot := strings.Index(result, "/")
	if pathRoot > 0 {
		result = string(result[pathRoot:])
	}
	if strings.HasSuffix(result, "/") {
		result = string(result[:len(result)-1])
	}

	return result
}

func copy(sourceService Service, sourceURL string, destinationService Service, destinationURL string, modifyContentHandler ModificationHandler, subPath string, copyHandler CopyHandler) error {
	sourceListURL := sourceURL
	if subPath != "" {
		sourceListURL = toolbox.URLPathJoin(sourceURL, subPath)
	}
	objects, err := sourceService.List(sourceListURL)
	var objectRelativePath string
	sourceURLPath := urlPath(sourceURL)
	for _, object := range objects {
		var objectURLPath = urlPath(object.URL())
		if object.IsFolder() {

			if sourceURLPath == objectURLPath {
				continue
			}
			if subPath != "" && objectURLPath == toolbox.URLPathJoin(sourceURLPath, subPath) {
				continue
			}
		}

		if len(objectURLPath) > len(sourceURLPath) {
			objectRelativePath = objectURLPath[len(sourceURLPath):]
			if strings.HasPrefix(objectRelativePath, "/") {
				objectRelativePath = string(objectRelativePath[1:])
			}
		}

		var destinationObjectURL = destinationURL
		if objectRelativePath != "" {
			destinationObjectURL = toolbox.URLPathJoin(destinationURL, objectRelativePath)
		}
		var reader io.Reader
		if object.IsContent() {
			reader, err = sourceService.Download(object)
			if err != nil {
				err = fmt.Errorf("Unable download, %v -> %v, %v", object.URL(), destinationObjectURL, err)
				return err
			}
			if modifyContentHandler != nil {
				reader, err = modifyContentHandler(reader)
				if err != nil {
					err = fmt.Errorf("Unable modify content, %v %v %v", object.URL(), destinationObjectURL, err)
					return err
				}
			}
		if subPath == "" {
				_, sourceName := path.Split(object.URL())
				_, destinationName := path.Split(destinationURL)
				if strings.HasSuffix(destinationObjectURL, "/") {
					destinationObjectURL = toolbox.URLPathJoin(destinationObjectURL, sourceName)
				} else {
					destinationObject, _ := destinationService.StorageObject(destinationObjectURL)
					if destinationObject != nil && destinationObject.IsFolder() {
						destinationObjectURL = toolbox.URLPathJoin(destinationObjectURL, sourceName)
					} else if destinationName != sourceName {
						if !strings.Contains(destinationName, ".") {
							destinationObjectURL = toolbox.URLPathJoin(destinationURL, sourceName)
						}

					}
				}
			}
			err = copyHandler(object, reader, destinationService, destinationObjectURL)
			if err != nil {
				return err
			}

		} else {
			err = copy(sourceService, sourceURL, destinationService, destinationURL, modifyContentHandler, objectRelativePath, copyHandler)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func copySourceToDestination(sourceObject Object, reader io.Reader, destinationService Service, destinationURL string) error {
	err := destinationService.Upload(destinationURL, reader)
	if err != nil {
		err = fmt.Errorf("Unable upload, %v %v %v", sourceObject.URL(), destinationURL, err)
	}
	return err
}

func addPathIfNeeded(directories map[string]bool, path string, archive zip.Writer) {
	if path == "" {
		return
	}
	if _, has := directories[path]; has {
		return
	}

}

func getArchiveCopyHandler(archive zip.Writer, parentURL string) CopyHandler {
	var directories = make(map[string]bool)
	return func(sourceObject Object, reader io.Reader, destinationService Service, destinationURL string) error {
		var _, relativePath = toolbox.URLSplit(destinationURL)
		if destinationURL != parentURL {
			relativePath = strings.Replace(destinationURL, parentURL, "", 1)
			var parent, _ = path.Split(relativePath)
			addPathIfNeeded(directories, parent, archive)
		}
		header, err := zip.FileInfoHeader(sourceObject.FileInfo())
		if err != nil {
			return err
		}
		header.Method = zip.Deflate
		header.Name = relativePath
		writer, err := archive.CreateHeader(header)
		if err != nil {
			return err
		}
		_, err = io.Copy(writer, reader)
		return err
	}
}

//Copy downloads objects from source URL to upload them to destination URL.
func Copy(sourceService Service, sourceURL string, destinationService Service, destinationURL string, modifyContentHandler ModificationHandler, copyHandler CopyHandler) (err error) {
	if copyHandler == nil {
		copyHandler = copySourceToDestination
	}
	err = copy(sourceService, sourceURL, destinationService, destinationURL, modifyContentHandler, "", copyHandler)
	if err != nil {
		err = fmt.Errorf("failed to copy %v -> %v: %v", sourceURL, destinationURL, err)
	}
	return err
}
<commit_msg>lowercased error message<commit_after>package storage

import (
	"archive/zip"
	"fmt"
	"github.com/viant/toolbox"
	"io"
	"path"
	"strings"
)

type CopyHandler func(sourceObject Object, source io.Reader, destinationService Service, destinationURL string) error
type ModificationHandler func(reader io.Reader) (io.Reader, error)

func urlPath(URL string) string {
	var result = URL
	schemaPosition := strings.Index(URL, "://")
	if schemaPosition != -1 {
		result = string(URL[schemaPosition+3:])
	}
	pathRoot := strings.Index(result, "/")
	if pathRoot > 0 {
		result = string(result[pathRoot:])
	}
	if strings.HasSuffix(result, "/") {
		result = string(result[:len(result)-1])
	}

	return result
}

func copy(sourceService Service, sourceURL string, destinationService Service, destinationURL string, modifyContentHandler ModificationHandler, subPath string, copyHandler CopyHandler) error {
	sourceListURL := sourceURL
	if subPath != "" {
		sourceListURL = toolbox.URLPathJoin(sourceURL, subPath)
	}
	objects, err := sourceService.List(sourceListURL)
	var objectRelativePath string
	sourceURLPath := urlPath(sourceURL)
	for _, object := range objects {
		var objectURLPath = urlPath(object.URL())
		if object.IsFolder() {

			if sourceURLPath == objectURLPath {
				continue
			}
			if subPath != "" && objectURLPath == toolbox.URLPathJoin(sourceURLPath, subPath) {
				continue
			}
		}

		if len(objectURLPath) > len(sourceURLPath) {
			objectRelativePath = objectURLPath[len(sourceURLPath):]
			if strings.HasPrefix(objectRelativePath, "/") {
				objectRelativePath = string(objectRelativePath[1:])
			}
		}

		var destinationObjectURL = destinationURL
		if objectRelativePath != "" {
			destinationObjectURL = toolbox.URLPathJoin(destinationURL, objectRelativePath)
		}
		var reader io.Reader
		if object.IsContent() {
			reader, err = sourceService.Download(object)
			if err != nil {
				err = fmt.Errorf("Unable download, %v -> %v, %v", object.URL(), destinationObjectURL, err)
				return err
			}
			if modifyContentHandler != nil {
				reader, err = modifyContentHandler(reader)
				if err != nil {
					err = fmt.Errorf("Unable modify content, %v %v %v", object.URL(), destinationObjectURL, err)
					return err
				}
			}
		if subPath == "" {
				_, sourceName := path.Split(object.URL())
				_, destinationName := path.Split(destinationURL)
				if strings.HasSuffix(destinationObjectURL, "/") {
					destinationObjectURL = toolbox.URLPathJoin(destinationObjectURL, sourceName)
				} else {
					destinationObject, _ := destinationService.StorageObject(destinationObjectURL)
					if destinationObject != nil && destinationObject.IsFolder() {
						destinationObjectURL = toolbox.URLPathJoin(destinationObjectURL, sourceName)
					} else if destinationName != sourceName {
						if !strings.Contains(destinationName, ".") {
							destinationObjectURL = toolbox.URLPathJoin(destinationURL, sourceName)
						}

					}
				}
			}
			err = copyHandler(object, reader, destinationService, destinationObjectURL)
			if err != nil {
				return err
			}

		} else {
			err = copy(sourceService, sourceURL, destinationService, destinationURL, modifyContentHandler, objectRelativePath, copyHandler)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func copySourceToDestination(sourceObject Object, reader io.Reader, destinationService Service, destinationURL string) error {
	err := destinationService.Upload(destinationURL, reader)
	if err != nil {
		err = fmt.Errorf("unable upload, %v %v %v", sourceObject.URL(), destinationURL, err)
	}
	return err
}

func addPathIfNeeded(directories map[string]bool, path string, archive zip.Writer) {
	if path == "" {
		return
	}
	if _, has := directories[path]; has {
		return
	}

}

func getArchiveCopyHandler(archive zip.Writer, parentURL string) CopyHandler {
	var directories = make(map[string]bool)
	return func(sourceObject Object, reader io.Reader, destinationService Service, destinationURL string) error {
		var _, relativePath = toolbox.URLSplit(destinationURL)
		if destinationURL != parentURL {
			relativePath = strings.Replace(destinationURL, parentURL, "", 1)
			var parent, _ = path.Split(relativePath)
			addPathIfNeeded(directories, parent, archive)
		}
		header, err := zip.FileInfoHeader(sourceObject.FileInfo())
		if err != nil {
			return err
		}
		header.Method = zip.Deflate
		header.Name = relativePath
		writer, err := archive.CreateHeader(header)
		if err != nil {
			return err
		}
		_, err = io.Copy(writer, reader)
		return err
	}
}

//Copy downloads objects from source URL to upload them to destination URL.
func Copy(sourceService Service, sourceURL string, destinationService Service, destinationURL string, modifyContentHandler ModificationHandler, copyHandler CopyHandler) (err error) {
	if copyHandler == nil {
		copyHandler = copySourceToDestination
	}
	err = copy(sourceService, sourceURL, destinationService, destinationURL, modifyContentHandler, "", copyHandler)
	if err != nil {
		err = fmt.Errorf("failed to copy %v -> %v: %v", sourceURL, destinationURL, err)
	}
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	//"github.com/udhos/gowut/gwu"
	"github.com/icza/gowut/gwu"
	"log"
	//"math/rand"
	"os"
	//"strconv"
)

const appName = "jazigo"

var logger = log.New(os.Stdout, "", log.LstdFlags)

func main() {

	appAddr := "0.0.0.0:8080"
	serverName := fmt.Sprintf("%s application", appName)

	// Create GUI server
	server := gwu.NewServer(appName, appAddr)
	//folder := "./tls/"
	//server := gwu.NewServerTLS(appName, appAddr, folder+"cert.pem", folder+"key.pem")
	server.SetText(serverName)

	buildHomeWin(server)
	buildLoginWin(server)

	server.SetLogger(logger)

	// Start GUI server
	if err := server.Start(); err != nil {
		logger.Println("jazigo main: Cound not start GUI server:", err)
		return
	}
}

func buildHomeWin(s gwu.Session) {

	winName := fmt.Sprintf("%s home", appName)
	win := gwu.NewWindow("home", winName)

	l := gwu.NewLabel(fmt.Sprintf("%s home", winName))

	l.Style().SetFontWeight(gwu.FontWeightBold).SetFontSize("130%")
	win.Add(l)
	win.Add(gwu.NewLabel("Click on the button to login:"))
	b := gwu.NewButton("Login")
	b.AddEHandlerFunc(func(e gwu.Event) {
		e.ReloadWin("login")
	}, gwu.ETypeClick)
	win.Add(b)

	s.AddWin(win)
}

func buildLoginWin(s gwu.Session) {

	winName := fmt.Sprintf("%s login", appName)
	win := gwu.NewWindow("login", winName)

	win.Style().SetFullSize()
	win.SetAlign(gwu.HACenter, gwu.VAMiddle)

	p := gwu.NewPanel()
	p.SetHAlign(gwu.HACenter)
	p.SetCellPadding(2)

	l := gwu.NewLabel(winName)
	l.Style().SetFontWeight(gwu.FontWeightBold).SetFontSize("150%")
	p.Add(l)
	l = gwu.NewLabel("Login")
	l.Style().SetFontWeight(gwu.FontWeightBold).SetFontSize("130%")
	p.Add(l)
	p.CellFmt(l).Style().SetBorder2(1, gwu.BrdStyleDashed, gwu.ClrNavy)
	l = gwu.NewLabel("user/pass: admin/a")
	l.Style().SetFontSize("80%").SetFontStyle(gwu.FontStyleItalic)
	p.Add(l)

	errL := gwu.NewLabel("")
	errL.Style().SetColor(gwu.ClrRed)
	p.Add(errL)

	table := gwu.NewTable()
	table.SetCellPadding(2)
	table.EnsureSize(2, 2)
	table.Add(gwu.NewLabel("Username:"), 0, 0)
	tb := gwu.NewTextBox("")

	tb.Style().SetWidthPx(160)
	table.Add(tb, 0, 1)
	table.Add(gwu.NewLabel("Password:"), 1, 0)
	pb := gwu.NewPasswBox("")

	pb.Style().SetWidthPx(160)
	table.Add(pb, 1, 1)
	p.Add(table)
	b := gwu.NewButton("OK")

	p.Add(b)
	l = gwu.NewLabel("")
	p.Add(l)
	p.CellFmt(l).Style().SetHeightPx(200)

	loginHandler := func(e gwu.Event) {
		user := tb.Text()
		if loginAuth(user, pb.Text()) {

			// FIXME: Should clear username/password fields?

			newSession := e.NewSession()
			newSession.SetAttr("username", user)

			remoteAddr := "(remoteAddr?)"
			if hrr, ok := e.(gwu.HasRequestResponse); ok {
				req := hrr.Request()
				remoteAddr = req.RemoteAddr
			}

			buildPrivateWins(newSession, remoteAddr)
			e.ReloadWin("admin")
		} else {
			e.SetFocusedComp(tb)
			errL.SetText("Invalid user name or password!")
			e.MarkDirty(errL)
		}
	}

	enterHandler := func(e gwu.Event) {
		if e.Type() == gwu.ETypeKeyPress && e.KeyCode() == gwu.KeyEnter {
			// enter key was pressed
			loginHandler(e)
		}
	}

	tb.AddEHandlerFunc(enterHandler, gwu.ETypeKeyPress)
	pb.AddEHandlerFunc(enterHandler, gwu.ETypeKeyPress)
	b.AddEHandlerFunc(loginHandler, gwu.ETypeClick)

	win.Add(p)
	win.SetFocusedCompId(tb.Id())

	s.AddWin(win)
}

func loginAuth(user, pass string) bool {
	return user == "admin" && pass == "a"
}

func buildPrivateWins(s gwu.Session, remoteAddr string) {
	user := s.Attr("username").(string)

	buildLogoutWin(s, user, remoteAddr)
	buildAdminWin(s, user, remoteAddr)
}

func buildLogoutWin(s gwu.Session, user, remoteAddr string) {
	winName := fmt.Sprintf("%s logout", appName)
	winHeader := fmt.Sprintf("%s - user=%s - address=%s", winName, user, remoteAddr)

	win := gwu.NewWindow("logout", winName)
	win.Style().SetFullWidth()
	win.SetCellPadding(2)

	title := gwu.NewLabel(winHeader)
	win.Add(title)

	p := gwu.NewPanel()
	p.SetCellPadding(2)

	logoutButton := gwu.NewButton("Logout")

	p.Add(logoutButton)

	win.Add(p)
	s.AddWin(win)
}

func buildAdminWin(s gwu.Session, user, remoteAddr string) {
	winName := fmt.Sprintf("%s admin", appName)
	winHeader := fmt.Sprintf("%s - user=%s - address=%s", winName, user, remoteAddr)

	win := gwu.NewWindow("admin", winName)
	win.Style().SetFullWidth()
	win.SetCellPadding(2)

	title := gwu.NewLabel(winHeader)
	win.Add(title)

	win.Add(gwu.NewLabel("click on this window to see updates"))

	win.AddEHandlerFunc(func(e gwu.Event) {

		if hrr, ok := e.(gwu.HasRequestResponse); ok {
			req := hrr.Request()
			remoteAddr = req.RemoteAddr
		}

		win.Add(gwu.NewLabel(fmt.Sprintf("click - addr=%v", remoteAddr)))
		e.MarkDirty(win)
	}, gwu.ETypeClick)

	s.AddWin(win)
}
<commit_msg>Debug login.<commit_after>package main

import (
	"fmt"
	//"github.com/udhos/gowut/gwu"
	"github.com/icza/gowut/gwu"
	"log"
	//"math/rand"
	"os"
	//"strconv"
)

const appName = "jazigo"

var logger = log.New(os.Stdout, "", log.LstdFlags)

const hardUser = "a"
const hardPass = "a"

func main() {

	appAddr := "0.0.0.0:8080"
	serverName := fmt.Sprintf("%s application", appName)

	// Create GUI server
	server := gwu.NewServer(appName, appAddr)
	//folder := "./tls/"
	//server := gwu.NewServerTLS(appName, appAddr, folder+"cert.pem", folder+"key.pem")
	server.SetText(serverName)

	buildHomeWin(server)
	buildLoginWin(server)

	server.SetLogger(logger)

	// Start GUI server
	if err := server.Start(); err != nil {
		logger.Println("jazigo main: Cound not start GUI server:", err)
		return
	}
}

func buildHomeWin(s gwu.Session) {

	winName := fmt.Sprintf("%s home", appName)
	win := gwu.NewWindow("home", winName)

	l := gwu.NewLabel(fmt.Sprintf("%s home", winName))

	l.Style().SetFontWeight(gwu.FontWeightBold).SetFontSize("130%")
	win.Add(l)
	win.Add(gwu.NewLabel("Click on the button to login:"))
	b := gwu.NewButton("Login")
	b.AddEHandlerFunc(func(e gwu.Event) {
		e.ReloadWin("login")
	}, gwu.ETypeClick)
	win.Add(b)

	s.AddWin(win)
}

func buildLoginWin(s gwu.Session) {

	winName := fmt.Sprintf("%s login", appName)
	win := gwu.NewWindow("login", winName)

	win.Style().SetFullSize()
	win.SetAlign(gwu.HACenter, gwu.VAMiddle)

	p := gwu.NewPanel()
	p.SetHAlign(gwu.HACenter)
	p.SetCellPadding(2)

	l := gwu.NewLabel(winName)
	l.Style().SetFontWeight(gwu.FontWeightBold).SetFontSize("150%")
	p.Add(l)
	l = gwu.NewLabel("Login")
	l.Style().SetFontWeight(gwu.FontWeightBold).SetFontSize("130%")
	p.Add(l)
	p.CellFmt(l).Style().SetBorder2(1, gwu.BrdStyleDashed, gwu.ClrNavy)
	l = gwu.NewLabel(fmt.Sprintf("user/pass: %s/%s", hardUser, hardPass))
	l.Style().SetFontSize("80%").SetFontStyle(gwu.FontStyleItalic)
	p.Add(l)

	errL := gwu.NewLabel("")
	errL.Style().SetColor(gwu.ClrRed)
	p.Add(errL)

	table := gwu.NewTable()
	table.SetCellPadding(2)
	table.EnsureSize(2, 2)
	table.Add(gwu.NewLabel("Username:"), 0, 0)
	tb := gwu.NewTextBox("")

	tb.Style().SetWidthPx(160)
	table.Add(tb, 0, 1)
	table.Add(gwu.NewLabel("Password:"), 1, 0)
	pb := gwu.NewPasswBox("")

	pb.Style().SetWidthPx(160)
	table.Add(pb, 1, 1)
	p.Add(table)
	b := gwu.NewButton("OK")

	p.Add(b)
	l = gwu.NewLabel("")
	p.Add(l)
	p.CellFmt(l).Style().SetHeightPx(200)

	loginHandler := func(e gwu.Event) {

		user := tb.Text()
		pass := pb.Text()
		auth := loginAuth(user, pass)

		logger.Printf("debug login user=[%s] pass=[%s] result=[%v]", user, pass, auth)

		if auth {

			// FIXME: Should clear username/password fields?

			newSession := e.NewSession()
			newSession.SetAttr("username", user)

			remoteAddr := "(remoteAddr?)"
			if hrr, ok := e.(gwu.HasRequestResponse); ok {
				req := hrr.Request()
				remoteAddr = req.RemoteAddr
			}

			buildPrivateWins(newSession, remoteAddr)
			e.ReloadWin("admin")
		} else {
			e.SetFocusedComp(tb)
			errL.SetText("Invalid user name or password!")
			e.MarkDirty(errL)
		}
	}

	enterHandler := func(e gwu.Event) {
		if e.Type() == gwu.ETypeKeyPress && e.KeyCode() == gwu.KeyEnter {
			// enter key was pressed
			loginHandler(e)
		}
	}

	tb.AddEHandlerFunc(enterHandler, gwu.ETypeKeyPress)
	pb.AddEHandlerFunc(enterHandler, gwu.ETypeKeyPress)
	b.AddEHandlerFunc(loginHandler, gwu.ETypeClick)

	win.Add(p)
	win.SetFocusedCompId(tb.Id())

	s.AddWin(win)
}

func loginAuth(user, pass string) bool {
	return user == hardUser && pass == hardPass
}

func buildPrivateWins(s gwu.Session, remoteAddr string) {
	user := s.Attr("username").(string)

	buildLogoutWin(s, user, remoteAddr)
	buildAdminWin(s, user, remoteAddr)
}

func buildLogoutWin(s gwu.Session, user, remoteAddr string) {
	winName := fmt.Sprintf("%s logout", appName)
	winHeader := fmt.Sprintf("%s - user=%s - address=%s", winName, user, remoteAddr)

	win := gwu.NewWindow("logout", winName)
	win.Style().SetFullWidth()
	win.SetCellPadding(2)

	title := gwu.NewLabel(winHeader)
	win.Add(title)

	p := gwu.NewPanel()
	p.SetCellPadding(2)

	logoutButton := gwu.NewButton("Logout")

	p.Add(logoutButton)

	win.Add(p)
	s.AddWin(win)
}

func buildAdminWin(s gwu.Session, user, remoteAddr string) {
	winName := fmt.Sprintf("%s admin", appName)
	winHeader := fmt.Sprintf("%s - user=%s - address=%s", winName, user, remoteAddr)

	win := gwu.NewWindow("admin", winName)
	win.Style().SetFullWidth()
	win.SetCellPadding(2)

	title := gwu.NewLabel(winHeader)
	win.Add(title)

	win.Add(gwu.NewLabel("click on this window to see updates"))

	win.AddEHandlerFunc(func(e gwu.Event) {

		if hrr, ok := e.(gwu.HasRequestResponse); ok {
			req := hrr.Request()
			remoteAddr = req.RemoteAddr
		}

		win.Add(gwu.NewLabel(fmt.Sprintf("click - addr=%v", remoteAddr)))
		e.MarkDirty(win)
	}, gwu.ETypeClick)

	s.AddWin(win)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"runtime"
	"strings"
)

var (
	jdkdir        = homeDir() + "/.jdkenv/java"
	macSystemJdk  = "/System/Library/Java/JavaVirtualMachines/"
	macLibraryJdk = "/Library/Java/JavaVirtualMachines/"
)

func main() {
	//var jdkdir = homeDir() + "/.jdkenv/java"
	flag.Parse()
	args := flag.Args()
	if len(args) == 0 {
		fmt.Println("print help")
	} else {
		switch args[0] {
		case "init":
			initialize()
		case "list", "versions":
			list()
		case "use":
			if len(args) >= 2 {
				use(args[1])
				return
			} else {
				fmt.Println("please select jdk directory")
			}
		case "current", "version":
			current()
		default:
			fmt.Println("print help")
		}
	}

	os.Exit(0)
}

func use(ver string) {
	if runtime.GOOS == "darwin" {
		macUse(ver)
		return
	}
	if !exist(jdkdir + "/" + ver) {
		fmt.Println(ver + "is not exist")
		return
	}

	jdkpath := jdkdir + "/" + ver
	javahomesymlink := jdkdir + "/current"

	removeCurrnetSymlink(javahomesymlink)
	makeJavahomeSymlink(jdkpath, javahomesymlink)
}

func macUse(ver string) {
	var jdkpath string
	if exist(macSystemJdk + ver) {
		jdkpath = macSystemJdk + ver + "/Contents/Home"
	} else if exist(macLibraryJdk + ver) {
		jdkpath = macLibraryJdk + ver + "/Contents/Home"
	} else {
		fmt.Println(ver + " isn't exists at this System")
		return
	}

	javahomesymlink := jdkdir + "/current"

	removeCurrnetSymlink(javahomesymlink)
	makeJavahomeSymlink(jdkpath, javahomesymlink)
}

func removeCurrnetSymlink(javahomesymlink string) {
	if exist(javahomesymlink) {
		if err := os.Remove(javahomesymlink); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	}
}

func makeJavahomeSymlink(jdkpath, javahomesymlink string) {
	if err := os.Symlink(jdkpath, javahomesymlink); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func list() {
	if runtime.GOOS == "darwin" {
		macJdkList()
		return
	}
	dirs, err := ioutil.ReadDir(jdkdir)
	if err != nil {
		fmt.Println(err)
		return
	}

	if len(dirs) == 0 {
		fmt.Println("jdk isn't exists at " + jdkdir)
		return
	}

	for _, value := range dirs {
		if strings.HasPrefix(value.Name(), "jdk") {
			fmt.Println(value.Name())
		}
	}
}

func macJdkList() {
	printMacJdk(macSystemJdk)
	printMacJdk(macLibraryJdk)
}

func printMacJdk(dirPath string) {
	dirs, err := ioutil.ReadDir(dirPath)
	if err != nil {
		fmt.Println(err)
		return
	}

	for _, value := range dirs {
		fmt.Println(value.Name())
	}
}

func current() {
	javahomesymlink := jdkdir + "/current"
	if !exist(javahomesymlink) {
		fmt.Println("jdkenv not used")
		return
	}

	dest, err := os.Readlink(javahomesymlink)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	switch runtime.GOOS {
	case "darwin":
		{
			splitedpath := strings.Split(dest, string(os.PathSeparator))
			fmt.Println(splitedpath[len(splitedpath)-3])
		}
	default:
		fmt.Println(filepath.Base(dest))
	}

}

func initialize() {
	if !exist(jdkdir) {
		err := os.MkdirAll(jdkdir, 0777)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	}

	switch runtime.GOOS {
	case "windows":
		windowsInit()
	default:
		unixTypeInit()
	}
}

func windowsInit() {
	_, err := exec.Command("setx", "JAVA_HOME", jdkdir+"/current").Output()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	fmt.Println("please reboot command prompt to recognize JAVA_HOME")

	if hasGitBash() {
		fmt.Println("if you use git bash, write in your .bashrc below")
		printSetJavaHomeMsg()
	}
}

func unixTypeInit() {
	fmt.Println("write in your .bashrc below")
	printSetJavaHomeMsg()
}

func printSetJavaHomeMsg() {
	fmt.Println("export JAVA_HOME=" + jdkdir + "/current")
	fmt.Println("and execute below")
	fmt.Println(". ~/.bashrc")
}

func homeDir() string {
	usr, err := user.Current()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	return usr.HomeDir
}

func exist(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

func hasGitBash() bool {
	return runtime.GOOS == "windows" && os.Getenv("HOME") == homeDir()
}
<commit_msg>edit go-file algo<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"runtime"
	"strings"
)

const VERSION = `0.0.2`

var (
	version       = flag.Bool("version", false, "display version information")
	v             = flag.Bool("v", false, "display version information")
	help          = flag.Bool("help", false, "display help information")
	h             = flag.Bool("h", false, "display help information")
	jdkdir        = homeDir() + "/.jdkenv/java"
	macSystemJdk  = "/System/Library/Java/JavaVirtualMachines/"
	macLibraryJdk = "/Library/Java/JavaVirtualMachines/"
)

func main() {
	//var jdkdir = homeDir() + "/.jdkenv/java"
	flag.Parse()

	if *v || *version {
		fmt.Printf("jdkenv %s\n", VERSION)
		os.Exit(0)
	}

	if *h || *help {
		fmt.Println("print help")
		os.Exit(0)
	}

	args := flag.Args()
	if len(args) == 0 {
		fmt.Println("print help")
	} else {
		switch args[0] {
		case "init":
			initialize()
		case "list", "versions":
			printList()
		case "use", "set":
			if len(args) >= 2 {
				use(args[1])
				return
			} else {
				fmt.Println("please select jdk directory")
			}
		case "current", "version":
			fmt.Println(getCurrent())
		default:
			fmt.Printf("not found: %s\n", args[0])
		}
	}

	os.Exit(0)
}

func use(ver string) {
	ver = getSearchedJdkName(ver)

	if runtime.GOOS == "darwin" {
		macUse(ver)
		return
	}
	if !exist(jdkdir + "/" + ver) {
		fmt.Println(ver + "is not exist")
		return
	}

	jdkpath := jdkdir + "/" + ver
	javahomesymlink := jdkdir + "/current"

	removeCurrnetSymlink(javahomesymlink)
	makeJavahomeSymlink(jdkpath, javahomesymlink)
}

func getSearchedJdkName(ver string) string {
	jdkList := getList()
	if haveAJdk(jdkList, ver) {
		for _, value := range jdkList {
			if strings.Contains(value, ver) { ver = value }
		}
	}

	return ver
}

func haveAJdk(jdkList []string, ver string) bool {
	count := 0
	for _, value := range jdkList {
		if strings.Contains(value, ver) { count++ }
	}

	return count == 1
}

func macUse(ver string) {
	var jdkpath string
	if exist(macSystemJdk + ver) {
		jdkpath = macSystemJdk + ver + "/Contents/Home"
	} else if exist(macLibraryJdk + ver) {
		jdkpath = macLibraryJdk + ver + "/Contents/Home"
	} else {
		fmt.Println(ver + " isn't exists at this System")
		return
	}

	javahomesymlink := jdkdir + "/current"

	removeCurrnetSymlink(javahomesymlink)
	makeJavahomeSymlink(jdkpath, javahomesymlink)
}

func removeCurrnetSymlink(javahomesymlink string) {
	if exist(javahomesymlink) {
		if err := os.Remove(javahomesymlink); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	}
}

func makeJavahomeSymlink(jdkpath, javahomesymlink string) {
	if err := os.Symlink(jdkpath, javahomesymlink); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func printList()  {
	for _, value := range getList() {
		if getCurrent() == value {
			fmt.Println("* " + value)
		} else {
			fmt.Println("  " + value)
		}
	}
}

func getList() []string {
	if runtime.GOOS == "darwin" {
		return getMacJdkList()
	}
	dirs, err := ioutil.ReadDir(jdkdir)
	if err != nil {
		fmt.Println(err)
		return nil
	}

	if len(dirs) == 0 {
		fmt.Println("jdk isn't exists at " + jdkdir)
		return nil
	}

	slice := make([]string, 0)
	for _, value := range dirs {
		if strings.HasPrefix(value.Name(), "jdk") {
			slice = append(slice, value.Name())
		}
	}

	return slice
}

func getMacJdkList() []string {
	slice := make([]string, 0)
	slice = append(slice, getJdkList(macSystemJdk)...)
	slice = append(slice, getJdkList(macLibraryJdk)...)
	return slice
}

func getJdkList(dirPath string) []string {
	dirs, err := ioutil.ReadDir(dirPath)
	if err != nil {
		fmt.Println(err)
		return nil
	}

	slice := make([]string, 0)
	for _, value := range dirs { slice = append(slice, value.Name()) }

	return slice
}

func getCurrent() string {
	javahomesymlink := jdkdir + "/current"
	if !exist(javahomesymlink) { return "jdkenv not used" }

	dest, err := os.Readlink(javahomesymlink)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	currentJdkVersion := ""
	switch runtime.GOOS {
	case "darwin":
		{
			splitedpath := strings.Split(dest, string(os.PathSeparator))
			currentJdkVersion = splitedpath[len(splitedpath)-3]
		}
	default:
		currentJdkVersion = filepath.Base(dest)
	}

	return currentJdkVersion
}

func initialize() {
	if !exist(jdkdir) {
		err := os.MkdirAll(jdkdir, 0777)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	}

	switch runtime.GOOS {
	case "windows":
		windowsInit()
	default:
		unixTypeInit()
	}
}

func windowsInit() {
	_, err := exec.Command("setx", "JAVA_HOME", jdkdir+"/current").Output()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	fmt.Println("please reboot command prompt to recognize JAVA_HOME")

	if hasGitBash() {
		fmt.Println("if you use git bash, write in your .bashrc below")
		printSetJavaHomeMsg()
	}
}

func unixTypeInit() {
	fmt.Println("write in your .bashrc below")
	printSetJavaHomeMsg()
}

func printSetJavaHomeMsg() {
	fmt.Println("export JAVA_HOME=" + jdkdir + "/current")
	fmt.Println("and execute below")
	fmt.Println(". ~/.bashrc")
}

func homeDir() string {
	usr, err := user.Current()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	return usr.HomeDir
}

func exist(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

func hasGitBash() bool {
	return runtime.GOOS == "windows" && os.Getenv("HOME") == homeDir()
}
<|endoftext|>


<commit_before>// Copyright 2012 Gary Burd
//
// 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 redis

import (
	"errors"
	"sync"
	"time"
)

var errorCompatibility = errors.New("RedisGo-Async: should use Do func")

// AsyncPool maintains one connection.
type AsyncPool struct {
	// Dial is an application supplied function for creating and configuring a
	// connection.
	//
	// The connection returned from Dial must not be in a special state
	// (subscribed to pubsub channel, transaction started, ...).
	Dial func() (AsynConn, error)
	// TestOnBorrow is an optional application supplied function for checking
	// the health of an idle connection before the connection is used again by
	// the application. Argument t is the time that the connection was returned
	// to the pool. If the function returns an error, then the connection is
	// closed.
	TestOnBorrow func(c AsynConn, t time.Time) error
	c            *asyncPoolConnection
	mu           sync.Mutex
	closed       bool
}

// NewAsyncPool creates a new async pool.
func NewAsyncPool(newFn func() (AsynConn, error), testFn func(AsynConn, time.Time) error) *AsyncPool {
	return &AsyncPool{Dial: newFn, TestOnBorrow: testFn}
}

// Get gets a connection.
func (p *AsyncPool) Get() AsynConn {
	p.mu.Lock()
	defer p.mu.Unlock()

	if p.closed {
		return errorConnection{errors.New("RedisGo-Async: get on closed pool")}
	}

	if p.c != nil && p.c.Err() == nil {
		if test := p.TestOnBorrow; test != nil {
			ic := p.c.c.(*asynConn)
			if test(p.c, ic.t) == nil {
				return p.c
			}
			p.c.c.Close()
		} else {
			return p.c
		}
	} else if p.c != nil {
		p.c.c.Close()
	}

	c, err := p.Dial()
	if err != nil {
		return errorConnection{err}
	}

	p.c = &asyncPoolConnection{p: p, c: c}
	return p.c
}

// ActiveCount returns the number of client of this pool.
func (p *AsyncPool) ActiveCount() int {
	p.mu.Lock()
	defer p.mu.Unlock()

	if p.c != nil && p.c.Err() == nil {
		return 1
	}
	return 0
}

// IdleCount returns the number of idle connections in the pool.
func (p *AsyncPool) IdleCount() int {
	return 0
}

// Close releases the resources used by the pool.
func (p *AsyncPool) Close() error {
	p.mu.Lock()
	defer p.mu.Unlock()

	if p.closed {
		return nil
	}
	p.closed = true
	err := p.c.c.Close()
	p.c = nil

	return err
}

type asyncPoolConnection struct {
	p *AsyncPool
	c AsynConn
}

func (pc *asyncPoolConnection) Close() error {
	return nil
}

func (pc *asyncPoolConnection) Err() error {
	return pc.c.Err()
}

func (pc *asyncPoolConnection) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
	return pc.c.Do(commandName, args...)
}

func (pc *asyncPoolConnection) AsyncDo(commandName string, args ...interface{}) (ret AsyncRet, err error) {
	return pc.c.AsyncDo(commandName, args...)
}

func (pc *asyncPoolConnection) Send(commandName string, args ...interface{}) error {
	return errorCompatibility
}

func (pc *asyncPoolConnection) Flush() error {
	return errorCompatibility
}

func (pc *asyncPoolConnection) Receive() (reply interface{}, err error) {
	return nil, errorCompatibility
}

func (ec errorConnection) AsyncDo(string, ...interface{}) (AsyncRet, error) { return nil, ec.err }
<commit_msg>modify a err log<commit_after>// Copyright 2012 Gary Burd
//
// 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 redis

import (
	"errors"
	"sync"
	"time"
)

var errorCompatibility = errors.New("RedisGo-Async: should use AsyncDo func")

// AsyncPool maintains one connection.
type AsyncPool struct {
	// Dial is an application supplied function for creating and configuring a
	// connection.
	//
	// The connection returned from Dial must not be in a special state
	// (subscribed to pubsub channel, transaction started, ...).
	Dial func() (AsynConn, error)
	// TestOnBorrow is an optional application supplied function for checking
	// the health of an idle connection before the connection is used again by
	// the application. Argument t is the time that the connection was returned
	// to the pool. If the function returns an error, then the connection is
	// closed.
	TestOnBorrow func(c AsynConn, t time.Time) error
	c            *asyncPoolConnection
	mu           sync.Mutex
	closed       bool
}

// NewAsyncPool creates a new async pool.
func NewAsyncPool(newFn func() (AsynConn, error), testFn func(AsynConn, time.Time) error) *AsyncPool {
	return &AsyncPool{Dial: newFn, TestOnBorrow: testFn}
}

// Get gets a connection.
func (p *AsyncPool) Get() AsynConn {
	p.mu.Lock()
	defer p.mu.Unlock()

	if p.closed {
		return errorConnection{errors.New("RedisGo-Async: get on closed pool")}
	}

	if p.c != nil && p.c.Err() == nil {
		if test := p.TestOnBorrow; test != nil {
			ic := p.c.c.(*asynConn)
			if test(p.c, ic.t) == nil {
				return p.c
			}
			p.c.c.Close()
		} else {
			return p.c
		}
	} else if p.c != nil {
		p.c.c.Close()
	}

	c, err := p.Dial()
	if err != nil {
		return errorConnection{err}
	}

	p.c = &asyncPoolConnection{p: p, c: c}
	return p.c
}

// ActiveCount returns the number of client of this pool.
func (p *AsyncPool) ActiveCount() int {
	p.mu.Lock()
	defer p.mu.Unlock()

	if p.c != nil && p.c.Err() == nil {
		return 1
	}
	return 0
}

// IdleCount returns the number of idle connections in the pool.
func (p *AsyncPool) IdleCount() int {
	return 0
}

// Close releases the resources used by the pool.
func (p *AsyncPool) Close() error {
	p.mu.Lock()
	defer p.mu.Unlock()

	if p.closed {
		return nil
	}
	p.closed = true
	err := p.c.c.Close()
	p.c = nil

	return err
}

type asyncPoolConnection struct {
	p *AsyncPool
	c AsynConn
}

func (pc *asyncPoolConnection) Close() error {
	return nil
}

func (pc *asyncPoolConnection) Err() error {
	return pc.c.Err()
}

func (pc *asyncPoolConnection) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
	return pc.c.Do(commandName, args...)
}

func (pc *asyncPoolConnection) AsyncDo(commandName string, args ...interface{}) (ret AsyncRet, err error) {
	return pc.c.AsyncDo(commandName, args...)
}

func (pc *asyncPoolConnection) Send(commandName string, args ...interface{}) error {
	return errorCompatibility
}

func (pc *asyncPoolConnection) Flush() error {
	return errorCompatibility
}

func (pc *asyncPoolConnection) Receive() (reply interface{}, err error) {
	return nil, errorCompatibility
}

func (ec errorConnection) AsyncDo(string, ...interface{}) (AsyncRet, error) { return nil, ec.err }
<|endoftext|>