<commit_before>package asyncpi

// Binding.
// This file contains functions for name binding.

import (
	"log"
)

// Bind takes a parsed process p and returned a process with valid binding.
func Bind(p Process) Process {
	return bind(p, []Name{})
}

func bind(p Process, boundNames []Name) Process {
	switch proc := p.(type) {
	case *NilProcess:
		return proc
	case *Repeat:
		return bind(proc.Proc, boundNames)
	case *Par:
		for i := range proc.Procs {
			bind(proc.Procs[i], boundNames)
		}
		return proc
	case *Recv:
		names := make([]Name, len(boundNames))
		for i := range boundNames {
			names[i] = boundNames[i]
		}
		names = append(names, proc.Vars...)
		for _, v := range proc.Vars {
			for j := 0; j < len(names)-len(proc.Vars); j++ {
				if v.Name() == names[j].Name() {
					log.Println("Warning: rebinding name", v.Name(), "in recv")
					names = append(names[:j], names[j+1:]...)
				}
			}
		}
		for i, bn := range names {
			if proc.Chan.Name() == bn.Name() { // Found bound Chan
				proc.Chan = names[i]
			}
		}
		proc.Cont = bind(proc.Cont, names)
		return proc
	case *Send:
		count := 0
		for i, bn := range boundNames {
			for j, v := range proc.Vals {
				if bn.Name() == v.Name() { // Found bound name.
					proc.Vals[j] = boundNames[i]
					count++
				}
			}
		}
		for i, bn := range boundNames {
			if bn.Name() == proc.Chan.Name() { // Found bound Chan.
				proc.Chan = boundNames[i]
				count++
			}
		}
		if count < len(proc.Vals)+1 {
			log.Println("Warning:", len(proc.Vals)+1-count, "names are left unbound")
		}
		return proc
	case *Restrict:
		names := append(boundNames, proc.Name)
		for i := 0; i < len(names)-1; i++ {
			if names[i].Name() == proc.Name.Name() {
				log.Println("Warning: rebinding name", proc.Name.Name(), "in restrict")
				names = append(names[:i], names[i+1:]...)
			}
		}
		proc.Proc = bind(proc.Proc, names)
		return proc
	default:
		log.Fatal(ErrUnknownProcType{Caller: "Bind", Proc: p})
	}
	return proc
}
<commit_msg>Bind should bind free channels<commit_after>package asyncpi

// Binding.
// This file contains functions for name binding.

import (
	"log"
)

// Bind takes a parsed process p and returned a process with valid binding.
func Bind(p Process) Process {
	return bind(p, []Name{})
}

func bind(p Process, boundNames []Name) Process {
	switch proc := p.(type) {
	case *NilProcess:
		return proc
	case *Repeat:
		return bind(proc.Proc, boundNames)
	case *Par:
		for i := range proc.Procs {
			bind(proc.Procs[i], boundNames)
		}
		return proc
	case *Recv:
		names := make([]Name, len(boundNames))
		for i := range boundNames {
			names[i] = boundNames[i]
		}
		names = append(names, proc.Vars...)
		for _, v := range proc.Vars {
			for j := 0; j < len(names)-len(proc.Vars); j++ {
				if v.Name() == names[j].Name() {
					log.Println("Warning: rebinding name", v.Name(), "in recv")
					names = append(names[:j], names[j+1:]...)
				}
			}
		}
		var chanBound bool
		for i, bn := range names {
			if proc.Chan.Name() == bn.Name() { // Found bound Chan
				proc.Chan = names[i]
				chanBound = true
			}
		}
		if !chanBound {
			names = append(names, proc.Chan)
		}
		proc.Cont = bind(proc.Cont, names)
		return proc
	case *Send:
		count := 0
		for i, bn := range boundNames {
			for j, v := range proc.Vals {
				if bn.Name() == v.Name() { // Found bound name.
					proc.Vals[j] = boundNames[i]
					count++
				}
			}
		}
		for i, bn := range boundNames {
			if bn.Name() == proc.Chan.Name() { // Found bound Chan.
				proc.Chan = boundNames[i]
				count++
			}
		}
		if count < len(proc.Vals)+1 {
			log.Println("Warning:", len(proc.Vals)+1-count, "names are left unbound")
		}
		return proc
	case *Restrict:
		names := append(boundNames, proc.Name)
		for i := 0; i < len(names)-1; i++ {
			if names[i].Name() == proc.Name.Name() {
				log.Println("Warning: rebinding name", proc.Name.Name(), "in restrict")
				names = append(names[:i], names[i+1:]...)
			}
		}
		proc.Proc = bind(proc.Proc, names)
		return proc
	default:
		log.Fatal(ErrUnknownProcType{Caller: "Bind", Proc: p})
	}
	return proc
}
<|endoftext|>


<commit_before>// Package wait provides Group, an extended version of sync.WaitGroup.
package wait

import "sync"

// A Group waits for a collection of goroutines to exit.
// It tracks the error result of each goroutine.
type Group struct {
	wg     sync.WaitGroup
	mu     sync.Mutex
	quit   chan struct{}
	closed bool
	err    error
}

// Go runs f in a new goroutine.
// The quit chan is closed to indicate that f should exit early,
// so f is expected to periodically receive from quit
// and immediately return nil if a value arrives.
func (g *Group) Go(f func(quit <-chan struct{}) error) {
	g.mu.Lock()
	if g.quit == nil {
		g.quit = make(chan struct{})
	}
	g.mu.Unlock()

	g.wg.Add(1)
	go func() {
		err := f(g.quit)
		if err != nil {
			g.mu.Lock()
			if g.err == nil {
				g.err = err
			}
			g.mu.Unlock()
		}
		g.wg.Done()
	}()
}

// Quit asks all goroutines started with GoQuit to exit
// (by closing the quit chan given to each function).
// After the first call to Quit, subsequent calls have no effect.
func (g *Group) Quit() {
	g.mu.Lock()
	if !g.closed {
		close(g.quit)
		g.closed = true
	}
	g.mu.Unlock()
}

// Wait waits for all goroutines to exit and returns
// the first non-nil error seen.
func (g *Group) Wait() error {
	g.wg.Wait()
	return g.err
}
<commit_msg>Quit after one goroutine fails<commit_after>// Package wait provides Group, an extended version of sync.WaitGroup.
package wait

import "sync"

// A Group waits for a collection of goroutines to exit.
// With Wait, one may wait for all goroutines to exit and
// get the first error that was seen.
// Cooperative cancellation is also supported via Quit
// and the broadcast chan passed to each goroutine.
type Group struct {
	wg     sync.WaitGroup
	mu     sync.Mutex
	quit   chan struct{}
	closed bool
	err    error
}

// Go runs f in a new goroutine.
// The quit chan is closed to indicate that f should exit early,
// so f is expected to periodically receive from quit
// and immediately return nil if a value arrives.
// Quit is called automatically if f returns a non-nil error.
func (g *Group) Go(f func(quit <-chan struct{}) error) {
	g.mu.Lock()
	if g.quit == nil {
		g.quit = make(chan struct{})
	}
	g.mu.Unlock()

	g.wg.Add(1)
	go func() {
		err := f(g.quit)
		if err != nil {
			g.mu.Lock()
			if g.err == nil {
				g.err = err
			}
			g.mu.Unlock()
			g.Quit()
		}
		g.wg.Done()
	}()
}

// Quit asks all goroutines started with GoQuit to exit
// (by closing the quit chan given to each function).
// After the first call to Quit, subsequent calls have no effect.
func (g *Group) Quit() {
	g.mu.Lock()
	if !g.closed {
		close(g.quit)
		g.closed = true
	}
	g.mu.Unlock()
}

// Wait waits for all goroutines to exit and returns
// the first non-nil error seen.
func (g *Group) Wait() error {
	g.wg.Wait()
	return g.err
}
<|endoftext|>


<commit_before>// Copyright ©2012 Dan Kortschak <dan.kortschak@adelaide.edu.au>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// Package boom is a wrapper for the samtools bam library.
package boom

// http://samtools.sourceforge.net/samtools/sam/index.html

/*
#cgo CFLAGS: -g -O2 -fPIC -m64 -pthread
#cgo LDFLAGS: -lz
#include "sam.h"
#include "bam_endian.h"
*/
import "C"

import (
	"encoding/binary"
	"fmt"
	"io"
	"reflect"
	"runtime"
	"unsafe"
)

var (
	valueIsNil       = fmt.Errorf("boom: value is nil")
	notBamFile       = fmt.Errorf("boom: not bam file")
	couldNotAllocate = fmt.Errorf("boom: could not allocate")
	cannotAddr       = fmt.Errorf("boom: cannot address value")
	bamIsBigEndian   = C.bam_is_big_endian() == 1
	endian           = [2]binary.ByteOrder{
		binary.LittleEndian,
		binary.BigEndian,
	}[C.bam_is_big_endian()]
)

type bamRecord struct {
	b *C.bam1_t
}

func newBamRecord(b *C.bam1_t) (br *bamRecord) {
	br = &bamRecord{b}
	runtime.SetFinalizer(br, (*bamRecord).bamRecordFree)

	return
}

func (br *bamRecord) tid() int32 {
	if br.b != nil {
		return int32(br.b.core.tid)
	}
	panic(valueIsNil)
}
func (br *bamRecord) pos() int32 {
	if br.b != nil {
		return int32(br.b.core.pos)
	}
	panic(valueIsNil)
}
func (br *bamRecord) bin() uint {
	if br.b != nil {
		return uint(br.b.core.bin)
	}
	panic(valueIsNil)
}
func (br *bamRecord) qual() byte {
	if br.b != nil {
		return byte(br.b.core.qual)
	}
	panic(valueIsNil)
}
func (br *bamRecord) lQname() byte {
	if br.b != nil {
		return byte(br.b.core.l_qname)
	}
	panic(valueIsNil)
}
func (br *bamRecord) flag() Flags {
	if br.b != nil {
		return Flags(br.b.core.flag)
	}
	panic(valueIsNil)
}
func (br *bamRecord) nCigar() uint {
	if br.b != nil {
		return uint(br.b.core.n_cigar)
	}
	panic(valueIsNil)
}
func (br *bamRecord) lQseq() int32 {
	if br.b != nil {
		return int32(br.b.core.l_qseq)
	}
	panic(valueIsNil)
}
func (br *bamRecord) mtid() int32 {
	if br.b != nil {
		return int32(br.b.core.mtid)
	}
	panic(valueIsNil)
}
func (br *bamRecord) mpos() int32 {
	if br.b != nil {
		return int32(br.b.core.mpos)
	}
	panic(valueIsNil)
}
func (br *bamRecord) isize() int32 {
	if br.b != nil {
		return int32(br.b.core.isize)
	}
	panic(valueIsNil)
}
func (br *bamRecord) lAux() int {
	if br.b != nil {
		return int(br.b.l_aux)
	}
	panic(valueIsNil)
}
func (br *bamRecord) dataLen() int {
	if br.b != nil {
		return int(br.b.data_len)
	}
	panic(valueIsNil)
}
func (br *bamRecord) dataCap() int {
	if br.b != nil {
		return int(br.b.m_data)
	}
	panic(valueIsNil)
}
func (br *bamRecord) dataPtr() uintptr {
	if br.b != nil {
		return uintptr(unsafe.Pointer(br.b.data))
	}
	panic(valueIsNil)
}
func (br *bamRecord) dataUnsafe() []byte {
	if br.b == nil {
		panic(valueIsNil)
	}

	l := int(br.b.data_len)
	var data []byte
	sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&data))
	sliceHeader.Cap = l
	sliceHeader.Len = l
	sliceHeader.Data = uintptr(unsafe.Pointer(br.b.data))

	return data
}
func (br *bamRecord) bamRecordFree() {
	if br.b != nil {
		C.free(unsafe.Pointer(br.b))
		br.b = nil
	}
}

type samFile struct {
	fp *C.samfile_t
}

func (sf *samFile) fileType() int {
	if sf.fp != nil {
		return int(sf.fp._type)
	}
	panic(valueIsNil)
}

func samOpen(filename, mode string, aux header) (sf *samFile, err error) {
	fn, m := C.CString(filename), C.CString(mode)
	defer C.free(unsafe.Pointer(fn))
	defer C.free(unsafe.Pointer(m))

	var auxAddr uintptr
	switch aux.(type) {
	case textHeader:
		xv := reflect.ValueOf(aux)
		if xv.Len() > 0 {
			auxAddr = xv.Index(0).UnsafeAddr()
		} else {
			auxAddr = 0
		}
	case *bamHeader:
		auxAddr = reflect.ValueOf(aux).UnsafeAddr()
	default:
		if aux == nil {
			break
		}
		panic(fmt.Sprintf("boom: wrong type %T", aux))
	}

	fp, err := C.samopen(
		(*C.char)(unsafe.Pointer(fn)),
		(*C.char)(unsafe.Pointer(m)),
		unsafe.Pointer(auxAddr),
	)
	sf = &samFile{fp: (*C.samfile_t)(unsafe.Pointer(fp))}
	runtime.SetFinalizer(sf, (*samFile).samClose)

	return
}

func (sf *samFile) header() *bamHeader {
	return &bamHeader{bh: sf.fp.header}
}

func (sf *samFile) samClose() error {
	if sf.fp == nil {
		return valueIsNil
	}

	C.samclose((*C.samfile_t)(unsafe.Pointer(sf.fp)))

	return nil
}

func (sf *samFile) samRead() (n int, br *bamRecord, err error) {
	if sf.fp == nil {
		return 0, nil, valueIsNil
	}

	b := (*C.bam1_t)(unsafe.Pointer(C.malloc((C.size_t)(unsafe.Sizeof(C.bam1_t{})))))

	if b == nil {
		return 0, nil, couldNotAllocate
	}
	*b = C.bam1_t{}
	br = newBamRecord(b)

	cn, err := C.samread(
		(*C.samfile_t)(unsafe.Pointer(sf.fp)),
		(*C.bam1_t)(unsafe.Pointer(br.b)),
	)
	n = int(cn)
	if n < 0 {
		err = io.EOF
	}

	return
}

func (sf *samFile) samWrite(br *bamRecord) (n int, err error) {
	if sf.fp == nil || br.b == nil {
		return 0, valueIsNil
	}

	return int(C.samwrite(
		(*C.samfile_t)(unsafe.Pointer(sf.fp)),
		(*C.bam1_t)(unsafe.Pointer(br.b)),
	)), nil
}

type bamIndex struct {
	idx *C.bam_index_t
}

func bamIndexBuild(filename string) (ret int, err error) {
	fn := C.CString(filename)
	defer C.free(unsafe.Pointer(fn))

	r, err := C.bam_index_build(
		(*C.char)(unsafe.Pointer(fn)),
	)

	return int(r), err
}

func bamIndexLoad(filename string) (bi *bamIndex, err error) {
	fn := C.CString(filename)
	defer C.free(unsafe.Pointer(fn))

	ip, err := C.bam_index_load(
		(*C.char)(unsafe.Pointer(fn)),
	)
	bi = &bamIndex{idx: (*C.bam_index_t)(unsafe.Pointer(ip))}
	runtime.SetFinalizer(bi, (*bamIndex).bamIndexDestroy)

	return
}

func (bi *bamIndex) bamIndexDestroy() (err error) {
	if bi.idx == nil {
		return valueIsNil
	}

	C.bam_index_destroy(
		(*C.bam_index_t)(unsafe.Pointer(bi.idx)),
	)

	return
}

type BamFetchFn func(*bamRecord, interface{}) int

func (sf *samFile) bamFetch(bi *bamIndex, tid, beg, end int, data interface{}, fn BamFetchFn) (ret int, err error) {
	if sf.fp == nil || bi.idx == nil {
		return 0, valueIsNil
	}

	if sf.fileType()&1 != 1 {
		return 0, notBamFile
	}

	dv := reflect.ValueOf(data)
	if dv.CanAddr() {
		return 0, cannotAddr
	}

	f := func(b *C.bam1_t, _ *[0]byte) C.int {
		return C.int(fn(&bamRecord{(*C.bam1_t)(unsafe.Pointer(b))}, data))
	}

	r := C.bam_fetch(
		(*C.BGZF)(unsafe.Pointer(sf.fp)),
		bi.idx,
		C.int(tid),
		C.int(beg),
		C.int(end),
		unsafe.Pointer(dv.UnsafeAddr()),
		(*[0]byte)(unsafe.Pointer(&f)),
	)

	return int(r), nil
}

type header interface {
	header()
}

type bamHeader struct {
	bh *C.bam_header_t
}

func (bh *bamHeader) bamGetTid(name string) (int, error) {
	sn := C.CString(name)
	defer C.free(unsafe.Pointer(sn))

	tid, err := C.bam_get_tid(
		(*C.bam_header_t)(unsafe.Pointer(bh.bh)),
		(*C.char)(unsafe.Pointer(sn)),
	)

	return int(tid), err
}

func (bh *bamHeader) nTargets() int32 {
	if bh.bh != nil {
		return int32(bh.bh.n_targets)
	}
	panic(valueIsNil)
}

func (bh *bamHeader) targetNames() (n []string) {
	if bh.bh != nil {
		n = make([]string, bh.bh.n_targets)
		l := int(bh.bh.n_targets)
		var nPtrs []*C.char
		sh := (*reflect.SliceHeader)(unsafe.Pointer(&nPtrs))
		sh.Cap = l
		sh.Len = l
		sh.Data = uintptr(unsafe.Pointer(bh.bh.target_name))

		for i, p := range nPtrs {
			n[i] = C.GoString(p)
		}

		return
	}
	panic(valueIsNil)
}

func (bh *bamHeader) targetLengths() []uint32 {
	if bh.bh != nil {
		l := int(bh.bh.n_targets)
		var unsafeLengths []uint32
		sh := (*reflect.SliceHeader)(unsafe.Pointer(&unsafeLengths))
		sh.Cap = l
		sh.Len = l
		sh.Data = uintptr(unsafe.Pointer(bh.bh.target_len))

		return append([]uint32(nil), unsafeLengths...)
	}
	panic(valueIsNil)
}

func (bh *bamHeader) header() {}

type textHeader []byte

func (th textHeader) header() {}

const (
	Paired       Flags = paired       // The read is paired in sequencing, no matter whether it is mapped in a pair.
	ProperPair   Flags = properPair   // The read is mapped in a proper pair.
	Unmapped     Flags = unmapped     // The read itself is unmapped; conflictive with BAM_FPROPER_PAIR.
	MateUnmapped Flags = mateUnmapped // The mate is unmapped.
	Reverse      Flags = reverse      // The read is mapped to the reverse strand.
	MateReverse  Flags = mateReverse  // The mate is mapped to the reverse strand.
	Read1        Flags = read1        // This is read1.
	Read2        Flags = read2        // This is read2.
	Secondary    Flags = secondary    // Not primary alignment.
	QCFail       Flags = qCFail       // QC failure.
	Duplicate    Flags = duplicate    // Optical or PCR duplicate.
	paired       = C.BAM_FPAIRED
	properPair   = C.BAM_FPROPER_PAIR
	unmapped     = C.BAM_FUNMAP
	mateUnmapped = C.BAM_FMUNMAP
	reverse      = C.BAM_FREVERSE
	mateReverse  = C.BAM_FMREVERSE
	read1        = C.BAM_FREAD1
	read2        = C.BAM_FREAD2
	secondary    = C.BAM_FSECONDARY
	qCFail       = C.BAM_FQCFAIL
	duplicate    = C.BAM_FDUP
)

type Flags uint32

// String representation of BAM alignment flags:
//  0x001 - p - Paired
//  0x002 - P - ProperPair
//  0x004 - u - Unmapped
//  0x008 - U - MateUnmapped
//  0x010 - r - Reverse
//  0x020 - R - MateReverse
//  0x040 - 1 - Read1
//  0x080 - 2 - Read2
//  0x100 - s - Secondary
//  0x200 - f - QCFail
//  0x400 - d - Duplicate
func (f Flags) String() string {
	const flags = "DqSLFrRuUAM"
	b := make([]byte, len(flags))
	for i, c := range flags {
		if f&(1<<uint(10-i)) != 0 {
			b[i] = byte(c)
		} else {
			b[i] = '-'
		}
	}

	return string(b)
}
<commit_msg>Lagging go fmt<commit_after>// Copyright ©2012 Dan Kortschak <dan.kortschak@adelaide.edu.au>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// Package boom is a wrapper for the samtools bam library.
package boom

// http://samtools.sourceforge.net/samtools/sam/index.html

/*
#cgo CFLAGS: -g -O2 -fPIC -m64 -pthread
#cgo LDFLAGS: -lz
#include "sam.h"
#include "bam_endian.h"
*/
import "C"

import (
	"encoding/binary"
	"fmt"
	"io"
	"reflect"
	"runtime"
	"unsafe"
)

var (
	valueIsNil       = fmt.Errorf("boom: value is nil")
	notBamFile       = fmt.Errorf("boom: not bam file")
	couldNotAllocate = fmt.Errorf("boom: could not allocate")
	cannotAddr       = fmt.Errorf("boom: cannot address value")
	bamIsBigEndian   = C.bam_is_big_endian() == 1
	endian           = [2]binary.ByteOrder{
		binary.LittleEndian,
		binary.BigEndian,
	}[C.bam_is_big_endian()]
)

type bamRecord struct {
	b *C.bam1_t
}

func newBamRecord(b *C.bam1_t) (br *bamRecord) {
	br = &bamRecord{b}
	runtime.SetFinalizer(br, (*bamRecord).bamRecordFree)

	return
}

func (br *bamRecord) tid() int32 {
	if br.b != nil {
		return int32(br.b.core.tid)
	}
	panic(valueIsNil)
}
func (br *bamRecord) pos() int32 {
	if br.b != nil {
		return int32(br.b.core.pos)
	}
	panic(valueIsNil)
}
func (br *bamRecord) bin() uint {
	if br.b != nil {
		return uint(br.b.core.bin)
	}
	panic(valueIsNil)
}
func (br *bamRecord) qual() byte {
	if br.b != nil {
		return byte(br.b.core.qual)
	}
	panic(valueIsNil)
}
func (br *bamRecord) lQname() byte {
	if br.b != nil {
		return byte(br.b.core.l_qname)
	}
	panic(valueIsNil)
}
func (br *bamRecord) flag() Flags {
	if br.b != nil {
		return Flags(br.b.core.flag)
	}
	panic(valueIsNil)
}
func (br *bamRecord) nCigar() uint {
	if br.b != nil {
		return uint(br.b.core.n_cigar)
	}
	panic(valueIsNil)
}
func (br *bamRecord) lQseq() int32 {
	if br.b != nil {
		return int32(br.b.core.l_qseq)
	}
	panic(valueIsNil)
}
func (br *bamRecord) mtid() int32 {
	if br.b != nil {
		return int32(br.b.core.mtid)
	}
	panic(valueIsNil)
}
func (br *bamRecord) mpos() int32 {
	if br.b != nil {
		return int32(br.b.core.mpos)
	}
	panic(valueIsNil)
}
func (br *bamRecord) isize() int32 {
	if br.b != nil {
		return int32(br.b.core.isize)
	}
	panic(valueIsNil)
}
func (br *bamRecord) lAux() int {
	if br.b != nil {
		return int(br.b.l_aux)
	}
	panic(valueIsNil)
}
func (br *bamRecord) dataLen() int {
	if br.b != nil {
		return int(br.b.data_len)
	}
	panic(valueIsNil)
}
func (br *bamRecord) dataCap() int {
	if br.b != nil {
		return int(br.b.m_data)
	}
	panic(valueIsNil)
}
func (br *bamRecord) dataPtr() uintptr {
	if br.b != nil {
		return uintptr(unsafe.Pointer(br.b.data))
	}
	panic(valueIsNil)
}
func (br *bamRecord) dataUnsafe() []byte {
	if br.b == nil {
		panic(valueIsNil)
	}

	l := int(br.b.data_len)
	var data []byte
	sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&data))
	sliceHeader.Cap = l
	sliceHeader.Len = l
	sliceHeader.Data = uintptr(unsafe.Pointer(br.b.data))

	return data
}
func (br *bamRecord) bamRecordFree() {
	if br.b != nil {
		C.free(unsafe.Pointer(br.b))
		br.b = nil
	}
}

type samFile struct {
	fp *C.samfile_t
}

func (sf *samFile) fileType() int {
	if sf.fp != nil {
		return int(sf.fp._type)
	}
	panic(valueIsNil)
}

func samOpen(filename, mode string, aux header) (sf *samFile, err error) {
	fn, m := C.CString(filename), C.CString(mode)
	defer C.free(unsafe.Pointer(fn))
	defer C.free(unsafe.Pointer(m))

	var auxAddr uintptr
	switch aux.(type) {
	case textHeader:
		xv := reflect.ValueOf(aux)
		if xv.Len() > 0 {
			auxAddr = xv.Index(0).UnsafeAddr()
		} else {
			auxAddr = 0
		}
	case *bamHeader:
		auxAddr = reflect.ValueOf(aux).UnsafeAddr()
	default:
		if aux == nil {
			break
		}
		panic(fmt.Sprintf("boom: wrong type %T", aux))
	}

	fp, err := C.samopen(
		(*C.char)(unsafe.Pointer(fn)),
		(*C.char)(unsafe.Pointer(m)),
		unsafe.Pointer(auxAddr),
	)
	sf = &samFile{fp: (*C.samfile_t)(unsafe.Pointer(fp))}
	runtime.SetFinalizer(sf, (*samFile).samClose)

	return
}

func (sf *samFile) header() *bamHeader {
	return &bamHeader{bh: sf.fp.header}
}

func (sf *samFile) samClose() error {
	if sf.fp == nil {
		return valueIsNil
	}

	C.samclose((*C.samfile_t)(unsafe.Pointer(sf.fp)))

	return nil
}

func (sf *samFile) samRead() (n int, br *bamRecord, err error) {
	if sf.fp == nil {
		return 0, nil, valueIsNil
	}

	b := (*C.bam1_t)(unsafe.Pointer(C.malloc((C.size_t)(unsafe.Sizeof(C.bam1_t{})))))

	if b == nil {
		return 0, nil, couldNotAllocate
	}
	*b = C.bam1_t{}
	br = newBamRecord(b)

	cn, err := C.samread(
		(*C.samfile_t)(unsafe.Pointer(sf.fp)),
		(*C.bam1_t)(unsafe.Pointer(br.b)),
	)
	n = int(cn)
	if n < 0 {
		err = io.EOF
	}

	return
}

func (sf *samFile) samWrite(br *bamRecord) (n int, err error) {
	if sf.fp == nil || br.b == nil {
		return 0, valueIsNil
	}

	return int(C.samwrite(
		(*C.samfile_t)(unsafe.Pointer(sf.fp)),
		(*C.bam1_t)(unsafe.Pointer(br.b)),
	)), nil
}

type bamIndex struct {
	idx *C.bam_index_t
}

func bamIndexBuild(filename string) (ret int, err error) {
	fn := C.CString(filename)
	defer C.free(unsafe.Pointer(fn))

	r, err := C.bam_index_build(
		(*C.char)(unsafe.Pointer(fn)),
	)

	return int(r), err
}

func bamIndexLoad(filename string) (bi *bamIndex, err error) {
	fn := C.CString(filename)
	defer C.free(unsafe.Pointer(fn))

	ip, err := C.bam_index_load(
		(*C.char)(unsafe.Pointer(fn)),
	)
	bi = &bamIndex{idx: (*C.bam_index_t)(unsafe.Pointer(ip))}
	runtime.SetFinalizer(bi, (*bamIndex).bamIndexDestroy)

	return
}

func (bi *bamIndex) bamIndexDestroy() (err error) {
	if bi.idx == nil {
		return valueIsNil
	}

	C.bam_index_destroy(
		(*C.bam_index_t)(unsafe.Pointer(bi.idx)),
	)

	return
}

type BamFetchFn func(*bamRecord, interface{}) int

func (sf *samFile) bamFetch(bi *bamIndex, tid, beg, end int, data interface{}, fn BamFetchFn) (ret int, err error) {
	if sf.fp == nil || bi.idx == nil {
		return 0, valueIsNil
	}

	if sf.fileType()&1 != 1 {
		return 0, notBamFile
	}

	dv := reflect.ValueOf(data)
	if dv.CanAddr() {
		return 0, cannotAddr
	}

	f := func(b *C.bam1_t, _ *[0]byte) C.int {
		return C.int(fn(&bamRecord{(*C.bam1_t)(unsafe.Pointer(b))}, data))
	}

	r := C.bam_fetch(
		(*C.BGZF)(unsafe.Pointer(sf.fp)),
		bi.idx,
		C.int(tid),
		C.int(beg),
		C.int(end),
		unsafe.Pointer(dv.UnsafeAddr()),
		(*[0]byte)(unsafe.Pointer(&f)),
	)

	return int(r), nil
}

type header interface {
	header()
}

type bamHeader struct {
	bh *C.bam_header_t
}

func (bh *bamHeader) bamGetTid(name string) (int, error) {
	sn := C.CString(name)
	defer C.free(unsafe.Pointer(sn))

	tid, err := C.bam_get_tid(
		(*C.bam_header_t)(unsafe.Pointer(bh.bh)),
		(*C.char)(unsafe.Pointer(sn)),
	)

	return int(tid), err
}

func (bh *bamHeader) nTargets() int32 {
	if bh.bh != nil {
		return int32(bh.bh.n_targets)
	}
	panic(valueIsNil)
}

func (bh *bamHeader) targetNames() (n []string) {
	if bh.bh != nil {
		n = make([]string, bh.bh.n_targets)
		l := int(bh.bh.n_targets)
		var nPtrs []*C.char
		sh := (*reflect.SliceHeader)(unsafe.Pointer(&nPtrs))
		sh.Cap = l
		sh.Len = l
		sh.Data = uintptr(unsafe.Pointer(bh.bh.target_name))

		for i, p := range nPtrs {
			n[i] = C.GoString(p)
		}

		return
	}
	panic(valueIsNil)
}

func (bh *bamHeader) targetLengths() []uint32 {
	if bh.bh != nil {
		l := int(bh.bh.n_targets)
		var unsafeLengths []uint32
		sh := (*reflect.SliceHeader)(unsafe.Pointer(&unsafeLengths))
		sh.Cap = l
		sh.Len = l
		sh.Data = uintptr(unsafe.Pointer(bh.bh.target_len))

		return append([]uint32(nil), unsafeLengths...)
	}
	panic(valueIsNil)
}

func (bh *bamHeader) header() {}

type textHeader []byte

func (th textHeader) header() {}

const (
	Paired       Flags = paired       // The read is paired in sequencing, no matter whether it is mapped in a pair.
	ProperPair   Flags = properPair   // The read is mapped in a proper pair.
	Unmapped     Flags = unmapped     // The read itself is unmapped; conflictive with BAM_FPROPER_PAIR.
	MateUnmapped Flags = mateUnmapped // The mate is unmapped.
	Reverse      Flags = reverse      // The read is mapped to the reverse strand.
	MateReverse  Flags = mateReverse  // The mate is mapped to the reverse strand.
	Read1        Flags = read1        // This is read1.
	Read2        Flags = read2        // This is read2.
	Secondary    Flags = secondary    // Not primary alignment.
	QCFail       Flags = qCFail       // QC failure.
	Duplicate    Flags = duplicate    // Optical or PCR duplicate.
	paired             = C.BAM_FPAIRED
	properPair         = C.BAM_FPROPER_PAIR
	unmapped           = C.BAM_FUNMAP
	mateUnmapped       = C.BAM_FMUNMAP
	reverse            = C.BAM_FREVERSE
	mateReverse        = C.BAM_FMREVERSE
	read1              = C.BAM_FREAD1
	read2              = C.BAM_FREAD2
	secondary          = C.BAM_FSECONDARY
	qCFail             = C.BAM_FQCFAIL
	duplicate          = C.BAM_FDUP
)

type Flags uint32

// String representation of BAM alignment flags:
//  0x001 - p - Paired
//  0x002 - P - ProperPair
//  0x004 - u - Unmapped
//  0x008 - U - MateUnmapped
//  0x010 - r - Reverse
//  0x020 - R - MateReverse
//  0x040 - 1 - Read1
//  0x080 - 2 - Read2
//  0x100 - s - Secondary
//  0x200 - f - QCFail
//  0x400 - d - Duplicate
func (f Flags) String() string {
	const flags = "DqSLFrRuUAM"
	b := make([]byte, len(flags))
	for i, c := range flags {
		if f&(1<<uint(10-i)) != 0 {
			b[i] = byte(c)
		} else {
			b[i] = '-'
		}
	}

	return string(b)
}
<|endoftext|>


<commit_before>// Copyright 2012 The Summstat Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package summstat

import (
	"math"
	"sort"
)

// The type of samples we track statistics for
type Sample float64

// Implements sort.Interface so that we can sort samples for percentiles
type sampleSlice []Sample

func (s sampleSlice) Len() int {
	return len(s)
}

func (s sampleSlice) Less(i, j int) bool {
	return s[i] < s[j]
}

func (s sampleSlice) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

// A Stats represents descriptive statistics about Samples which are being
// added incrementally.
type Stats struct {
	count     int
	sum       Sample
	sum2      Sample
	max       Sample
	min       Sample
	samples   []Sample
	sorted    bool
	bins      []Sample
	binCounts []int
}

// NewStats returns a new Stats
func NewStats() *Stats {
	return &Stats{
		max: -math.MaxFloat64,
		min: math.MaxFloat64,
	}
}

// AddSample adds a sample value and updates the statistics.
func (s *Stats) AddSample(val Sample) {
	s.count++
	s.sum += val
	s.sum2 += val * val
	if val > s.max {
		s.max = val
	}
	if val < s.min {
		s.min = val
	}
	if len(s.bins) > 0 {
		// TODO: use faster lookup method for large bin counts
		for bin, binVal := range s.bins {
			if val <= binVal {
				s.binCounts[bin]++
				break
			}
		}
	} else {
		s.samples = append(s.samples, val)
		s.sorted = false
	}
}

// Count returns the number of samples added.
func (s Stats) Count() int {
	return s.count
}

// Min returns minimal sample value added.
func (s Stats) Min() Sample {
	if s.min > s.max {
		return 0
	}
	return s.min
}

// Max returns the maximal sample value added.
func (s Stats) Max() Sample {
	if s.min > s.max {
		return 0
	}
	return s.max
}

func (s *Stats) sortSamples() {
	if !s.sorted {
		sort.Sort(sampleSlice(s.samples))
		s.sorted = true
	}
}

// Percentile returns the sample value at the given percentile.
//
// It may not be called after CreateBins, which discards the samples from
// which the percentile is calculated.
func (s Stats) Percentile(pct float64) Sample {
	if len(s.bins) > 0 {
		panic("cannot call Percentile() after CreateBins()")
	}
	if len(s.samples) == 0 {
		return 0
	}
	if pct < 0 {
		panic("pct too small")
	}
	if pct > 1 {
		panic("pct too large")
	}
	s.sortSamples()
	// scale pct into int in [0, len-1]
	// Adding 0.5 turns the implicit floor operation of int() into a rounding operation
	i := int(float64(len(s.samples)-1)*pct + 0.5)
	return s.samples[i]
}

// Median returns the median of the samples.
//
// It may not be called after CreateBins, which discards the samples from
// which the percentile is calculated.
func (s Stats) Median() float64 {
	if len(s.bins) > 0 {
		panic("cannot call Percentile() after CreateBins()")
	}
	l := len(s.samples)
	if l == 0 {
		return 0
	}
	s.sortSamples()
	half, rem := l/2, l%2
	if rem == 0 {
		return (float64(s.samples[half]) + float64(s.samples[half-1])) / 2
	}
	return float64(s.samples[half])
}

// Mean returns the mean of the samples.
func (s Stats) Mean() float64 {
	return float64(s.sum) / float64(s.count)
}

// Stddev returns the standard deviation of the samples.
func (s Stats) Stddev() float64 {
	m := s.Mean()
	return math.Sqrt(float64(s.sum2)/float64(s.count) - m*m)
}

// Spread returns the difference of the maximal and minimal sample values.
func (s Stats) Spread() Sample {
	if s.min > s.max {
		return 0
	}
	return s.max - s.min
}

// CreateBins divides the sample space into nbins bins for tracking counts.
//
// As samples are added, the count for the corresponding bin will be
// incremented and the sample value will not be stored.
//
// This saves memory at the expense of granularity. Percentile() and Median()
// cannot be called after CreateBins() because they are no longer meaningful.
// Instead, use Bin(i) to inspect the distribution of data by bin. Any existing
// stored samples are discarded.
//
// The bins created will be:
//   (-Inf,low], (low, s/nmid+low], (s/nmid+low, 2s/nmid], ..., (high,+Inf)
//   where:
//     s = high - low
//     nmid = nbins-2
//
// Thus, the space (high-low) is divided into nbins-2 equally sized pieces
// and the remaining two bins extend from -math.MaxFloat64 to low and high to
// math.MaxFloat64.
//
// Low must be strictly less than high, so nbins must be at least 3.
func (s *Stats) CreateBins(nbins int, low, high Sample) {
	if high <= low {
		panic("high must be greater than low")
	}
	if nbins < 3 {
		panic("Not enough bins")
	}
	spread := high - low
	s.bins = make([]Sample, nbins)
	s.binCounts = make([]int, nbins)
	for i := 0; i < nbins-1; i++ {
		s.bins[i] = Sample(i)*spread/Sample(nbins-2) + low
	}
	s.bins[nbins-1] = math.MaxFloat64
	// save memory: stop storing samples now that we track by bins
	s.samples = []Sample{}
}

// CreateBinsDiscard is shorthand for calling CreateBins(nbins, ...) with low
// value s.Percentile(discardPct) and high value s.Percentile(1-discardPct)
// with a check to make sure enough samples have been collected to make
// discardPct meaningful (1/discardPct samples are required).
func (s *Stats) CreateBinsDiscard(nbins int, discardPct float64) {
	if len(s.samples) < int(1.0/discardPct) {
		panic("Not enough samples")
	}
	s.CreateBins(nbins, s.Percentile(discardPct), s.Percentile(1.0-discardPct))
}

// Returns the count and low and high ends of the i'th bin.
//
// The bin interval is (low,high]
func (s Stats) Bin(i int) (count int, low, high Sample) {
	count = s.binCounts[i]
	high = s.bins[i]
	if i == 0 {
		low = -math.MaxFloat64
	} else {
		low = s.bins[i-1]
	}
	return
}

// Returns the number of bins
func (s Stats) NBins() int {
	return len(s.bins)
}
<commit_msg>Added AddSampleSince.<commit_after>// Copyright 2012 The Summstat Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package summstat

import (
	"math"
	"sort"
	"time"
)

// The type of samples we track statistics for
type Sample float64

// Implements sort.Interface so that we can sort samples for percentiles
type sampleSlice []Sample

func (s sampleSlice) Len() int {
	return len(s)
}

func (s sampleSlice) Less(i, j int) bool {
	return s[i] < s[j]
}

func (s sampleSlice) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

// A Stats represents descriptive statistics about Samples which are being
// added incrementally.
type Stats struct {
	count     int
	sum       Sample
	sum2      Sample
	max       Sample
	min       Sample
	samples   []Sample
	sorted    bool
	bins      []Sample
	binCounts []int
}

// NewStats returns a new Stats
func NewStats() *Stats {
	return &Stats{
		max: -math.MaxFloat64,
		min: math.MaxFloat64,
	}
}

// AddSample adds a sample value and updates the statistics.
func (s *Stats) AddSample(val Sample) {
	s.count++
	s.sum += val
	s.sum2 += val * val
	if val > s.max {
		s.max = val
	}
	if val < s.min {
		s.min = val
	}
	if len(s.bins) > 0 {
		// TODO: use faster lookup method for large bin counts
		for bin, binVal := range s.bins {
			if val <= binVal {
				s.binCounts[bin]++
				break
			}
		}
	} else {
		s.samples = append(s.samples, val)
		s.sorted = false
	}
}

// AddSampleSince adds the time duration since time t as a sample.
func (s *Stats) AddSampleSince(t time.Time) {
	s.AddSample(Sample(time.Since(t)))
}

// Count returns the number of samples added.
func (s Stats) Count() int {
	return s.count
}

// Min returns minimal sample value added.
func (s Stats) Min() Sample {
	if s.min > s.max {
		return 0
	}
	return s.min
}

// Max returns the maximal sample value added.
func (s Stats) Max() Sample {
	if s.min > s.max {
		return 0
	}
	return s.max
}

func (s *Stats) sortSamples() {
	if !s.sorted {
		sort.Sort(sampleSlice(s.samples))
		s.sorted = true
	}
}

// Percentile returns the sample value at the given percentile.
//
// It may not be called after CreateBins, which discards the samples from
// which the percentile is calculated.
func (s Stats) Percentile(pct float64) Sample {
	if len(s.bins) > 0 {
		panic("cannot call Percentile() after CreateBins()")
	}
	if len(s.samples) == 0 {
		return 0
	}
	if pct < 0 {
		panic("pct too small")
	}
	if pct > 1 {
		panic("pct too large")
	}
	s.sortSamples()
	// scale pct into int in [0, len-1]
	// Adding 0.5 turns the implicit floor operation of int() into a rounding operation
	i := int(float64(len(s.samples)-1)*pct + 0.5)
	return s.samples[i]
}

// Median returns the median of the samples.
//
// It may not be called after CreateBins, which discards the samples from
// which the percentile is calculated.
func (s Stats) Median() float64 {
	if len(s.bins) > 0 {
		panic("cannot call Percentile() after CreateBins()")
	}
	l := len(s.samples)
	if l == 0 {
		return 0
	}
	s.sortSamples()
	half, rem := l/2, l%2
	if rem == 0 {
		return (float64(s.samples[half]) + float64(s.samples[half-1])) / 2
	}
	return float64(s.samples[half])
}

// Mean returns the mean of the samples.
func (s Stats) Mean() float64 {
	return float64(s.sum) / float64(s.count)
}

// Stddev returns the standard deviation of the samples.
func (s Stats) Stddev() float64 {
	m := s.Mean()
	return math.Sqrt(float64(s.sum2)/float64(s.count) - m*m)
}

// Spread returns the difference of the maximal and minimal sample values.
func (s Stats) Spread() Sample {
	if s.min > s.max {
		return 0
	}
	return s.max - s.min
}

// CreateBins divides the sample space into nbins bins for tracking counts.
//
// As samples are added, the count for the corresponding bin will be
// incremented and the sample value will not be stored.
//
// This saves memory at the expense of granularity. Percentile() and Median()
// cannot be called after CreateBins() because they are no longer meaningful.
// Instead, use Bin(i) to inspect the distribution of data by bin. Any existing
// stored samples are discarded.
//
// The bins created will be:
//   (-Inf,low], (low, s/nmid+low], (s/nmid+low, 2s/nmid], ..., (high,+Inf)
//   where:
//     s = high - low
//     nmid = nbins-2
//
// Thus, the space (high-low) is divided into nbins-2 equally sized pieces
// and the remaining two bins extend from -math.MaxFloat64 to low and high to
// math.MaxFloat64.
//
// Low must be strictly less than high, so nbins must be at least 3.
func (s *Stats) CreateBins(nbins int, low, high Sample) {
	if high <= low {
		panic("high must be greater than low")
	}
	if nbins < 3 {
		panic("Not enough bins")
	}
	spread := high - low
	s.bins = make([]Sample, nbins)
	s.binCounts = make([]int, nbins)
	for i := 0; i < nbins-1; i++ {
		s.bins[i] = Sample(i)*spread/Sample(nbins-2) + low
	}
	s.bins[nbins-1] = math.MaxFloat64
	// save memory: stop storing samples now that we track by bins
	s.samples = []Sample{}
}

// CreateBinsDiscard is shorthand for calling CreateBins(nbins, ...) with low
// value s.Percentile(discardPct) and high value s.Percentile(1-discardPct)
// with a check to make sure enough samples have been collected to make
// discardPct meaningful (1/discardPct samples are required).
func (s *Stats) CreateBinsDiscard(nbins int, discardPct float64) {
	if len(s.samples) < int(1.0/discardPct) {
		panic("Not enough samples")
	}
	s.CreateBins(nbins, s.Percentile(discardPct), s.Percentile(1.0-discardPct))
}

// Returns the count and low and high ends of the i'th bin.
//
// The bin interval is (low,high]
func (s Stats) Bin(i int) (count int, low, high Sample) {
	count = s.binCounts[i]
	high = s.bins[i]
	if i == 0 {
		low = -math.MaxFloat64
	} else {
		low = s.bins[i-1]
	}
	return
}

// Returns the number of bins
func (s Stats) NBins() int {
	return len(s.bins)
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The logr Authors.

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

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

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

// Package stdr implements github.com/go-logr/logr.Logger in terms of
// Go's standard log package.
package stdr

import (
	"log"
	"os"

	"github.com/go-logr/logr"
	"github.com/go-logr/logr/funcr"
)

// The global verbosity level.  See SetVerbosity().
var globalVerbosity int

// SetVerbosity sets the global level against which all info logs will be
// compared.  If this is greater than or equal to the "V" of the logger, the
// message will be logged.  A higher value here means more logs will be written.
// The previous verbosity value is returned.  This is not concurrent-safe -
// callers must be sure to call it from only one goroutine.
func SetVerbosity(v int) int {
	old := globalVerbosity
	globalVerbosity = v
	return old
}

// New returns a logr.Logger which is implemented by Go's standard log package,
// or something like it.  If std is nil, this will use a default logger
// instead.
//
// Example: stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile)))
func New(std StdLogger) logr.Logger {
	return NewWithOptions(std, Options{})
}

// NewWithOptions returns a logr.Logger which is implemented by Go's standard
// log package, or something like it.  See New for details.
func NewWithOptions(std StdLogger, opts Options) logr.Logger {
	if std == nil {
		// Go's log.Default() is only available in 1.16 and higher.
		std = log.New(os.Stderr, "", log.LstdFlags)
	}

	if opts.Depth < 0 {
		opts.Depth = 0
	}

	fopts := funcr.Options{
		LogCaller: funcr.MessageClass(opts.LogCaller),
	}

	sl := &logger{
		Formatter: funcr.NewFormatter(fopts),
		std:       std,
	}

	// For skipping our own logger.Info/Error.
	sl.Formatter.AddCallDepth(1 + opts.Depth)

	return logr.New(sl)
}

// Options carries parameters which influence the way logs are generated.
type Options struct {
	// Depth biases the assumed number of call frames to the "true" caller.
	// This is useful when the calling code calls a function which then calls
	// stdr (e.g. a logging shim to another API).  Values less than zero will
	// be treated as zero.
	Depth int

	// LogCaller tells glogr to add a "caller" key to some or all log lines.
	// The glog implementation always logs this information in its per-line
	// header, whether this option is set or not.
	LogCaller MessageClass

	// TODO: add an option to log the date/time
}

// MessageClass indicates which category or categories of messages to consider.
type MessageClass int

const (
	// None ignores all message classes.
	None MessageClass = iota
	// All considers all message classes.
	All
	// Info only considers info messages.
	Info
	// Error only considers error messages.
	Error
)

// StdLogger is the subset of the Go stdlib log.Logger API that is needed for
// this adapter.
type StdLogger interface {
	// Output is the same as log.Output and log.Logger.Output.
	Output(calldepth int, logline string) error
}

type logger struct {
	funcr.Formatter
	std StdLogger
}

var _ logr.LogSink = &logger{}
var _ logr.CallDepthLogSink = &logger{}

func (l logger) Enabled(level int) bool {
	return globalVerbosity >= level
}

func (l logger) Info(level int, msg string, kvList ...interface{}) {
	prefix, args := l.FormatInfo(level, msg, kvList)
	if prefix != "" {
		args = prefix + ": " + args
	}
	_ = l.std.Output(l.Formatter.GetDepth()+1, args)
}

func (l logger) Error(err error, msg string, kvList ...interface{}) {
	prefix, args := l.FormatError(err, msg, kvList)
	if prefix != "" {
		args = prefix + ": " + args
	}
	_ = l.std.Output(l.Formatter.GetDepth()+1, args)
}

func (l logger) WithName(name string) logr.LogSink {
	l.Formatter.AddName(name)
	return &l
}

func (l logger) WithValues(kvList ...interface{}) logr.LogSink {
	l.Formatter.AddValues(kvList)
	return &l
}

func (l logger) WithCallDepth(depth int) logr.LogSink {
	l.Formatter.AddCallDepth(depth)
	return &l
}

// Underlier exposes access to the underlying logging implementation.  Since
// callers only have a logr.Logger, they have to know which implementation is
// in use, so this interface is less of an abstraction and more of way to test
// type conversion.
type Underlier interface {
	GetUnderlying() StdLogger
}

// GetUnderlying returns the StdLogger underneath this logger.  Since StdLogger
// is itself an interface, the result may or may not be a Go log.Logger.
func (l logger) GetUnderlying() StdLogger {
	return l.std
}
<commit_msg>Fix cut-paste ref to glog<commit_after>/*
Copyright 2019 The logr Authors.

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

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

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

// Package stdr implements github.com/go-logr/logr.Logger in terms of
// Go's standard log package.
package stdr

import (
	"log"
	"os"

	"github.com/go-logr/logr"
	"github.com/go-logr/logr/funcr"
)

// The global verbosity level.  See SetVerbosity().
var globalVerbosity int

// SetVerbosity sets the global level against which all info logs will be
// compared.  If this is greater than or equal to the "V" of the logger, the
// message will be logged.  A higher value here means more logs will be written.
// The previous verbosity value is returned.  This is not concurrent-safe -
// callers must be sure to call it from only one goroutine.
func SetVerbosity(v int) int {
	old := globalVerbosity
	globalVerbosity = v
	return old
}

// New returns a logr.Logger which is implemented by Go's standard log package,
// or something like it.  If std is nil, this will use a default logger
// instead.
//
// Example: stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile)))
func New(std StdLogger) logr.Logger {
	return NewWithOptions(std, Options{})
}

// NewWithOptions returns a logr.Logger which is implemented by Go's standard
// log package, or something like it.  See New for details.
func NewWithOptions(std StdLogger, opts Options) logr.Logger {
	if std == nil {
		// Go's log.Default() is only available in 1.16 and higher.
		std = log.New(os.Stderr, "", log.LstdFlags)
	}

	if opts.Depth < 0 {
		opts.Depth = 0
	}

	fopts := funcr.Options{
		LogCaller: funcr.MessageClass(opts.LogCaller),
	}

	sl := &logger{
		Formatter: funcr.NewFormatter(fopts),
		std:       std,
	}

	// For skipping our own logger.Info/Error.
	sl.Formatter.AddCallDepth(1 + opts.Depth)

	return logr.New(sl)
}

// Options carries parameters which influence the way logs are generated.
type Options struct {
	// Depth biases the assumed number of call frames to the "true" caller.
	// This is useful when the calling code calls a function which then calls
	// stdr (e.g. a logging shim to another API).  Values less than zero will
	// be treated as zero.
	Depth int

	// LogCaller tells stdr to add a "caller" key to some or all log lines.
	// Go's log package has options to log this natively, too.
	LogCaller MessageClass

	// TODO: add an option to log the date/time
}

// MessageClass indicates which category or categories of messages to consider.
type MessageClass int

const (
	// None ignores all message classes.
	None MessageClass = iota
	// All considers all message classes.
	All
	// Info only considers info messages.
	Info
	// Error only considers error messages.
	Error
)

// StdLogger is the subset of the Go stdlib log.Logger API that is needed for
// this adapter.
type StdLogger interface {
	// Output is the same as log.Output and log.Logger.Output.
	Output(calldepth int, logline string) error
}

type logger struct {
	funcr.Formatter
	std StdLogger
}

var _ logr.LogSink = &logger{}
var _ logr.CallDepthLogSink = &logger{}

func (l logger) Enabled(level int) bool {
	return globalVerbosity >= level
}

func (l logger) Info(level int, msg string, kvList ...interface{}) {
	prefix, args := l.FormatInfo(level, msg, kvList)
	if prefix != "" {
		args = prefix + ": " + args
	}
	_ = l.std.Output(l.Formatter.GetDepth()+1, args)
}

func (l logger) Error(err error, msg string, kvList ...interface{}) {
	prefix, args := l.FormatError(err, msg, kvList)
	if prefix != "" {
		args = prefix + ": " + args
	}
	_ = l.std.Output(l.Formatter.GetDepth()+1, args)
}

func (l logger) WithName(name string) logr.LogSink {
	l.Formatter.AddName(name)
	return &l
}

func (l logger) WithValues(kvList ...interface{}) logr.LogSink {
	l.Formatter.AddValues(kvList)
	return &l
}

func (l logger) WithCallDepth(depth int) logr.LogSink {
	l.Formatter.AddCallDepth(depth)
	return &l
}

// Underlier exposes access to the underlying logging implementation.  Since
// callers only have a logr.Logger, they have to know which implementation is
// in use, so this interface is less of an abstraction and more of way to test
// type conversion.
type Underlier interface {
	GetUnderlying() StdLogger
}

// GetUnderlying returns the StdLogger underneath this logger.  Since StdLogger
// is itself an interface, the result may or may not be a Go log.Logger.
func (l logger) GetUnderlying() StdLogger {
	return l.std
}
<|endoftext|>


<commit_before>package sms

import (
	"encoding/xml"
	"fmt"
	"log"
	"os"

	xco "github.com/mndrix/go-xco"
)

// xmppProcess is the piece which interacts with the XMPP network and
// converts those communications into values which the rest of
// sms-over-xmpp can understand.
type xmppProcess struct {
	// where to connect to the XMPP server
	host string
	port int

	// credentials for XMPP auth
	name   string
	secret string

	// channels for communicating with the Gateway process
	gatewayRx chan<- *xco.Message
	gatewayTx <-chan *xco.Message
}

// runXmppComponent creates a goroutine for sending and receiving XMPP
// stanzas.  it returns a channel for monitoring the goroutine's
// health.  if that channel closes, the XMPP process has died.
func (x *xmppProcess) run() <-chan struct{} {
	opts := xco.Options{
		Name:         x.name,
		SharedSecret: x.secret,
		Address:      fmt.Sprintf("%s:%d", x.host, x.port),
		Logger:       log.New(os.Stderr, "", log.LstdFlags),
	}

	healthCh := make(chan struct{})
	go x.loop(opts, healthCh)
	return healthCh
}

func (x *xmppProcess) loop(opts xco.Options, healthCh chan<- struct{}) {
	defer func() { close(healthCh) }()

	c, err := xco.NewComponent(opts)
	if err != nil {
		log.Printf("can't create internal XMPP component: %s", err)
		return
	}

	tx, rx, errx := c.RunAsync()
	for {
		select {
		case st := <-rx:
			switch stanza := st.(type) {
			case *xco.Message:
				log.Printf("Message: %+v", stanza)
				if stanza.Body == "" {
					log.Printf("  ignoring message with empty body")
					break
				}
				go func() { x.gatewayRx <- stanza }()
			case *xco.Iq:
				if stanza.IsDiscoInfo() {
					log.Printf("Disco: %+v", stanza)
					ids, features := x.describeService()
					stanza, err = stanza.DiscoInfoReply(ids, features)
					if err == nil {
						go func() { tx <- stanza }()
					}
				} else {
					log.Printf("Iq: %+v", stanza)
				}
			case *xco.Presence:
				log.Printf("Presence: %+v", stanza)
				switch stanza.Type {
				case "probe":
					stanza = x.presenceAvailable(stanza)
					go func() { tx <- stanza }()
				case "subscribe", "unsubscribe":
					stanzas := x.handleSubscription(stanza)
					go func() {
						for _, stanza := range stanzas {
							tx <- stanza
						}
					}()
				}
			case *xml.StartElement:
				log.Printf("Unknown: %+v", stanza)
			default:
				panic(fmt.Sprintf("Unexpected stanza type: %#v", st))
			}
		case stanza := <-x.gatewayTx:
			go func() { tx <- stanza }()
		case err = <-errx:
		}

		if err != nil {
			break
		}
	}

	log.Printf("lost XMPP connection: %s", err)
}

func (x *xmppProcess) describeService() ([]xco.DiscoIdentity, []xco.DiscoFeature) {
	ids := []xco.DiscoIdentity{
		{
			Category: "gateway",
			Type:     "sms",
			Name:     "SMS over XMPP",
		},
	}
	features := []xco.DiscoFeature{
		{
			Var: "urn:xmpp:receipts",
		},
	}
	return ids, features
}

func (x *xmppProcess) presenceAvailable(p *xco.Presence) *xco.Presence {
	stanza := &xco.Presence{
		Header: xco.Header{
			From: p.Header.To,
			To:   p.Header.From,
			ID:   NewId(),
		},
	}
	return stanza
}

func (x *xmppProcess) handleSubscription(p *xco.Presence) []*xco.Presence {
	// RFC says to use full JIDs
	p.Header.To.ResourcePart = ""
	p.Header.From.ResourcePart = ""

	stanza := &xco.Presence{
		Header: xco.Header{
			From: p.Header.To,
			To:   p.Header.From,
			ID:   NewId(),
		},
	}
	switch p.Type {
	case "subscribe":
		stanza.Type = "subscribed"
		return []*xco.Presence{
			stanza,
			x.presenceAvailable(p),   // tell user we're available
			x.requestSubscription(p), // request reciprocal subscription
		}
	case "unsubscribe":
		stanza.Type = "unavailable"
		return []*xco.Presence{stanza}
	}

	return nil
}

func (x *xmppProcess) requestSubscription(p *xco.Presence) *xco.Presence {
	stanza := &xco.Presence{
		Header: xco.Header{
			From: p.Header.To,
			To:   p.Header.From,
			ID:   NewId(),
		},
		Type: "subscribe",
	}
	return stanza
}
<commit_msg>Rename handleSubscription<commit_after>package sms

import (
	"encoding/xml"
	"fmt"
	"log"
	"os"

	xco "github.com/mndrix/go-xco"
)

// xmppProcess is the piece which interacts with the XMPP network and
// converts those communications into values which the rest of
// sms-over-xmpp can understand.
type xmppProcess struct {
	// where to connect to the XMPP server
	host string
	port int

	// credentials for XMPP auth
	name   string
	secret string

	// channels for communicating with the Gateway process
	gatewayRx chan<- *xco.Message
	gatewayTx <-chan *xco.Message
}

// runXmppComponent creates a goroutine for sending and receiving XMPP
// stanzas.  it returns a channel for monitoring the goroutine's
// health.  if that channel closes, the XMPP process has died.
func (x *xmppProcess) run() <-chan struct{} {
	opts := xco.Options{
		Name:         x.name,
		SharedSecret: x.secret,
		Address:      fmt.Sprintf("%s:%d", x.host, x.port),
		Logger:       log.New(os.Stderr, "", log.LstdFlags),
	}

	healthCh := make(chan struct{})
	go x.loop(opts, healthCh)
	return healthCh
}

func (x *xmppProcess) loop(opts xco.Options, healthCh chan<- struct{}) {
	defer func() { close(healthCh) }()

	c, err := xco.NewComponent(opts)
	if err != nil {
		log.Printf("can't create internal XMPP component: %s", err)
		return
	}

	tx, rx, errx := c.RunAsync()
	for {
		select {
		case st := <-rx:
			switch stanza := st.(type) {
			case *xco.Message:
				log.Printf("Message: %+v", stanza)
				if stanza.Body == "" {
					log.Printf("  ignoring message with empty body")
					break
				}
				go func() { x.gatewayRx <- stanza }()
			case *xco.Iq:
				if stanza.IsDiscoInfo() {
					log.Printf("Disco: %+v", stanza)
					ids, features := x.describeService()
					stanza, err = stanza.DiscoInfoReply(ids, features)
					if err == nil {
						go func() { tx <- stanza }()
					}
				} else {
					log.Printf("Iq: %+v", stanza)
				}
			case *xco.Presence:
				log.Printf("Presence: %+v", stanza)
				switch stanza.Type {
				case "probe":
					stanza = x.presenceAvailable(stanza)
					go func() { tx <- stanza }()
				case "subscribe", "unsubscribe":
					stanzas := x.handleSubscribeUnsubscribe(stanza)
					go func() {
						for _, stanza := range stanzas {
							tx <- stanza
						}
					}()
				}
			case *xml.StartElement:
				log.Printf("Unknown: %+v", stanza)
			default:
				panic(fmt.Sprintf("Unexpected stanza type: %#v", st))
			}
		case stanza := <-x.gatewayTx:
			go func() { tx <- stanza }()
		case err = <-errx:
		}

		if err != nil {
			break
		}
	}

	log.Printf("lost XMPP connection: %s", err)
}

func (x *xmppProcess) describeService() ([]xco.DiscoIdentity, []xco.DiscoFeature) {
	ids := []xco.DiscoIdentity{
		{
			Category: "gateway",
			Type:     "sms",
			Name:     "SMS over XMPP",
		},
	}
	features := []xco.DiscoFeature{
		{
			Var: "urn:xmpp:receipts",
		},
	}
	return ids, features
}

func (x *xmppProcess) presenceAvailable(p *xco.Presence) *xco.Presence {
	stanza := &xco.Presence{
		Header: xco.Header{
			From: p.Header.To,
			To:   p.Header.From,
			ID:   NewId(),
		},
	}
	return stanza
}

func (x *xmppProcess) handleSubscribeUnsubscribe(p *xco.Presence) []*xco.Presence {
	// RFC says to use full JIDs
	p.Header.To.ResourcePart = ""
	p.Header.From.ResourcePart = ""

	stanza := &xco.Presence{
		Header: xco.Header{
			From: p.Header.To,
			To:   p.Header.From,
			ID:   NewId(),
		},
	}
	switch p.Type {
	case "subscribe":
		stanza.Type = "subscribed"
		return []*xco.Presence{
			stanza,
			x.presenceAvailable(p),   // tell user we're available
			x.requestSubscription(p), // request reciprocal subscription
		}
	case "unsubscribe":
		stanza.Type = "unavailable"
		return []*xco.Presence{stanza}
	}

	return nil
}

func (x *xmppProcess) requestSubscription(p *xco.Presence) *xco.Presence {
	stanza := &xco.Presence{
		Header: xco.Header{
			From: p.Header.To,
			To:   p.Header.From,
			ID:   NewId(),
		},
		Type: "subscribe",
	}
	return stanza
}
<|endoftext|>


<commit_before>package tail

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strings"
	"time"
)

type Tail struct {
	fileName     string
	pollInterval time.Duration
	file         *os.File
	stat         os.FileInfo
	reader       *bufio.Reader
}

func NewTail(fileName string, offset int64, pollInterval time.Duration) (tail Tail, err error) {
	tail.fileName = fileName
	tail.pollInterval = pollInterval
	tail.file, err = os.Open(fileName)
	if err != nil {
		return tail, fmt.Errorf("failed to open file %s: %s", fileName, err)
	}

	tail.stat, err = tail.file.Stat()
	if err != nil {
		return tail, fmt.Errorf("failed to stat file %s: %s", fileName, err)
	}

	if offset != 0 {
		_, err = tail.file.Seek(offset, os.SEEK_SET)
		if err != nil {
			return tail, fmt.Errorf("failed to seek file %s: %s", fileName, err)
		}
	}

	tail.reader = bufio.NewReader(tail.file)

	return tail, nil
}

func (tail *Tail) ReadLine() string {
	var linePart string
	for {
		line, err := tail.reader.ReadString('\n')
		if err == nil {
			if linePart != "" {
				line = linePart + line
				linePart = ""
			}
			return strings.TrimRight(line, "\n")
		}
		linePart = line
		tail.waitForChanges()
	}
}

func (tail *Tail) waitForChanges() {
	log.Printf("waiting for changes %s", tail.fileName)
	var stat os.FileInfo
	var err error
	for {
		time.Sleep(tail.pollInterval)
		stat, err = os.Stat(tail.fileName)
		if err != nil {
			log.Printf("failed to stat file %s: %s", tail.fileName, err)
			continue
		}
		if !os.SameFile(tail.stat, stat) {
			log.Printf("file was moved %s", tail.fileName)
			tail.file.Close()
			tail.file, err = os.Open(tail.fileName)
			if err != nil {
				log.Printf("failed to open file %s: %s", tail.fileName, err)
				continue
			}
			tail.reader = bufio.NewReader(tail.file)
			break
		}
		if stat.Size() < tail.stat.Size() {
			log.Printf("file was truncated %s", tail.fileName)
			_, err = tail.file.Seek(0, os.SEEK_SET)
			if err != nil {
				log.Printf("failed to seek file %s: %s", tail.fileName, err)
				continue
			}
			break
		}
		if stat.Size() > tail.stat.Size() {
			log.Printf("file was appended %s", tail.fileName)
			break
		}
	}
	tail.stat = stat
}

func (tail *Tail) Close() {
	if tail.file != nil {
		tail.file.Close()
	}
}

func (tail *Tail) Offset() (int64, error) {
	offset, err := tail.file.Seek(0, os.SEEK_CUR)
	if err != nil {
		return 0, err
	}
	return offset - int64(tail.reader.Buffered()), nil
}
<commit_msg>Добавил функцию RequestEmptyLine для принудительного завершения ReadLine с пустой строкой + отключил говорливый дебаг.<commit_after>package tail

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strings"
	"time"
)

type Tail struct {
	fileName     string
	pollInterval time.Duration
	file         *os.File
	stat         os.FileInfo
	reader       *bufio.Reader
	rel          bool
}

func NewTail(fileName string, offset int64, pollInterval time.Duration) (tail Tail, err error) {
	tail.fileName = fileName
	tail.pollInterval = pollInterval
	tail.file, err = os.Open(fileName)
	tail.rel = false
	if err != nil {
		return tail, fmt.Errorf("failed to open file %s: %s", fileName, err)
	}

	tail.stat, err = tail.file.Stat()
	if err != nil {
		return tail, fmt.Errorf("failed to stat file %s: %s", fileName, err)
	}

	if offset != 0 {
		_, err = tail.file.Seek(offset, os.SEEK_SET)
		if err != nil {
			return tail, fmt.Errorf("failed to seek file %s: %s", fileName, err)
		}
	}

	tail.reader = bufio.NewReader(tail.file)

	return tail, nil
}

func (tail *Tail) ReadLine() string {
	var linePart string
	for {
		if tail.rel {
			log.Println("tailer: Return empty line")
			tail.rel = false
			return ""
		}
		line, err := tail.reader.ReadString('\n')
		if err == nil {
			if linePart != "" {
				line = linePart + line
				linePart = ""
			}
			return strings.TrimRight(line, "\n")
		}
		linePart = line
		tail.waitForChanges()
	}
}

// Request for next or current call to ReadLine returns the empty line
func (tail *Tail) RequestEmptyLine() {
	tail.rel = true
}

func (tail *Tail) waitForChanges() {
	var stat os.FileInfo
	var err error
	for {
		time.Sleep(tail.pollInterval)
		stat, err = os.Stat(tail.fileName)
		if err != nil {
			log.Printf("failed to stat file %s: %s", tail.fileName, err)
			continue
		}
		if !os.SameFile(tail.stat, stat) {
			log.Printf("file was moved %s", tail.fileName)
			tail.file.Close()
			tail.file, err = os.Open(tail.fileName)
			if err != nil {
				log.Printf("failed to open file %s: %s", tail.fileName, err)
				continue
			}
			tail.reader = bufio.NewReader(tail.file)
			break
		}
		if stat.Size() < tail.stat.Size() {
			log.Printf("file was truncated %s", tail.fileName)
			_, err = tail.file.Seek(0, os.SEEK_SET)
			if err != nil {
				log.Printf("failed to seek file %s: %s", tail.fileName, err)
				continue
			}
			break
		}
		if stat.Size() > tail.stat.Size() {
			break
		}
		if tail.rel {
			break
		}
	}
	tail.stat = stat
}

func (tail *Tail) Close() {
	if tail.file != nil {
		tail.file.Close()
	}
}

func (tail *Tail) Offset() (int64, error) {
	offset, err := tail.file.Seek(0, os.SEEK_CUR)
	if err != nil {
		return 0, err
	}
	return offset - int64(tail.reader.Buffered()), nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"
	"bytes"

	"github.com/JustinBeckwith/go-yelp/yelp"
	"github.com/guregu/null"
	"github.com/line/line-bot-sdk-go/linebot"
)

var o *yelp.AuthOptions

type UrlShortener struct {
	ShortUrl    string
	OriginalUrl string
}

func yelp_init() {
	rand.Seed(time.Now().UnixNano())

	// check environment variables
	o = &yelp.AuthOptions{
		ConsumerKey:       os.Getenv("CONSUMER_KEY"),
		ConsumerSecret:    os.Getenv("CONSUMER_SECRET"),
		AccessToken:       os.Getenv("ACCESS_TOKEN"),
		AccessTokenSecret: os.Getenv("ACCESS_TOKEN_SECRET"),
	}

	if o.ConsumerKey == "" || o.ConsumerSecret == "" || o.AccessToken == "" || o.AccessTokenSecret == "" {
		log.Fatal("Wrong environment setting about yelp-api-keys")
	}
}

func yelp_parse(bot *linebot.Client, token string, loc *linebot.LocationMessage, food string) {
	var err error

	// create a new yelp client with the auth keys
	client := yelp.New(o, nil)

	if loc == nil {
		// make a simple query for food and location
		results, err := client.DoSimpleSearch(food, "台北市大安區")
		if err == nil {
			log.Println("loc nil")
			yelp_parse_result(bot, token, results)
		}
	} else {
		// Build an advanced set of search criteria that include
		// general options, and coordinate options.
		s := yelp.SearchOptions{
			GeneralOptions: &yelp.GeneralOptions{
				Term: food,
			},
			LocationOptions: &yelp.CoordinateOptions{
				Location: loc.Address,
			},
			CoordinateOptions: &yelp.CoordinateOptions{
				Latitude:  null.FloatFrom(loc.Latitude),
				Longitude: null.FloatFrom(loc.Longitude),
			},
			LocaleOptions: &yelp.CoordinateOptions{
				lang: "zh",
			},
		}

		// Perform the search using the search options
		results, err := client.DoSearch(s)
		if err == nil {
			log.Println(fmt.Sprintf("loc:(%f, %f)", loc.Latitude, loc.Longitude))
			yelp_parse_result(bot, token, results)
		}
	}
	if err != nil {
		log.Println(err)
		_, err = bot.ReplyMessage(token, linebot.NewTextMessage("查無資料！")).Do()
	}
}

func yelp_parse_result(bot *linebot.Client, token string, results yelp.SearchResult) {
	var err error
//	var msgs []linebot.Message
	var outmsg bytes.Buffer

	for i := 0; i < 3; i++ {
		//i := 0
		//if results.Total >= 20 {
		//	i = rand.Intn(20)
		//} else if results.Total >= 10 {
		//	i = rand.Intn(10)
		//} else if results.Total > j {
		//	i = j
		//} else if results.Total <= j && results.Total != 0 {
		if ( results.Total <= i) {
			_, err = bot.ReplyMessage(token, linebot.NewTextMessage("已無更多資料！")).Do()
			break
		}
		urlOrig := UrlShortener{}
		urlOrig.short(results.Businesses[i].MobileURL)
		address := strings.Join(results.Businesses[i].Location.DisplayAddress, ",")
		//var largeImageURL = strings.Replace(results.Businesses[i].ImageURL, "ms.jpg", "l.jpg", 1)

		outmsg.WriteString("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n地址："+address+"\n更多資訊："+urlOrig.ShortUrl+"\n")

//		_, err = bot.ReplyMessage(token, linebot.NewImageMessage(largeImageURL, largeImageURL), linebot.NewTextMessage("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n更多資訊："+urlOrig.ShortUrl), linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude)) ).Do()
//		msgs = append(msgs, linebot.NewImageMessage(largeImageURL, largeImageURL))
//		msgs = append(msgs, linebot.NewTextMessage("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n更多資訊："+urlOrig.ShortUrl))
//		msgs = append(msgs, linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude)))
//		_, err = bot.ReplyMessage(token, linebot.NewImageMessage(largeImageURL, largeImageURL)).Do()
//		_, err = bot.ReplyMessage(token, linebot.NewTextMessage("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n更多資訊："+urlOrig.ShortUrl)).Do()
//		_, err = bot.ReplyMessage(token, linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude))).Do()
	}

	if outmsg.String() != "" {
		_, err = bot.ReplyMessage(token, linebot.NewTextMessage(outmsg.String())).Do()
	}
	if err != nil {
		log.Println(err)
	}
}

func getResponseData(urlOrig string) string {
	response, err := http.Get(urlOrig)
	if err != nil {
		log.Println(err)
	}
	defer response.Body.Close()
	contents, err := ioutil.ReadAll(response.Body)
	return string(contents)
}

func isGdShortener(urlOrig string) (string, string) {
	escapedUrl := url.QueryEscape(urlOrig)
	isGdUrl := fmt.Sprintf("http://is.gd/create.php?url=%s&format=simple", escapedUrl)
	return getResponseData(isGdUrl), urlOrig
}

func (u *UrlShortener) short(urlOrig string) *UrlShortener {
	shortUrl, originalUrl := isGdShortener(urlOrig)
	u.ShortUrl = shortUrl
	u.OriginalUrl = originalUrl
	return u
}
<commit_msg>test location<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"
	"bytes"

	"github.com/JustinBeckwith/go-yelp/yelp"
	"github.com/guregu/null"
	"github.com/line/line-bot-sdk-go/linebot"
)

var o *yelp.AuthOptions

type UrlShortener struct {
	ShortUrl    string
	OriginalUrl string
}

func yelp_init() {
	rand.Seed(time.Now().UnixNano())

	// check environment variables
	o = &yelp.AuthOptions{
		ConsumerKey:       os.Getenv("CONSUMER_KEY"),
		ConsumerSecret:    os.Getenv("CONSUMER_SECRET"),
		AccessToken:       os.Getenv("ACCESS_TOKEN"),
		AccessTokenSecret: os.Getenv("ACCESS_TOKEN_SECRET"),
	}

	if o.ConsumerKey == "" || o.ConsumerSecret == "" || o.AccessToken == "" || o.AccessTokenSecret == "" {
		log.Fatal("Wrong environment setting about yelp-api-keys")
	}
}

func yelp_parse(bot *linebot.Client, token string, loc *linebot.LocationMessage, food string) {
	var err error

	// create a new yelp client with the auth keys
	client := yelp.New(o, nil)

	if loc == nil {
		// make a simple query for food and location
		results, err := client.DoSimpleSearch(food, "台北市大安區")
		if err == nil {
			log.Println("loc nil")
			yelp_parse_result(bot, token, results)
		}
	} else {
		// Build an advanced set of search criteria that include
		// general options, and coordinate options.
		s := yelp.SearchOptions{
			GeneralOptions: &yelp.GeneralOptions{
				Term: food,
			},
			LocationOptions: &yelp.LocationOptions{
				Location: loc.Address,
			},
			CoordinateOptions: &yelp.CoordinateOptions{
				Latitude:  null.FloatFrom(loc.Latitude),
				Longitude: null.FloatFrom(loc.Longitude),
			},
			LocaleOptions: &yelp.LocaleOptions{
				lang: "zh",
			},
		}

		// Perform the search using the search options
		results, err := client.DoSearch(s)
		if err == nil {
			log.Println(fmt.Sprintf("loc:(%f, %f)", loc.Latitude, loc.Longitude))
			yelp_parse_result(bot, token, results)
		}
	}
	if err != nil {
		log.Println(err)
		_, err = bot.ReplyMessage(token, linebot.NewTextMessage("查無資料！")).Do()
	}
}

func yelp_parse_result(bot *linebot.Client, token string, results yelp.SearchResult) {
	var err error
//	var msgs []linebot.Message
	var outmsg bytes.Buffer

	for i := 0; i < 3; i++ {
		//i := 0
		//if results.Total >= 20 {
		//	i = rand.Intn(20)
		//} else if results.Total >= 10 {
		//	i = rand.Intn(10)
		//} else if results.Total > j {
		//	i = j
		//} else if results.Total <= j && results.Total != 0 {
		if ( results.Total <= i) {
			_, err = bot.ReplyMessage(token, linebot.NewTextMessage("已無更多資料！")).Do()
			break
		}
		urlOrig := UrlShortener{}
		urlOrig.short(results.Businesses[i].MobileURL)
		address := strings.Join(results.Businesses[i].Location.DisplayAddress, ",")
		//var largeImageURL = strings.Replace(results.Businesses[i].ImageURL, "ms.jpg", "l.jpg", 1)

		outmsg.WriteString("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n地址："+address+"\n更多資訊："+urlOrig.ShortUrl+"\n")

//		_, err = bot.ReplyMessage(token, linebot.NewImageMessage(largeImageURL, largeImageURL), linebot.NewTextMessage("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n更多資訊："+urlOrig.ShortUrl), linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude)) ).Do()
//		msgs = append(msgs, linebot.NewImageMessage(largeImageURL, largeImageURL))
//		msgs = append(msgs, linebot.NewTextMessage("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n更多資訊："+urlOrig.ShortUrl))
//		msgs = append(msgs, linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude)))
//		_, err = bot.ReplyMessage(token, linebot.NewImageMessage(largeImageURL, largeImageURL)).Do()
//		_, err = bot.ReplyMessage(token, linebot.NewTextMessage("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n更多資訊："+urlOrig.ShortUrl)).Do()
//		_, err = bot.ReplyMessage(token, linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude))).Do()
	}

	if outmsg.String() != "" {
		_, err = bot.ReplyMessage(token, linebot.NewTextMessage(outmsg.String())).Do()
	}
	if err != nil {
		log.Println(err)
	}
}

func getResponseData(urlOrig string) string {
	response, err := http.Get(urlOrig)
	if err != nil {
		log.Println(err)
	}
	defer response.Body.Close()
	contents, err := ioutil.ReadAll(response.Body)
	return string(contents)
}

func isGdShortener(urlOrig string) (string, string) {
	escapedUrl := url.QueryEscape(urlOrig)
	isGdUrl := fmt.Sprintf("http://is.gd/create.php?url=%s&format=simple", escapedUrl)
	return getResponseData(isGdUrl), urlOrig
}

func (u *UrlShortener) short(urlOrig string) *UrlShortener {
	shortUrl, originalUrl := isGdShortener(urlOrig)
	u.ShortUrl = shortUrl
	u.OriginalUrl = originalUrl
	return u
}
<|endoftext|>


<commit_before>package main

import (
	"sync"
	"io"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"golang.org/x/net/html"
	"os"
	"os/signal"
	"sort"
)

var mapChanges chan func(m map[url.URL]Page)

type Page struct {
	Url *url.URL
	Body io.ReadCloser
	Links []*Page
	Views int
}

func (page *Page) get() error {
	r, err := http.Get(page.Url.String())
	if err != nil {
		return err
	}

	page.Body = r.Body

	return nil
}

func (page *Page) genLinks(out chan<- *Page) {
	go func() {
		defer page.Body.Close()
		z := html.NewTokenizer(page.Body)
		for {
			tt := z.Next()
			switch tt {
			case html.ErrorToken:
				return
			case html.StartTagToken, html.EndTagToken:
				tn, _ := z.TagName()
				if len(tn) == 1 && tn[0] == 'a' {
					more := true
					for more {
						key, val, m := z.TagAttr()
						more = m
						if string(key) == "href" {
							// Parse URL
							ur, err := page.Url.Parse(string(val))
							if err != nil {
								break
							}

							go func() {
								mapChanges <-func(m map[url.URL]Page) {
									// Only one copy of the page exits,
									// and it lives in the map
									p := m[*ur]
									// Update it with the new url
									// in case it isn't set
									p.Url = ur
									p.Views++
									m[*ur] = p
									// Add link to this page's children
									page.Links = append(page.Links, &p)
									// Send it on to be updated further
									// if it hasn't been seen yet
									if p.Views == 0 {
										out <- &p
									}
								}
							}()
						}
					}
				}
			}
		}
	}()
}

func ConnectPages(pages <-chan *Page) <-chan *Page {
	ch := make(chan *Page)

	// Create goroutines to push ints to channel
	// regardless of order recieved
	go func() {
		var wg sync.WaitGroup
		for page := range pages {
			c := make(chan *Page)
			wg.Add(1)
			go func() {
				defer wg.Done() // call wg.Done() after doIO completes
				page := <-c
				// Update page by adding page source
				err := page.get()
				if err != nil {
					return
				}
				ch <- page
			}()
			c <- page
		}

		// Close channel as soon as all the goroutines
		// above have completed
		wg.Wait()
		close(ch)
	}()

	// return as a read-only channel
	return ch
}

func LinkGrepPages(in <-chan *Page) <-chan *Page  {
	ch := make(chan *Page)

	// Create goroutines to push ints to channel
	// regardless of order recieved
	go func() {
		for page := range in {
			// Parse html for anchor tags
			page.genLinks(ch)
		}
	}()

	return ch
}

type pageList []Page

func (p pageList) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}

func (p pageList) Len() int {
	return len(p)
}

func (p pageList) Less(i, j int) bool {
	return p[i].Views < p[j].Views
}

func main() {
	urloverseer := make(chan *Page)

	mapChanges = make(chan func(m map[url.URL]Page))
	frequencyMap := make(map[url.URL]Page)
	go func() {
		for f := range mapChanges {
			f(frequencyMap)
		}
	}()

	// Page pipeline
	pages := ConnectPages(urloverseer)
	urls := LinkGrepPages(pages)

	var rootPage *Page
	if len(os.Args) == 2 {
		ur, err := url.Parse(os.Args[1])
		if err != nil {
			log.Fatal(err)
		}

		// Initial page to start crawler
		rootPage = &Page{Url: ur}
		urloverseer <- rootPage
	} else {
		fmt.Println("Needs url to start crawler")
		os.Exit(1)
	}

	c := make(chan os.Signal)
	signal.Notify(c, os.Interrupt)

	for {
		select {
		case _ = <-c:
			pages := make(pageList, len(frequencyMap))
			i := 0
			for _, p := range frequencyMap {
				pages[i] = p
				i++
			}
			sort.Sort(pages)

			for i, p := range pages {
				fmt.Printf("%d: Views %d: %s\n", i, p.Views, p.Url)
			}

			return
		case page := <- urls:
			urloverseer <- page
		}
	}
}
<commit_msg>works<commit_after>package main

import (
	"fmt"
	"golang.org/x/net/html"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"sort"
	"sync"
	"time"
)

var requestTimeout = time.Duration(5 * time.Second)

type Page struct {
	Url   *url.URL
	Body  io.ReadCloser
	Views int
}

func (page *Page) get() error {
	transport := http.Transport{
		Dial: func(network, addr string) (net.Conn, error) {
			return net.DialTimeout(network, addr, requestTimeout)
		},
	}

	client := http.Client{
		Transport: &transport,
	}

	resp, err := client.Get(page.Url.String())
	if err != nil {
		return err
	}

	page.Body = resp.Body

	return nil
}

func (page *Page) genLinks(wg sync.WaitGroup, out chan<- *Page) {
	go func() {
		defer page.Body.Close()
		defer wg.Done()
		z := html.NewTokenizer(page.Body)
		for {
			tt := z.Next()
			switch tt {
			case html.ErrorToken:
				return
			case html.StartTagToken, html.EndTagToken:
				tn, _ := z.TagName()
				if len(tn) == 1 && tn[0] == 'a' {
					more := true
					for more {
						key, val, m := z.TagAttr()
						more = m
						if string(key) == "href" {
							// Parse URL
							ur, err := page.Url.Parse(string(val))
							if err != nil {
								break
							}

							var p Page
							p.Url = ur
							out <- &p
						}
					}
				}
			}
		}
	}()
}

func ConnectPages(pages <-chan *Page) <-chan *Page {
	ch := make(chan *Page)

	// Create goroutines to push ints to channel
	// regardless of order recieved
	go func() {
		var wg sync.WaitGroup
		for page := range pages {
			c := make(chan *Page)
			wg.Add(1)
			go func() {
				defer wg.Done() // call wg.Done() after doIO completes
				page := <-c
				// Update page by adding page source
				err := page.get()
				if err != nil {
					return
				}
				ch <- page
			}()
			c <- page
		}

		// Close channel as soon as all the goroutines
		// above have completed
		wg.Wait()
		close(ch)
	}()

	// return as a read-only channel
	return ch
}

func LinkGrepPages(in <-chan *Page) <-chan *Page {
	ch := make(chan *Page)
	var wg sync.WaitGroup

	// Create goroutines to push ints to channel
	// regardless of order recieved
	go func() {
		for page := range in {
			wg.Add(1)
			// Parse html for anchor tags
			page.genLinks(wg, ch)
		}

		wg.Wait()
		close(ch)
	}()

	return ch
}

type pageList []Page

func (p pageList) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}

func (p pageList) Len() int {
	return len(p)
}

func (p pageList) Less(i, j int) bool {
	return p[i].Views < p[j].Views
}

func main() {
	urloverseer := make(chan *Page)

	frequencyMap := make(map[url.URL]Page)

	// Page pipeline
	pages := ConnectPages(urloverseer)
	urls := LinkGrepPages(pages)

	if len(os.Args) > 1 {
		for _, arg := range os.Args {
			ur, err := url.Parse(arg)
			if err != nil {
				log.Fatal(err)
			}

			// Initial page to start crawler
			urloverseer <- &Page{Url: ur}
		}
	} else {
		fmt.Println("Needs url to start crawler")
		return
	}

	done := make(chan os.Signal)
	signal.Notify(done, os.Interrupt)

	for {
		select {
		case <-done:
			fmt.Println("Preparing results")
			pages := make(pageList, len(frequencyMap))
			i := 0
			for u, p := range frequencyMap {
				pages[i] = p
				// Save memory by removing
				delete(frequencyMap, u)
				i++
			}
			sort.Sort(pages)

			sz := 0
			if len(pages) > 100 {
				sz = len(pages) - 100
			}

			for i, p := range pages[sz:] {
				fmt.Printf("%d: Views %d: %s\n", 100-i, p.Views, p.Url)
			}
			return
		case page := <-urls:

			// Only request absolute urls, and no stupid javascript
			js := "javascript"
			if !page.Url.IsAbs() || (len(page.Url.String()) >= len(js) &&  page.Url.String()[:len(js)] == js) {
				break
			}

			// Add to map
			p := frequencyMap[*page.Url]
			// Update view count
			page.Views = p.Views + 1
			frequencyMap[*page.Url] = *page

			// fmt.Printf("Url (%d): %s\n", page.Views, page.Url.String())

			// Only visit a website once
			if page.Views == 1 {
				urloverseer <- page
			}
		}
	}
}
<|endoftext|>