<commit_before>/* https://leetcode.com/problems/number-of-islands/description/
Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.

Example 1:

11110
11010
11000
00000
Answer: 1

Example 2:

11000
11000
00100
00011
Answer: 3
*/

package leetcode

import "github.com/TTWShell/algorithms/union-find"

func numIslands(grid [][]byte) int {
	uf := unionfind.New()

	for i := range grid {
		for j := range grid[i] {
			if grid[i][j] == '1' {
				if i-1 >= 0 && grid[i-1][j] == '1' {
					uf.Union([2]int{i, j}, [2]int{i - 1, j})
				}
				if j+1 < len(grid[0]) && grid[i][j+1] == '1' {
					uf.Union([2]int{i, j}, [2]int{i, j + 1})
				}
				uf.Union([2]int{i, j}, [2]int{i, j})
			}
		}
	}
	return uf.Count()
}
<commit_msg>add dfs solution<commit_after>/* https://leetcode.com/problems/number-of-islands/description/
Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.

Example 1:

11110
11010
11000
00000
Answer: 1

Example 2:

11000
11000
00100
00011
Answer: 3
*/

package leetcode

func numIslands(grid [][]byte) int {
	var dfs func(i, j int)
	dfs = func(i, j int) {
		grid[i][j] = '0'
		if i > 0 && grid[i-1][j] == '1' {
			dfs(i-1, j)
		}
		if j > 0 && grid[i][j-1] == '1' {
			dfs(i, j-1)
		}
		if i < len(grid)-1 && grid[i+1][j] == '1' {
			dfs(i+1, j)
		}
		if j < len(grid[0])-1 && grid[i][j+1] == '1' {
			dfs(i, j+1)
		}
	}

	res := 0
	for i := range grid {
		for j := range grid[0] {
			if grid[i][j] == '1' {
				dfs(i, j)
				res++
			}
		}
	}
	return res
}

/*
import "github.com/TTWShell/algorithms/union-find"

// Union-find solution
func numIslands(grid [][]byte) int {
	uf := unionfind.New()

	for i := range grid {
		for j := range grid[i] {
			if grid[i][j] == '1' {
				if i-1 >= 0 && grid[i-1][j] == '1' {
					uf.Union([2]int{i, j}, [2]int{i - 1, j})
				}
				if j+1 < len(grid[0]) && grid[i][j+1] == '1' {
					uf.Union([2]int{i, j}, [2]int{i, j + 1})
				}
				uf.Union([2]int{i, j}, [2]int{i, j})
			}
		}
	}
	return uf.Count()
}
*/
<|endoftext|>


<commit_before>package transactions

import (
	"errors"

	"github.com/joshheinrichs/geosource/server/types"
)

func IsChannelCreator(userID, channelname string) (bool, error) {
	return false, errors.New("function has not yet been implemented.")
}

func AddChannel(channel *types.Channel) error {
	return db.Create(channel).Error
}

func GetChannel(requester, channelname string) (*types.PersonalizedChannel, error) {
	// TODO: Account for requester permission
	var channel types.PersonalizedChannel
	err := db.Table("channels").
		Joins("LEFT JOIN user_subscriptions ON (ch_channelname = us_channelname AND us_userid = ?)", requester).
		Joins("LEFT JOIN users ON (u_userid = ch_userid_creator)").
		Select("*, (us_channelname IS NOT NULL) AS subscribed").
		First(&channel).Error
	return &channel, err
}

func GetChannels(requester string) ([]*types.PersonalizedChannelInfo, error) {
	var channels []*types.PersonalizedChannelInfo
	err := db.Table("channels").
		Joins("LEFT JOIN user_subscriptions ON (ch_channelname = us_channelname AND us_userid = ?)", requester).
		Joins("LEFT JOIN users ON (u_userid = ch_userid_creator)").
		Select("*, (us_channelname IS NOT NULL) AS subscribed").
		Order("ch_channelname").Find(&channels).Error
	if err != nil {
		return nil, err
	}
	return channels, nil
}

func RemoveChannel(requester, channelname string) error {
	return errors.New("function has not yet been implemented.")
}
<commit_msg>Fix error in channel retreival Introduced a bug which retreived incorrect channels when adding personalization information. That has been fixed.<commit_after>package transactions

import (
	"errors"

	"github.com/joshheinrichs/geosource/server/types"
)

func IsChannelCreator(userID, channelname string) (bool, error) {
	return false, errors.New("function has not yet been implemented.")
}

func AddChannel(channel *types.Channel) error {
	return db.Create(channel).Error
}

func GetChannel(requester, channelname string) (*types.PersonalizedChannel, error) {
	// TODO: Account for requester permission
	var channel types.PersonalizedChannel
	err := db.Table("channels").
		Joins("LEFT JOIN user_subscriptions ON (ch_channelname = us_channelname AND us_userid = ?)", requester).
		Joins("LEFT JOIN users ON (u_userid = ch_userid_creator)").
		Select("*, (us_channelname IS NOT NULL) AS subscribed").
		Where("ch_channelname = ?", channelname).
		First(&channel).Error
	return &channel, err
}

func GetChannels(requester string) ([]*types.PersonalizedChannelInfo, error) {
	var channels []*types.PersonalizedChannelInfo
	err := db.Table("channels").
		Joins("LEFT JOIN user_subscriptions ON (ch_channelname = us_channelname AND us_userid = ?)", requester).
		Joins("LEFT JOIN users ON (u_userid = ch_userid_creator)").
		Select("*, (us_channelname IS NOT NULL) AS subscribed").
		Order("ch_channelname").Find(&channels).Error
	if err != nil {
		return nil, err
	}
	return channels, nil
}

func RemoveChannel(requester, channelname string) error {
	return errors.New("function has not yet been implemented.")
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
	alexa "github.com/mikeflynn/go-alexa/skillserver"
	"github.com/mmcdole/gofeed"
)

const (
	FEEDURL   = "http://www.wettermail.de/wetter/current/wettermail.rss"
	CACHEFILE = "/tmp/.rsscacheWO"
)

var (
	buildstamp string
	githash    string
)

var feedCache FeedCache

// Hauptprogramm. Startet den Download des RSS-Feeds und initialisiert den HTTP-Handler.
func main() {
	IP := flag.String("ip", "", "ip to bind to")
	Port := flag.Uint("port", 3080, "port to use")
	AppID := flag.String("appid", "", "AppId from Amazon Developer Portal")
	PidFile := flag.String("pid", "/var/run/wetterochs.pid", "pidfile to write")
	Version := flag.Bool("v", false, "prints current version and exit")
	flag.Parse()

	if *Version {
		fmt.Println("Build:", buildstamp)
		fmt.Println("Githash:", githash)
		os.Exit(0)
	}

	log.Printf("Alexa-Wetterochs startet %s - %s", buildstamp, githash)

	if *AppID == "" {
		log.Fatalln("Amazon AppID fehlt!")
	}

	if *PidFile != "" {
		WritePid(*PidFile)
	}

	feedCache.Init()

	log.Println("Erstmaliger Download des Newsfeeds")
	fp := gofeed.NewParser()
	feed, err := fp.ParseURL(FEEDURL)
	if err != nil || len(feed.Items) == 0 {
		log.Fatalln("Konnte Feed nicht abrufen - Abbruch.")
	}
	feedCache.Set(feed)
	ticker := time.NewTicker(15 * time.Minute)
	quit := make(chan struct{})
	go func() {
		for {
			select {
			case <-ticker.C:
				log.Println("Update des Newsfeeds")
				newfeed, err := fp.ParseURL(FEEDURL)
				if err != nil {
					log.Println("Konnte Newsfeed nicht updaten: ", err.Error())
				} else {
					feedCache.Set(newfeed)
				}
			case <-quit:
				ticker.Stop()
				return
			}
		}
	}()
	defer close(quit)

	var Applications = map[string]interface{}{
		"/echo/wetterochs": alexa.EchoApplication{
			AppID:    *AppID,
			OnIntent: WetterochsHandler,
			OnLaunch: WetterochsHandler,
		},
	}

	RunAlexa(Applications, *IP, fmt.Sprintf("%d", *Port))
}

func RunAlexa(apps map[string]interface{}, ip, port string) {
	router := mux.NewRouter()
	alexa.Init(apps, router)

	n := negroni.Classic()
	n.UseHandler(router)
	n.Run(ip + ":" + port)
}

// Handler für unseren Newsdienst. Dieser Code wird einmal pro Anfrage ausgeführt.
func WetterochsHandler(echoReq *alexa.EchoRequest, echoResp *alexa.EchoResponse) {
	log.Printf("----> Request für Intent %s empfangen, UserID %s\n", echoReq.Request.Intent.Name, echoReq.Session.User.UserID)

	card, speech := feedCache.Get()

	echoResp.OutputSpeechSSML(speech).Card("Wetterochs Wettermail", card)
	log.Printf("<---- Antworte mit %s\n", card)
}

// Da der Newsfeed asynchron im Hintergrund neu geladen wird muss der Zugriff synchronisiert werden. FeedCache realisiert das mit einem Mutex.
type FeedCache struct {
	lock  sync.RWMutex
	entry CacheEntry
}

type CacheEntry struct {
	Id     string `json:"id"`
	Card   string `json:"card"`
	Speech string `json:"speech"`
}

func (c *FeedCache) Init() {
	c.lock.Lock()
	defer c.lock.Unlock()

	_, err := ioutil.ReadFile(CACHEFILE)
	if err != nil {
		SaveCache(&CacheEntry{})
	}

	err = LoadCache(&c.entry)

	if err != nil {
		log.Fatalf("Unable to load cache : %v", err)
	}
}

func (c *FeedCache) Get() (string, string) {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.entry.Card, c.entry.Speech
}

func (c *FeedCache) Set(f *gofeed.Feed) {
	c.lock.Lock()
	defer c.lock.Unlock()

	for _, v := range f.Items {
		if c.entry.Id != v.Published {
			speech := fmt.Sprint(`<speak>`)
			card := fmt.Sprint("Die Wettermail")

			speech = fmt.Sprintf(`%s<break strength="x-strong"/>Wetter Mail vom %s<break strength="x-strong"/>`, speech, v.PublishedParsed.Format(`<say-as interpret-as="date" format="dm">2.1.</say-as> 15:04`))
			card = fmt.Sprintf("%s vom %s ", card, v.PublishedParsed.Format(`02.01. 15:04`))

			desc := regexp.MustCompile(`(?i)\n`).ReplaceAllLiteralString(v.Description, " ")
			desc = regexp.MustCompile(`(?i)\r`).ReplaceAllLiteralString(desc, " ")
			desc = regexp.MustCompile(`(?i) `).ReplaceAllLiteralString(desc, " ")

			//remove quote of old Mail
			descs := strings.Split(desc, "<p> Stefan Ochs Wettermail - ")
			if len(descs) > 1 {
				desc = descs[0]
			}

			desc = regexp.MustCompile(`(?i)<p>`).ReplaceAllLiteralString(desc, " ")
			desc = regexp.MustCompile(`(?i)</p>`).ReplaceAllLiteralString(desc, " ")
			desc = regexp.MustCompile(`(?i)<br>`).ReplaceAllLiteralString(desc, " ")
			desc = regexp.MustCompile(`(?i)<br />`).ReplaceAllLiteralString(desc, " ")

			//replace some pharses for better speaking
			desc = regexp.MustCompile(`(?i)d\.h\.`).ReplaceAllLiteralString(desc, "das heisst")
			desc = regexp.MustCompile(`(?i)gfs-modell`).ReplaceAllLiteralString(desc, `<say-as interpret-as="spell-out">GFS</say-as><break time="10ms" />Modell`)
			desc = regexp.MustCompile(`(?i)wetterochs`).ReplaceAllLiteralString(desc, "wetter-ochs")
			desc = regexp.MustCompile(`(?i)(\d)-(\d)`).ReplaceAllString(desc, "$1 bis $2")

			for strings.Contains(desc, "  ") {
				desc = strings.Replace(desc, "  ", " ", -1)
			}

			descs = strings.Split(desc, "</a>")
			if len(descs) > 1 {
				speech = fmt.Sprintf(`%s %s<break strength="x-strong"/>%s`, speech, v.Title, descs[1])
				card = fmt.Sprintf("%s\n%s - %s", card, v.Title, descs[1])
			} else {
				speech = fmt.Sprintf(`%s %s<break strength="x-strong"/>%s`, speech, v.Title, desc)
				card = fmt.Sprintf("%s\n%s - %s", card, v.Title, desc)
			}

			speech = fmt.Sprintf("%s</speak>", speech)

			c.entry.Id = v.Published
			c.entry.Card = card
			c.entry.Speech = speech

			SaveCache(&c.entry)
		}

		break
	}
}

func SaveCache(entry *CacheEntry) {
	log.Printf("Saving cache to file: %s\n", CACHEFILE)

	f, err := os.OpenFile(CACHEFILE, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		log.Fatalf("Unable to cache : %v", err)
	}
	defer f.Close()

	e, err := json.Marshal(entry)
	if err != nil {
		log.Fatalf("Unable to marshal json: %v", err)
	}

	json.NewEncoder(f).Encode(e)
}

func LoadCache(entry *CacheEntry) error {
	f, err := os.Open(CACHEFILE)
	if err != nil {
		return err
	}

	t := &[]byte{}
	err = json.NewDecoder(f).Decode(t)
	json.Unmarshal(*t, &entry)

	defer f.Close()
	return err
}

func WritePid(PidFile string) {
	pid := os.Getpid()

	f, err := os.OpenFile(PidFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		log.Fatalf("Unable to create pid file : %v", err)
	}
	defer f.Close()

	f.WriteString(fmt.Sprintf("%d", pid))
}
<commit_msg>Bugfixes<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
	alexa "github.com/mikeflynn/go-alexa/skillserver"
	"github.com/mmcdole/gofeed"
)

const (
	FEEDURL   = "http://www.wettermail.de/wetter/current/wettermail.rss"
	CACHEFILE = "/tmp/.rsscacheWO"
)

var (
	buildstamp string
	githash    string
)

var feedCache FeedCache

// Hauptprogramm. Startet den Download des RSS-Feeds und initialisiert den HTTP-Handler.
func main() {
	IP := flag.String("ip", "", "ip to bind to")
	Port := flag.Uint("port", 3080, "port to use")
	AppID := flag.String("appid", "", "AppId from Amazon Developer Portal")
	PidFile := flag.String("pid", "/var/run/wetterochs.pid", "pidfile to write")
	Version := flag.Bool("v", false, "prints current version and exit")
	flag.Parse()

	if *Version {
		fmt.Println("Build:", buildstamp)
		fmt.Println("Githash:", githash)
		os.Exit(0)
	}

	log.Printf("Alexa-Wetterochs startet %s - %s", buildstamp, githash)

	if *AppID == "" {
		log.Fatalln("Amazon AppID fehlt!")
	}

	if *PidFile != "" {
		WritePid(*PidFile)
	}

	feedCache.Init()

	log.Println("Erstmaliger Download des Newsfeeds")
	fp := gofeed.NewParser()
	feed, err := fp.ParseURL(FEEDURL)
	if err != nil || len(feed.Items) == 0 {
		log.Fatalln("Konnte Feed nicht abrufen - Abbruch.")
	}
	feedCache.Set(feed)
	ticker := time.NewTicker(15 * time.Minute)
	quit := make(chan struct{})
	go func() {
		for {
			select {
			case <-ticker.C:
				log.Println("Update des Newsfeeds")
				newfeed, err := fp.ParseURL(FEEDURL)
				if err != nil {
					log.Println("Konnte Newsfeed nicht updaten: ", err.Error())
				} else {
					feedCache.Set(newfeed)
				}
			case <-quit:
				ticker.Stop()
				return
			}
		}
	}()
	defer close(quit)

	var Applications = map[string]interface{}{
		"/echo/wetterochs": alexa.EchoApplication{
			AppID:    *AppID,
			OnIntent: WetterochsHandler,
			OnLaunch: WetterochsHandler,
		},
	}

	RunAlexa(Applications, *IP, fmt.Sprintf("%d", *Port))
}

func RunAlexa(apps map[string]interface{}, ip, port string) {
	router := mux.NewRouter()
	alexa.Init(apps, router)

	n := negroni.Classic()
	n.UseHandler(router)
	n.Run(ip + ":" + port)
}

// Handler für unseren Newsdienst. Dieser Code wird einmal pro Anfrage ausgeführt.
func WetterochsHandler(echoReq *alexa.EchoRequest, echoResp *alexa.EchoResponse) {
	log.Printf("----> Request für Intent %s empfangen, UserID %s\n", echoReq.Request.Intent.Name, echoReq.Session.User.UserID)

	card, speech := feedCache.Get()

	echoResp.OutputSpeechSSML(speech).Card("Wetterochs Wettermail", card)
	log.Printf("<---- Antworte mit %s\n", card)
}

// Da der Newsfeed asynchron im Hintergrund neu geladen wird muss der Zugriff synchronisiert werden. FeedCache realisiert das mit einem Mutex.
type FeedCache struct {
	lock  sync.RWMutex
	entry CacheEntry
}

type CacheEntry struct {
	Id     string `json:"id"`
	Card   string `json:"card"`
	Speech string `json:"speech"`
}

func (c *FeedCache) Init() {
	c.lock.Lock()
	defer c.lock.Unlock()

	_, err := ioutil.ReadFile(CACHEFILE)
	if err != nil {
		SaveCache(&CacheEntry{})
	}

	err = LoadCache(&c.entry)

	if err != nil {
		log.Fatalf("Unable to load cache : %v", err)
	}
}

func (c *FeedCache) Get() (string, string) {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.entry.Card, c.entry.Speech
}

func (c *FeedCache) Set(f *gofeed.Feed) {
	c.lock.Lock()
	defer c.lock.Unlock()

	for _, v := range f.Items {
		if c.entry.Id != v.Published {
			speech := fmt.Sprint(`<speak>`)
			card := fmt.Sprint("Die Wettermail")

			speech = fmt.Sprintf(`%s<break strength="x-strong"/>Wetter Mail vom %s<break strength="x-strong"/>`, speech, v.PublishedParsed.Format(`<say-as interpret-as="date" format="dm">2.1.</say-as> 15:04`))
			card = fmt.Sprintf("%s vom %s ", card, v.PublishedParsed.Format(`02.01. 15:04`))

			desc := regexp.MustCompile(`(?i)\n`).ReplaceAllLiteralString(v.Description, " ")
			desc = regexp.MustCompile(`(?i)\r`).ReplaceAllLiteralString(desc, " ")
			desc = regexp.MustCompile(`(?i) `).ReplaceAllLiteralString(desc, " ")

			//remove quote of old Mail
			descs := strings.Split(desc, "<p> Stefan Ochs Wettermail - ")
			if len(descs) > 1 {
				desc = descs[0]
			}

			desc = regexp.MustCompile(`(?i)<p>`).ReplaceAllLiteralString(desc, " ")
			desc = regexp.MustCompile(`(?i)</p>`).ReplaceAllLiteralString(desc, " ")
			desc = regexp.MustCompile(`(?i)<br>`).ReplaceAllLiteralString(desc, " ")
			desc = regexp.MustCompile(`(?i)<br />`).ReplaceAllLiteralString(desc, " ")

			desc = regexp.MustCompile(`(?i)<a.*</a>`).ReplaceAllLiteralString(desc, " ")
			desc = regexp.MustCompile(`(?i)<`).ReplaceAllLiteralString(desc, " kleiner ")
			desc = regexp.MustCompile(`(?i)>`).ReplaceAllLiteralString(desc, " größer ")

			//replace some pharses for better speaking
			desc = regexp.MustCompile(`(?i)d\.h\.`).ReplaceAllLiteralString(desc, "das heisst")
			desc = regexp.MustCompile(`(?i)gfs-modell`).ReplaceAllLiteralString(desc, `<say-as interpret-as="spell-out">GFS</say-as><break time="10ms" />Modell`)
			desc = regexp.MustCompile(`(?i)wetterochs`).ReplaceAllLiteralString(desc, "wetter-ochs")
			desc = regexp.MustCompile(`(?i)(\d)-(\d)`).ReplaceAllString(desc, "$1 bis $2")

			for strings.Contains(desc, "  ") {
				desc = strings.Replace(desc, "  ", " ", -1)
			}

			speech = fmt.Sprintf(`%s %s<break strength="x-strong"/>%s`, speech, v.Title, desc)
			card = fmt.Sprintf("%s\n%s - %s", card, v.Title, desc)

			speech = fmt.Sprintf("%s</speak>", speech)

			c.entry.Id = v.Published
			c.entry.Card = card
			c.entry.Speech = speech

			SaveCache(&c.entry)
		}

		break
	}
}

func SaveCache(entry *CacheEntry) {
	log.Printf("Saving cache to file: %s\n", CACHEFILE)

	f, err := os.OpenFile(CACHEFILE, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		log.Fatalf("Unable to cache : %v", err)
	}
	defer f.Close()

	e, err := json.Marshal(entry)
	if err != nil {
		log.Fatalf("Unable to marshal json: %v", err)
	}

	json.NewEncoder(f).Encode(e)
}

func LoadCache(entry *CacheEntry) error {
	f, err := os.Open(CACHEFILE)
	if err != nil {
		return err
	}

	t := &[]byte{}
	err = json.NewDecoder(f).Decode(t)
	json.Unmarshal(*t, &entry)

	defer f.Close()
	return err
}

func WritePid(PidFile string) {
	pid := os.Getpid()

	f, err := os.OpenFile(PidFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		log.Fatalf("Unable to create pid file : %v", err)
	}
	defer f.Close()

	f.WriteString(fmt.Sprintf("%d", pid))
}
<|endoftext|>


<commit_before>// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package ghttp

import (
	"crypto/tls"
	"fmt"
	"net/http"
	"strconv"
	"time"

	"github.com/gogf/gf/util/gconv"

	"github.com/gogf/gf/os/gsession"

	"github.com/gogf/gf/os/gview"

	"github.com/gogf/gf/os/gfile"
	"github.com/gogf/gf/os/glog"
)

const (
	gDEFAULT_HTTP_ADDR  = ":80"  // 默认HTTP监听地址
	gDEFAULT_HTTPS_ADDR = ":443" // 默认HTTPS监听地址
	URI_TYPE_DEFAULT    = 0      // 服务注册时对象和方法名称转换为URI时，全部转为小写，单词以'-'连接符号连接
	URI_TYPE_FULLNAME   = 1      // 不处理名称，以原有名称构建成URI
	URI_TYPE_ALLLOWER   = 2      // 仅转为小写，单词间不使用连接符号
	URI_TYPE_CAMEL      = 3      // 采用驼峰命名方式
)

// 自定义日志处理方法类型
type LogHandler func(r *Request, err ...error)

// HTTP Server 设置结构体，静态配置
type ServerConfig struct {
	Addr              string            // 监听IP和端口，监听本地所有IP使用":端口"(支持多个地址，使用","号分隔)
	HTTPSAddr         string            // HTTPS服务监听地址(支持多个地址，使用","号分隔)
	HTTPSCertPath     string            // HTTPS证书文件路径
	HTTPSKeyPath      string            // HTTPS签名文件路径
	Handler           http.Handler      // 默认的处理函数
	ReadTimeout       time.Duration     // 读取超时
	WriteTimeout      time.Duration     // 写入超时
	IdleTimeout       time.Duration     // 等待超时
	MaxHeaderBytes    int               // 最大的header长度
	TLSConfig         tls.Config        // HTTPS证书配置
	KeepAlive         bool              // 是否开启长连接
	ServerAgent       string            // Server Agent
	View              *gview.View       // 模板引擎对象
	Rewrites          map[string]string // URI Rewrite重写配置
	IndexFiles        []string          // Static: 默认访问的文件列表
	IndexFolder       bool              // Static: 如果访问目录是否显示目录列表
	ServerRoot        string            // Static: 服务器服务的本地目录根路径(检索优先级比StaticPaths低)
	SearchPaths       []string          // Static: 静态文件搜索目录(包含ServerRoot，按照优先级进行排序)
	StaticPaths       []staticPathItem  // Static: 静态文件目录映射(按照优先级进行排序)
	FileServerEnabled bool              // Static: 是否允许静态文件服务(通过静态文件服务方法调用自动识别)
	CookieMaxAge      time.Duration     // Cookie: 有效期
	CookiePath        string            // Cookie: 有效Path(注意同时也会影响SessionID)
	CookieDomain      string            // Cookie: 有效Domain(注意同时也会影响SessionID)
	SessionMaxAge     time.Duration     // Session: 有效期
	SessionIdName     string            // Session: SessionId.
	SessionStorage    gsession.Storage  // Session: Session Storage implementer.
	DenyIps           []string          // Security: 不允许访问的ip列表，支持ip前缀过滤，如: 10 将不允许10开头的ip访问
	AllowIps          []string          // Security: 仅允许访问的ip列表，支持ip前缀过滤，如: 10 将仅允许10开头的ip访问
	DenyRoutes        []string          // Security: 不允许访问的路由规则列表
	LogPath           string            // Logging: 存放日志的目录路径(默认为空，表示不写文件)
	LogHandler        LogHandler        // Logging: 日志配置: 自定义日志处理回调方法(默认为空)
	LogStdout         bool              // Logging: 是否打印日志到终端(默认开启)
	ErrorStack        bool              // Logging: 当产生错误时打印调用链详细堆栈
	ErrorLogEnabled   bool              // Logging: 是否开启error log(默认开启)
	AccessLogEnabled  bool              // Logging: 是否开启access log(默认关闭)
	FormParsingMemory int64             // Mess: 表单解析内存限制(byte)
	NameToUriType     int               // Mess: 服务注册时对象和方法名称转换为URI时的规则
	GzipContentTypes  []string          // Mess: 允许进行gzip压缩的文件类型
	DumpRouteMap      bool              // Mess: 是否在程序启动时默认打印路由表信息
	RouterCacheExpire int               // Mess: 路由检索缓存过期时间(秒)
}

// 默认HTTP Server配置
var defaultServerConfig = ServerConfig{
	Addr:              "",
	HTTPSAddr:         "",
	Handler:           nil,
	ReadTimeout:       60 * time.Second,
	WriteTimeout:      60 * time.Second,
	IdleTimeout:       60 * time.Second,
	MaxHeaderBytes:    1024,
	KeepAlive:         true,
	IndexFiles:        []string{"index.html", "index.htm"},
	IndexFolder:       false,
	ServerAgent:       "gf http server",
	ServerRoot:        "",
	StaticPaths:       make([]staticPathItem, 0),
	FileServerEnabled: false,
	CookieMaxAge:      time.Hour * 24 * 365,
	CookiePath:        "/",
	CookieDomain:      "",
	SessionMaxAge:     time.Hour * 24,
	SessionIdName:     "gfsessionid",
	LogStdout:         true,
	ErrorStack:        true,
	ErrorLogEnabled:   true,
	AccessLogEnabled:  false,
	DumpRouteMap:      true,
	FormParsingMemory: 1024 * 1024 * 1024,
	RouterCacheExpire: 60,
	Rewrites:          make(map[string]string),
}

// 获取默认的http server设置
func Config() ServerConfig {
	return defaultServerConfig
}

// 通过Map创建Config配置对象，Map没有传递的属性将会使用模块的默认值
func ConfigFromMap(m map[string]interface{}) ServerConfig {
	config := defaultServerConfig
	gconv.Struct(m, &config)
	return config
}

// http server setting设置。
// 注意使用该方法进行http server配置时，需要配置所有的配置项，否则没有配置的属性将会默认变量为空
func (s *Server) SetConfig(c ServerConfig) error {
	if c.Handler == nil {
		c.Handler = http.HandlerFunc(s.defaultHttpHandle)
	}
	s.config = c

	if c.LogPath != "" {
		return s.logger.SetPath(c.LogPath)
	}
	return nil
}

// 通过map设置http server setting。
// 注意使用该方法进行http server配置时，需要配置所有的配置项，否则没有配置的属性将会默认变量为空
func (s *Server) SetConfigWithMap(m map[string]interface{}) {
	s.SetConfig(ConfigFromMap(m))
}

// 设置http server参数 - Addr
func (s *Server) SetAddr(address string) {
	s.config.Addr = address
}

// 设置http server参数 - Port
func (s *Server) SetPort(port ...int) {
	if len(port) > 0 {
		s.config.Addr = ""
		for _, v := range port {
			if len(s.config.Addr) > 0 {
				s.config.Addr += ","
			}
			s.config.Addr += ":" + strconv.Itoa(v)
		}
	}
}

// 设置http server参数 - HTTPS Addr
func (s *Server) SetHTTPSAddr(address string) {
	s.config.HTTPSAddr = address
}

// 设置http server参数 - HTTPS Port
func (s *Server) SetHTTPSPort(port ...int) {
	if len(port) > 0 {
		s.config.HTTPSAddr = ""
		for _, v := range port {
			if len(s.config.HTTPSAddr) > 0 {
				s.config.HTTPSAddr += ","
			}
			s.config.HTTPSAddr += ":" + strconv.Itoa(v)
		}
	}
}

// 开启HTTPS支持，但是必须提供Cert和Key文件，tlsConfig为可选项
func (s *Server) EnableHTTPS(certFile, keyFile string, tlsConfig ...tls.Config) {
	certFileRealPath := gfile.RealPath(certFile)
	if certFileRealPath == "" {
		certFileRealPath = gfile.RealPath(gfile.Pwd() + gfile.Separator + certFile)
		if certFileRealPath == "" {
			certFileRealPath = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + certFile)
		}
	}
	if certFileRealPath == "" {
		glog.Fatal(fmt.Sprintf(`[ghttp] EnableHTTPS failed: certFile "%s" does not exist`, certFile))
	}
	keyFileRealPath := gfile.RealPath(keyFile)
	if keyFileRealPath == "" {
		keyFileRealPath = gfile.RealPath(gfile.Pwd() + gfile.Separator + keyFile)
		if keyFileRealPath == "" {
			keyFileRealPath = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + keyFile)
		}
	}
	if keyFileRealPath == "" {
		glog.Fatal(fmt.Sprintf(`[ghttp] EnableHTTPS failed: keyFile "%s" does not exist`, keyFile))
	}
	s.config.HTTPSCertPath = certFileRealPath
	s.config.HTTPSKeyPath = keyFileRealPath
	if len(tlsConfig) > 0 {
		s.config.TLSConfig = tlsConfig[0]
	}
}

// 设置TLS配置对象
func (s *Server) SetTLSConfig(tlsConfig tls.Config) {
	s.config.TLSConfig = tlsConfig
}

// 设置http server参数 - ReadTimeout
func (s *Server) SetReadTimeout(t time.Duration) {
	s.config.ReadTimeout = t
}

// 设置http server参数 - WriteTimeout
func (s *Server) SetWriteTimeout(t time.Duration) {
	s.config.WriteTimeout = t
}

// 设置http server参数 - IdleTimeout
func (s *Server) SetIdleTimeout(t time.Duration) {
	s.config.IdleTimeout = t
}

// 设置http server参数 - MaxHeaderBytes
func (s *Server) SetMaxHeaderBytes(b int) {
	s.config.MaxHeaderBytes = b
}

// 设置http server参数 - ServerAgent
func (s *Server) SetServerAgent(agent string) {
	s.config.ServerAgent = agent
}

// 设置KeepAlive
func (s *Server) SetKeepAlive(enabled bool) {
	s.config.KeepAlive = enabled
}

// 设置模板引擎对象
func (s *Server) SetView(view *gview.View) {
	s.config.View = view
}

// 获取WebServer名称
func (s *Server) GetName() string {
	return s.name
}
<commit_msg>improve ConfigFromMap function for ghttp.Server<commit_after>// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package ghttp

import (
	"crypto/tls"
	"fmt"
	"net/http"
	"strconv"
	"time"

	"github.com/gogf/gf/util/gconv"

	"github.com/gogf/gf/os/gsession"

	"github.com/gogf/gf/os/gview"

	"github.com/gogf/gf/os/gfile"
	"github.com/gogf/gf/os/glog"
)

const (
	gDEFAULT_HTTP_ADDR  = ":80"  // 默认HTTP监听地址
	gDEFAULT_HTTPS_ADDR = ":443" // 默认HTTPS监听地址
	URI_TYPE_DEFAULT    = 0      // 服务注册时对象和方法名称转换为URI时，全部转为小写，单词以'-'连接符号连接
	URI_TYPE_FULLNAME   = 1      // 不处理名称，以原有名称构建成URI
	URI_TYPE_ALLLOWER   = 2      // 仅转为小写，单词间不使用连接符号
	URI_TYPE_CAMEL      = 3      // 采用驼峰命名方式
)

// 自定义日志处理方法类型
type LogHandler func(r *Request, err ...error)

// HTTP Server 设置结构体，静态配置
type ServerConfig struct {
	Addr              string            // 监听IP和端口，监听本地所有IP使用":端口"(支持多个地址，使用","号分隔)
	HTTPSAddr         string            // HTTPS服务监听地址(支持多个地址，使用","号分隔)
	HTTPSCertPath     string            // HTTPS证书文件路径
	HTTPSKeyPath      string            // HTTPS签名文件路径
	Handler           http.Handler      // 默认的处理函数
	ReadTimeout       time.Duration     // 读取超时
	WriteTimeout      time.Duration     // 写入超时
	IdleTimeout       time.Duration     // 等待超时
	MaxHeaderBytes    int               // 最大的header长度
	TLSConfig         tls.Config        // HTTPS证书配置
	KeepAlive         bool              // 是否开启长连接
	ServerAgent       string            // Server Agent
	View              *gview.View       // 模板引擎对象
	Rewrites          map[string]string // URI Rewrite重写配置
	IndexFiles        []string          // Static: 默认访问的文件列表
	IndexFolder       bool              // Static: 如果访问目录是否显示目录列表
	ServerRoot        string            // Static: 服务器服务的本地目录根路径(检索优先级比StaticPaths低)
	SearchPaths       []string          // Static: 静态文件搜索目录(包含ServerRoot，按照优先级进行排序)
	StaticPaths       []staticPathItem  // Static: 静态文件目录映射(按照优先级进行排序)
	FileServerEnabled bool              // Static: 是否允许静态文件服务(通过静态文件服务方法调用自动识别)
	CookieMaxAge      time.Duration     // Cookie: 有效期
	CookiePath        string            // Cookie: 有效Path(注意同时也会影响SessionID)
	CookieDomain      string            // Cookie: 有效Domain(注意同时也会影响SessionID)
	SessionMaxAge     time.Duration     // Session: 有效期
	SessionIdName     string            // Session: SessionId.
	SessionStorage    gsession.Storage  // Session: Session Storage implementer.
	DenyIps           []string          // Security: 不允许访问的ip列表，支持ip前缀过滤，如: 10 将不允许10开头的ip访问
	AllowIps          []string          // Security: 仅允许访问的ip列表，支持ip前缀过滤，如: 10 将仅允许10开头的ip访问
	DenyRoutes        []string          // Security: 不允许访问的路由规则列表
	LogPath           string            // Logging: 存放日志的目录路径(默认为空，表示不写文件)
	LogHandler        LogHandler        // Logging: 日志配置: 自定义日志处理回调方法(默认为空)
	LogStdout         bool              // Logging: 是否打印日志到终端(默认开启)
	ErrorStack        bool              // Logging: 当产生错误时打印调用链详细堆栈
	ErrorLogEnabled   bool              // Logging: 是否开启error log(默认开启)
	AccessLogEnabled  bool              // Logging: 是否开启access log(默认关闭)
	FormParsingMemory int64             // Mess: 表单解析内存限制(byte)
	NameToUriType     int               // Mess: 服务注册时对象和方法名称转换为URI时的规则
	GzipContentTypes  []string          // Mess: 允许进行gzip压缩的文件类型
	DumpRouteMap      bool              // Mess: 是否在程序启动时默认打印路由表信息
	RouterCacheExpire int               // Mess: 路由检索缓存过期时间(秒)
}

// 默认HTTP Server配置
var defaultServerConfig = ServerConfig{
	Addr:              "",
	HTTPSAddr:         "",
	Handler:           nil,
	ReadTimeout:       60 * time.Second,
	WriteTimeout:      60 * time.Second,
	IdleTimeout:       60 * time.Second,
	MaxHeaderBytes:    1024,
	KeepAlive:         true,
	IndexFiles:        []string{"index.html", "index.htm"},
	IndexFolder:       false,
	ServerAgent:       "gf http server",
	ServerRoot:        "",
	StaticPaths:       make([]staticPathItem, 0),
	FileServerEnabled: false,
	CookieMaxAge:      time.Hour * 24 * 365,
	CookiePath:        "/",
	CookieDomain:      "",
	SessionMaxAge:     time.Hour * 24,
	SessionIdName:     "gfsessionid",
	LogStdout:         true,
	ErrorStack:        true,
	ErrorLogEnabled:   true,
	AccessLogEnabled:  false,
	DumpRouteMap:      true,
	FormParsingMemory: 1024 * 1024 * 1024,
	RouterCacheExpire: 60,
	Rewrites:          make(map[string]string),
}

// 获取默认的http server设置
func Config() ServerConfig {
	return defaultServerConfig
}

// 通过Map创建Config配置对象，Map没有传递的属性将会使用模块的默认值
func ConfigFromMap(m map[string]interface{}) ServerConfig {
	config := defaultServerConfig
	if err := gconv.Struct(m, &config); err != nil {
		panic(err)
	}
	return config
}

// http server setting设置。
// 注意使用该方法进行http server配置时，需要配置所有的配置项，否则没有配置的属性将会默认变量为空
func (s *Server) SetConfig(c ServerConfig) error {
	if c.Handler == nil {
		c.Handler = http.HandlerFunc(s.defaultHttpHandle)
	}
	s.config = c

	if c.LogPath != "" {
		return s.logger.SetPath(c.LogPath)
	}
	return nil
}

// 通过map设置http server setting。
// 注意使用该方法进行http server配置时，需要配置所有的配置项，否则没有配置的属性将会默认变量为空
func (s *Server) SetConfigWithMap(m map[string]interface{}) {
	s.SetConfig(ConfigFromMap(m))
}

// 设置http server参数 - Addr
func (s *Server) SetAddr(address string) {
	s.config.Addr = address
}

// 设置http server参数 - Port
func (s *Server) SetPort(port ...int) {
	if len(port) > 0 {
		s.config.Addr = ""
		for _, v := range port {
			if len(s.config.Addr) > 0 {
				s.config.Addr += ","
			}
			s.config.Addr += ":" + strconv.Itoa(v)
		}
	}
}

// 设置http server参数 - HTTPS Addr
func (s *Server) SetHTTPSAddr(address string) {
	s.config.HTTPSAddr = address
}

// 设置http server参数 - HTTPS Port
func (s *Server) SetHTTPSPort(port ...int) {
	if len(port) > 0 {
		s.config.HTTPSAddr = ""
		for _, v := range port {
			if len(s.config.HTTPSAddr) > 0 {
				s.config.HTTPSAddr += ","
			}
			s.config.HTTPSAddr += ":" + strconv.Itoa(v)
		}
	}
}

// 开启HTTPS支持，但是必须提供Cert和Key文件，tlsConfig为可选项
func (s *Server) EnableHTTPS(certFile, keyFile string, tlsConfig ...tls.Config) {
	certFileRealPath := gfile.RealPath(certFile)
	if certFileRealPath == "" {
		certFileRealPath = gfile.RealPath(gfile.Pwd() + gfile.Separator + certFile)
		if certFileRealPath == "" {
			certFileRealPath = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + certFile)
		}
	}
	if certFileRealPath == "" {
		glog.Fatal(fmt.Sprintf(`[ghttp] EnableHTTPS failed: certFile "%s" does not exist`, certFile))
	}
	keyFileRealPath := gfile.RealPath(keyFile)
	if keyFileRealPath == "" {
		keyFileRealPath = gfile.RealPath(gfile.Pwd() + gfile.Separator + keyFile)
		if keyFileRealPath == "" {
			keyFileRealPath = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + keyFile)
		}
	}
	if keyFileRealPath == "" {
		glog.Fatal(fmt.Sprintf(`[ghttp] EnableHTTPS failed: keyFile "%s" does not exist`, keyFile))
	}
	s.config.HTTPSCertPath = certFileRealPath
	s.config.HTTPSKeyPath = keyFileRealPath
	if len(tlsConfig) > 0 {
		s.config.TLSConfig = tlsConfig[0]
	}
}

// 设置TLS配置对象
func (s *Server) SetTLSConfig(tlsConfig tls.Config) {
	s.config.TLSConfig = tlsConfig
}

// 设置http server参数 - ReadTimeout
func (s *Server) SetReadTimeout(t time.Duration) {
	s.config.ReadTimeout = t
}

// 设置http server参数 - WriteTimeout
func (s *Server) SetWriteTimeout(t time.Duration) {
	s.config.WriteTimeout = t
}

// 设置http server参数 - IdleTimeout
func (s *Server) SetIdleTimeout(t time.Duration) {
	s.config.IdleTimeout = t
}

// 设置http server参数 - MaxHeaderBytes
func (s *Server) SetMaxHeaderBytes(b int) {
	s.config.MaxHeaderBytes = b
}

// 设置http server参数 - ServerAgent
func (s *Server) SetServerAgent(agent string) {
	s.config.ServerAgent = agent
}

// 设置KeepAlive
func (s *Server) SetKeepAlive(enabled bool) {
	s.config.KeepAlive = enabled
}

// 设置模板引擎对象
func (s *Server) SetView(view *gview.View) {
	s.config.View = view
}

// 获取WebServer名称
func (s *Server) GetName() string {
	return s.name
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/scootdev/scoot/tests/swarmtest"
	"time"
)

// Runs an end to end integration test with work being scheduled
// via the ScootApi placed on the WorkQueue, Dequeued by the Scheduler
// and ran on local instances of Workers.
func main() {
	s := swarmtest.SwarmTest{}

	err := s.InitOptions(map[string]interface{}{
		"num_workers": 20,
		"num_jobs":    100,
		"timeout":     100 * time.Second,
	})
	if err != nil {
		panic(err)
	}
	s.Main()
}
<commit_msg>Remove Swarmtest Binary (#140)<commit_after><|endoftext|>


<commit_before>// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.

package check

import (
	"bytes"
	"fmt"
	"math/big"
	"reflect"
	"sort"
	"strings"
	"testing"

	"github.com/google/puffs/lang/ast"
	"github.com/google/puffs/lang/parse"
	"github.com/google/puffs/lang/render"
	"github.com/google/puffs/lang/token"
)

func compareToPuffsfmt(idMap *token.IDMap, tokens []token.Token, src string) error {
	buf := &bytes.Buffer{}
	if err := render.Render(buf, idMap, tokens, nil); err != nil {
		return err
	}
	got := strings.Split(buf.String(), "\n")
	want := strings.Split(src, "\n")
	for line := 1; len(got) != 0 || len(want) != 0; line++ {
		if len(got) == 0 {
			w := strings.TrimSpace(want[0])
			return fmt.Errorf("difference at line %d:\n%s\n%s", line, "", w)
		}
		if len(want) == 0 {
			g := strings.TrimSpace(got[0])
			return fmt.Errorf("difference at line %d:\n%s\n%s", line, g, "")
		}
		if g, w := strings.TrimSpace(got[0]), strings.TrimSpace(want[0]); g != w {
			return fmt.Errorf("difference at line %d:\n%s\n%s", line, g, w)
		}
		got = got[1:]
		want = want[1:]
	}
	return nil
}

func TestCheck(t *testing.T) {
	const filename = "test.puffs"
	src := strings.TrimSpace(`
		struct foo(
			i i32,
		)

		func foo.bar()() {
			var x u8
			var y i32 = +2
			var z u64[..123]
			var a[4] u8
			var b bool

			x = 0
			x = 1 + (x * 0)
			y = -y - 1
			y = this.i
			b = not true

			y = x as i32

			var p i32
			var q i32[0..8]

			assert true

			while p == q,
				post p != q,
			{
			}
		}
	`) + "\n"

	idMap := &token.IDMap{}

	tokens, _, err := token.Tokenize(idMap, filename, []byte(src))
	if err != nil {
		t.Fatalf("Tokenize: %v", err)
	}

	file, err := parse.Parse(idMap, filename, tokens)
	if err != nil {
		t.Fatalf("Parse: %v", err)
	}

	if err := compareToPuffsfmt(idMap, tokens, src); err != nil {
		t.Fatalf("compareToPuffsfmt: %v", err)
	}

	c, err := Check(idMap, file)
	if err != nil {
		t.Fatalf("Check: %v", err)
	}

	funcs := c.Funcs()
	if len(funcs) != 1 {
		t.Fatalf("Funcs: got %d elements, want 1", len(funcs))
	}
	fooBar := Func{}
	for _, f := range funcs {
		fooBar = f
		break
	}

	if got, want := fooBar.QID.String(idMap), "foo.bar"; got != want {
		t.Fatalf("Funcs[0] name: got %q, want %q", got, want)
	}

	got := [][2]string(nil)
	for id, typ := range fooBar.LocalVars {
		got = append(got, [2]string{
			idMap.ByID(id),
			typ.String(idMap),
		})
	}
	sort.Slice(got, func(i, j int) bool {
		return got[i][0] < got[j][0]
	})

	want := [][2]string{
		{"a", "[4] u8"},
		{"b", "bool"},
		{"in", "in"},
		{"out", "out"},
		{"p", "i32"},
		{"q", "i32[0..8]"},
		{"this", "ptr foo"},
		{"x", "u8"},
		{"y", "i32"},
		{"z", "u64[..123]"},
	}
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("\ngot  %v\nwant %v", got, want)
	}
}

func TestConstValues(t *testing.T) {
	const filename = "test.puffs"
	testCases := map[string]int64{
		"var i i32 = 42": 42,

		"var i i32 = +7": +7,
		"var i i32 = -7": -7,

		"var b bool = false":         0,
		"var b bool = not false":     1,
		"var b bool = not not false": 0,

		"var i i32 = 10  + 3": 13,
		"var i i32 = 10  - 3": 7,
		"var i i32 = 10  * 3": 30,
		"var i i32 = 10  / 3": 3,
		"var i i32 = 10 << 3": 80,
		"var i i32 = 10 >> 3": 1,
		"var i i32 = 10  & 3": 2,
		"var i i32 = 10 &^ 3": 8,
		"var i i32 = 10  | 3": 11,
		"var i i32 = 10  ^ 3": 9,

		"var b bool = 10 != 3": 1,
		"var b bool = 10  < 3": 0,
		"var b bool = 10 <= 3": 0,
		"var b bool = 10 == 3": 0,
		"var b bool = 10 >= 3": 1,
		"var b bool = 10  > 3": 1,

		"var b bool = false and true": 0,
		"var b bool = false  or true": 1,
	}

	idMap := &token.IDMap{}
	for s, wantInt64 := range testCases {
		src := "func foo()() {\n\t" + s + "\n}\n"

		tokens, _, err := token.Tokenize(idMap, filename, []byte(src))
		if err != nil {
			t.Errorf("%q: Tokenize: %v", s, err)
			continue
		}

		file, err := parse.Parse(idMap, filename, tokens)
		if err != nil {
			t.Errorf("%q: Parse: %v", s, err)
			continue
		}

		c, err := Check(idMap, file)
		if err != nil {
			t.Errorf("%q: Check: %v", s, err)
			continue
		}

		funcs := c.Funcs()
		if len(funcs) != 1 {
			t.Errorf("%q: Funcs: got %d elements, want 1", s, len(funcs))
			continue
		}
		foo := Func{}
		for _, f := range funcs {
			foo = f
			break
		}
		body := foo.Func.Body()
		if len(body) != 1 {
			t.Errorf("%q: Body: got %d elements, want 1", s, len(body))
			continue
		}
		if body[0].Kind() != ast.KVar {
			t.Errorf("%q: Body[0]: got %s, want %s", s, body[0].Kind(), ast.KVar)
			continue
		}

		got := body[0].Var().Value().ConstValue()
		want := big.NewInt(wantInt64)
		if got == nil || want == nil || got.Cmp(want) != 0 {
			t.Errorf("%q: got %v, want %v", s, got, want)
			continue
		}
	}
}
<commit_msg>Build fix for check.Check API change.<commit_after>// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.

package check

import (
	"bytes"
	"fmt"
	"math/big"
	"reflect"
	"sort"
	"strings"
	"testing"

	"github.com/google/puffs/lang/ast"
	"github.com/google/puffs/lang/parse"
	"github.com/google/puffs/lang/render"
	"github.com/google/puffs/lang/token"
)

func compareToPuffsfmt(idMap *token.IDMap, tokens []token.Token, src string) error {
	buf := &bytes.Buffer{}
	if err := render.Render(buf, idMap, tokens, nil); err != nil {
		return err
	}
	got := strings.Split(buf.String(), "\n")
	want := strings.Split(src, "\n")
	for line := 1; len(got) != 0 || len(want) != 0; line++ {
		if len(got) == 0 {
			w := strings.TrimSpace(want[0])
			return fmt.Errorf("difference at line %d:\n%s\n%s", line, "", w)
		}
		if len(want) == 0 {
			g := strings.TrimSpace(got[0])
			return fmt.Errorf("difference at line %d:\n%s\n%s", line, g, "")
		}
		if g, w := strings.TrimSpace(got[0]), strings.TrimSpace(want[0]); g != w {
			return fmt.Errorf("difference at line %d:\n%s\n%s", line, g, w)
		}
		got = got[1:]
		want = want[1:]
	}
	return nil
}

func TestCheck(t *testing.T) {
	const filename = "test.puffs"
	src := strings.TrimSpace(`
		struct foo(
			i i32,
		)

		func foo.bar()() {
			var x u8
			var y i32 = +2
			var z u64[..123]
			var a[4] u8
			var b bool

			x = 0
			x = 1 + (x * 0)
			y = -y - 1
			y = this.i
			b = not true

			y = x as i32

			var p i32
			var q i32[0..8]

			assert true

			while p == q,
				post p != q,
			{
			}
		}
	`) + "\n"

	idMap := &token.IDMap{}

	tokens, _, err := token.Tokenize(idMap, filename, []byte(src))
	if err != nil {
		t.Fatalf("Tokenize: %v", err)
	}

	file, err := parse.Parse(idMap, filename, tokens)
	if err != nil {
		t.Fatalf("Parse: %v", err)
	}

	if err := compareToPuffsfmt(idMap, tokens, src); err != nil {
		t.Fatalf("compareToPuffsfmt: %v", err)
	}

	c, err := Check(idMap, 0, file)
	if err != nil {
		t.Fatalf("Check: %v", err)
	}

	funcs := c.Funcs()
	if len(funcs) != 1 {
		t.Fatalf("Funcs: got %d elements, want 1", len(funcs))
	}
	fooBar := Func{}
	for _, f := range funcs {
		fooBar = f
		break
	}

	if got, want := fooBar.QID.String(idMap), "foo.bar"; got != want {
		t.Fatalf("Funcs[0] name: got %q, want %q", got, want)
	}

	got := [][2]string(nil)
	for id, typ := range fooBar.LocalVars {
		got = append(got, [2]string{
			idMap.ByID(id),
			typ.String(idMap),
		})
	}
	sort.Slice(got, func(i, j int) bool {
		return got[i][0] < got[j][0]
	})

	want := [][2]string{
		{"a", "[4] u8"},
		{"b", "bool"},
		{"in", "in"},
		{"out", "out"},
		{"p", "i32"},
		{"q", "i32[0..8]"},
		{"this", "ptr foo"},
		{"x", "u8"},
		{"y", "i32"},
		{"z", "u64[..123]"},
	}
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("\ngot  %v\nwant %v", got, want)
	}
}

func TestConstValues(t *testing.T) {
	const filename = "test.puffs"
	testCases := map[string]int64{
		"var i i32 = 42": 42,

		"var i i32 = +7": +7,
		"var i i32 = -7": -7,

		"var b bool = false":         0,
		"var b bool = not false":     1,
		"var b bool = not not false": 0,

		"var i i32 = 10  + 3": 13,
		"var i i32 = 10  - 3": 7,
		"var i i32 = 10  * 3": 30,
		"var i i32 = 10  / 3": 3,
		"var i i32 = 10 << 3": 80,
		"var i i32 = 10 >> 3": 1,
		"var i i32 = 10  & 3": 2,
		"var i i32 = 10 &^ 3": 8,
		"var i i32 = 10  | 3": 11,
		"var i i32 = 10  ^ 3": 9,

		"var b bool = 10 != 3": 1,
		"var b bool = 10  < 3": 0,
		"var b bool = 10 <= 3": 0,
		"var b bool = 10 == 3": 0,
		"var b bool = 10 >= 3": 1,
		"var b bool = 10  > 3": 1,

		"var b bool = false and true": 0,
		"var b bool = false  or true": 1,
	}

	idMap := &token.IDMap{}
	for s, wantInt64 := range testCases {
		src := "func foo()() {\n\t" + s + "\n}\n"

		tokens, _, err := token.Tokenize(idMap, filename, []byte(src))
		if err != nil {
			t.Errorf("%q: Tokenize: %v", s, err)
			continue
		}

		file, err := parse.Parse(idMap, filename, tokens)
		if err != nil {
			t.Errorf("%q: Parse: %v", s, err)
			continue
		}

		c, err := Check(idMap, 0, file)
		if err != nil {
			t.Errorf("%q: Check: %v", s, err)
			continue
		}

		funcs := c.Funcs()
		if len(funcs) != 1 {
			t.Errorf("%q: Funcs: got %d elements, want 1", s, len(funcs))
			continue
		}
		foo := Func{}
		for _, f := range funcs {
			foo = f
			break
		}
		body := foo.Func.Body()
		if len(body) != 1 {
			t.Errorf("%q: Body: got %d elements, want 1", s, len(body))
			continue
		}
		if body[0].Kind() != ast.KVar {
			t.Errorf("%q: Body[0]: got %s, want %s", s, body[0].Kind(), ast.KVar)
			continue
		}

		got := body[0].Var().Value().ConstValue()
		want := big.NewInt(wantInt64)
		if got == nil || want == nil || got.Cmp(want) != 0 {
			t.Errorf("%q: got %v, want %v", s, got, want)
			continue
		}
	}
}
<|endoftext|>


<commit_before>package bloomberg

import (
	"fmt"
	"strings"
)

var yahooToBloombergMap = map[string]string{
	"AS": "NA",
	"BR": "BB",
}

var bloombergToYahooMap map[string]string

func init() {
	bloombergToYahooMap = revmap(yahooToBloombergMap)
}

func revmap(orig map[string]string) map[string]string {
	rev := make(map[string]string, len(orig))
	for key, val := range orig {
		rev[val] = key
	}
	return rev
}

func yahooToBloomberg(symbol string) string {
	return conv(symbol, yahooToBloombergMap, ".", ":")
}

func bloombergToYahoo(symbol string) string {
	return conv(symbol, bloombergToYahooMap, ":", ".")
}

func conv(symbol string, symbmap map[string]string, oldsep string, newsep string) string {
	fmt.Println("CONVERTING, MAP", symbmap)
	if strings.Contains(symbol, oldsep) {
		parts := strings.Split(symbol, oldsep)
		if len(parts) != 2 {
			vprintln("bloomberg: can't convert symbol, ", symbol)
			return symbol
		}

		if ext, found := symbmap[parts[1]]; found {
			symbol = parts[0] + newsep + ext
		} else {
			vprintln("bloomberg: unknown symbol extension ", symbol)
		}
	}

	return symbol
}

func convertSymbols(symbols []string) []string {
	c := make([]string, 0, len(symbols))
	for _, symbol := range symbols {
		c = append(c, yahooToBloomberg(symbol))
	}
	return c
}
<commit_msg>bloomberg: added more recognized exchanges<commit_after>package bloomberg

import (
	"strings"
)

var yahooToBloombergMap = map[string]string{
	"AS": "NA", /* Amsterdam Euronext */
	"BR": "BB", /* Brussels Euronext */
	"L":  "LN", /* London Stock Exchange */
	"MI": "IM", /* Milan */
	"SI": "SP", /* Singapore */
	"DE": "GR", /* Xetra Germany */
	"SA": "BZ", /* Sao Paulo, Brasil */
	"MC": "SM", /* Madrid, Spain */
	"MX": "MM", /* Mexico */
}

var bloombergToYahooMap map[string]string

func init() {
	bloombergToYahooMap = revmap(yahooToBloombergMap)
}

func revmap(orig map[string]string) map[string]string {
	rev := make(map[string]string, len(orig))
	for key, val := range orig {
		rev[val] = key
	}
	return rev
}

func yahooToBloomberg(symbol string) string {
	/* symbols without a specific exchange are assumed to be US-based */
	if !strings.Contains(symbol, ".") {
		return symbol + ":US"
	}

	return conv(symbol, yahooToBloombergMap, ".", ":")
}

func bloombergToYahoo(symbol string) string {
	/* US symbols are special since yahoo doesn't suffix them, but bloomberg
	 * does */
	if strings.Contains(symbol, ":US") {
		return strings.Split(symbol, ":")[0]
	}

	return conv(symbol, bloombergToYahooMap, ":", ".")
}

func conv(symbol string, symbmap map[string]string, oldsep string, newsep string) string {
	if strings.Contains(symbol, oldsep) {
		parts := strings.Split(symbol, oldsep)
		if len(parts) != 2 {
			vprintln("bloomberg: can't convert symbol, ", symbol)
			return symbol
		}

		if ext, found := symbmap[parts[1]]; found {
			symbol = parts[0] + newsep + ext
		} else {
			vprintln("bloomberg: unknown symbol extension ", symbol)
		}
	}

	return symbol
}

func convertSymbols(symbols []string) []string {
	c := make([]string, 0, len(symbols))
	for _, symbol := range symbols {
		c = append(c, yahooToBloomberg(symbol))
	}
	return c
}
<|endoftext|>


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

// +build windows

package windriver

// TODO: implement a back buffer.

import (
	"image"
	"image/color"
	"image/draw"
	"sync"
	"syscall"
	"unsafe"

	"golang.org/x/exp/shiny/driver/internal/drawer"
	"golang.org/x/exp/shiny/driver/internal/event"
	"golang.org/x/exp/shiny/driver/internal/win32"
	"golang.org/x/exp/shiny/screen"
	"golang.org/x/image/math/f64"
	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/lifecycle"
	"golang.org/x/mobile/event/mouse"
	"golang.org/x/mobile/event/paint"
	"golang.org/x/mobile/event/size"
)

var (
	uploadsMu sync.Mutex
	uploads   = map[uintptr]upload{}
	uploadID  uintptr
)

type windowImpl struct {
	hwnd win32.HWND

	event.Queue

	sz             size.Event
	lifecycleStage lifecycle.Stage
}

func (w *windowImpl) Release() {
	win32.Release(w.hwnd)
}

func (w *windowImpl) Upload(dp image.Point, src screen.Buffer, sr image.Rectangle) {
	completion := make(chan struct{})

	// Protect struct contents from being GCed
	uploadsMu.Lock()
	uploadID++
	id := uploadID
	uploads[id] = upload{
		dp:         dp,
		src:        src.(*bufferImpl),
		sr:         sr,
		completion: completion,
	}
	uploadsMu.Unlock()

	win32.SendMessage(w.hwnd, msgUpload, id, 0)

	<-completion
}

type upload struct {
	dp         image.Point
	src        *bufferImpl
	sr         image.Rectangle
	completion chan struct{}
}

func handleUpload(hwnd win32.HWND, uMsg uint32, wParam, lParam uintptr) {
	id := wParam
	uploadsMu.Lock()
	u := uploads[id]
	delete(uploads, id)
	uploadsMu.Unlock()

	dc, err := win32.GetDC(hwnd)
	if err != nil {
		panic(err) // TODO handle errors
	}
	defer win32.ReleaseDC(hwnd, dc)

	// TODO(brainman): move preUpload / postUpload out of handleUpload,
	// because handleUpload can only be executed by one (message pump)
	// thread only
	u.src.preUpload()

	// TODO: adjust if dp is outside dst bounds, or sr is outside src bounds.
	err = copyBitmapToDC(dc, u.dp, u.src.hbitmap, u.sr, draw.Src)
	go func() {
		u.src.postUpload()
		close(u.completion)
	}()
	if err != nil {
		panic(err) // TODO handle errors
	}
}

func (w *windowImpl) Fill(dr image.Rectangle, src color.Color, op draw.Op) {
	rect := _RECT{
		Left:   int32(dr.Min.X),
		Top:    int32(dr.Min.Y),
		Right:  int32(dr.Max.X),
		Bottom: int32(dr.Max.Y),
	}
	r, g, b, a := src.RGBA()
	r >>= 8
	g >>= 8
	b >>= 8
	a >>= 8
	color := (a << 24) | (r << 16) | (g << 8) | b
	msg := uint32(msgFillOver)
	if op == draw.Src {
		msg = msgFillSrc
	}
	// Note: this SendMessage won't return until after the fill
	// completes, so using &rect is safe.
	win32.SendMessage(w.hwnd, msg, uintptr(color), uintptr(unsafe.Pointer(&rect)))
}

func (w *windowImpl) Draw(src2dst f64.Aff3, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) {
	// TODO
}

type handleCopyParams struct {
	dp  image.Point
	src syscall.Handle
	sr  image.Rectangle
	op  draw.Op
}

var msgCopy = win32.AddWindowMsg(handleCopy)

func (w *windowImpl) Copy(dp image.Point, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) {
	if op != draw.Src && op != draw.Over {
		drawer.Copy(w, dp, src, sr, op, opts)
		return
	}
	p := handleCopyParams{
		dp:  dp,
		src: src.(*textureImpl).bitmap,
		sr:  sr,
		op:  op,
	}
	win32.SendMessage(w.hwnd, msgCopy, 0, uintptr(unsafe.Pointer(&p)))
}

func handleCopy(hwnd win32.HWND, uMsg uint32, wParam, lParam uintptr) {
	p := (*handleCopyParams)(unsafe.Pointer(lParam))

	dc, err := win32.GetDC(hwnd)
	if err != nil {
		panic(err) // TODO handle errors
	}
	defer win32.ReleaseDC(hwnd, dc)

	err = copyBitmapToDC(dc, p.dp, p.src, p.sr, p.op)
	if err != nil {
		panic(err) // TODO handle errors
	}
}

func (w *windowImpl) Scale(dr image.Rectangle, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) {
	drawer.Scale(w, dr, src, sr, op, opts)
}

func (w *windowImpl) Publish() screen.PublishResult {
	// TODO
	return screen.PublishResult{}
}

func init() {
	send := func(hwnd win32.HWND, e interface{}) {
		theScreen.mu.Lock()
		w := theScreen.windows[hwnd]
		theScreen.mu.Unlock()

		w.Send(e)
	}
	win32.MouseEvent = func(hwnd win32.HWND, e mouse.Event) { send(hwnd, e) }
	win32.PaintEvent = func(hwnd win32.HWND, e paint.Event) { send(hwnd, e) }
	win32.KeyEvent = func(hwnd win32.HWND, e key.Event) { send(hwnd, e) }
	win32.LifecycleEvent = lifecycleEvent
	win32.SizeEvent = sizeEvent
}

func lifecycleEvent(hwnd win32.HWND, to lifecycle.Stage) {
	theScreen.mu.Lock()
	w := theScreen.windows[hwnd]
	theScreen.mu.Unlock()

	if w.lifecycleStage == to {
		return
	}
	w.Send(lifecycle.Event{
		From: w.lifecycleStage,
		To:   to,
	})
	w.lifecycleStage = to
}

func sizeEvent(hwnd win32.HWND, e size.Event) {
	theScreen.mu.Lock()
	w := theScreen.windows[hwnd]
	theScreen.mu.Unlock()

	w.Send(e)

	if e != w.sz {
		w.sz = e
		w.Send(paint.Event{})
	}
}
<commit_msg>shiny/driver/windriver: remove completion mechanism when uploading to window<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build windows

package windriver

// TODO: implement a back buffer.

import (
	"image"
	"image/color"
	"image/draw"
	"sync"
	"syscall"
	"unsafe"

	"golang.org/x/exp/shiny/driver/internal/drawer"
	"golang.org/x/exp/shiny/driver/internal/event"
	"golang.org/x/exp/shiny/driver/internal/win32"
	"golang.org/x/exp/shiny/screen"
	"golang.org/x/image/math/f64"
	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/lifecycle"
	"golang.org/x/mobile/event/mouse"
	"golang.org/x/mobile/event/paint"
	"golang.org/x/mobile/event/size"
)

var (
	uploadsMu sync.Mutex
	uploads   = map[uintptr]upload{}
	uploadID  uintptr
)

type windowImpl struct {
	hwnd win32.HWND

	event.Queue

	sz             size.Event
	lifecycleStage lifecycle.Stage
}

func (w *windowImpl) Release() {
	win32.Release(w.hwnd)
}

func (w *windowImpl) Upload(dp image.Point, src screen.Buffer, sr image.Rectangle) {
	// Protect struct contents from being GCed
	uploadsMu.Lock()
	uploadID++
	id := uploadID
	uploads[id] = upload{
		dp:  dp,
		src: src.(*bufferImpl),
		sr:  sr,
	}
	uploadsMu.Unlock()

	win32.SendMessage(w.hwnd, msgUpload, id, 0)
}

type upload struct {
	dp  image.Point
	src *bufferImpl
	sr  image.Rectangle
}

func handleUpload(hwnd win32.HWND, uMsg uint32, wParam, lParam uintptr) {
	id := wParam
	uploadsMu.Lock()
	u := uploads[id]
	delete(uploads, id)
	uploadsMu.Unlock()

	dc, err := win32.GetDC(hwnd)
	if err != nil {
		panic(err) // TODO handle errors
	}
	defer win32.ReleaseDC(hwnd, dc)

	// TODO(brainman): move preUpload / postUpload out of handleUpload,
	// because handleUpload can only be executed by one (message pump)
	// thread only
	u.src.preUpload()
	defer u.src.postUpload()

	// TODO: adjust if dp is outside dst bounds, or sr is outside src bounds.
	err = copyBitmapToDC(dc, u.dp, u.src.hbitmap, u.sr, draw.Src)
	if err != nil {
		panic(err) // TODO handle errors
	}
}

func (w *windowImpl) Fill(dr image.Rectangle, src color.Color, op draw.Op) {
	rect := _RECT{
		Left:   int32(dr.Min.X),
		Top:    int32(dr.Min.Y),
		Right:  int32(dr.Max.X),
		Bottom: int32(dr.Max.Y),
	}
	r, g, b, a := src.RGBA()
	r >>= 8
	g >>= 8
	b >>= 8
	a >>= 8
	color := (a << 24) | (r << 16) | (g << 8) | b
	msg := uint32(msgFillOver)
	if op == draw.Src {
		msg = msgFillSrc
	}
	// Note: this SendMessage won't return until after the fill
	// completes, so using &rect is safe.
	win32.SendMessage(w.hwnd, msg, uintptr(color), uintptr(unsafe.Pointer(&rect)))
}

func (w *windowImpl) Draw(src2dst f64.Aff3, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) {
	// TODO
}

type handleCopyParams struct {
	dp  image.Point
	src syscall.Handle
	sr  image.Rectangle
	op  draw.Op
}

var msgCopy = win32.AddWindowMsg(handleCopy)

func (w *windowImpl) Copy(dp image.Point, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) {
	if op != draw.Src && op != draw.Over {
		drawer.Copy(w, dp, src, sr, op, opts)
		return
	}
	p := handleCopyParams{
		dp:  dp,
		src: src.(*textureImpl).bitmap,
		sr:  sr,
		op:  op,
	}
	win32.SendMessage(w.hwnd, msgCopy, 0, uintptr(unsafe.Pointer(&p)))
}

func handleCopy(hwnd win32.HWND, uMsg uint32, wParam, lParam uintptr) {
	p := (*handleCopyParams)(unsafe.Pointer(lParam))

	dc, err := win32.GetDC(hwnd)
	if err != nil {
		panic(err) // TODO handle errors
	}
	defer win32.ReleaseDC(hwnd, dc)

	err = copyBitmapToDC(dc, p.dp, p.src, p.sr, p.op)
	if err != nil {
		panic(err) // TODO handle errors
	}
}

func (w *windowImpl) Scale(dr image.Rectangle, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) {
	drawer.Scale(w, dr, src, sr, op, opts)
}

func (w *windowImpl) Publish() screen.PublishResult {
	// TODO
	return screen.PublishResult{}
}

func init() {
	send := func(hwnd win32.HWND, e interface{}) {
		theScreen.mu.Lock()
		w := theScreen.windows[hwnd]
		theScreen.mu.Unlock()

		w.Send(e)
	}
	win32.MouseEvent = func(hwnd win32.HWND, e mouse.Event) { send(hwnd, e) }
	win32.PaintEvent = func(hwnd win32.HWND, e paint.Event) { send(hwnd, e) }
	win32.KeyEvent = func(hwnd win32.HWND, e key.Event) { send(hwnd, e) }
	win32.LifecycleEvent = lifecycleEvent
	win32.SizeEvent = sizeEvent
}

func lifecycleEvent(hwnd win32.HWND, to lifecycle.Stage) {
	theScreen.mu.Lock()
	w := theScreen.windows[hwnd]
	theScreen.mu.Unlock()

	if w.lifecycleStage == to {
		return
	}
	w.Send(lifecycle.Event{
		From: w.lifecycleStage,
		To:   to,
	})
	w.lifecycleStage = to
}

func sizeEvent(hwnd win32.HWND, e size.Event) {
	theScreen.mu.Lock()
	w := theScreen.windows[hwnd]
	theScreen.mu.Unlock()

	w.Send(e)

	if e != w.sz {
		w.sz = e
		w.Send(paint.Event{})
	}
}
<|endoftext|>


<commit_before>package fasthttp

import (
	"net"
	"runtime"
	"runtime/debug"
	"strings"
	"sync"
	"time"
)

// workerPool serves incoming connections via a pool of workers
// in FILO order, i.e. the most recently stopped worker will serve the next
// incoming connection.
//
// Such a scheme keeps CPU caches hot (in theory).
type workerPool struct {
	// Function for serving server connections.
	// It must leave c unclosed.
	WorkerFunc func(c net.Conn) error

	MaxWorkersCount int

	LogAllErrors bool

	Logger Logger

	lock         sync.Mutex
	workersCount int
	mustStop     bool

	ready []*workerChan

	stopCh chan struct{}
}

type workerChan struct {
	t  time.Time
	ch chan net.Conn
}

func (wp *workerPool) Start() {
	if wp.stopCh != nil {
		panic("BUG: workerPool already started")
	}
	wp.stopCh = make(chan struct{})
	stopCh := wp.stopCh
	go func() {
		for {
			select {
			case <-stopCh:
				return
			default:
				time.Sleep(10 * time.Second)
			}
			wp.clean()
		}
	}()
}

func (wp *workerPool) Stop() {
	if wp.stopCh == nil {
		panic("BUG: workerPool wasn't started")
	}
	close(wp.stopCh)
	wp.stopCh = nil

	// Stop all the workers waiting for incoming connections.
	// Do not wait for busy workers - they will stop after
	// serving the connection and noticing wp.mustStop = true.
	wp.lock.Lock()
	for _, ch := range wp.ready {
		ch.ch <- nil
	}
	wp.ready = nil
	wp.mustStop = true
	wp.lock.Unlock()
}

func (wp *workerPool) clean() {
	// Clean least recently used workers if they didn't serve connections
	// for more than one second.
	wp.lock.Lock()
	ready := wp.ready
	for len(ready) > 1 && time.Since(ready[0].t) > 10*time.Second {
		// notify the worker to stop.
		ready[0].ch <- nil

		ready = ready[1:]
		wp.workersCount--
	}
	if len(ready) < len(wp.ready) {
		copy(wp.ready, ready)
		for i := len(ready); i < len(wp.ready); i++ {
			wp.ready[i] = nil
		}
		wp.ready = wp.ready[:len(ready)]
	}
	wp.lock.Unlock()
}

func (wp *workerPool) Serve(c net.Conn) bool {
	ch := wp.getCh()
	if ch == nil {
		return false
	}
	ch.ch <- c
	return true
}

var workerChanCap = func() int {
	// Use blocking workerChan if GOMAXPROCS=1.
	// This immediately switches Serve to WorkerFunc, which results
	// in higher performance (under go1.5 at least).
	if runtime.GOMAXPROCS(0) == 1 {
		return 0
	}

	// Use non-blocking workerChan if GOMAXPROCS>1,
	// since otherwise the Serve caller (Acceptor) may lag accepting
	// new connections if WorkerFunc is CPU-bound.
	return 1
}()

func (wp *workerPool) getCh() *workerChan {
	var ch *workerChan
	createWorker := false

	wp.lock.Lock()
	ready := wp.ready
	n := len(ready) - 1
	if n < 0 {
		if wp.workersCount < wp.MaxWorkersCount {
			createWorker = true
			wp.workersCount++
		}
	} else {
		ch = ready[n]
		wp.ready = ready[:n]
	}
	wp.lock.Unlock()

	if ch == nil {
		if !createWorker {
			return nil
		}
		vch := workerChanPool.Get()
		if vch == nil {
			vch = &workerChan{
				ch: make(chan net.Conn, workerChanCap),
			}
		}
		ch = vch.(*workerChan)
		go func() {
			wp.workerFunc(ch)
			workerChanPool.Put(vch)
		}()
	}
	return ch
}

func (wp *workerPool) release(ch *workerChan) bool {
	ch.t = time.Now()
	wp.lock.Lock()
	if wp.mustStop {
		wp.lock.Unlock()
		return false
	}
	wp.ready = append(wp.ready, ch)
	wp.lock.Unlock()
	return true
}

var workerChanPool sync.Pool

func (wp *workerPool) workerFunc(ch *workerChan) {
	var c net.Conn
	var err error

	defer func() {
		if r := recover(); r != nil {
			wp.Logger.Printf("panic: %s\nStack trace:\n%s", r, debug.Stack())
		}

		if c != nil {
			c.Close()
			wp.release(ch)
		}
	}()

	for c = range ch.ch {
		if c == nil {
			break
		}
		if err = wp.WorkerFunc(c); err != nil && err != errHijacked {
			errStr := err.Error()
			if wp.LogAllErrors || !(strings.Contains(errStr, "broken pipe") ||
				strings.Contains(errStr, "reset by peer") ||
				strings.Contains(errStr, "i/o timeout")) {
				wp.Logger.Printf("error when serving connection %q<->%q: %s", c.LocalAddr(), c.RemoteAddr(), err)
			}
		}
		if err != errHijacked {
			c.Close()
		}
		c = nil

		if !wp.release(ch) {
			break
		}
	}
}
<commit_msg>code prettifying<commit_after>package fasthttp

import (
	"net"
	"runtime"
	"runtime/debug"
	"strings"
	"sync"
	"time"
)

// workerPool serves incoming connections via a pool of workers
// in FILO order, i.e. the most recently stopped worker will serve the next
// incoming connection.
//
// Such a scheme keeps CPU caches hot (in theory).
type workerPool struct {
	// Function for serving server connections.
	// It must leave c unclosed.
	WorkerFunc func(c net.Conn) error

	MaxWorkersCount int

	LogAllErrors bool

	Logger Logger

	lock         sync.Mutex
	workersCount int
	mustStop     bool

	ready []*workerChan

	stopCh chan struct{}
}

type workerChan struct {
	t  time.Time
	ch chan net.Conn
}

func (wp *workerPool) Start() {
	if wp.stopCh != nil {
		panic("BUG: workerPool already started")
	}
	wp.stopCh = make(chan struct{})
	stopCh := wp.stopCh
	go func() {
		for {
			select {
			case <-stopCh:
				return
			default:
				time.Sleep(10 * time.Second)
			}
			wp.clean()
		}
	}()
}

func (wp *workerPool) Stop() {
	if wp.stopCh == nil {
		panic("BUG: workerPool wasn't started")
	}
	close(wp.stopCh)
	wp.stopCh = nil

	// Stop all the workers waiting for incoming connections.
	// Do not wait for busy workers - they will stop after
	// serving the connection and noticing wp.mustStop = true.
	wp.lock.Lock()
	for _, ch := range wp.ready {
		ch.ch <- nil
	}
	wp.ready = nil
	wp.mustStop = true
	wp.lock.Unlock()
}

const maxIdleWorkerDuration = 10 * time.Second

func (wp *workerPool) clean() {
	// Clean least recently used workers if they didn't serve connections
	// for more than maxIdleWorkerDuration.
	wp.lock.Lock()
	ready := wp.ready
	for len(ready) > 1 && time.Since(ready[0].t) > maxIdleWorkerDuration {
		// notify the worker to stop.
		ready[0].ch <- nil

		ready = ready[1:]
		wp.workersCount--
	}
	if len(ready) < len(wp.ready) {
		copy(wp.ready, ready)
		for i := len(ready); i < len(wp.ready); i++ {
			wp.ready[i] = nil
		}
		wp.ready = wp.ready[:len(ready)]
	}
	wp.lock.Unlock()
}

func (wp *workerPool) Serve(c net.Conn) bool {
	ch := wp.getCh()
	if ch == nil {
		return false
	}
	ch.ch <- c
	return true
}

var workerChanCap = func() int {
	// Use blocking workerChan if GOMAXPROCS=1.
	// This immediately switches Serve to WorkerFunc, which results
	// in higher performance (under go1.5 at least).
	if runtime.GOMAXPROCS(0) == 1 {
		return 0
	}

	// Use non-blocking workerChan if GOMAXPROCS>1,
	// since otherwise the Serve caller (Acceptor) may lag accepting
	// new connections if WorkerFunc is CPU-bound.
	return 1
}()

func (wp *workerPool) getCh() *workerChan {
	var ch *workerChan
	createWorker := false

	wp.lock.Lock()
	ready := wp.ready
	n := len(ready) - 1
	if n < 0 {
		if wp.workersCount < wp.MaxWorkersCount {
			createWorker = true
			wp.workersCount++
		}
	} else {
		ch = ready[n]
		wp.ready = ready[:n]
	}
	wp.lock.Unlock()

	if ch == nil {
		if !createWorker {
			return nil
		}
		vch := workerChanPool.Get()
		if vch == nil {
			vch = &workerChan{
				ch: make(chan net.Conn, workerChanCap),
			}
		}
		ch = vch.(*workerChan)
		go func() {
			wp.workerFunc(ch)
			workerChanPool.Put(vch)
		}()
	}
	return ch
}

func (wp *workerPool) release(ch *workerChan) bool {
	ch.t = time.Now()
	wp.lock.Lock()
	if wp.mustStop {
		wp.lock.Unlock()
		return false
	}
	wp.ready = append(wp.ready, ch)
	wp.lock.Unlock()
	return true
}

var workerChanPool sync.Pool

func (wp *workerPool) workerFunc(ch *workerChan) {
	var c net.Conn
	var err error

	defer func() {
		if r := recover(); r != nil {
			wp.Logger.Printf("panic: %s\nStack trace:\n%s", r, debug.Stack())
		}

		if c != nil {
			c.Close()
			wp.release(ch)
		}
	}()

	for c = range ch.ch {
		if c == nil {
			break
		}
		if err = wp.WorkerFunc(c); err != nil && err != errHijacked {
			errStr := err.Error()
			if wp.LogAllErrors || !(strings.Contains(errStr, "broken pipe") ||
				strings.Contains(errStr, "reset by peer") ||
				strings.Contains(errStr, "i/o timeout")) {
				wp.Logger.Printf("error when serving connection %q<->%q: %s", c.LocalAddr(), c.RemoteAddr(), err)
			}
		}
		if err != errHijacked {
			c.Close()
		}
		c = nil

		if !wp.release(ch) {
			break
		}
	}
}
<|endoftext|>


<commit_before>package fasthttp

import (
	"net"
	"runtime"
	"strings"
	"sync"
	"time"
)

// workerPool serves incoming connections via a pool of workers
// in FILO order, i.e. the most recently stopped worker will serve the next
// incoming connection.
//
// Such a scheme keeps CPU caches hot (in theory).
type workerPool struct {
	// Function for serving server connections.
	// It must leave c unclosed.
	WorkerFunc func(c net.Conn) error

	MaxWorkersCount int

	LogAllErrors bool

	MaxIdleWorkerDuration time.Duration

	Logger Logger

	lock         sync.Mutex
	workersCount int
	mustStop     bool

	ready []*workerChan

	stopCh chan struct{}

	workerChanPool sync.Pool
}

type workerChan struct {
	lastUseTime time.Time
	ch          chan net.Conn
}

func (wp *workerPool) Start() {
	if wp.stopCh != nil {
		panic("BUG: workerPool already started")
	}
	wp.stopCh = make(chan struct{})
	stopCh := wp.stopCh
	go func() {
		var scratch []*workerChan
		for {
			wp.clean(&scratch)
			select {
			case <-stopCh:
				return
			default:
				time.Sleep(wp.getMaxIdleWorkerDuration())
			}
		}
	}()
}

func (wp *workerPool) Stop() {
	if wp.stopCh == nil {
		panic("BUG: workerPool wasn't started")
	}
	close(wp.stopCh)
	wp.stopCh = nil

	// Stop all the workers waiting for incoming connections.
	// Do not wait for busy workers - they will stop after
	// serving the connection and noticing wp.mustStop = true.
	wp.lock.Lock()
	ready := wp.ready
	for i, ch := range ready {
		ch.ch <- nil
		ready[i] = nil
	}
	wp.ready = ready[:0]
	wp.mustStop = true
	wp.lock.Unlock()
}

func (wp *workerPool) getMaxIdleWorkerDuration() time.Duration {
	if wp.MaxIdleWorkerDuration <= 0 {
		return 10 * time.Second
	}
	return wp.MaxIdleWorkerDuration
}

func (wp *workerPool) clean(scratch *[]*workerChan) {
	maxIdleWorkerDuration := wp.getMaxIdleWorkerDuration()

	// Clean least recently used workers if they didn't serve connections
	// for more than maxIdleWorkerDuration.
	currentTime := time.Now()

	wp.lock.Lock()
	ready := wp.ready
	n := len(ready)
	i := 0
	for i < n && currentTime.Sub(ready[i].lastUseTime) > maxIdleWorkerDuration {
		i++
	}
	*scratch = append((*scratch)[:0], ready[:i]...)
	if i > 0 {
		m := copy(ready, ready[i:])
		for i = m; i < n; i++ {
			ready[i] = nil
		}
		wp.ready = ready[:m]
	}
	wp.lock.Unlock()

	// Notify obsolete workers to stop.
	// This notification must be outside the wp.lock, since ch.ch
	// may be blocking and may consume a lot of time if many workers
	// are located on non-local CPUs.
	tmp := *scratch
	for i, ch := range tmp {
		ch.ch <- nil
		tmp[i] = nil
	}
}

func (wp *workerPool) Serve(c net.Conn) bool {
	ch := wp.getCh()
	if ch == nil {
		return false
	}
	ch.ch <- c
	return true
}

var workerChanCap = func() int {
	// Use blocking workerChan if GOMAXPROCS=1.
	// This immediately switches Serve to WorkerFunc, which results
	// in higher performance (under go1.5 at least).
	if runtime.GOMAXPROCS(0) == 1 {
		return 0
	}

	// Use non-blocking workerChan if GOMAXPROCS>1,
	// since otherwise the Serve caller (Acceptor) may lag accepting
	// new connections if WorkerFunc is CPU-bound.
	return 1
}()

func (wp *workerPool) getCh() *workerChan {
	var ch *workerChan
	createWorker := false

	wp.lock.Lock()
	ready := wp.ready
	n := len(ready) - 1
	if n < 0 {
		if wp.workersCount < wp.MaxWorkersCount {
			createWorker = true
			wp.workersCount++
		}
	} else {
		ch = ready[n]
		ready[n] = nil
		wp.ready = ready[:n]
	}
	wp.lock.Unlock()

	if ch == nil {
		if !createWorker {
			return nil
		}
		vch := wp.workerChanPool.Get()
		if vch == nil {
			vch = &workerChan{
				ch: make(chan net.Conn, workerChanCap),
			}
		}
		ch = vch.(*workerChan)
		go func() {
			wp.workerFunc(ch)
			wp.workerChanPool.Put(vch)
		}()
	}
	return ch
}

func (wp *workerPool) release(ch *workerChan) bool {
	ch.lastUseTime = time.Now()
	wp.lock.Lock()
	if wp.mustStop {
		wp.lock.Unlock()
		return false
	}
	wp.ready = append(wp.ready, ch)
	wp.lock.Unlock()
	return true
}

func (wp *workerPool) workerFunc(ch *workerChan) {
	var c net.Conn

	var err error
	for c = range ch.ch {
		if c == nil {
			break
		}

		if err = wp.WorkerFunc(c); err != nil && err != errHijacked {
			errStr := err.Error()
			if wp.LogAllErrors || !(strings.Contains(errStr, "broken pipe") ||
				strings.Contains(errStr, "reset by peer") ||
				strings.Contains(errStr, "i/o timeout")) {
				wp.Logger.Printf("error when serving connection %q<->%q: %s", c.LocalAddr(), c.RemoteAddr(), err)
			}
		}
		if err != errHijacked {
			c.Close()
		}
		c = nil

		if !wp.release(ch) {
			break
		}
	}

	wp.lock.Lock()
	wp.workersCount--
	wp.lock.Unlock()
}
<commit_msg>Do not log ErrSmallBuffer for request headers<commit_after>package fasthttp

import (
	"net"
	"runtime"
	"strings"
	"sync"
	"time"
)

// workerPool serves incoming connections via a pool of workers
// in FILO order, i.e. the most recently stopped worker will serve the next
// incoming connection.
//
// Such a scheme keeps CPU caches hot (in theory).
type workerPool struct {
	// Function for serving server connections.
	// It must leave c unclosed.
	WorkerFunc func(c net.Conn) error

	MaxWorkersCount int

	LogAllErrors bool

	MaxIdleWorkerDuration time.Duration

	Logger Logger

	lock         sync.Mutex
	workersCount int
	mustStop     bool

	ready []*workerChan

	stopCh chan struct{}

	workerChanPool sync.Pool
}

type workerChan struct {
	lastUseTime time.Time
	ch          chan net.Conn
}

func (wp *workerPool) Start() {
	if wp.stopCh != nil {
		panic("BUG: workerPool already started")
	}
	wp.stopCh = make(chan struct{})
	stopCh := wp.stopCh
	go func() {
		var scratch []*workerChan
		for {
			wp.clean(&scratch)
			select {
			case <-stopCh:
				return
			default:
				time.Sleep(wp.getMaxIdleWorkerDuration())
			}
		}
	}()
}

func (wp *workerPool) Stop() {
	if wp.stopCh == nil {
		panic("BUG: workerPool wasn't started")
	}
	close(wp.stopCh)
	wp.stopCh = nil

	// Stop all the workers waiting for incoming connections.
	// Do not wait for busy workers - they will stop after
	// serving the connection and noticing wp.mustStop = true.
	wp.lock.Lock()
	ready := wp.ready
	for i, ch := range ready {
		ch.ch <- nil
		ready[i] = nil
	}
	wp.ready = ready[:0]
	wp.mustStop = true
	wp.lock.Unlock()
}

func (wp *workerPool) getMaxIdleWorkerDuration() time.Duration {
	if wp.MaxIdleWorkerDuration <= 0 {
		return 10 * time.Second
	}
	return wp.MaxIdleWorkerDuration
}

func (wp *workerPool) clean(scratch *[]*workerChan) {
	maxIdleWorkerDuration := wp.getMaxIdleWorkerDuration()

	// Clean least recently used workers if they didn't serve connections
	// for more than maxIdleWorkerDuration.
	currentTime := time.Now()

	wp.lock.Lock()
	ready := wp.ready
	n := len(ready)
	i := 0
	for i < n && currentTime.Sub(ready[i].lastUseTime) > maxIdleWorkerDuration {
		i++
	}
	*scratch = append((*scratch)[:0], ready[:i]...)
	if i > 0 {
		m := copy(ready, ready[i:])
		for i = m; i < n; i++ {
			ready[i] = nil
		}
		wp.ready = ready[:m]
	}
	wp.lock.Unlock()

	// Notify obsolete workers to stop.
	// This notification must be outside the wp.lock, since ch.ch
	// may be blocking and may consume a lot of time if many workers
	// are located on non-local CPUs.
	tmp := *scratch
	for i, ch := range tmp {
		ch.ch <- nil
		tmp[i] = nil
	}
}

func (wp *workerPool) Serve(c net.Conn) bool {
	ch := wp.getCh()
	if ch == nil {
		return false
	}
	ch.ch <- c
	return true
}

var workerChanCap = func() int {
	// Use blocking workerChan if GOMAXPROCS=1.
	// This immediately switches Serve to WorkerFunc, which results
	// in higher performance (under go1.5 at least).
	if runtime.GOMAXPROCS(0) == 1 {
		return 0
	}

	// Use non-blocking workerChan if GOMAXPROCS>1,
	// since otherwise the Serve caller (Acceptor) may lag accepting
	// new connections if WorkerFunc is CPU-bound.
	return 1
}()

func (wp *workerPool) getCh() *workerChan {
	var ch *workerChan
	createWorker := false

	wp.lock.Lock()
	ready := wp.ready
	n := len(ready) - 1
	if n < 0 {
		if wp.workersCount < wp.MaxWorkersCount {
			createWorker = true
			wp.workersCount++
		}
	} else {
		ch = ready[n]
		ready[n] = nil
		wp.ready = ready[:n]
	}
	wp.lock.Unlock()

	if ch == nil {
		if !createWorker {
			return nil
		}
		vch := wp.workerChanPool.Get()
		if vch == nil {
			vch = &workerChan{
				ch: make(chan net.Conn, workerChanCap),
			}
		}
		ch = vch.(*workerChan)
		go func() {
			wp.workerFunc(ch)
			wp.workerChanPool.Put(vch)
		}()
	}
	return ch
}

func (wp *workerPool) release(ch *workerChan) bool {
	ch.lastUseTime = time.Now()
	wp.lock.Lock()
	if wp.mustStop {
		wp.lock.Unlock()
		return false
	}
	wp.ready = append(wp.ready, ch)
	wp.lock.Unlock()
	return true
}

func (wp *workerPool) workerFunc(ch *workerChan) {
	var c net.Conn

	var err error
	for c = range ch.ch {
		if c == nil {
			break
		}

		if err = wp.WorkerFunc(c); err != nil && err != errHijacked {
			errStr := err.Error()
			if wp.LogAllErrors || !(strings.Contains(errStr, "broken pipe") ||
				strings.Contains(errStr, "reset by peer") ||
				strings.Contains(errStr, "request headers: small read buffer") ||
				strings.Contains(errStr, "i/o timeout")) {
				wp.Logger.Printf("error when serving connection %q<->%q: %s", c.LocalAddr(), c.RemoteAddr(), err)
			}
		}
		if err != errHijacked {
			c.Close()
		}
		c = nil

		if !wp.release(ch) {
			break
		}
	}

	wp.lock.Lock()
	wp.workersCount--
	wp.lock.Unlock()
}
<|endoftext|>


<commit_before>package fasthttp

import (
	"net"
	"runtime/debug"
	"strings"
	"sync"
	"time"
)

// workerPool serves incoming connections via a pool of workers
// in FIFO order, i.e. the most recently stopped worker will serve the next
// incoming connection.
type workerPool struct {
	// Function for serving server connections.
	// It must leave c unclosed.
	WorkerFunc func(c net.Conn) error

	// Maximum number of workers to create.
	MaxWorkersCount int

	// Logger used by workerPool.
	Logger Logger

	lock         sync.Mutex
	workersCount int
	mustStop     bool

	ready []*workerChan

	stopCh chan struct{}
}

type workerChan struct {
	t  time.Time
	ch chan net.Conn
}

func (wp *workerPool) Start() {
	if wp.stopCh != nil {
		panic("BUG: workerPool already started")
	}
	wp.stopCh = make(chan struct{})
	stopCh := wp.stopCh
	go func() {
		for {
			select {
			case <-stopCh:
				return
			default:
				time.Sleep(time.Second)
			}
			wp.clean()
		}
	}()
}

func (wp *workerPool) Stop() {
	if wp.stopCh == nil {
		panic("BUG: workerPool wasn't started")
	}
	close(wp.stopCh)
	wp.stopCh = nil

	// Stop all the workers waiting for incoming connections.
	// Do not wait for busy workers - they will stop after
	// serving the connection and noticing wp.mustStop = true.
	wp.lock.Lock()
	for _, ch := range wp.ready {
		ch.ch <- nil
	}
	wp.ready = nil
	wp.mustStop = true
	wp.lock.Unlock()
}

func (wp *workerPool) clean() {
	// Clean least recently used workers if they didn't serve connections
	// for more than one second.
	wp.lock.Lock()
	chans := wp.ready
	for len(chans) > 1 && time.Since(chans[0].t) > time.Second {
		chans[0].ch <- nil
		copy(chans, chans[1:])
		chans = chans[:len(chans)-1]
		wp.ready = chans
		wp.workersCount--
	}
	wp.lock.Unlock()
}

func (wp *workerPool) Serve(c net.Conn) bool {
	ch := wp.getCh()
	if ch == nil {
		return false
	}
	ch.ch <- c
	return true
}

func (wp *workerPool) getCh() *workerChan {
	var ch *workerChan
	createWorker := false

	wp.lock.Lock()
	chans := wp.ready
	n := len(chans) - 1
	if n < 0 {
		if wp.workersCount < wp.MaxWorkersCount {
			createWorker = true
			wp.workersCount++
		}
	} else {
		ch = chans[n]
		wp.ready = chans[:n]
	}
	wp.lock.Unlock()

	if ch == nil {
		if !createWorker {
			return nil
		}
		vch := workerChanPool.Get()
		if vch == nil {
			vch = &workerChan{
				ch: make(chan net.Conn, 1),
			}
		}
		ch = vch.(*workerChan)
		go func() {
			wp.workerFunc(ch)
			workerChanPool.Put(vch)
		}()
	}
	return ch
}

func (wp *workerPool) release(ch *workerChan) bool {
	ch.t = time.Now()
	wp.lock.Lock()
	if wp.mustStop {
		wp.lock.Unlock()
		return false
	}
	wp.ready = append(wp.ready, ch)
	wp.lock.Unlock()
	return true
}

var workerChanPool sync.Pool

func (wp *workerPool) workerFunc(ch *workerChan) {
	var c net.Conn
	var err error

	defer func() {
		if r := recover(); r != nil {
			wp.Logger.Printf("panic: %s\nStack trace:\n%s", r, debug.Stack())
		}

		if c != nil {
			c.Close()
		}
		wp.release(ch)
	}()

	for c = range ch.ch {
		if c == nil {
			break
		}
		if err = wp.WorkerFunc(c); err != nil && err != errHijacked {
			errStr := err.Error()
			if !strings.Contains(errStr, "broken pipe") && !strings.Contains(errStr, "reset by peer") {
				wp.Logger.Printf("error when serving connection %q<->%q: %s", c.LocalAddr(), c.RemoteAddr(), err)
			}
		}
		if err != errHijacked {
			c.Close()
		}
		c = nil

		if !wp.release(ch) {
			break
		}
	}
}
<commit_msg>fixed a typo<commit_after>package fasthttp

import (
	"net"
	"runtime/debug"
	"strings"
	"sync"
	"time"
)

// workerPool serves incoming connections via a pool of workers
// in FIFO order, i.e. the most recently stopped worker will serve the next
// incoming connection.
type workerPool struct {
	// Function for serving server connections.
	// It must leave c unclosed.
	WorkerFunc func(c net.Conn) error

	// Maximum number of workers to create.
	MaxWorkersCount int

	// Logger used by workerPool.
	Logger Logger

	lock         sync.Mutex
	workersCount int
	mustStop     bool

	ready []*workerChan

	stopCh chan struct{}
}

type workerChan struct {
	t  time.Time
	ch chan net.Conn
}

func (wp *workerPool) Start() {
	if wp.stopCh != nil {
		panic("BUG: workerPool already started")
	}
	wp.stopCh = make(chan struct{})
	stopCh := wp.stopCh
	go func() {
		for {
			select {
			case <-stopCh:
				return
			default:
				time.Sleep(time.Second)
			}
			wp.clean()
		}
	}()
}

func (wp *workerPool) Stop() {
	if wp.stopCh == nil {
		panic("BUG: workerPool wasn't started")
	}
	close(wp.stopCh)
	wp.stopCh = nil

	// Stop all the workers waiting for incoming connections.
	// Do not wait for busy workers - they will stop after
	// serving the connection and noticing wp.mustStop = true.
	wp.lock.Lock()
	for _, ch := range wp.ready {
		ch.ch <- nil
	}
	wp.ready = nil
	wp.mustStop = true
	wp.lock.Unlock()
}

func (wp *workerPool) clean() {
	// Clean least recently used workers if they didn't serve connections
	// for more than one second.
	wp.lock.Lock()
	chans := wp.ready
	for len(chans) > 1 && time.Since(chans[0].t) > time.Second {
		chans[0].ch <- nil
		copy(chans, chans[1:])
		chans = chans[:len(chans)-1]
		wp.ready = chans
		wp.workersCount--
	}
	wp.lock.Unlock()
}

func (wp *workerPool) Serve(c net.Conn) bool {
	ch := wp.getCh()
	if ch == nil {
		return false
	}
	ch.ch <- c
	return true
}

func (wp *workerPool) getCh() *workerChan {
	var ch *workerChan
	createWorker := false

	wp.lock.Lock()
	chans := wp.ready
	n := len(chans) - 1
	if n < 0 {
		if wp.workersCount < wp.MaxWorkersCount {
			createWorker = true
			wp.workersCount++
		}
	} else {
		ch = chans[n]
		wp.ready = chans[:n]
	}
	wp.lock.Unlock()

	if ch == nil {
		if !createWorker {
			return nil
		}
		vch := workerChanPool.Get()
		if vch == nil {
			vch = &workerChan{
				ch: make(chan net.Conn, 1),
			}
		}
		ch = vch.(*workerChan)
		go func() {
			wp.workerFunc(ch)
			workerChanPool.Put(vch)
		}()
	}
	return ch
}

func (wp *workerPool) release(ch *workerChan) bool {
	ch.t = time.Now()
	wp.lock.Lock()
	if wp.mustStop {
		wp.lock.Unlock()
		return false
	}
	wp.ready = append(wp.ready, ch)
	wp.lock.Unlock()
	return true
}

var workerChanPool sync.Pool

func (wp *workerPool) workerFunc(ch *workerChan) {
	var c net.Conn
	var err error

	defer func() {
		if r := recover(); r != nil {
			wp.Logger.Printf("panic: %s\nStack trace:\n%s", r, debug.Stack())
		}

		if c != nil {
			c.Close()
			wp.release(ch)
		}
	}()

	for c = range ch.ch {
		if c == nil {
			break
		}
		if err = wp.WorkerFunc(c); err != nil && err != errHijacked {
			errStr := err.Error()
			if !strings.Contains(errStr, "broken pipe") && !strings.Contains(errStr, "reset by peer") {
				wp.Logger.Printf("error when serving connection %q<->%q: %s", c.LocalAddr(), c.RemoteAddr(), err)
			}
		}
		if err != errHijacked {
			c.Close()
		}
		c = nil

		if !wp.release(ch) {
			break
		}
	}
}
<|endoftext|>


<commit_before>/*******************************************************************************
The MIT License (MIT)

Copyright (c) 2013-2014 Hajime Nakagami

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*******************************************************************************/

package firebirdsql

import (
	"database/sql"
	"testing"
	"time"
)

func TestBasic(t *testing.T) {
	conn, err := sql.Open("firebirdsql_createdb", "sysdba:masterkey@localhost:3050/tmp/go_test.fdb")
	defer conn.Close()

	if err != nil {
		t.Fatalf("Error connecting: %v", err)
	}
	var sql string
	var n int

	sql = "SELECT Count(*) FROM rdb$relations where rdb$relation_name='FOO'"
	err = conn.QueryRow(sql).Scan(&n)
	if err != nil {
		t.Fatalf("Error QueryRow: %v", err)
	}
	if n > 0 {
		conn.Exec("DROP TABLE foo")
	}

	sql = `
        CREATE TABLE foo (
            a INTEGER NOT NULL,
            b VARCHAR(30) NOT NULL UNIQUE,
            c VARCHAR(1024),
            d DECIMAL(16,3) DEFAULT -0.123,
            e DATE DEFAULT '1967-08-11',
            f TIMESTAMP DEFAULT '1967-08-11 23:45:01',
            g TIME DEFAULT '23:45:01',
            h BLOB SUB_TYPE 1,
            i DOUBLE PRECISION DEFAULT 0.0,
            j FLOAT DEFAULT 0.0,
            PRIMARY KEY (a),
            CONSTRAINT CHECK_A CHECK (a <> 0)
        )
    `
	conn.Exec(sql)
	_, err = conn.Exec("CREATE TABLE foo (a INTEGER)")
	if err == nil {
		t.Fatalf("Need metadata update error")
	}
	if err.Error() != "unsuccessful metadata update\nTable FOO already exists\n" {
		t.Fatalf("Bad message:%v", err.Error())
	}

	// 3 records insert
	conn.Exec("insert into foo(a, b, c,h) values (1, 'a', 'b','This is a memo')")
	conn.Exec("insert into foo(a, b, c, e, g, i, j) values (2, 'A', 'B', '1999-01-25', '00:00:01', 0.1, 0.1)")
	conn.Exec("insert into foo(a, b, c, e, g, i, j) values (3, 'X', 'Y', '2001-07-05', '00:01:02', 0.2, 0.2)")

	err = conn.QueryRow("select count(*) cnt from foo").Scan(&n)
	if err != nil {
		t.Fatalf("Error QueryRow: %v", err)
	}
	if n != 3 {
		t.Fatalf("Error bad record count: %v", n)
	}

	rows, err := conn.Query("select a, b, c, d, e, f, g, i, j from foo")
	var a int
	var b, c string
	var d float64
	var e time.Time
	var f time.Time
	var g time.Time
	var i float64
	var j float32

	for rows.Next() {
		rows.Scan(&a, &b, &c, &d, &e, &f, &g, &i, &j)
	}

	stmt, _ := conn.Prepare("select count(*) from foo where a=? and b=? and d=? and e=? and f=? and g=?")
	ep := time.Date(1967, 8, 11, 0, 0, 0, 0, time.UTC)
	fp := time.Date(1967, 8, 11, 23, 45, 1, 0, time.UTC)
	gp, err := time.Parse("15:04:05", "23:45:01")
	err = stmt.QueryRow(1, "a", -0.123, ep, fp, gp).Scan(&n)
	if err != nil {
		t.Fatalf("Error QueryRow: %v", err)
	}
	if n != 1 {
		t.Fatalf("Error bad record count: %v", n)
	}

}

func TestIssue2(t *testing.T) {
	conn, _ := sql.Open("firebirdsql_createdb", "sysdba:masterkey@localhost:3050/tmp/go_test.fdb")

	conn.Exec(`
        CREATE TABLE test_issue2
         (f1 integer NOT NULL,
          f2 integer,
          f3 integer NOT NULL,
          f4 integer NOT NULL,
          f5 integer NOT NULL,
          f6 integer NOT NULL,
          f7 varchar(255) NOT NULL,
          f8 varchar(255) NOT NULL,
          f9 varchar(255) NOT NULL,
          f10 varchar(255) NOT NULL,
          f11 varchar(255) NOT NULL,
          f12 varchar(255) NOT NULL,
          f13 varchar(255) NOT NULL,
          f14 varchar(255) NOT NULL,
          f15 integer,
          f16 integer,
          f17 integer,
          f18 integer,
          f19 integer,
          f20 integer,
          f21 integer,
          f22 varchar(1),
          f23 varchar(255),
          f24 integer,
          f25 varchar(64),
          f26 integer)`)
	defer conn.Close()

	conn.Exec(`
        INSERT INTO test_issue2 VALUES
        (1, 2, 3, 4, 5, 6, '7', '8', '9', '10', '11', '12', '13', '14',
          15, 16, 17, 18, 19, 20, 21, 'A', '23', 24, '25', '26')`)

	rows, err := conn.Query("SELECT * FROM test_issue2")
	if err != nil {
		t.Fatalf("Error Query: %v", err)
	}
	for rows.Next() {
	}
}

func TestError(t *testing.T) {
	conn, err := sql.Open("firebirdsql_createdb", "sysdba:masterkey@localhost:3050/tmp/go_test.fdb")
	if err != nil {
		t.Fatalf("Error connecting: %v", err)
	}
	_, err = conn.Exec("incorrect sql statement")
	if err == nil || err.Error() != "Dynamic SQL Error\nSQL error code = -104\nToken unknown - line 1, column 1\nincorrect\n" {
		t.Fatalf("Incorrect error")
	}
}

/*
func TestFB3(t *testing.T) {
	conn, err := sql.Open("firebirdsql_createdb", "sysdba:masterkey@localhost:3050/tmp/go_test.fdb")
	if err != nil {
		t.Fatalf("Error connecting: %v", err)
	}
	var sql string
	var n int

	sql = "SELECT Count(*) FROM rdb$relations where rdb$relation_name='TEST_FB3'"
	err = conn.QueryRow(sql).Scan(&n)
	if err != nil {
		t.Fatalf("Error QueryRow: %v", err)
	}
	if n > 0 {
		conn.Exec("DROP TABLE test_fb3")
	}

	sql = `
        CREATE TABLE test_fb3 (
            b BOOLEAN
        )
    `
	conn.Exec(sql)
	conn.Exec("insert into test_fb3(b) values (true)")
	conn.Exec("insert into test_fb3(b) values (false)")
    var b bool
	err = conn.QueryRow("select * from test_fb3 where b is true").Scan(&b)
	if err != nil {
		t.Fatalf("Error QueryRow: %v", err)
	}
	if b != true{
		conn.Exec("Invalid boolean value")
	}
	err = conn.QueryRow("select * from test_fb3 where b is false").Scan(&b)
	if err != nil {
		t.Fatalf("Error QueryRow: %v", err)
	}
	if b != false{
		conn.Exec("Invalid boolean value")
	}

	stmt, _ := conn.Prepare("select * from test_fb3 where b=?")
	err = stmt.QueryRow(true).Scan(&b)
	if err != nil {
		t.Fatalf("Error QueryRow: %v", err)
	}
	if b != false{
		conn.Exec("Invalid boolean value")
	}

	defer conn.Close()
}
*/
<commit_msg>add test for issue 3<commit_after>/*******************************************************************************
The MIT License (MIT)

Copyright (c) 2013-2014 Hajime Nakagami

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*******************************************************************************/

package firebirdsql

import (
	"database/sql"
	"testing"
	"time"
)

func TestBasic(t *testing.T) {
	conn, err := sql.Open("firebirdsql_createdb", "sysdba:masterkey@localhost:3050/tmp/go_test.fdb")
	defer conn.Close()

	if err != nil {
		t.Fatalf("Error connecting: %v", err)
	}
	var sql string
	var n int

	sql = "SELECT Count(*) FROM rdb$relations where rdb$relation_name='FOO'"
	err = conn.QueryRow(sql).Scan(&n)
	if err != nil {
		t.Fatalf("Error QueryRow: %v", err)
	}
	if n > 0 {
		conn.Exec("DROP TABLE foo")
	}

	sql = `
        CREATE TABLE foo (
            a INTEGER NOT NULL,
            b VARCHAR(30) NOT NULL UNIQUE,
            c VARCHAR(1024),
            d DECIMAL(16,3) DEFAULT -0.123,
            e DATE DEFAULT '1967-08-11',
            f TIMESTAMP DEFAULT '1967-08-11 23:45:01',
            g TIME DEFAULT '23:45:01',
            h BLOB SUB_TYPE 1,
            i DOUBLE PRECISION DEFAULT 0.0,
            j FLOAT DEFAULT 0.0,
            PRIMARY KEY (a),
            CONSTRAINT CHECK_A CHECK (a <> 0)
        )
    `
	conn.Exec(sql)
	_, err = conn.Exec("CREATE TABLE foo (a INTEGER)")
	if err == nil {
		t.Fatalf("Need metadata update error")
	}
	if err.Error() != "unsuccessful metadata update\nTable FOO already exists\n" {
		t.Fatalf("Bad message:%v", err.Error())
	}

	// 3 records insert
	conn.Exec("insert into foo(a, b, c,h) values (1, 'a', 'b','This is a memo')")
	conn.Exec("insert into foo(a, b, c, e, g, i, j) values (2, 'A', 'B', '1999-01-25', '00:00:01', 0.1, 0.1)")
	conn.Exec("insert into foo(a, b, c, e, g, i, j) values (3, 'X', 'Y', '2001-07-05', '00:01:02', 0.2, 0.2)")

	err = conn.QueryRow("select count(*) cnt from foo").Scan(&n)
	if err != nil {
		t.Fatalf("Error QueryRow: %v", err)
	}
	if n != 3 {
		t.Fatalf("Error bad record count: %v", n)
	}

	rows, err := conn.Query("select a, b, c, d, e, f, g, i, j from foo")
	var a int
	var b, c string
	var d float64
	var e time.Time
	var f time.Time
	var g time.Time
	var i float64
	var j float32

	for rows.Next() {
		rows.Scan(&a, &b, &c, &d, &e, &f, &g, &i, &j)
	}

	stmt, _ := conn.Prepare("select count(*) from foo where a=? and b=? and d=? and e=? and f=? and g=?")
	ep := time.Date(1967, 8, 11, 0, 0, 0, 0, time.UTC)
	fp := time.Date(1967, 8, 11, 23, 45, 1, 0, time.UTC)
	gp, err := time.Parse("15:04:05", "23:45:01")
	err = stmt.QueryRow(1, "a", -0.123, ep, fp, gp).Scan(&n)
	if err != nil {
		t.Fatalf("Error QueryRow: %v", err)
	}
	if n != 1 {
		t.Fatalf("Error bad record count: %v", n)
	}

}

func TestIssue2(t *testing.T) {
	conn, _ := sql.Open("firebirdsql_createdb", "sysdba:masterkey@localhost:3050/tmp/go_test.fdb")

	conn.Exec(`
        CREATE TABLE test_issue2
         (f1 integer NOT NULL,
          f2 integer,
          f3 integer NOT NULL,
          f4 integer NOT NULL,
          f5 integer NOT NULL,
          f6 integer NOT NULL,
          f7 varchar(255) NOT NULL,
          f8 varchar(255) NOT NULL,
          f9 varchar(255) NOT NULL,
          f10 varchar(255) NOT NULL,
          f11 varchar(255) NOT NULL,
          f12 varchar(255) NOT NULL,
          f13 varchar(255) NOT NULL,
          f14 varchar(255) NOT NULL,
          f15 integer,
          f16 integer,
          f17 integer,
          f18 integer,
          f19 integer,
          f20 integer,
          f21 integer,
          f22 varchar(1),
          f23 varchar(255),
          f24 integer,
          f25 varchar(64),
          f26 integer)`)
	defer conn.Close()

	conn.Exec(`
        INSERT INTO test_issue2 VALUES
        (1, 2, 3, 4, 5, 6, '7', '8', '9', '10', '11', '12', '13', '14',
          15, 16, 17, 18, 19, 20, 21, 'A', '23', 24, '25', '26')`)

	rows, err := conn.Query("SELECT * FROM test_issue2")
	if err != nil {
		t.Fatalf("Error Query: %v", err)
	}
	for rows.Next() {
	}
}

func TestIssue3(t *testing.T) {
	conn, _ := sql.Open("firebirdsql_createdb", "sysdba:masterkey@localhost:3050/tmp/go_test.fdb")
	too_many := 401

	conn.Exec("CREATE TABLE test_issue3 (f1 integer NOT NULL)")
	defer conn.Close()
	stmt, _ := conn.Prepare("INSERT INTO test_issue3 values (?)")
	for i := 1; i < too_many; i++ {
		stmt.Exec(i)
	}

	rows, _ := conn.Query("SELECT * FROM test_issue3")
	i := 0
	for rows.Next() {
		i++
	}
	if i != too_many {
		t.Fatalf("Can't get all %v records. only %v", too_many, i)
	}
}

func TestError(t *testing.T) {
	conn, err := sql.Open("firebirdsql_createdb", "sysdba:masterkey@localhost:3050/tmp/go_test.fdb")
	if err != nil {
		t.Fatalf("Error connecting: %v", err)
	}
	_, err = conn.Exec("incorrect sql statement")
	if err == nil || err.Error() != "Dynamic SQL Error\nSQL error code = -104\nToken unknown - line 1, column 1\nincorrect\n" {
		t.Fatalf("Incorrect error")
	}
}

/*
func TestFB3(t *testing.T) {
	conn, err := sql.Open("firebirdsql_createdb", "sysdba:masterkey@localhost:3050/tmp/go_test.fdb")
	if err != nil {
		t.Fatalf("Error connecting: %v", err)
	}
	var sql string
	var n int

	sql = "SELECT Count(*) FROM rdb$relations where rdb$relation_name='TEST_FB3'"
	err = conn.QueryRow(sql).Scan(&n)
	if err != nil {
		t.Fatalf("Error QueryRow: %v", err)
	}
	if n > 0 {
		conn.Exec("DROP TABLE test_fb3")
	}

	sql = `
        CREATE TABLE test_fb3 (
            b BOOLEAN
        )
    `
	conn.Exec(sql)
	conn.Exec("insert into test_fb3(b) values (true)")
	conn.Exec("insert into test_fb3(b) values (false)")
    var b bool
	err = conn.QueryRow("select * from test_fb3 where b is true").Scan(&b)
	if err != nil {
		t.Fatalf("Error QueryRow: %v", err)
	}
	if b != true{
		conn.Exec("Invalid boolean value")
	}
	err = conn.QueryRow("select * from test_fb3 where b is false").Scan(&b)
	if err != nil {
		t.Fatalf("Error QueryRow: %v", err)
	}
	if b != false{
		conn.Exec("Invalid boolean value")
	}

	stmt, _ := conn.Prepare("select * from test_fb3 where b=?")
	err = stmt.QueryRow(true).Scan(&b)
	if err != nil {
		t.Fatalf("Error QueryRow: %v", err)
	}
	if b != false{
		conn.Exec("Invalid boolean value")
	}

	defer conn.Close()
}
*/
<|endoftext|>