<commit_before>// Package torrent provides a BitTorrent client implementation.
package torrent

import (
	"context"
	"crypto/tls"
	"errors"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/boltdb/bolt"
	"github.com/cenkalti/rain/internal/bitfield"
	"github.com/cenkalti/rain/internal/blocklist"
	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/internal/piececache"
	"github.com/cenkalti/rain/internal/resolver"
	"github.com/cenkalti/rain/internal/resourcemanager"
	"github.com/cenkalti/rain/internal/resumer/boltdbresumer"
	"github.com/cenkalti/rain/internal/semaphore"
	"github.com/cenkalti/rain/internal/tracker"
	"github.com/cenkalti/rain/internal/trackermanager"
	"github.com/juju/ratelimit"
	"github.com/mitchellh/go-homedir"
	"github.com/nictuku/dht"
)

var (
	sessionBucket         = []byte("session")
	torrentsBucket        = []byte("torrents")
	blocklistKey          = []byte("blocklist")
	blocklistTimestampKey = []byte("blocklist-timestamp")
)

// Session contains torrents, DHT node, caches and other data structures shared by multiple torrents.
type Session struct {
	config         Config
	db             *bolt.DB
	resumer        *boltdbresumer.Resumer
	log            logger.Logger
	extensions     [8]byte
	dht            *dht.DHT
	rpc            *rpcServer
	trackerManager *trackermanager.TrackerManager
	ram            *resourcemanager.ResourceManager
	pieceCache     *piececache.Cache
	webseedClient  http.Client
	createdAt      time.Time
	semWrite       *semaphore.Semaphore
	metrics        *sessionMetrics
	bucketDownload *ratelimit.Bucket
	bucketUpload   *ratelimit.Bucket
	closeC         chan struct{}

	mPeerRequests   sync.Mutex
	dhtPeerRequests map[*torrent]struct{}

	mTorrents          sync.RWMutex
	torrents           map[string]*Torrent
	torrentsByInfoHash map[dht.InfoHash][]*Torrent

	mPorts         sync.RWMutex
	availablePorts map[int]struct{}

	mBlocklist         sync.RWMutex
	blocklist          *blocklist.Blocklist
	blocklistTimestamp time.Time
}

// NewSession creates a new Session for downloading and seeding torrents.
// Returned session must be closed after use.
func NewSession(cfg Config) (*Session, error) {
	if cfg.PortBegin >= cfg.PortEnd {
		return nil, errors.New("invalid port range")
	}
	err := setNoFile(cfg.MaxOpenFiles)
	if err != nil {
		return nil, errors.New("cannot change max open files limit: " + err.Error())
	}
	cfg.Database, err = homedir.Expand(cfg.Database)
	if err != nil {
		return nil, err
	}
	cfg.DataDir, err = homedir.Expand(cfg.DataDir)
	if err != nil {
		return nil, err
	}
	err = os.MkdirAll(filepath.Dir(cfg.Database), 0750)
	if err != nil {
		return nil, err
	}
	l := logger.New("session")
	db, err := bolt.Open(cfg.Database, 0640, &bolt.Options{Timeout: time.Second})
	if err == bolt.ErrTimeout {
		return nil, errors.New("resume database is locked by another process")
	} else if err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			db.Close()
		}
	}()
	var ids []string
	err = db.Update(func(tx *bolt.Tx) error {
		_, err2 := tx.CreateBucketIfNotExists(sessionBucket)
		if err2 != nil {
			return err2
		}
		b, err2 := tx.CreateBucketIfNotExists(torrentsBucket)
		if err2 != nil {
			return err2
		}
		return b.ForEach(func(k, _ []byte) error {
			ids = append(ids, string(k))
			return nil
		})
	})
	if err != nil {
		return nil, err
	}
	res, err := boltdbresumer.New(db, torrentsBucket)
	if err != nil {
		return nil, err
	}
	var dhtNode *dht.DHT
	if cfg.DHTEnabled {
		dhtConfig := dht.NewConfig()
		dhtConfig.Address = cfg.DHTHost
		dhtConfig.Port = int(cfg.DHTPort)
		dhtConfig.DHTRouters = strings.Join(cfg.DHTBootstrapNodes, ",")
		dhtConfig.SaveRoutingTable = false
		dhtNode, err = dht.New(dhtConfig)
		if err != nil {
			return nil, err
		}
		err = dhtNode.Start()
		if err != nil {
			return nil, err
		}
	}
	ports := make(map[int]struct{})
	for p := cfg.PortBegin; p < cfg.PortEnd; p++ {
		ports[int(p)] = struct{}{}
	}
	bl := blocklist.New()
	var blTracker *blocklist.Blocklist
	if cfg.BlocklistEnabledForTrackers {
		blTracker = bl
	}
	c := &Session{
		config:             cfg,
		db:                 db,
		resumer:            res,
		blocklist:          bl,
		trackerManager:     trackermanager.New(blTracker, cfg.DNSResolveTimeout, !cfg.TrackerHTTPVerifyTLS),
		log:                l,
		torrents:           make(map[string]*Torrent),
		torrentsByInfoHash: make(map[dht.InfoHash][]*Torrent),
		availablePorts:     ports,
		dht:                dhtNode,
		pieceCache:         piececache.New(cfg.ReadCacheSize, cfg.ReadCacheTTL, cfg.ParallelReads),
		ram:                resourcemanager.New(cfg.WriteCacheSize),
		createdAt:          time.Now(),
		semWrite:           semaphore.New(int(cfg.ParallelWrites)),
		closeC:             make(chan struct{}),
		webseedClient: http.Client{
			Transport: &http.Transport{
				DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
					ip, port, err := resolver.Resolve(ctx, addr, cfg.DNSResolveTimeout, bl)
					if err != nil {
						return nil, err
					}
					var d net.Dialer
					taddr := &net.TCPAddr{IP: ip, Port: port}
					dctx, cancel := context.WithTimeout(ctx, cfg.WebseedDialTimeout)
					defer cancel()
					return d.DialContext(dctx, network, taddr.String())
				},
				TLSHandshakeTimeout:   cfg.WebseedTLSHandshakeTimeout,
				TLSClientConfig:       &tls.Config{InsecureSkipVerify: !cfg.WebseedVerifyTLS}, // nolint: gosec
				ResponseHeaderTimeout: cfg.WebseedResponseHeaderTimeout,
			},
		},
	}
	if cfg.SpeedLimitDownload > 0 {
		c.bucketDownload = ratelimit.NewBucketWithRate(float64(cfg.SpeedLimitDownload), cfg.SpeedLimitDownload)
	}
	if cfg.SpeedLimitUpload > 0 {
		c.bucketUpload = ratelimit.NewBucketWithRate(float64(cfg.SpeedLimitUpload), cfg.SpeedLimitUpload)
	}
	err = c.startBlocklistReloader()
	if err != nil {
		return nil, err
	}
	ext, err := bitfield.NewBytes(c.extensions[:], 64)
	if err != nil {
		panic(err)
	}
	ext.Set(61) // Fast Extension (BEP 6)
	ext.Set(43) // Extension Protocol (BEP 10)
	if cfg.DHTEnabled {
		ext.Set(63) // DHT Protocol (BEP 5)
		c.dhtPeerRequests = make(map[*torrent]struct{})
	}
	err = c.initMetrics()
	if err != nil {
		return nil, err
	}
	c.loadExistingTorrents(ids)
	if c.config.RPCEnabled {
		c.rpc = newRPCServer(c)
		err = c.rpc.Start(c.config.RPCHost, c.config.RPCPort)
		if err != nil {
			return nil, err
		}
	}
	if cfg.DHTEnabled {
		go c.processDHTResults()
	}
	go c.updateStatsLoop()
	return c, nil
}

func (s *Session) parseTrackers(tiers [][]string, private bool) []tracker.Tracker {
	ret := make([]tracker.Tracker, 0, len(tiers))
	for _, tier := range tiers {
		trackers := make([]tracker.Tracker, 0, len(tier))
		for _, tr := range tier {
			t, err := s.trackerManager.Get(tr, s.config.TrackerHTTPTimeout, s.getTrackerUserAgent(private), int64(s.config.TrackerHTTPMaxResponseSize))
			if err != nil {
				continue
			}
			trackers = append(trackers, t)
		}
		if len(trackers) > 0 {
			tra := tracker.NewTier(trackers)
			ret = append(ret, tra)
		}
	}
	return ret
}

func (s *Session) getTrackerUserAgent(private bool) string {
	if private {
		return s.config.TrackerHTTPPrivateUserAgent
	}
	return trackerHTTPPublicUserAgent
}

func (s *Session) Close() error {
	close(s.closeC)

	if s.config.DHTEnabled {
		s.dht.Stop()
	}

	s.updateStats()

	var wg sync.WaitGroup
	s.mTorrents.Lock()
	wg.Add(len(s.torrents))
	for _, t := range s.torrents {
		go func(t *Torrent) {
			t.torrent.Close()
			wg.Done()
		}(t)
	}
	wg.Wait()
	s.torrents = nil
	s.mTorrents.Unlock()

	if s.rpc != nil {
		err := s.rpc.Stop(s.config.RPCShutdownTimeout)
		if err != nil {
			s.log.Errorln("cannot stop RPC server:", err.Error())
		}
	}

	s.ram.Close()
	s.pieceCache.Close()
	s.metrics.Close()
	return s.db.Close()
}

func (s *Session) ListTorrents() []*Torrent {
	s.mTorrents.RLock()
	defer s.mTorrents.RUnlock()
	torrents := make([]*Torrent, 0, len(s.torrents))
	for _, t := range s.torrents {
		torrents = append(torrents, t)
	}
	return torrents
}

func (s *Session) getPort() (int, error) {
	s.mPorts.Lock()
	defer s.mPorts.Unlock()
	for p := range s.availablePorts {
		delete(s.availablePorts, p)
		return p, nil
	}
	return 0, errors.New("no free port")
}

func (s *Session) releasePort(port int) {
	s.mPorts.Lock()
	defer s.mPorts.Unlock()
	s.availablePorts[port] = struct{}{}
}

func (s *Session) GetTorrent(id string) *Torrent {
	s.mTorrents.RLock()
	defer s.mTorrents.RUnlock()
	return s.torrents[id]
}

func (s *Session) RemoveTorrent(id string) error {
	t, err := s.removeTorrentFromClient(id)
	if t != nil {
		go func() { _ = s.stopAndRemoveData(t) }()
	}
	return err
}

func (s *Session) removeTorrentFromClient(id string) (*Torrent, error) {
	s.mTorrents.Lock()
	defer s.mTorrents.Unlock()
	t, ok := s.torrents[id]
	if !ok {
		return nil, nil
	}
	t.torrent.log.Info("removing torrent")
	delete(s.torrents, id)
	delete(s.torrentsByInfoHash, dht.InfoHash(t.torrent.InfoHash()))
	return t, s.db.Update(func(tx *bolt.Tx) error {
		return tx.Bucket(torrentsBucket).DeleteBucket([]byte(id))
	})
}

func (s *Session) stopAndRemoveData(t *Torrent) error {
	t.torrent.Close()
	s.releasePort(t.torrent.port)
	var err error
	var dest string
	if s.config.DataDirIncludesTorrentID {
		dest = filepath.Join(s.config.DataDir, t.torrent.id)
	} else if t.torrent.info != nil {
		dest = t.torrent.info.Name
	}
	if dest != "" {
		err = os.RemoveAll(dest)
		if err != nil {
			s.log.Errorf("cannot remove torrent data. err: %s dest: %s", err, dest)
		}
	}
	return err
}

func (s *Session) StartAll() error {
	err := s.db.Update(func(tx *bolt.Tx) error {
		tb := tx.Bucket(torrentsBucket)
		s.mTorrents.RLock()
		for _, t := range s.torrents {
			b := tb.Bucket([]byte(t.torrent.id))
			_ = b.Put([]byte("started"), []byte("true"))
		}
		defer s.mTorrents.RUnlock()
		return nil
	})
	if err != nil {
		return err
	}
	for _, t := range s.torrents {
		t.torrent.Start()
	}
	return nil
}

func (s *Session) StopAll() error {
	err := s.db.Update(func(tx *bolt.Tx) error {
		tb := tx.Bucket(torrentsBucket)
		s.mTorrents.RLock()
		for _, t := range s.torrents {
			b := tb.Bucket([]byte(t.torrent.id))
			_ = b.Put([]byte("started"), []byte("false"))
		}
		defer s.mTorrents.RUnlock()
		return nil
	})
	if err != nil {
		return err
	}
	for _, t := range s.torrents {
		t.torrent.Stop()
	}
	return nil
}
<commit_msg>change open files limit only if defined<commit_after>// Package torrent provides a BitTorrent client implementation.
package torrent

import (
	"context"
	"crypto/tls"
	"errors"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/boltdb/bolt"
	"github.com/cenkalti/rain/internal/bitfield"
	"github.com/cenkalti/rain/internal/blocklist"
	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/internal/piececache"
	"github.com/cenkalti/rain/internal/resolver"
	"github.com/cenkalti/rain/internal/resourcemanager"
	"github.com/cenkalti/rain/internal/resumer/boltdbresumer"
	"github.com/cenkalti/rain/internal/semaphore"
	"github.com/cenkalti/rain/internal/tracker"
	"github.com/cenkalti/rain/internal/trackermanager"
	"github.com/juju/ratelimit"
	"github.com/mitchellh/go-homedir"
	"github.com/nictuku/dht"
)

var (
	sessionBucket         = []byte("session")
	torrentsBucket        = []byte("torrents")
	blocklistKey          = []byte("blocklist")
	blocklistTimestampKey = []byte("blocklist-timestamp")
)

// Session contains torrents, DHT node, caches and other data structures shared by multiple torrents.
type Session struct {
	config         Config
	db             *bolt.DB
	resumer        *boltdbresumer.Resumer
	log            logger.Logger
	extensions     [8]byte
	dht            *dht.DHT
	rpc            *rpcServer
	trackerManager *trackermanager.TrackerManager
	ram            *resourcemanager.ResourceManager
	pieceCache     *piececache.Cache
	webseedClient  http.Client
	createdAt      time.Time
	semWrite       *semaphore.Semaphore
	metrics        *sessionMetrics
	bucketDownload *ratelimit.Bucket
	bucketUpload   *ratelimit.Bucket
	closeC         chan struct{}

	mPeerRequests   sync.Mutex
	dhtPeerRequests map[*torrent]struct{}

	mTorrents          sync.RWMutex
	torrents           map[string]*Torrent
	torrentsByInfoHash map[dht.InfoHash][]*Torrent

	mPorts         sync.RWMutex
	availablePorts map[int]struct{}

	mBlocklist         sync.RWMutex
	blocklist          *blocklist.Blocklist
	blocklistTimestamp time.Time
}

// NewSession creates a new Session for downloading and seeding torrents.
// Returned session must be closed after use.
func NewSession(cfg Config) (*Session, error) {
	if cfg.PortBegin >= cfg.PortEnd {
		return nil, errors.New("invalid port range")
	}
	if cfg.MaxOpenFiles > 0 {
		err := setNoFile(cfg.MaxOpenFiles)
		if err != nil {
			return nil, errors.New("cannot change max open files limit: " + err.Error())
		}
	}
	var err error
	cfg.Database, err = homedir.Expand(cfg.Database)
	if err != nil {
		return nil, err
	}
	cfg.DataDir, err = homedir.Expand(cfg.DataDir)
	if err != nil {
		return nil, err
	}
	err = os.MkdirAll(filepath.Dir(cfg.Database), 0750)
	if err != nil {
		return nil, err
	}
	l := logger.New("session")
	db, err := bolt.Open(cfg.Database, 0640, &bolt.Options{Timeout: time.Second})
	if err == bolt.ErrTimeout {
		return nil, errors.New("resume database is locked by another process")
	} else if err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			db.Close()
		}
	}()
	var ids []string
	err = db.Update(func(tx *bolt.Tx) error {
		_, err2 := tx.CreateBucketIfNotExists(sessionBucket)
		if err2 != nil {
			return err2
		}
		b, err2 := tx.CreateBucketIfNotExists(torrentsBucket)
		if err2 != nil {
			return err2
		}
		return b.ForEach(func(k, _ []byte) error {
			ids = append(ids, string(k))
			return nil
		})
	})
	if err != nil {
		return nil, err
	}
	res, err := boltdbresumer.New(db, torrentsBucket)
	if err != nil {
		return nil, err
	}
	var dhtNode *dht.DHT
	if cfg.DHTEnabled {
		dhtConfig := dht.NewConfig()
		dhtConfig.Address = cfg.DHTHost
		dhtConfig.Port = int(cfg.DHTPort)
		dhtConfig.DHTRouters = strings.Join(cfg.DHTBootstrapNodes, ",")
		dhtConfig.SaveRoutingTable = false
		dhtNode, err = dht.New(dhtConfig)
		if err != nil {
			return nil, err
		}
		err = dhtNode.Start()
		if err != nil {
			return nil, err
		}
	}
	ports := make(map[int]struct{})
	for p := cfg.PortBegin; p < cfg.PortEnd; p++ {
		ports[int(p)] = struct{}{}
	}
	bl := blocklist.New()
	var blTracker *blocklist.Blocklist
	if cfg.BlocklistEnabledForTrackers {
		blTracker = bl
	}
	c := &Session{
		config:             cfg,
		db:                 db,
		resumer:            res,
		blocklist:          bl,
		trackerManager:     trackermanager.New(blTracker, cfg.DNSResolveTimeout, !cfg.TrackerHTTPVerifyTLS),
		log:                l,
		torrents:           make(map[string]*Torrent),
		torrentsByInfoHash: make(map[dht.InfoHash][]*Torrent),
		availablePorts:     ports,
		dht:                dhtNode,
		pieceCache:         piececache.New(cfg.ReadCacheSize, cfg.ReadCacheTTL, cfg.ParallelReads),
		ram:                resourcemanager.New(cfg.WriteCacheSize),
		createdAt:          time.Now(),
		semWrite:           semaphore.New(int(cfg.ParallelWrites)),
		closeC:             make(chan struct{}),
		webseedClient: http.Client{
			Transport: &http.Transport{
				DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
					ip, port, err := resolver.Resolve(ctx, addr, cfg.DNSResolveTimeout, bl)
					if err != nil {
						return nil, err
					}
					var d net.Dialer
					taddr := &net.TCPAddr{IP: ip, Port: port}
					dctx, cancel := context.WithTimeout(ctx, cfg.WebseedDialTimeout)
					defer cancel()
					return d.DialContext(dctx, network, taddr.String())
				},
				TLSHandshakeTimeout:   cfg.WebseedTLSHandshakeTimeout,
				TLSClientConfig:       &tls.Config{InsecureSkipVerify: !cfg.WebseedVerifyTLS}, // nolint: gosec
				ResponseHeaderTimeout: cfg.WebseedResponseHeaderTimeout,
			},
		},
	}
	if cfg.SpeedLimitDownload > 0 {
		c.bucketDownload = ratelimit.NewBucketWithRate(float64(cfg.SpeedLimitDownload), cfg.SpeedLimitDownload)
	}
	if cfg.SpeedLimitUpload > 0 {
		c.bucketUpload = ratelimit.NewBucketWithRate(float64(cfg.SpeedLimitUpload), cfg.SpeedLimitUpload)
	}
	err = c.startBlocklistReloader()
	if err != nil {
		return nil, err
	}
	ext, err := bitfield.NewBytes(c.extensions[:], 64)
	if err != nil {
		panic(err)
	}
	ext.Set(61) // Fast Extension (BEP 6)
	ext.Set(43) // Extension Protocol (BEP 10)
	if cfg.DHTEnabled {
		ext.Set(63) // DHT Protocol (BEP 5)
		c.dhtPeerRequests = make(map[*torrent]struct{})
	}
	err = c.initMetrics()
	if err != nil {
		return nil, err
	}
	c.loadExistingTorrents(ids)
	if c.config.RPCEnabled {
		c.rpc = newRPCServer(c)
		err = c.rpc.Start(c.config.RPCHost, c.config.RPCPort)
		if err != nil {
			return nil, err
		}
	}
	if cfg.DHTEnabled {
		go c.processDHTResults()
	}
	go c.updateStatsLoop()
	return c, nil
}

func (s *Session) parseTrackers(tiers [][]string, private bool) []tracker.Tracker {
	ret := make([]tracker.Tracker, 0, len(tiers))
	for _, tier := range tiers {
		trackers := make([]tracker.Tracker, 0, len(tier))
		for _, tr := range tier {
			t, err := s.trackerManager.Get(tr, s.config.TrackerHTTPTimeout, s.getTrackerUserAgent(private), int64(s.config.TrackerHTTPMaxResponseSize))
			if err != nil {
				continue
			}
			trackers = append(trackers, t)
		}
		if len(trackers) > 0 {
			tra := tracker.NewTier(trackers)
			ret = append(ret, tra)
		}
	}
	return ret
}

func (s *Session) getTrackerUserAgent(private bool) string {
	if private {
		return s.config.TrackerHTTPPrivateUserAgent
	}
	return trackerHTTPPublicUserAgent
}

func (s *Session) Close() error {
	close(s.closeC)

	if s.config.DHTEnabled {
		s.dht.Stop()
	}

	s.updateStats()

	var wg sync.WaitGroup
	s.mTorrents.Lock()
	wg.Add(len(s.torrents))
	for _, t := range s.torrents {
		go func(t *Torrent) {
			t.torrent.Close()
			wg.Done()
		}(t)
	}
	wg.Wait()
	s.torrents = nil
	s.mTorrents.Unlock()

	if s.rpc != nil {
		err := s.rpc.Stop(s.config.RPCShutdownTimeout)
		if err != nil {
			s.log.Errorln("cannot stop RPC server:", err.Error())
		}
	}

	s.ram.Close()
	s.pieceCache.Close()
	s.metrics.Close()
	return s.db.Close()
}

func (s *Session) ListTorrents() []*Torrent {
	s.mTorrents.RLock()
	defer s.mTorrents.RUnlock()
	torrents := make([]*Torrent, 0, len(s.torrents))
	for _, t := range s.torrents {
		torrents = append(torrents, t)
	}
	return torrents
}

func (s *Session) getPort() (int, error) {
	s.mPorts.Lock()
	defer s.mPorts.Unlock()
	for p := range s.availablePorts {
		delete(s.availablePorts, p)
		return p, nil
	}
	return 0, errors.New("no free port")
}

func (s *Session) releasePort(port int) {
	s.mPorts.Lock()
	defer s.mPorts.Unlock()
	s.availablePorts[port] = struct{}{}
}

func (s *Session) GetTorrent(id string) *Torrent {
	s.mTorrents.RLock()
	defer s.mTorrents.RUnlock()
	return s.torrents[id]
}

func (s *Session) RemoveTorrent(id string) error {
	t, err := s.removeTorrentFromClient(id)
	if t != nil {
		go func() { _ = s.stopAndRemoveData(t) }()
	}
	return err
}

func (s *Session) removeTorrentFromClient(id string) (*Torrent, error) {
	s.mTorrents.Lock()
	defer s.mTorrents.Unlock()
	t, ok := s.torrents[id]
	if !ok {
		return nil, nil
	}
	t.torrent.log.Info("removing torrent")
	delete(s.torrents, id)
	delete(s.torrentsByInfoHash, dht.InfoHash(t.torrent.InfoHash()))
	return t, s.db.Update(func(tx *bolt.Tx) error {
		return tx.Bucket(torrentsBucket).DeleteBucket([]byte(id))
	})
}

func (s *Session) stopAndRemoveData(t *Torrent) error {
	t.torrent.Close()
	s.releasePort(t.torrent.port)
	var err error
	var dest string
	if s.config.DataDirIncludesTorrentID {
		dest = filepath.Join(s.config.DataDir, t.torrent.id)
	} else if t.torrent.info != nil {
		dest = t.torrent.info.Name
	}
	if dest != "" {
		err = os.RemoveAll(dest)
		if err != nil {
			s.log.Errorf("cannot remove torrent data. err: %s dest: %s", err, dest)
		}
	}
	return err
}

func (s *Session) StartAll() error {
	err := s.db.Update(func(tx *bolt.Tx) error {
		tb := tx.Bucket(torrentsBucket)
		s.mTorrents.RLock()
		for _, t := range s.torrents {
			b := tb.Bucket([]byte(t.torrent.id))
			_ = b.Put([]byte("started"), []byte("true"))
		}
		defer s.mTorrents.RUnlock()
		return nil
	})
	if err != nil {
		return err
	}
	for _, t := range s.torrents {
		t.torrent.Start()
	}
	return nil
}

func (s *Session) StopAll() error {
	err := s.db.Update(func(tx *bolt.Tx) error {
		tb := tx.Bucket(torrentsBucket)
		s.mTorrents.RLock()
		for _, t := range s.torrents {
			b := tb.Bucket([]byte(t.torrent.id))
			_ = b.Put([]byte("started"), []byte("false"))
		}
		defer s.mTorrents.RUnlock()
		return nil
	})
	if err != nil {
		return err
	}
	for _, t := range s.torrents {
		t.torrent.Stop()
	}
	return nil
}
<|endoftext|>


<commit_before>package torutil

import (
	"fmt"
	"net"
	"os"
	"os/exec"
	"strconv"

	freeport "github.com/phayes/freeport"
)

type Connection struct {
	ControlPort int `db:"control_port" json:"control_port"`
	Port        int `db:"port"         json:"port"`
}

func Spawn(dataDir string, tc Connection) {
	cmd := "tor"
	pid := fmt.Sprintf("tor%d.pid", tc.ControlPort)
	ddir := fmt.Sprintf("%s/tor%d", dataDir, tc.ControlPort)
	args := []string{"--RunAsDaemon", "1", "--CookieAuthentication", "0", "--ControlPort", strconv.Itoa(tc.ControlPort), "--PidFile", pid, "--SocksPort", strconv.Itoa(tc.Port), "--DataDirectory", ddir}

	os.MkdirAll(ddir, 0777)

	out, err := exec.Command(cmd, args...).Output()
	if err != nil {
		fmt.Printf("Error: %v\nOutput: %s\n", err, out)
	}
}

func Create(dataDir string) Connection {
	tc := Connection{ControlPort: freeport.GetPort(), Port: freeport.GetPort()}
	Spawn(dataDir, tc)
	return tc
}

func ControlCommand(command string, port int) {
	conn, _ := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", port))
	fmt.Fprintf(conn, "AUTHENTICATE\r\n")
	fmt.Fprintf(conn, "%s\r\n", command)
}

func Cycle(controlPort int) {
	ControlCommand("SIGNAL NEWNYM", controlPort)
}

func Shutdown(controlPort int) {
	ControlCommand("SIGNAL HALT", controlPort)
}
<commit_msg>Add ClientOnly flag<commit_after>package torutil

import (
	"fmt"
	"net"
	"os"
	"os/exec"
	"strconv"

	freeport "github.com/phayes/freeport"
)

type Connection struct {
	ControlPort int `db:"control_port" json:"control_port"`
	Port        int `db:"port"         json:"port"`
}

func Spawn(dataDir string, tc Connection) {
	cmd := "tor"
	pid := fmt.Sprintf("tor%d.pid", tc.ControlPort)
	ddir := fmt.Sprintf("%s/tor%d", dataDir, tc.ControlPort)
	args := []string{"--ClientOnly", "1", "--RunAsDaemon", "1", "--CookieAuthentication", "0", "--ControlPort", strconv.Itoa(tc.ControlPort), "--PidFile", pid, "--SocksPort", strconv.Itoa(tc.Port), "--DataDirectory", ddir}

	os.MkdirAll(ddir, 0777)

	out, err := exec.Command(cmd, args...).Output()
	if err != nil {
		fmt.Printf("Error: %v\nOutput: %s\n", err, out)
	}
}

func Create(dataDir string) Connection {
	tc := Connection{ControlPort: freeport.GetPort(), Port: freeport.GetPort()}
	Spawn(dataDir, tc)
	return tc
}

func ControlCommand(command string, port int) {
	conn, _ := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", port))
	fmt.Fprintf(conn, "AUTHENTICATE\r\n")
	fmt.Fprintf(conn, "%s\r\n", command)
}

func Cycle(controlPort int) {
	ControlCommand("SIGNAL NEWNYM", controlPort)
}

func Shutdown(controlPort int) {
	ControlCommand("SIGNAL HALT", controlPort)
}
<|endoftext|>


<commit_before>package terp

import (
	"bytes"
	. "fmt"
	"go/ast"
	"log"
	R "reflect"
	"strconv"
	"strings"
	"sync"

	"generated"
)

type Any interface{}

type List []Any
type Dict map[string]Any
type TDict map[string]T

type Command func(fr *Frame, argv List) Any
type TCommand func(fr *Frame, argv []T) T

type Scope map[string]Any
type TScope map[string]T

type CmdScope map[string]Command
type TCmdScope map[string]TCommand

type Frame struct {
	Vars  Scope
	Slots Scope
	TVars  TScope
	TSlots TScope

	Prev *Frame
	G    *Global
	Mu   sync.Mutex
}

type Global struct {
	Cmds CmdScope
	TCmds TCmdScope
	Fr   Frame // global scope

	Mu sync.Mutex
}

func New() *Frame {
	g := &Global{
		Cmds: make(CmdScope),
		TCmds: make(TCmdScope),
		Fr: Frame{
			Vars: make(Scope),
			TVars: make(TScope),
		},
	}

	g.Fr.G = g
	g.Fr.initBuiltins()
	g.Fr.initExterns()
	return &g.Fr
}

func (fr *Frame) NewFrame() *Frame {
	return &Frame{
		Vars:  make(Scope),
		Slots: nil,
		Prev:  fr,
		G:     fr.G,
	}
}

func IsGlobal(name string) bool {
	if len(name) == 0 {
		panic("Empty variable name")
	}
	return ast.IsExported(name) // Same criteria, First is Uppercase.
}

func IsLocal(name string) bool {
	if len(name) == 0 {
		panic("Empty variable name")
	}
	return !ast.IsExported(name) && name[0] != '_'
}

func (fr *Frame) GetVarScope(name string) Scope {
	if len(name) == 0 {
		panic("Empty variable name")
	}
	if name[0] == '_' {
		if fr.Slots == nil {
			panic("No slots in this frame: " + name)
		}
		return fr.Slots
	}

	if IsGlobal(name) {
		return fr.G.Fr.Vars
	}
	return fr.Vars
}

func (fr *Frame) GetVar(name string) Any {
	return fr.GetVarScope(name)[name]
}

func (fr *Frame) SetVar(name string, x Any) {
	fr.GetVarScope(name)[name] = x
}

func (fr *Frame) Apply(argv List) Any {
	log.Printf("< Apply < %#v\n", argv)
	head := argv[0]
	cmdName, ok := head.(string)
	if !ok {
		// Some day this may not be true; for now, it helps debug.
		panic(Sprintf("Command must be a string: %#v", head))
	}

	fn, ok := fr.G.Cmds[cmdName]
	log.Printf("Looked in Cmds %v %v %v", fn, ok, cmdName)
	if !ok {
		fn, ok = Builtins[cmdName]
		log.Printf("Looked in Builtins %v %v %v", fn, ok, cmdName)
	}
	if !ok {
		_, ok = generated.Funcs[cmdName]
		log.Printf("Looked in gen.Funcs -- %v %v", ok, cmdName)
		if ok {
			fn = cmdCall
			tmp := List{"call", cmdName}
			for _, a := range argv[1:] {
				tmp = append(tmp, a)
			}
			argv = tmp
		    log.Printf("NEW argv: $#v", argv)
		}
	}
	if !ok {
		panic(Sprintf("Command not found: %q", cmdName))
	}
	z := fn(fr, argv)
	log.Printf("> Apply > %#v\n", z)
	return z
}

func Bool2Int(b bool) int {
	if b {return 1}
	return 0
}
func Bool2Str(b bool) string {
	if b {return "1"}
	return "0"
}
func Float32Str(x float32) string {
	if float32(int64(x)) == x {
		return Sprintf("%d", int64(x))
	}
	return Sprintf("%g", float32(x))
}
func Float64Str(x float64) string {
	if float64(int64(x)) == x {
		return Sprintf("%d", int64(x))
	}
	return Sprintf("%g", float64(x))
}
func List2Str(v List) string {
	buf := bytes.NewBuffer(nil)
	sep := ""
	for _, e := range v {
		buf.WriteString(sep)
		sep = " "
		estr := Any2ListElement(e)
		buf.WriteString(estr)
	}
	return buf.String()
}
func Any2ListElement(a Any) string {
	// TODO: Not perfect, but we are not doing \ yet.
	// TODO: Broken for mismatched {}.
	if a == nil {
		return "{}"
	}
	s := Str(a)
	if s == "" {
		return "{}"
	}

	if strings.ContainsAny(s, " \t\n\r{}\\") {
		return "{" + s + "}"
	}
	return s
}
func Repr(a Any) string { return Sprintf("REPR<<%#v>>", a) }
func Str(a Any) string {
	switch x := a.(type) {
	case nil: return ""
	case string: return x
	case uint: return Sprintf("%d", x)
	case uint8: return Sprintf("%d", x)
	case uint16: return Sprintf("%d", x)
	case uint32: return Sprintf("%d", x)
	case uint64: return Sprintf("%d", x)
	case uintptr: return Sprintf("%d", x)
	case int: return Sprintf("%d", x)
	case int8: return Sprintf("%d", x)
	case int16: return Sprintf("%d", x)
	case int32: return Sprintf("%d", x)
	case int64: return Sprintf("%d", x)
	case float32: return Float32Str(x)
	case float64: return Float64Str(x)
	case bool: return Bool2Str(x)
	case error: return Sprintf("%#v", x)
	case List: return List2Str(x)
	}
	// panic(Sprintf("DEFAULT Str: %#v", a))
	return Sprintf("{%#v}", a)
}

func Must(a, b Any, extra ...Any) {
	if a != b {
		panic(Repr(a) + " vs. " + Repr(b) + " :: " + Repr(extra))
	}
}

func NewList(a ...Any) List { return List(a) }

func ToList(a Any) List {
	switch x := a.(type) {
		case List: return x
		case []Any: return List(x)
		case []interface{}:
			z := make([]Any, 0, 4)
			for _, e := range x {
				z = append(z, e)
			}
			return List(z)
		case string: return ParseList(x)
	}
	return ParseList(Str(a))
}
func LAppend(p Any, a ...Any) List {
	v := ToList(p)
	for _, e := range a {
		v = append(v, e)
	}
	return v
}

///////////////////////////////////////

type T interface {
	String() string
	Float() float64
	Int() int64
	Uint() uint64
	Bool() bool
	ListElement() string

	ToTf() Tf
	ToTs() Ts
	ToTl() Tl
}

type Tf struct {  // Tfloat
	f float64
}
type Ts struct {  // Tstring
	s string
}
type Tl struct {  // Tlist
	l []T
}
type Tv struct {  // Tvalue
	v R.Value
}

func FloatToT(a float64) Tf {
	return Tf{f: a}
}
func IntToT(a int64) Tf {
	return Tf{f: float64(a)}
}
func UintToT(a uint64) Tf {
	return Tf{f: float64(a)}
}
func StringToT(a string) Ts {
	return Ts{s: a}
}
func ListToT(a []T) Tl {
	return Tl{l: a}
}
func ValueToT(a R.Value) T {
	return Tv{v: a}
}
func ToT(a interface{}) T {
	switch z := a.(type) {
	case float64: return FloatToT(z)
	case float32: return FloatToT(float64(z))
	case int: return IntToT(int64(z))
	case int8: return IntToT(int64(z))
	case int16: return IntToT(int64(z))
	case int32: return IntToT(int64(z))
	case int64: return IntToT(z)
	case uint: return UintToT(uint64(z))
	case uint8: return UintToT(uint64(z))
	case uint16: return UintToT(uint64(z))
	case uint32: return UintToT(uint64(z))
	case uint64: return UintToT(z)
	case string: return StringToT(z)
	case T: panic("Already a T")
	}
	return ValueToT(R.ValueOf(a))
}

func (t Tf) String() string {
	return Sprintf("%g", t.f)
}
func (t Tf) ListElement() string {
	return t.String()
}
func (t Tf) Float() float64 {
	return t.f
}
func (t Tf) Int() int64 {
	return int64(t.f)
}
func (t Tf) Uint() uint64 {
	return uint64(t.f)
}
func (t Tf) Bool() bool {
	if t.f == 0 {
		return false
	}
	return true
}
func (t Tf) ToTf() Tf {
	return t
}
func (t Tf) ToTs() Ts {
	return Ts{s: t.String()}
}
func (t Tf) ToTl() Tl {
	return Tl{l: []T{t,}}
}

func (t Ts) String() string {
	return t.s
}
func (t Ts) ListElement() string {
	return ToListElement(t.s)
}
func (t Ts) Float() float64 {
	z, err := strconv.ParseFloat(t.s, 64)
	if err != nil {
		panic(err)
	}
	return z
}
func (t Ts) Int() int64 {
	return int64(t.Float())  //TODO
}
func (t Ts) Uint() uint64 {
	return uint64(t.Float())  //TODO
}
func (t Ts) Bool() bool {
	if t.s == "" || t.s == "0" {
		return false
	}
	return true
}
func (t Ts) ToTf() Tf {
	return FloatToT(t.Float())
}
func (t Ts) ToTs() Ts {
	return t
}
func (t Ts) ToTl() Tl {
	v := ParseList(t.s)
	z := make([]T, len(v))
	for i, e := range v {
		z[i] = ToT(e)
	}
	return Tl{l: z}
}

func (t Tl) String() string {
	z := ""
	for k, v := range t.l {
		if k > 0 {
			z += " "
		}
		z += v.ListElement()
	}
	return z
}
func (t Tl) ListElement() string {
	return ToListElement(t.String())
}
func (t Tl) Float() float64 {
	if len(t.l) != 1 {panic("cant")}
	return t.l[0].Float()
}
func (t Tl) Int() int64 {
	if len(t.l) != 1 {panic("cant")}
	return t.l[0].Int()
}
func (t Tl) Uint() uint64 {
	if len(t.l) != 1 {panic("cant")}
	return t.l[0].Uint()
}
func (t Tl) Bool() bool {
	if len(t.l) == 0 {
		return false
	}
	return true
}
func (t Tl) ToTf() Tf {
	return FloatToT(t.Float())
}
func (t Tl) ToTs() Ts {
	return StringToT(t.String())
}
func (t Tl) ToTl() Tl {
	return t
}

func (t Tv) String() string {
	return Sprintf("Value:%s:%s:%d", t.v.Kind(), t.v.Type(), t.v.Addr())
}
func (t Tv) ListElement() string {
	return ToListElement(t.String())
}
func (t Tv) Float() float64 {
	panic("cant yet")
}
func (t Tv) Int() int64 {
	panic("cant yet")
}
func (t Tv) Uint() uint64 {
	panic("cant yet")
}
func (t Tv) Bool() bool {
	panic("cant yet")
}
func (t Tv) ToTf() Tf {
	return Tf{f: t.Float()}
}
func (t Tv) ToTs() Ts {
	return Ts{s: t.String()}
}
func (t Tv) ToTl() Tl {
	return Tl{l: []T{t,}}
}

func ToListElement(s string) string {
	// TODO: Not perfect, but we are not doing \ yet.
	// TODO: Broken for mismatched {}.
	if s == "" {
		return "{}"
	}

	if strings.ContainsAny(s, " \t\n\r{}\\") {
		return "{" + s + "}"
	}
	return s
}

<commit_msg>Adding T versions 2<commit_after>package terp

import (
	"bytes"
	. "fmt"
	"go/ast"
	"log"
	R "reflect"
	"strconv"
	"strings"
	"sync"

	"generated"
)

type Any interface{}

type List []Any
type Dict map[string]Any
type TDict map[string]T

type Command func(fr *Frame, argv List) Any
type TCommand func(fr *Frame, argv []T) T

type Scope map[string]Any
type TScope map[string]T

type CmdScope map[string]Command
type TCmdScope map[string]TCommand

type Frame struct {
	Vars  Scope
	Slots Scope
	TVars  TScope
	TSlots TScope

	Prev *Frame
	G    *Global
	Mu   sync.Mutex
}

type Global struct {
	Cmds CmdScope
	TCmds TCmdScope
	Fr   Frame // global scope

	Mu sync.Mutex
}

func New() *Frame {
	g := &Global{
		Cmds: make(CmdScope),
		TCmds: make(TCmdScope),
		Fr: Frame{
			Vars: make(Scope),
			TVars: make(TScope),
		},
	}

	g.Fr.G = g
	g.Fr.initBuiltins()
	g.Fr.initExterns()
	return &g.Fr
}

func (fr *Frame) NewFrame() *Frame {
	return &Frame{
		Vars:  make(Scope),
		Slots: nil,
		TVars:  make(TScope),
		TSlots: nil,
		Prev:  fr,
		G:     fr.G,
	}
}

func IsGlobal(name string) bool {
	if len(name) == 0 {
		panic("Empty variable name")
	}
	return ast.IsExported(name) // Same criteria, First is Uppercase.
}

func IsLocal(name string) bool {
	if len(name) == 0 {
		panic("Empty variable name")
	}
	return !ast.IsExported(name) && name[0] != '_'
}

func (fr *Frame) GetVarScope(name string) Scope {
	if len(name) == 0 {
		panic("Empty variable name")
	}
	if name[0] == '_' {
		if fr.Slots == nil {
			panic("No slots in this frame: " + name)
		}
		return fr.Slots
	}

	if IsGlobal(name) {
		return fr.G.Fr.Vars
	}
	return fr.Vars
}

func (fr *Frame) GetVar(name string) Any {
	return fr.GetVarScope(name)[name]
}

func (fr *Frame) SetVar(name string, x Any) {
	fr.GetVarScope(name)[name] = x
}

func (fr *Frame) TGetVarScope(name string) TScope {
	if len(name) == 0 {
		panic("Empty variable name")
	}
	if name[0] == '_' {
		if fr.Slots == nil {
			panic("No slots in this frame: " + name)
		}
		return fr.TSlots
	}

	if IsGlobal(name) {
		return fr.G.Fr.TVars
	}
	return fr.TVars
}

func (fr *Frame) TGetVar(name string) T {
	return fr.TGetVarScope(name)[name]
}

func (fr *Frame) TSetVar(name string, x T) {
	fr.TGetVarScope(name)[name] = x
}

func (fr *Frame) Apply(argv List) Any {
	log.Printf("< Apply < %#v\n", argv)
	head := argv[0]
	cmdName, ok := head.(string)
	if !ok {
		// Some day this may not be true; for now, it helps debug.
		panic(Sprintf("Command must be a string: %#v", head))
	}

	fn, ok := fr.G.Cmds[cmdName]
	log.Printf("Looked in Cmds %v %v %v", fn, ok, cmdName)
	if !ok {
		fn, ok = Builtins[cmdName]
		log.Printf("Looked in Builtins %v %v %v", fn, ok, cmdName)
	}
	if !ok {
		_, ok = generated.Funcs[cmdName]
		log.Printf("Looked in gen.Funcs -- %v %v", ok, cmdName)
		if ok {
			fn = cmdCall
			tmp := List{"call", cmdName}
			for _, a := range argv[1:] {
				tmp = append(tmp, a)
			}
			argv = tmp
		    log.Printf("NEW argv: $#v", argv)
		}
	}
	if !ok {
		panic(Sprintf("Command not found: %q", cmdName))
	}
	z := fn(fr, argv)
	log.Printf("> Apply > %#v\n", z)
	return z
}

func Bool2Int(b bool) int {
	if b {return 1}
	return 0
}
func Bool2Str(b bool) string {
	if b {return "1"}
	return "0"
}
func Float32Str(x float32) string {
	if float32(int64(x)) == x {
		return Sprintf("%d", int64(x))
	}
	return Sprintf("%g", float32(x))
}
func Float64Str(x float64) string {
	if float64(int64(x)) == x {
		return Sprintf("%d", int64(x))
	}
	return Sprintf("%g", float64(x))
}
func List2Str(v List) string {
	buf := bytes.NewBuffer(nil)
	sep := ""
	for _, e := range v {
		buf.WriteString(sep)
		sep = " "
		estr := Any2ListElement(e)
		buf.WriteString(estr)
	}
	return buf.String()
}
func Any2ListElement(a Any) string {
	// TODO: Not perfect, but we are not doing \ yet.
	// TODO: Broken for mismatched {}.
	if a == nil {
		return "{}"
	}
	s := Str(a)
	if s == "" {
		return "{}"
	}

	if strings.ContainsAny(s, " \t\n\r{}\\") {
		return "{" + s + "}"
	}
	return s
}
func Repr(a Any) string { return Sprintf("REPR<<%#v>>", a) }
func Str(a Any) string {
	switch x := a.(type) {
	case nil: return ""
	case string: return x
	case uint: return Sprintf("%d", x)
	case uint8: return Sprintf("%d", x)
	case uint16: return Sprintf("%d", x)
	case uint32: return Sprintf("%d", x)
	case uint64: return Sprintf("%d", x)
	case uintptr: return Sprintf("%d", x)
	case int: return Sprintf("%d", x)
	case int8: return Sprintf("%d", x)
	case int16: return Sprintf("%d", x)
	case int32: return Sprintf("%d", x)
	case int64: return Sprintf("%d", x)
	case float32: return Float32Str(x)
	case float64: return Float64Str(x)
	case bool: return Bool2Str(x)
	case error: return Sprintf("%#v", x)
	case List: return List2Str(x)
	}
	// panic(Sprintf("DEFAULT Str: %#v", a))
	return Sprintf("{%#v}", a)
}

func Must(a, b Any, extra ...Any) {
	if a != b {
		panic(Repr(a) + " vs. " + Repr(b) + " :: " + Repr(extra))
	}
}

func NewList(a ...Any) List { return List(a) }

func ToList(a Any) List {
	switch x := a.(type) {
		case List: return x
		case []Any: return List(x)
		case []interface{}:
			z := make([]Any, 0, 4)
			for _, e := range x {
				z = append(z, e)
			}
			return List(z)
		case string: return ParseList(x)
	}
	return ParseList(Str(a))
}
func LAppend(p Any, a ...Any) List {
	v := ToList(p)
	for _, e := range a {
		v = append(v, e)
	}
	return v
}

///////////////////////////////////////

type T interface {
	String() string
	Float() float64
	Int() int64
	Uint() uint64
	Bool() bool
	ListElement() string

	ToTf() Tf
	ToTs() Ts
	ToTl() Tl
}

type Tf struct {  // Tfloat
	f float64
}
type Ts struct {  // Tstring
	s string
}
type Tl struct {  // Tlist
	l []T
}
type Tv struct {  // Tvalue
	v R.Value
}

func FloatToT(a float64) Tf {
	return Tf{f: a}
}
func IntToT(a int64) Tf {
	return Tf{f: float64(a)}
}
func UintToT(a uint64) Tf {
	return Tf{f: float64(a)}
}
func StringToT(a string) Ts {
	return Ts{s: a}
}
func ListToT(a []T) Tl {
	return Tl{l: a}
}
func ValueToT(a R.Value) T {
	return Tv{v: a}
}
func ToT(a interface{}) T {
	switch z := a.(type) {
	case float64: return FloatToT(z)
	case float32: return FloatToT(float64(z))
	case int: return IntToT(int64(z))
	case int8: return IntToT(int64(z))
	case int16: return IntToT(int64(z))
	case int32: return IntToT(int64(z))
	case int64: return IntToT(z)
	case uint: return UintToT(uint64(z))
	case uint8: return UintToT(uint64(z))
	case uint16: return UintToT(uint64(z))
	case uint32: return UintToT(uint64(z))
	case uint64: return UintToT(z)
	case string: return StringToT(z)
	case T: panic("Already a T")
	}
	return ValueToT(R.ValueOf(a))
}

func (t Tf) String() string {
	return Sprintf("%g", t.f)
}
func (t Tf) ListElement() string {
	return t.String()
}
func (t Tf) Float() float64 {
	return t.f
}
func (t Tf) Int() int64 {
	return int64(t.f)
}
func (t Tf) Uint() uint64 {
	return uint64(t.f)
}
func (t Tf) Bool() bool {
	if t.f == 0 {
		return false
	}
	return true
}
func (t Tf) ToTf() Tf {
	return t
}
func (t Tf) ToTs() Ts {
	return Ts{s: t.String()}
}
func (t Tf) ToTl() Tl {
	return Tl{l: []T{t,}}
}

func (t Ts) String() string {
	return t.s
}
func (t Ts) ListElement() string {
	return ToListElement(t.s)
}
func (t Ts) Float() float64 {
	z, err := strconv.ParseFloat(t.s, 64)
	if err != nil {
		panic(err)
	}
	return z
}
func (t Ts) Int() int64 {
	return int64(t.Float())  //TODO
}
func (t Ts) Uint() uint64 {
	return uint64(t.Float())  //TODO
}
func (t Ts) Bool() bool {
	if t.s == "" || t.s == "0" {
		return false
	}
	return true
}
func (t Ts) ToTf() Tf {
	return FloatToT(t.Float())
}
func (t Ts) ToTs() Ts {
	return t
}
func (t Ts) ToTl() Tl {
	v := ParseList(t.s)
	z := make([]T, len(v))
	for i, e := range v {
		z[i] = ToT(e)
	}
	return Tl{l: z}
}

func (t Tl) String() string {
	z := ""
	for k, v := range t.l {
		if k > 0 {
			z += " "
		}
		z += v.ListElement()
	}
	return z
}
func (t Tl) ListElement() string {
	return ToListElement(t.String())
}
func (t Tl) Float() float64 {
	if len(t.l) != 1 {panic("cant")}
	return t.l[0].Float()
}
func (t Tl) Int() int64 {
	if len(t.l) != 1 {panic("cant")}
	return t.l[0].Int()
}
func (t Tl) Uint() uint64 {
	if len(t.l) != 1 {panic("cant")}
	return t.l[0].Uint()
}
func (t Tl) Bool() bool {
	if len(t.l) == 0 {
		return false
	}
	return true
}
func (t Tl) ToTf() Tf {
	return FloatToT(t.Float())
}
func (t Tl) ToTs() Ts {
	return StringToT(t.String())
}
func (t Tl) ToTl() Tl {
	return t
}

func (t Tv) String() string {
	return Sprintf("Value:%s:%s:%d", t.v.Kind(), t.v.Type(), t.v.Addr())
}
func (t Tv) ListElement() string {
	return ToListElement(t.String())
}
func (t Tv) Float() float64 {
	panic("cant yet")
}
func (t Tv) Int() int64 {
	panic("cant yet")
}
func (t Tv) Uint() uint64 {
	panic("cant yet")
}
func (t Tv) Bool() bool {
	panic("cant yet")
}
func (t Tv) ToTf() Tf {
	return Tf{f: t.Float()}
}
func (t Tv) ToTs() Ts {
	return Ts{s: t.String()}
}
func (t Tv) ToTl() Tl {
	return Tl{l: []T{t,}}
}

func ToListElement(s string) string {
	// TODO: Not perfect, but we are not doing \ yet.
	// TODO: Broken for mismatched {}.
	if s == "" {
		return "{}"
	}

	if strings.ContainsAny(s, " \t\n\r{}\\") {
		return "{" + s + "}"
	}
	return s
}

<|endoftext|>


<commit_before>package util

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"

	"github.com/fatih/color"
)

// DoesCommandOuputContain runs the given command
// and returns whether its output contains the given string.
func DoesCommandOuputContain(cmd []string, value string) bool {
	return strings.Contains(GetCommandOutput(cmd...), value)
}

// DoesCommandOuputContainLine runs the given command
// and returns whether its output contains the given string as an entire line.
func DoesCommandOuputContainLine(cmd []string, value string) bool {
	list := strings.Split(GetCommandOutput(cmd...), "\n")
	return DoesStringArrayContain(list, value)
}

// DoesStringArrayContain returns whether the given string slice
// contains the given string.
func DoesStringArrayContain(list []string, value string) bool {
	for _, element := range list {
		if element == value {
			return true
		}
	}
	return false
}

// ExitWithErrorMessage prints the given error message and terminates the application.
func ExitWithErrorMessage(messages ...string) {
	PrintError(messages...)
	os.Exit(1)
}

// GetCommandOutput runs the given command and returns its output.
func GetCommandOutput(cmd ...string) string {
	subProcess := exec.Command(cmd[0], cmd[1:]...)
	output, err := subProcess.CombinedOutput()
	if err != nil {
		log.Fatal("Command: ", strings.Join(cmd, " "), "\nOutput: "+string(output), "\nError: ", err)
	}
	return strings.TrimSpace(string(output))
}

// GetFullCommandOutput runs the given command and returns its output and error
func GetFullCommandOutput(cmd ...string) (string, error) {
	subProcess := exec.Command(cmd[0], cmd[1:]...)
	output, err := subProcess.CombinedOutput()
	return strings.TrimSpace(string(output)), err
}

var openBrowserCommands = []string{
	"xdg-open",
	"open",
	"cygstart",
	"x-www-browser",
	"firefox",
	"opera",
	"mozilla",
	"netscape",
}
var missingOpenBrowserCommandMessages = []string{
	"Cannot open a browser.",
	"If you think this is a bug,",
	"please open an issue at https://github.com/Originate/git-town/issues",
	"and mention your OS and browser.",
}

// GetOpenBrowserCommand returns the command to run on the console
// to open the default browser.
func GetOpenBrowserCommand() string {
	for _, command := range openBrowserCommands {
		output, err := GetFullCommandOutput("which", command)
		if err == nil && output != "" {
			return command
		}
	}
	ExitWithErrorMessage(missingOpenBrowserCommandMessages...)
	return ""
}

var inputReader = bufio.NewReader(os.Stdin)

// GetUserInput reads input from the user and returns it.
func GetUserInput() string {
	text, err := inputReader.ReadString('\n')
	if err != nil {
		log.Fatal("Error getting user input:", err)
	}
	return strings.TrimSpace(text)
}

// Indent outputs the given string with the given level of indentation
// on each line. Each level of indentation is two spaces.
func Indent(message string, level int) string {
	prefix := strings.Repeat("  ", level)
	return prefix + strings.Replace(message, "\n", "\n"+prefix, -1)
}

// Pluralize outputs the count and the word. The word is made plural
// if the count isn't one
func Pluralize(count, word string) string {
	result := count + " " + word
	if count != "1" {
		result = result + "s"
	}
	return result
}

// PrintError prints the given error message to the console.
func PrintError(messages ...string) {
	errHeaderFmt := color.New(color.Bold).Add(color.FgRed)
	errMessageFmt := color.New(color.FgRed)
	fmt.Println()
	errHeaderFmt.Println("  Error")
	for _, message := range messages {
		errMessageFmt.Println("  " + message)
	}
	fmt.Println()
}

// PrintLabelAndValue prints the label bolded and underlined
// the value indented on the next line
// followed by an empty line
func PrintLabelAndValue(label, value string) {
	labelFmt := color.New(color.Bold).Add(color.Underline)
	labelFmt.Println(label + ":")
	fmt.Println(Indent(value, 1))
	fmt.Println()
}

// RemoveStringFromSlice returns a new string slice which is the given string slice
// with the given string removed
func RemoveStringFromSlice(list []string, value string) (result []string) {
	for _, element := range list {
		if element != value {
			result = append(result, element)
		}
	}
	return
}
<commit_msg>Dry up running commands<commit_after>package util

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"

	"github.com/fatih/color"
)

// DoesCommandOuputContain runs the given command
// and returns whether its output contains the given string.
func DoesCommandOuputContain(cmd []string, value string) bool {
	return strings.Contains(GetCommandOutput(cmd...), value)
}

// DoesCommandOuputContainLine runs the given command
// and returns whether its output contains the given string as an entire line.
func DoesCommandOuputContainLine(cmd []string, value string) bool {
	list := strings.Split(GetCommandOutput(cmd...), "\n")
	return DoesStringArrayContain(list, value)
}

// DoesStringArrayContain returns whether the given string slice
// contains the given string.
func DoesStringArrayContain(list []string, value string) bool {
	for _, element := range list {
		if element == value {
			return true
		}
	}
	return false
}

// ExitWithErrorMessage prints the given error message and terminates the application.
func ExitWithErrorMessage(messages ...string) {
	PrintError(messages...)
	os.Exit(1)
}

// GetCommandOutput runs the given command and returns its output.
func GetCommandOutput(cmd ...string) string {
	output, err := GetFullCommandOutput(cmd...)
	if err != nil {
		log.Fatal("Command: ", strings.Join(cmd, " "), "\nOutput: "+string(output), "\nError: ", err)
	}
	return strings.TrimSpace(string(output))
}

// GetFullCommandOutput runs the given command and returns its output and error
func GetFullCommandOutput(cmd ...string) (string, error) {
	subProcess := exec.Command(cmd[0], cmd[1:]...)
	output, err := subProcess.CombinedOutput()
	return strings.TrimSpace(string(output)), err
}

var openBrowserCommands = []string{
	"xdg-open",
	"open",
	"cygstart",
	"x-www-browser",
	"firefox",
	"opera",
	"mozilla",
	"netscape",
}
var missingOpenBrowserCommandMessages = []string{
	"Cannot open a browser.",
	"If you think this is a bug,",
	"please open an issue at https://github.com/Originate/git-town/issues",
	"and mention your OS and browser.",
}

// GetOpenBrowserCommand returns the command to run on the console
// to open the default browser.
func GetOpenBrowserCommand() string {
	for _, command := range openBrowserCommands {
		output, err := GetFullCommandOutput("which", command)
		if err == nil && output != "" {
			return command
		}
	}
	ExitWithErrorMessage(missingOpenBrowserCommandMessages...)
	return ""
}

var inputReader = bufio.NewReader(os.Stdin)

// GetUserInput reads input from the user and returns it.
func GetUserInput() string {
	text, err := inputReader.ReadString('\n')
	if err != nil {
		log.Fatal("Error getting user input:", err)
	}
	return strings.TrimSpace(text)
}

// Indent outputs the given string with the given level of indentation
// on each line. Each level of indentation is two spaces.
func Indent(message string, level int) string {
	prefix := strings.Repeat("  ", level)
	return prefix + strings.Replace(message, "\n", "\n"+prefix, -1)
}

// Pluralize outputs the count and the word. The word is made plural
// if the count isn't one
func Pluralize(count, word string) string {
	result := count + " " + word
	if count != "1" {
		result = result + "s"
	}
	return result
}

// PrintError prints the given error message to the console.
func PrintError(messages ...string) {
	errHeaderFmt := color.New(color.Bold).Add(color.FgRed)
	errMessageFmt := color.New(color.FgRed)
	fmt.Println()
	errHeaderFmt.Println("  Error")
	for _, message := range messages {
		errMessageFmt.Println("  " + message)
	}
	fmt.Println()
}

// PrintLabelAndValue prints the label bolded and underlined
// the value indented on the next line
// followed by an empty line
func PrintLabelAndValue(label, value string) {
	labelFmt := color.New(color.Bold).Add(color.Underline)
	labelFmt.Println(label + ":")
	fmt.Println(Indent(value, 1))
	fmt.Println()
}

// RemoveStringFromSlice returns a new string slice which is the given string slice
// with the given string removed
func RemoveStringFromSlice(list []string, value string) (result []string) {
	for _, element := range list {
		if element != value {
			result = append(result, element)
		}
	}
	return
}
<|endoftext|>


<commit_before>package clang

// #include <stdlib.h>
// #include "clang-c/Index.h"
// #include "go-clang.h"
//
import "C"
import

// A single translation unit, which resides in an index
"unsafe"

type TranslationUnit struct {
	c C.CXTranslationUnit
}

/**
 * \brief Perform code completion at a given location in a translation unit.
 *
 * This function performs code completion at a particular file, line, and
 * column within source code, providing results that suggest potential
 * code snippets based on the context of the completion. The basic model
 * for code completion is that Clang will parse a complete source file,
 * performing syntax checking up to the location where code-completion has
 * been requested. At that point, a special code-completion token is passed
 * to the parser, which recognizes this token and determines, based on the
 * current location in the C/Objective-C/C++ grammar and the state of
 * semantic analysis, what completions to provide. These completions are
 * returned via a new \c CXCodeCompleteResults structure.
 *
 * Code completion itself is meant to be triggered by the client when the
 * user types punctuation characters or whitespace, at which point the
 * code-completion location will coincide with the cursor. For example, if \c p
 * is a pointer, code-completion might be triggered after the "-" and then
 * after the ">" in \c p->. When the code-completion location is afer the ">",
 * the completion results will provide, e.g., the members of the struct that
 * "p" points to. The client is responsible for placing the cursor at the
 * beginning of the token currently being typed, then filtering the results
 * based on the contents of the token. For example, when code-completing for
 * the expression \c p->get, the client should provide the location just after
 * the ">" (e.g., pointing at the "g") to this code-completion hook. Then, the
 * client can filter the results based on the current token text ("get"), only
 * showing those results that start with "get". The intent of this interface
 * is to separate the relatively high-latency acquisition of code-completion
 * results from the filtering of results on a per-character basis, which must
 * have a lower latency.
 *
 * \param TU The translation unit in which code-completion should
 * occur. The source files for this translation unit need not be
 * completely up-to-date (and the contents of those source files may
 * be overridden via \p unsaved_files). Cursors referring into the
 * translation unit may be invalidated by this invocation.
 *
 * \param complete_filename The name of the source file where code
 * completion should be performed. This filename may be any file
 * included in the translation unit.
 *
 * \param complete_line The line at which code-completion should occur.
 *
 * \param complete_column The column at which code-completion should occur.
 * Note that the column should point just after the syntactic construct that
 * initiated code completion, and not in the middle of a lexical token.
 *
 * \param unsaved_files the Tiles that have not yet been saved to disk
 * but may be required for parsing or code completion, including the
 * contents of those files.  The contents and name of these files (as
 * specified by CXUnsavedFile) are copied when necessary, so the
 * client only needs to guarantee their validity until the call to
 * this function returns.
 *
 * \param num_unsaved_files The number of unsaved file entries in \p
 * unsaved_files.
 *
 * \param options Extra options that control the behavior of code
 * completion, expressed as a bitwise OR of the enumerators of the
 * CXCodeComplete_Flags enumeration. The
 * \c clang_defaultCodeCompleteOptions() function returns a default set
 * of code-completion options.
 *
 * \returns If successful, a new \c CXCodeCompleteResults structure
 * containing code-completion results, which should eventually be
 * freed with \c clang_disposeCodeCompleteResults(). If code
 * completion fails, returns NULL.
 */
func (tu TranslationUnit) CompleteAt(complete_filename string, complete_line, complete_column int, us UnsavedFiles, options CodeCompleteFlags) CodeCompleteResults {
	cfname := C.CString(complete_filename)
	defer C.free(unsafe.Pointer(cfname))
	c_us := us.to_c()
	defer c_us.Dispose()
	cr := C.clang_codeCompleteAt(tu.c, cfname, C.uint(complete_line), C.uint(complete_column), c_us.ptr(), C.uint(len(c_us)), C.uint(options))
	return CodeCompleteResults{cr}
}

func (tu TranslationUnit) IsValid() bool {
	return tu.c != nil
}

func (tu TranslationUnit) File(file_name string) File {
	cfname := C.CString(file_name)
	defer C.free(unsafe.Pointer(cfname))
	f := C.clang_getFile(tu.c, cfname)
	return File{f}
}

// IsFileMultipleIncludeGuarded determines whether the given header is guarded
// against multiple inclusions, either with the conventional
// #ifndef/#define/#endif macro guards or with #pragma once.
func (tu TranslationUnit) IsFileMultipleIncludeGuarded(file File) bool {
	o := C.clang_isFileMultipleIncludeGuarded(tu.c, file.c)
	if o != C.uint(0) {
		return true
	}
	return false
}

/**
 * \brief Retrieve the cursor that represents the given translation unit.
 *
 * The translation unit cursor can be used to start traversing the
 * various declarations within the given translation unit.
 */
func (tu TranslationUnit) ToCursor() Cursor {
	o := C.clang_getTranslationUnitCursor(tu.c)
	return Cursor{o}
}

/**
 * \brief Get the original translation unit source file name.
 */
func (tu TranslationUnit) Spelling() string {
	cstr := cxstring{C.clang_getTranslationUnitSpelling(tu.c)}
	defer cstr.Dispose()
	return cstr.String()
}

// CursorOf maps a source location to the cursor that describes the entity at that
// location in the source code.
//
// clang_getCursor() maps an arbitrary source location within a translation
// unit down to the most specific cursor that describes the entity at that
// location. For example, given an expression \c x + y, invoking
// clang_getCursor() with a source location pointing to "x" will return the
// cursor for "x"; similarly for "y". If the cursor points anywhere between
// "x" or "y" (e.g., on the + or the whitespace around it), clang_getCursor()
// will return a cursor referring to the "+" expression.
//
// Returns a cursor representing the entity at the given source location, or
// a NULL cursor if no such entity can be found.
func (tu TranslationUnit) Cursor(loc SourceLocation) Cursor {
	o := C.clang_getCursor(tu.c, loc.c)
	return Cursor{o}
}

/**
 * \brief Reparse the source files that produced this translation unit.
 *
 * This routine can be used to re-parse the source files that originally
 * created the given translation unit, for example because those source files
 * have changed (either on disk or as passed via \p unsaved_files). The
 * source code will be reparsed with the same command-line options as it
 * was originally parsed.
 *
 * Reparsing a translation unit invalidates all cursors and source locations
 * that refer into that translation unit. This makes reparsing a translation
 * unit semantically equivalent to destroying the translation unit and then
 * creating a new translation unit with the same command-line arguments.
 * However, it may be more efficient to reparse a translation
 * unit using this routine.
 *
 * \param TU The translation unit whose contents will be re-parsed. The
 * translation unit must originally have been built with
 * \c clang_createTranslationUnitFromSourceFile().
 *
 * \param num_unsaved_files The number of unsaved file entries in \p
 * unsaved_files.
 *
 * \param unsaved_files The files that have not yet been saved to disk
 * but may be required for parsing, including the contents of
 * those files.  The contents and name of these files (as specified by
 * CXUnsavedFile) are copied when necessary, so the client only needs to
 * guarantee their validity until the call to this function returns.
 *
 * \param options A bitset of options composed of the flags in CXReparse_Flags.
 * The function \c clang_defaultReparseOptions() produces a default set of
 * options recommended for most uses, based on the translation unit.
 *
 * \returns 0 if the sources could be reparsed. A non-zero value will be
 * returned if reparsing was impossible, such that the translation unit is
 * invalid. In such cases, the only valid call for \p TU is
 * \c clang_disposeTranslationUnit(TU).
 */
func (tu TranslationUnit) Reparse(us UnsavedFiles, options TranslationUnitFlags) int {
	c_us := us.to_c()
	defer c_us.Dispose()
	return int(C.clang_reparseTranslationUnit(tu.c, C.uint(len(c_us)), c_us.ptr(), C.uint(options)))
}

/**
 * \brief Saves a translation unit into a serialized representation of
 * that translation unit on disk.
 *
 * Any translation unit that was parsed without error can be saved
 * into a file. The translation unit can then be deserialized into a
 * new \c CXTranslationUnit with \c clang_createTranslationUnit() or,
 * if it is an incomplete translation unit that corresponds to a
 * header, used as a precompiled header when parsing other translation
 * units.
 *
 * \param TU The translation unit to save.
 *
 * \param FileName The file to which the translation unit will be saved.
 *
 * \param options A bitmask of options that affects how the translation unit
 * is saved. This should be a bitwise OR of the
 * CXSaveTranslationUnit_XXX flags.
 *
 * \returns A value that will match one of the enumerators of the CXSaveError
 * enumeration. Zero (CXSaveError_None) indicates that the translation unit was
 * saved successfully, while a non-zero value indicates that a problem occurred.
 */
func (tu TranslationUnit) Save(fname string, options uint) uint32 {
	cstr := C.CString(fname)
	defer C.free(unsafe.Pointer(cstr))
	o := C.clang_saveTranslationUnit(tu.c, cstr, C.uint(options))
	// FIXME: should be a SaveError type...
	return uint32(o)
}

/**
 * \brief Retrieve a diagnostic associated with the given translation unit.
 *
 * \param Unit the translation unit to query.
 * \param Index the zero-based diagnostic number to retrieve.
 *
 * \returns the requested diagnostic. This diagnostic must be freed
 * via a call to \c clang_disposeDiagnostic().
 */
func (tu TranslationUnit) Diagnostics() (ret Diagnostics) {
	ret = make(Diagnostics, C.clang_getNumDiagnostics(tu.c))
	for i := range ret {
		ret[i].c = C.clang_getDiagnostic(tu.c, C.uint(i))
	}
	return
}

/**
 * \brief Destroy the specified CXTranslationUnit object.
 */
func (tu TranslationUnit) Dispose() {
	C.clang_disposeTranslationUnit(tu.c)
}

// Location returns the source location associated with a given file/line/column
// in a particular translation unit.
func (tu TranslationUnit) Location(f File, line, column uint) SourceLocation {
	loc := C.clang_getLocation(tu.c, f.c, C.uint(line), C.uint(column))
	return SourceLocation{loc}
}

// LocationForOffset returns the source location associated with a given
// character offset in a particular translation unit.
func (tu TranslationUnit) LocationForOffset(f File, offset uint) SourceLocation {
	loc := C.clang_getLocationForOffset(tu.c, f.c, C.uint(offset))
	return SourceLocation{loc}
}

/**
 * \param Module a module object.
 *
 * \returns the number of top level headers associated with this module.
 */
func (tu TranslationUnit) NumTopLevelHeaders(m Module) int {
	return int(C.clang_Module_getNumTopLevelHeaders(tu.c, m.c))
}

/**
 * \param Module a module object.
 *
 * \param Index top level header index (zero-based).
 *
 * \returns the specified top level header associated with the module.
 */
func (tu TranslationUnit) TopLevelHeader(m Module, i int) File {
	return File{C.clang_Module_getTopLevelHeader(tu.c, m.c, C.unsigned(i))}
}

// TODO
//
// /**
//  * \brief Find #import/#include directives in a specific file.
//  *
//  * \param TU translation unit containing the file to query.
//  *
//  * \param file to search for #import/#include directives.
//  *
//  * \param visitor callback that will receive pairs of CXCursor/CXSourceRange for
//  * each directive found.
//  *
//  * \returns one of the CXResult enumerators.
//  */
// CINDEX_LINKAGE CXResult clang_findIncludesInFile(CXTranslationUnit TU,
//                                                  CXFile file,
//                                               CXCursorAndRangeVisitor visitor);
<commit_msg>translationunit: stub for clang_findIncludesInFileWithBlock<commit_after>package clang

// #include <stdlib.h>
// #include "clang-c/Index.h"
// #include "go-clang.h"
//
import "C"
import

// A single translation unit, which resides in an index
"unsafe"

type TranslationUnit struct {
	c C.CXTranslationUnit
}

/**
 * \brief Perform code completion at a given location in a translation unit.
 *
 * This function performs code completion at a particular file, line, and
 * column within source code, providing results that suggest potential
 * code snippets based on the context of the completion. The basic model
 * for code completion is that Clang will parse a complete source file,
 * performing syntax checking up to the location where code-completion has
 * been requested. At that point, a special code-completion token is passed
 * to the parser, which recognizes this token and determines, based on the
 * current location in the C/Objective-C/C++ grammar and the state of
 * semantic analysis, what completions to provide. These completions are
 * returned via a new \c CXCodeCompleteResults structure.
 *
 * Code completion itself is meant to be triggered by the client when the
 * user types punctuation characters or whitespace, at which point the
 * code-completion location will coincide with the cursor. For example, if \c p
 * is a pointer, code-completion might be triggered after the "-" and then
 * after the ">" in \c p->. When the code-completion location is afer the ">",
 * the completion results will provide, e.g., the members of the struct that
 * "p" points to. The client is responsible for placing the cursor at the
 * beginning of the token currently being typed, then filtering the results
 * based on the contents of the token. For example, when code-completing for
 * the expression \c p->get, the client should provide the location just after
 * the ">" (e.g., pointing at the "g") to this code-completion hook. Then, the
 * client can filter the results based on the current token text ("get"), only
 * showing those results that start with "get". The intent of this interface
 * is to separate the relatively high-latency acquisition of code-completion
 * results from the filtering of results on a per-character basis, which must
 * have a lower latency.
 *
 * \param TU The translation unit in which code-completion should
 * occur. The source files for this translation unit need not be
 * completely up-to-date (and the contents of those source files may
 * be overridden via \p unsaved_files). Cursors referring into the
 * translation unit may be invalidated by this invocation.
 *
 * \param complete_filename The name of the source file where code
 * completion should be performed. This filename may be any file
 * included in the translation unit.
 *
 * \param complete_line The line at which code-completion should occur.
 *
 * \param complete_column The column at which code-completion should occur.
 * Note that the column should point just after the syntactic construct that
 * initiated code completion, and not in the middle of a lexical token.
 *
 * \param unsaved_files the Tiles that have not yet been saved to disk
 * but may be required for parsing or code completion, including the
 * contents of those files.  The contents and name of these files (as
 * specified by CXUnsavedFile) are copied when necessary, so the
 * client only needs to guarantee their validity until the call to
 * this function returns.
 *
 * \param num_unsaved_files The number of unsaved file entries in \p
 * unsaved_files.
 *
 * \param options Extra options that control the behavior of code
 * completion, expressed as a bitwise OR of the enumerators of the
 * CXCodeComplete_Flags enumeration. The
 * \c clang_defaultCodeCompleteOptions() function returns a default set
 * of code-completion options.
 *
 * \returns If successful, a new \c CXCodeCompleteResults structure
 * containing code-completion results, which should eventually be
 * freed with \c clang_disposeCodeCompleteResults(). If code
 * completion fails, returns NULL.
 */
func (tu TranslationUnit) CompleteAt(complete_filename string, complete_line, complete_column int, us UnsavedFiles, options CodeCompleteFlags) CodeCompleteResults {
	cfname := C.CString(complete_filename)
	defer C.free(unsafe.Pointer(cfname))
	c_us := us.to_c()
	defer c_us.Dispose()
	cr := C.clang_codeCompleteAt(tu.c, cfname, C.uint(complete_line), C.uint(complete_column), c_us.ptr(), C.uint(len(c_us)), C.uint(options))
	return CodeCompleteResults{cr}
}

func (tu TranslationUnit) IsValid() bool {
	return tu.c != nil
}

func (tu TranslationUnit) File(file_name string) File {
	cfname := C.CString(file_name)
	defer C.free(unsafe.Pointer(cfname))
	f := C.clang_getFile(tu.c, cfname)
	return File{f}
}

// IsFileMultipleIncludeGuarded determines whether the given header is guarded
// against multiple inclusions, either with the conventional
// #ifndef/#define/#endif macro guards or with #pragma once.
func (tu TranslationUnit) IsFileMultipleIncludeGuarded(file File) bool {
	o := C.clang_isFileMultipleIncludeGuarded(tu.c, file.c)
	if o != C.uint(0) {
		return true
	}
	return false
}

/**
 * \brief Retrieve the cursor that represents the given translation unit.
 *
 * The translation unit cursor can be used to start traversing the
 * various declarations within the given translation unit.
 */
func (tu TranslationUnit) ToCursor() Cursor {
	o := C.clang_getTranslationUnitCursor(tu.c)
	return Cursor{o}
}

/**
 * \brief Get the original translation unit source file name.
 */
func (tu TranslationUnit) Spelling() string {
	cstr := cxstring{C.clang_getTranslationUnitSpelling(tu.c)}
	defer cstr.Dispose()
	return cstr.String()
}

// CursorOf maps a source location to the cursor that describes the entity at that
// location in the source code.
//
// clang_getCursor() maps an arbitrary source location within a translation
// unit down to the most specific cursor that describes the entity at that
// location. For example, given an expression \c x + y, invoking
// clang_getCursor() with a source location pointing to "x" will return the
// cursor for "x"; similarly for "y". If the cursor points anywhere between
// "x" or "y" (e.g., on the + or the whitespace around it), clang_getCursor()
// will return a cursor referring to the "+" expression.
//
// Returns a cursor representing the entity at the given source location, or
// a NULL cursor if no such entity can be found.
func (tu TranslationUnit) Cursor(loc SourceLocation) Cursor {
	o := C.clang_getCursor(tu.c, loc.c)
	return Cursor{o}
}

/**
 * \brief Reparse the source files that produced this translation unit.
 *
 * This routine can be used to re-parse the source files that originally
 * created the given translation unit, for example because those source files
 * have changed (either on disk or as passed via \p unsaved_files). The
 * source code will be reparsed with the same command-line options as it
 * was originally parsed.
 *
 * Reparsing a translation unit invalidates all cursors and source locations
 * that refer into that translation unit. This makes reparsing a translation
 * unit semantically equivalent to destroying the translation unit and then
 * creating a new translation unit with the same command-line arguments.
 * However, it may be more efficient to reparse a translation
 * unit using this routine.
 *
 * \param TU The translation unit whose contents will be re-parsed. The
 * translation unit must originally have been built with
 * \c clang_createTranslationUnitFromSourceFile().
 *
 * \param num_unsaved_files The number of unsaved file entries in \p
 * unsaved_files.
 *
 * \param unsaved_files The files that have not yet been saved to disk
 * but may be required for parsing, including the contents of
 * those files.  The contents and name of these files (as specified by
 * CXUnsavedFile) are copied when necessary, so the client only needs to
 * guarantee their validity until the call to this function returns.
 *
 * \param options A bitset of options composed of the flags in CXReparse_Flags.
 * The function \c clang_defaultReparseOptions() produces a default set of
 * options recommended for most uses, based on the translation unit.
 *
 * \returns 0 if the sources could be reparsed. A non-zero value will be
 * returned if reparsing was impossible, such that the translation unit is
 * invalid. In such cases, the only valid call for \p TU is
 * \c clang_disposeTranslationUnit(TU).
 */
func (tu TranslationUnit) Reparse(us UnsavedFiles, options TranslationUnitFlags) int {
	c_us := us.to_c()
	defer c_us.Dispose()
	return int(C.clang_reparseTranslationUnit(tu.c, C.uint(len(c_us)), c_us.ptr(), C.uint(options)))
}

/**
 * \brief Saves a translation unit into a serialized representation of
 * that translation unit on disk.
 *
 * Any translation unit that was parsed without error can be saved
 * into a file. The translation unit can then be deserialized into a
 * new \c CXTranslationUnit with \c clang_createTranslationUnit() or,
 * if it is an incomplete translation unit that corresponds to a
 * header, used as a precompiled header when parsing other translation
 * units.
 *
 * \param TU The translation unit to save.
 *
 * \param FileName The file to which the translation unit will be saved.
 *
 * \param options A bitmask of options that affects how the translation unit
 * is saved. This should be a bitwise OR of the
 * CXSaveTranslationUnit_XXX flags.
 *
 * \returns A value that will match one of the enumerators of the CXSaveError
 * enumeration. Zero (CXSaveError_None) indicates that the translation unit was
 * saved successfully, while a non-zero value indicates that a problem occurred.
 */
func (tu TranslationUnit) Save(fname string, options uint) uint32 {
	cstr := C.CString(fname)
	defer C.free(unsafe.Pointer(cstr))
	o := C.clang_saveTranslationUnit(tu.c, cstr, C.uint(options))
	// FIXME: should be a SaveError type...
	return uint32(o)
}

/**
 * \brief Retrieve a diagnostic associated with the given translation unit.
 *
 * \param Unit the translation unit to query.
 * \param Index the zero-based diagnostic number to retrieve.
 *
 * \returns the requested diagnostic. This diagnostic must be freed
 * via a call to \c clang_disposeDiagnostic().
 */
func (tu TranslationUnit) Diagnostics() (ret Diagnostics) {
	ret = make(Diagnostics, C.clang_getNumDiagnostics(tu.c))
	for i := range ret {
		ret[i].c = C.clang_getDiagnostic(tu.c, C.uint(i))
	}
	return
}

/**
 * \brief Destroy the specified CXTranslationUnit object.
 */
func (tu TranslationUnit) Dispose() {
	C.clang_disposeTranslationUnit(tu.c)
}

// Location returns the source location associated with a given file/line/column
// in a particular translation unit.
func (tu TranslationUnit) Location(f File, line, column uint) SourceLocation {
	loc := C.clang_getLocation(tu.c, f.c, C.uint(line), C.uint(column))
	return SourceLocation{loc}
}

// LocationForOffset returns the source location associated with a given
// character offset in a particular translation unit.
func (tu TranslationUnit) LocationForOffset(f File, offset uint) SourceLocation {
	loc := C.clang_getLocationForOffset(tu.c, f.c, C.uint(offset))
	return SourceLocation{loc}
}

/**
 * \param Module a module object.
 *
 * \returns the number of top level headers associated with this module.
 */
func (tu TranslationUnit) NumTopLevelHeaders(m Module) int {
	return int(C.clang_Module_getNumTopLevelHeaders(tu.c, m.c))
}

/**
 * \param Module a module object.
 *
 * \param Index top level header index (zero-based).
 *
 * \returns the specified top level header associated with the module.
 */
func (tu TranslationUnit) TopLevelHeader(m Module, i int) File {
	return File{C.clang_Module_getTopLevelHeader(tu.c, m.c, C.unsigned(i))}
}

// TODO
//
// /**
//  * \brief Find #import/#include directives in a specific file.
//  *
//  * \param TU translation unit containing the file to query.
//  *
//  * \param file to search for #import/#include directives.
//  *
//  * \param visitor callback that will receive pairs of CXCursor/CXSourceRange for
//  * each directive found.
//  *
//  * \returns one of the CXResult enumerators.
//  */
// CINDEX_LINKAGE CXResult clang_findIncludesInFile(CXTranslationUnit TU,
//                                                  CXFile file,
//                                               CXCursorAndRangeVisitor visitor);

// TODO
//
// CINDEX_LINKAGE
// CXResult clang_findIncludesInFileWithBlock(CXTranslationUnit, CXFile,
//                                            CXCursorAndRangeVisitorBlock);
<|endoftext|>


<commit_before>package signed

// The Sign function is a choke point for all code paths that do signing.
// We use this fact to do key ID translation. There are 2 types of key ID:
//   - Scoped: the key ID based purely on the data that appears in the TUF
//             files. This may be wrapped by a certificate that scopes the
//             key to be used in a specific context.
//   - Canonical: the key ID based purely on the public key bytes. This is
//             used by keystores to easily identify keys that may be reused
//             in many scoped locations.
// Currently these types only differ in the context of Root Keys in Notary
// for which the root key is wrapped using an x509 certificate.

import (
	"crypto/rand"

	"github.com/sirupsen/logrus"
	"github.com/theupdateframework/notary/trustmanager"
	"github.com/theupdateframework/notary/tuf/data"
	"github.com/theupdateframework/notary/tuf/utils"
)

// Sign takes a data.Signed and a cryptoservice containing private keys,
// calculates and adds at least minSignature signatures using signingKeys the
// data.Signed.  It will also clean up any signatures that are not in produced
// by either a signingKey or an otherWhitelistedKey.
// Note that in most cases, otherWhitelistedKeys should probably be null. They
// are for keys you don't want to sign with, but you also don't want to remove
// existing signatures by those keys.  For instance, if you want to call Sign
// multiple times with different sets of signing keys without undoing removing
// signatures produced by the previous call to Sign.
func Sign(service CryptoService, s *data.Signed, signingKeys []data.PublicKey,
	minSignatures int, otherWhitelistedKeys []data.PublicKey) error {

	logrus.Debugf("sign called with %d/%d required keys", minSignatures, len(signingKeys))
	signatures := make([]data.Signature, 0, len(s.Signatures)+1)
	signingKeyIDs := make(map[string]struct{})
	tufIDs := make(map[string]data.PublicKey)

	privKeys := make(map[string]data.PrivateKey)

	// Get all the private key objects related to the public keys
	missingKeyIDs := []string{}
	for _, key := range signingKeys {
		canonicalID, err := utils.CanonicalKeyID(key)
		tufIDs[key.ID()] = key
		if err != nil {
			return err
		}
		k, _, err := service.GetPrivateKey(canonicalID)
		if err != nil {
			if _, ok := err.(trustmanager.ErrKeyNotFound); ok {
				missingKeyIDs = append(missingKeyIDs, canonicalID)
				continue
			}
			return err
		}
		privKeys[key.ID()] = k
	}

	// include the list of otherWhitelistedKeys
	for _, key := range otherWhitelistedKeys {
		if _, ok := tufIDs[key.ID()]; !ok {
			tufIDs[key.ID()] = key
		}
	}

	// Check to ensure we have enough signing keys
	if len(privKeys) < minSignatures {
		return ErrInsufficientSignatures{FoundKeys: len(privKeys),
			NeededKeys: minSignatures, MissingKeyIDs: missingKeyIDs}
	}

	emptyStruct := struct{}{}
	// Do signing and generate list of signatures
	for keyID, pk := range privKeys {
		sig, err := pk.Sign(rand.Reader, *s.Signed, nil)
		if err != nil {
			logrus.Debugf("Failed to sign with key: %s. Reason: %v", keyID, err)
			return err
		}
		signingKeyIDs[keyID] = emptyStruct
		signatures = append(signatures, data.Signature{
			KeyID:     keyID,
			Method:    pk.SignatureAlgorithm(),
			Signature: sig[:],
		})
	}

	for _, sig := range s.Signatures {
		if _, ok := signingKeyIDs[sig.KeyID]; ok {
			// key is in the set of key IDs for which a signature has been created
			continue
		}
		var (
			k  data.PublicKey
			ok bool
		)
		if k, ok = tufIDs[sig.KeyID]; !ok {
			// key is no longer a valid signing key
			continue
		}
		if err := VerifySignature(*s.Signed, &sig, k); err != nil {
			// signature is no longer valid
			continue
		}
		// keep any signatures that still represent valid keys and are
		// themselves valid
		signatures = append(signatures, sig)
	}
	s.Signatures = signatures
	return nil
}
<commit_msg>linting: fix Implicit memory aliasing in RangeStmt<commit_after>package signed

// The Sign function is a choke point for all code paths that do signing.
// We use this fact to do key ID translation. There are 2 types of key ID:
//   - Scoped: the key ID based purely on the data that appears in the TUF
//             files. This may be wrapped by a certificate that scopes the
//             key to be used in a specific context.
//   - Canonical: the key ID based purely on the public key bytes. This is
//             used by keystores to easily identify keys that may be reused
//             in many scoped locations.
// Currently these types only differ in the context of Root Keys in Notary
// for which the root key is wrapped using an x509 certificate.

import (
	"crypto/rand"

	"github.com/sirupsen/logrus"
	"github.com/theupdateframework/notary/trustmanager"
	"github.com/theupdateframework/notary/tuf/data"
	"github.com/theupdateframework/notary/tuf/utils"
)

// Sign takes a data.Signed and a cryptoservice containing private keys,
// calculates and adds at least minSignature signatures using signingKeys the
// data.Signed.  It will also clean up any signatures that are not in produced
// by either a signingKey or an otherWhitelistedKey.
// Note that in most cases, otherWhitelistedKeys should probably be null. They
// are for keys you don't want to sign with, but you also don't want to remove
// existing signatures by those keys.  For instance, if you want to call Sign
// multiple times with different sets of signing keys without undoing removing
// signatures produced by the previous call to Sign.
func Sign(service CryptoService, s *data.Signed, signingKeys []data.PublicKey,
	minSignatures int, otherWhitelistedKeys []data.PublicKey) error {

	logrus.Debugf("sign called with %d/%d required keys", minSignatures, len(signingKeys))
	signatures := make([]data.Signature, 0, len(s.Signatures)+1)
	signingKeyIDs := make(map[string]struct{})
	tufIDs := make(map[string]data.PublicKey)

	privKeys := make(map[string]data.PrivateKey)

	// Get all the private key objects related to the public keys
	missingKeyIDs := []string{}
	for _, key := range signingKeys {
		canonicalID, err := utils.CanonicalKeyID(key)
		tufIDs[key.ID()] = key
		if err != nil {
			return err
		}
		k, _, err := service.GetPrivateKey(canonicalID)
		if err != nil {
			if _, ok := err.(trustmanager.ErrKeyNotFound); ok {
				missingKeyIDs = append(missingKeyIDs, canonicalID)
				continue
			}
			return err
		}
		privKeys[key.ID()] = k
	}

	// include the list of otherWhitelistedKeys
	for _, key := range otherWhitelistedKeys {
		if _, ok := tufIDs[key.ID()]; !ok {
			tufIDs[key.ID()] = key
		}
	}

	// Check to ensure we have enough signing keys
	if len(privKeys) < minSignatures {
		return ErrInsufficientSignatures{FoundKeys: len(privKeys),
			NeededKeys: minSignatures, MissingKeyIDs: missingKeyIDs}
	}

	emptyStruct := struct{}{}
	// Do signing and generate list of signatures
	for keyID, pk := range privKeys {
		sig, err := pk.Sign(rand.Reader, *s.Signed, nil)
		if err != nil {
			logrus.Debugf("Failed to sign with key: %s. Reason: %v", keyID, err)
			return err
		}
		signingKeyIDs[keyID] = emptyStruct
		signatures = append(signatures, data.Signature{
			KeyID:     keyID,
			Method:    pk.SignatureAlgorithm(),
			Signature: sig[:],
		})
	}

	for i := range s.Signatures {
		sig := s.Signatures[i]
		if _, ok := signingKeyIDs[sig.KeyID]; ok {
			// key is in the set of key IDs for which a signature has been created
			continue
		}
		var (
			k  data.PublicKey
			ok bool
		)
		if k, ok = tufIDs[sig.KeyID]; !ok {
			// key is no longer a valid signing key
			continue
		}
		if err := VerifySignature(*s.Signed, &sig, k); err != nil {
			// signature is no longer valid
			continue
		}
		// keep any signatures that still represent valid keys and are
		// themselves valid
		signatures = append(signatures, sig)
	}
	s.Signatures = signatures
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	twitter "github.com/darkhelmet/twitterstream"
	_ "github.com/go-sql-driver/mysql"
	"io/ioutil"
	"os"
)

var (
	tweetsTableCreate = "CREATE TABLE IF NOT EXISTS `tweets` (" +
		"`tweet_id` int(11) unsigned NOT NULL," +
		"`text` varchar(140) NOT NULL DEFAULT ''," +
		"`created_at` datetime NOT NULL," +
		"`in_reply_to_status_id` bigint(20) DEFAULT NULL," +
		"`in_reply_to_user_id` int(11) DEFAULT NULL," +
		"`retweeted_status_id` bigint(20) DEFAULT NULL," +
		"`source` varchar(200) DEFAULT ''," +
		"`user_id` int(11) NOT NULL," +
		"PRIMARY KEY (`tweet_id`)" +
		") ENGINE=InnoDB DEFAULT CHARSET=utf8;"
	usersTableCreate = "CREATE TABLE IF NOT EXISTS `users` (" +
		"`user_id` int(11) NOT NULL," +
		"`name` varchar(200) DEFAULT NULL," +
		"`screen_name` varchar(200) NOT NULL DEFAULT ''," +
		"`followers_count` int(11) NOT NULL," +
		"`friends_count` int(11) NOT NULL," +
		"`listed_count` int(11) NOT NULL," +
		"`created_at` datetime NOT NULL," +
		"`favourites_count` int(11) NOT NULL," +
		"`verified` tinyint(1) NOT NULL," +
		"`statuses_count` int(11) NOT NULL," +
		"`default_profile_image` tinyint(1) NOT NULL," +
		"PRIMARY KEY (`user_id`,`screen_name`)" +
		") ENGINE=InnoDB DEFAULT CHARSET=utf8;"
	trackKeywords = []string{"RT", "http"}
)

func main() {
	db, err := sql.Open("mysql", "XXXX")
	if err != nil {
		panic(err)
	}
	defer db.Close()

	err = setupTables(db)
	if err != nil {
		panic(err)
	}

	authCred, err := loadAuthCred()
	if err != nil {
		panic(err)
	}

	conn, err := setupStreamConnection(authCred)
	if err != nil {
		panic(err)
	}

	tweets := make(chan *twitter.Tweet)
	go tweetConsumer(conn, tweets)

	for tweet := range tweets {
		fmt.Println(tweet)
	}
}

func tweetConsumer(conn *twitter.Connection, c chan<- *twitter.Tweet) {
	var tweet *twitter.Tweet
	var err error

	for {
		if tweet, err := conn.Next(); err == nil {
			c <- tweet
		} else {
			panic(err)
		}
	}
}

func setupTables(db *sql.DB) error {
	var err error
	_, err = db.Exec(tweetsTableCreate)
	if err != nil {
		return err
	}

	_, err = db.Exec(usersTableCreate)
	if err != nil {
		return err
	}

	return nil
}

func loadAuthCred() (map[string]string, error) {
	var m map[string]string

	f, err := os.Open("config.json")
	if err != nil {
		return nil, err
	}

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

	err = json.Unmarshal(contents, &m)
	if err != nil {
		return nil, err
	}

	return m, nil
}

func setupStreamConnection(authCred map[string]string) (*twitter.Connection, error) {
	client := twitter.NewClient(authCred["consumer_key"], authCred["consumer_secret"], authCred["access_token_key"], authCred["access_token_secret"])
	return client.Track(trackKeywords...)
}
<commit_msg>Added getTweet and getUser functions.<commit_after>package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	twitter "github.com/darkhelmet/twitterstream"
	_ "github.com/go-sql-driver/mysql"
	"io/ioutil"
	"os"
)

var _ = fmt.Println

var (
	tweetsTableCreate = "CREATE TABLE IF NOT EXISTS `tweets` (" +
		"`tweet_id` bigint(20) unsigned NOT NULL," +
		"`text` varchar(140) NOT NULL DEFAULT ''," +
		"`created_at` datetime NOT NULL," +
		"`in_reply_to_status_id` bigint(20) unsigned DEFAULT NULL," +
		"`in_reply_to_user_id` int(11) unsigned DEFAULT NULL," +
		"`retweeted_status_id` bigint(20) unsigned DEFAULT NULL," +
		"`source` varchar(200) DEFAULT ''," +
		"`user_id` int(11) unsigned NOT NULL," +
		"PRIMARY KEY (`tweet_id`)" +
		") ENGINE=InnoDB DEFAULT CHARSET=utf8;"
	usersTableCreate = "CREATE TABLE IF NOT EXISTS `users` (" +
		"`user_id` int(11) unsigned NOT NULL," +
		"`name` varchar(200) DEFAULT NULL," +
		"`screen_name` varchar(200) NOT NULL DEFAULT ''," +
		"`followers_count` int(11) NOT NULL," +
		"`friends_count` int(11) NOT NULL," +
		"`listed_count` int(11) NOT NULL," +
		"`created_at` datetime NOT NULL," +
		"`favourites_count` int(11) NOT NULL," +
		"`verified` tinyint(1) NOT NULL," +
		"`statuses_count` int(11) NOT NULL," +
		"`default_profile_image` tinyint(1) NOT NULL," +
		"PRIMARY KEY (`user_id`,`screen_name`)" +
		") ENGINE=InnoDB DEFAULT CHARSET=utf8;"
	userByIdSql   = "SELECT user_id FROM users WHERE user_id = ?;"
	tweetByIdSql  = "SELECT tweet_id FROM tweets WHERE tweet_id = ?;"
	trackKeywords = []string{"RT", "http"}
)

func main() {
	db, err := sql.Open("mysql", os.Getenv("DB_CONNECTION"))
	if err != nil {
		panic(err)
	}
	defer db.Close()

	err = setupTables(db)
	if err != nil {
		panic(err)
	}

	authCred, err := loadAuthCred()
	if err != nil {
		panic(err)
	}

	conn, err := setupStreamConnection(authCred)
	if err != nil {
		panic(err)
	}

	tweets := make(chan *twitter.Tweet)
	go tweetConsumer(conn, tweets)

	processTweets(db, tweets)
}

func processTweets(db *sql.DB, tweets <-chan *twitter.Tweet) {
	for tweet := range tweets {

	}
}

func tweetConsumer(conn *twitter.Connection, c chan<- *twitter.Tweet) {
	for {
		if tweet, err := conn.Next(); err == nil {
			c <- tweet
		} else {
			close(c)
			panic(err)
		}
	}
}

func setupTables(db *sql.DB) error {
	var err error
	_, err = db.Exec(tweetsTableCreate)
	if err != nil {
		return err
	}

	_, err = db.Exec(usersTableCreate)
	if err != nil {
		return err
	}

	return nil
}

func loadAuthCred() (map[string]string, error) {
	var m map[string]string

	f, err := os.Open("config.json")
	if err != nil {
		return nil, err
	}

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

	err = json.Unmarshal(contents, &m)
	if err != nil {
		return nil, err
	}

	return m, nil
}

func setupStreamConnection(authCred map[string]string) (*twitter.Connection, error) {
	client := twitter.NewClient(authCred["consumer_key"], authCred["consumer_secret"], authCred["access_token_key"], authCred["access_token_secret"])
	return client.Track(trackKeywords...)
}

func prepareGetUser(db *sql.DB) func(*twitter.User) bool {
	userByIdStmt, err := db.Prepare(userByIdSql)
	if err != nil {
		panic(err)
	}

	return func(user *twitter.User) bool {
		row := userByIdStmt.QueryRow(user.Id)
		if err != nil {
			panic(err)
		}

		var id int
		err = row.Scan(&id)

		return err == nil
	}
}

func prepareGetTweet(db *sql.DB) func(*twitter.Tweet) bool {
	tweetByIdStmt, err := db.Prepare(tweetByIdSql)
	if err != nil {
		panic(err)
	}

	return func(tweet *twitter.Tweet) bool {
		row := tweetByIdStmt.QueryRow(tweet.Id)
		if err != nil {
			panic(err)
		}

		var id int
		err = row.Scan(&id)

		return err == nil
	}
}
<|endoftext|>


<commit_before>package tcppeer

import (
	"github.com/davyxu/cellnet"
	"github.com/davyxu/cellnet/comm"
	"github.com/davyxu/cellnet/internal"
	"net"
	"sync"
)

// Socket会话
type tcpSession struct {

	// Socket原始连接
	conn net.Conn

	tag interface{}

	// 退出同步器
	exitSync sync.WaitGroup

	// 归属的通讯端
	peer *internal.PeerShare

	id int64

	// 发送队列
	sendChan chan interface{}

	endNotify func()
}

// 取原始连接
func (self *tcpSession) Raw() interface{} {
	return self.conn
}

func (self *tcpSession) Tag() interface{} {
	return self.tag
}

func (self *tcpSession) SetTag(v interface{}) {
	self.tag = v
}

func (self *tcpSession) ID() int64 {
	return self.id
}

func (self *tcpSession) SetID(id int64) {
	self.id = id
}

func (self *tcpSession) Close() {
	self.sendChan <- nil
}

// 取会话归属的通讯端
func (self *tcpSession) Peer() cellnet.Peer {
	return self.peer.Peer()
}

// 发送封包
func (self *tcpSession) Send(msg interface{}) {
	self.sendChan <- msg
}

// 接收循环
func (self *tcpSession) recvLoop() {

	for self.conn != nil {

		// 发送接收消息，要求读取数据
		raw := self.peer.InvokeInboundEvent(&cellnet.ReadEvent{self})

		// 连接断开
		if raw != nil && self.conn != nil {

			self.peer.InvokeInboundEvent(&cellnet.RecvMsgEvent{self, &comm.SessionClosed{}})
			//self.peer.InvokeInboundEvent(cellnet.RecvErrorEvent{self, raw.(error)})
			break
		}
	}

	self.cleanup()
}

// 发送循环
func (self *tcpSession) sendLoop() {

	// 遍历要发送的数据
	for msg := range self.sendChan {

		// nil表示需要退出会话通讯
		if msg == nil {
			break
		}

		// 要求发送数据
		err := self.peer.InvokeOutboundEvent(&cellnet.SendMsgEvent{self, msg})

		// 发送错误时派发事件
		if err != nil {
			self.peer.InvokeInboundEvent(&cellnet.SendMsgErrorEvent{self, err.(error), msg})
			break
		}

	}

	self.cleanup()
}

// 清理资源
func (self *tcpSession) cleanup() {

	// 关闭连接
	if self.conn != nil {
		self.conn.Close()
		self.conn = nil
	}

	// 关闭发送队列
	if self.sendChan != nil {
		close(self.sendChan)
		self.sendChan = nil
	}

	// 通知完成
	self.exitSync.Done()
}

// 启动会话的各种资源
func (self *tcpSession) Start() {

	// 将会话添加到管理器
	self.Peer().(internal.SessionManager).Add(self)

	// 需要接收和发送线程同时完成时才算真正的完成
	self.exitSync.Add(2)

	go func() {

		// 等待2个任务结束
		self.exitSync.Wait()

		// 将会话从管理器移除
		self.Peer().(internal.SessionManager).Remove(self)

		if self.endNotify != nil {
			self.endNotify()
		}

	}()

	// 启动并发接收goroutine
	go self.recvLoop()

	// 启动并发发送goroutine
	go self.sendLoop()
}

// 默认10个长度的发送队列
const SendQueueLen = 100

func newTCPSession(conn net.Conn, peer *internal.PeerShare, endNotify func()) cellnet.Session {
	return &tcpSession{
		conn:      conn,
		peer:      peer,
		endNotify: endNotify,
		sendChan:  make(chan interface{}, SendQueueLen),
	}
}
<commit_msg>修复: cleanup的并发问题<commit_after>package tcppeer

import (
	"github.com/davyxu/cellnet"
	"github.com/davyxu/cellnet/comm"
	"github.com/davyxu/cellnet/internal"
	"net"
	"sync"
)

// Socket会话
type tcpSession struct {

	// Socket原始连接
	conn net.Conn

	tag interface{}

	// 退出同步器
	exitSync sync.WaitGroup

	// 归属的通讯端
	peer *internal.PeerShare

	id int64

	// 发送队列
	sendChan chan interface{}

	cleanupGuard sync.Mutex

	endNotify func()
}

// 取原始连接
func (self *tcpSession) Raw() interface{} {
	return self.conn
}

func (self *tcpSession) Tag() interface{} {
	return self.tag
}

func (self *tcpSession) SetTag(v interface{}) {
	self.tag = v
}

func (self *tcpSession) ID() int64 {
	return self.id
}

func (self *tcpSession) SetID(id int64) {
	self.id = id
}

func (self *tcpSession) Close() {
	self.sendChan <- nil
}

// 取会话归属的通讯端
func (self *tcpSession) Peer() cellnet.Peer {
	return self.peer.Peer()
}

// 发送封包
func (self *tcpSession) Send(msg interface{}) {
	self.sendChan <- msg
}

// 接收循环
func (self *tcpSession) recvLoop() {

	for self.conn != nil {

		// 发送接收消息，要求读取数据
		raw := self.peer.InvokeInboundEvent(&cellnet.ReadEvent{self})

		// 连接断开
		if raw != nil && self.conn != nil {

			self.peer.InvokeInboundEvent(&cellnet.RecvMsgEvent{self, &comm.SessionClosed{}})
			//self.peer.InvokeInboundEvent(cellnet.RecvErrorEvent{self, raw.(error)})
			break
		}
	}

	self.cleanup()
}

// 发送循环
func (self *tcpSession) sendLoop() {

	// 遍历要发送的数据
	for msg := range self.sendChan {

		// nil表示需要退出会话通讯
		if msg == nil {
			break
		}

		// 要求发送数据
		err := self.peer.InvokeOutboundEvent(&cellnet.SendMsgEvent{self, msg})

		// 发送错误时派发事件
		if err != nil {
			self.peer.InvokeInboundEvent(&cellnet.SendMsgErrorEvent{self, err.(error), msg})
			break
		}

	}

	self.cleanup()
}

// 清理资源
func (self *tcpSession) cleanup() {

	self.cleanupGuard.Lock()

	defer self.cleanupGuard.Unlock()

	// 关闭连接
	if self.conn != nil {
		self.conn.Close()
		self.conn = nil
	}

	// 关闭发送队列
	if self.sendChan != nil {
		close(self.sendChan)
		self.sendChan = nil
	}

	// 通知完成
	self.exitSync.Done()
}

// 启动会话的各种资源
func (self *tcpSession) Start() {

	// 将会话添加到管理器
	self.Peer().(internal.SessionManager).Add(self)

	// 需要接收和发送线程同时完成时才算真正的完成
	self.exitSync.Add(2)

	go func() {

		// 等待2个任务结束
		self.exitSync.Wait()

		// 将会话从管理器移除
		self.Peer().(internal.SessionManager).Remove(self)

		if self.endNotify != nil {
			self.endNotify()
		}

	}()

	// 启动并发接收goroutine
	go self.recvLoop()

	// 启动并发发送goroutine
	go self.sendLoop()
}

// 默认10个长度的发送队列
const SendQueueLen = 100

func newTCPSession(conn net.Conn, peer *internal.PeerShare, endNotify func()) cellnet.Session {
	return &tcpSession{
		conn:      conn,
		peer:      peer,
		endNotify: endNotify,
		sendChan:  make(chan interface{}, SendQueueLen),
	}
}
<|endoftext|>