<commit_before>package image

// https://en.wikipedia.org/wiki/Truevision_TGA

// STATUS: 1%

import (
	"encoding/binary"
	"os"

	"github.com/martinlindhe/formats/parse"
)

// TGA parses the Targa image format
func TGA(c *parse.Checker) (*parse.ParsedLayout, error) {

	if !isTGA(c.Header) {
		return nil, nil
	}
	return parseTGA(c.File, c.ParsedLayout)
}

func isTGA(b []byte) bool {

	/* XXX
	   # at 2, byte ImgType must be 1, 2, 3, 9, 10 or 11
	   # at 1, byte CoMapType must be 1 if ImgType is 1 or 9, 0 otherwise
	   # at 3, leshort Index is 0 for povray, ppmtotga and xv outputs
	*/

	val := binary.LittleEndian.Uint32(b)
	chk := val & 0xfff7ffff

	if chk == 0x01010000 {
		// Targa image data - Map
		// >2	byte&8			8		- RLE
		// >12	leshort			>0		%hd x
		// >14	leshort			>0		%hd
		return true
	}
	if chk == 0x00020000 {
		// Targa image data - RGB
		// >2	byte&8			8		- RLE
		// >12	leshort			>0		%hd x
		// >14	leshort			>0		%hd
		return true
	}
	if chk == 0x00030000 {
		// Targa image data - Mono
		// >2	byte&8			8		- RLE
		// >12	leshort			>0		%hd x
		// >14	leshort			>0		%hd
		return true
	}
	return false
}

func parseTGA(file *os.File, pl parse.ParsedLayout) (*parse.ParsedLayout, error) {

	pos := int64(0)
	pl.FileKind = parse.Archive
	pl.Layout = []parse.Layout{{
		Offset: pos,
		Length: 4, // XXX
		Info:   "header",
		Type:   parse.Group,
		Childs: []parse.Layout{
			// XXX this is le-format only
			{Offset: pos, Length: 4, Info: "magic", Type: parse.Uint32be},
		}}}

	return &pl, nil
}
<commit_msg>tga: improve isTGA()<commit_after>package image

// STATUS: 1%
// https://en.wikipedia.org/wiki/Truevision_TGA

import (
	"encoding/binary"
	"os"

	"github.com/martinlindhe/formats/parse"
)

// TGA parses the Truevision Advanced Raster Graphics Adapter (TARGA) image format
func TGA(c *parse.Checker) (*parse.ParsedLayout, error) {

	if !isTGA(c.Header) {
		return nil, nil
	}
	return parseTGA(c.File, c.ParsedLayout)
}

func isTGA(b []byte) bool {

	// XXX at 3, leshort Index is 0 for povray, ppmtotga and xv outputs

	colorMapType := b[1]
	imgType := b[2]

	// colorMapType must be 1 if ImgType is 1 or 9, 0 otherwise
	if imgType == 1 || imgType == 9 {
		if colorMapType != 1 {
			return false
		}
	} else {
		if colorMapType != 0 {
			return false
		}
	}

	switch imgType {
	case 1, 2, 3, 9, 10, 11:
	default:
		return false
	}

	val := binary.LittleEndian.Uint32(b)
	chk := val & 0xfff7ffff

	if chk == 0x01010000 {
		// Targa image data - Map
		// >2	byte&8			8		- RLE
		// >12	leshort			>0		%hd x
		// >14	leshort			>0		%hd
		return true
	}
	if chk == 0x00020000 {
		// Targa image data - RGB
		// >2	byte&8			8		- RLE
		// >12	leshort			>0		%hd x
		// >14	leshort			>0		%hd
		return true
	}
	if chk == 0x00030000 {
		// Targa image data - Mono
		// >2	byte&8			8		- RLE
		// >12	leshort			>0		%hd x
		// >14	leshort			>0		%hd
		return true
	}
	return false
}

func parseTGA(file *os.File, pl parse.ParsedLayout) (*parse.ParsedLayout, error) {

	pos := int64(0)
	pl.FileKind = parse.Archive
	pl.Layout = []parse.Layout{{
		Offset: pos,
		Length: 4, // XXX
		Info:   "header",
		Type:   parse.Group,
		Childs: []parse.Layout{
			// XXX this is le-format only
			{Offset: pos, Length: 4, Info: "magic", Type: parse.Uint32be},
		}}}

	return &pl, nil
}
<|endoftext|>


<commit_before>package pbf

import (
	"bytes"
	"code.google.com/p/goprotobuf/proto"
	"compress/zlib"
	"fmt"
	"goposm/parser/pbf/osmpbf"
	"io"
	"log"
	"os"
	"testing"
)

func BenchmarkHello(b *testing.B) {
	b.StopTimer()
	pbf, err := open("../azores.osm.pbf")
	if err != nil {
		panic(err)
	}

	for pos := range pbf.BlockPositions() {
		fmt.Println(pos.size, pos.offset)
		b.StartTimer()
		for i := 0; i < b.N; i++ {
			readPrimitiveBlock(pos)
		}
		return
		// for {
		// 	stringtable := NewStringTable(block.GetStringtable())

		// 	for _, group := range block.Primitivegroup {
		// 		dense := group.GetDense()
		// 		ReadDenseNodes(dense, block, stringtable)
		// 	}
		// }
		// return
	}

}

func BenchmarkPrimitiveBlock(b *testing.B) {
	b.StopTimer()

	file, err := os.Open("../azores.osm.pbf")
	if err != nil {
		log.Panic(err)
	}
	defer file.Close()

	var block = &osmpbf.PrimitiveBlock{}
	var blob = &osmpbf.Blob{}

	var size = 56092
	var offset int64 = 197

	blobData := make([]byte, size)
	file.Seek(offset, 0)
	io.ReadFull(file, blobData)
	err = proto.Unmarshal(blobData, blob)
	if err != nil {
		log.Panic("unmarshaling error blob: ", err)
	}

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		buf := bytes.NewBuffer(blob.GetZlibData())
		r, err := zlib.NewReader(buf)
		if err != nil {
			log.Panic("zlib error: ", err)
		}
		raw := make([]byte, blob.GetRawSize())
		io.ReadFull(r, raw)
		err = proto.Unmarshal(raw, block)
		if err != nil {
			log.Panic("unmarshaling error: ", err)
		}
	}
}
<commit_msg>fixed pbf test<commit_after>package pbf

import (
	"bytes"
	"code.google.com/p/goprotobuf/proto"
	"compress/zlib"
	"fmt"
	"goposm/parser/pbf/osmpbf"
	"io"
	"log"
	"os"
	"testing"
)

func BenchmarkHello(b *testing.B) {
	b.StopTimer()
	pbf, err := Open("../azores.osm.pbf")
	if err != nil {
		panic(err)
	}

	for pos := range pbf.BlockPositions() {
		fmt.Println(pos.size, pos.offset)
		b.StartTimer()
		for i := 0; i < b.N; i++ {
			readPrimitiveBlock(pos)
		}
		return
		// for {
		// 	stringtable := NewStringTable(block.GetStringtable())

		// 	for _, group := range block.Primitivegroup {
		// 		dense := group.GetDense()
		// 		ReadDenseNodes(dense, block, stringtable)
		// 	}
		// }
		// return
	}

}

func BenchmarkPrimitiveBlock(b *testing.B) {
	b.StopTimer()

	file, err := os.Open("../azores.osm.pbf")
	if err != nil {
		log.Panic(err)
	}
	defer file.Close()

	var block = &osmpbf.PrimitiveBlock{}
	var blob = &osmpbf.Blob{}

	var size = 56092
	var offset int64 = 197

	blobData := make([]byte, size)
	file.Seek(offset, 0)
	io.ReadFull(file, blobData)
	err = proto.Unmarshal(blobData, blob)
	if err != nil {
		log.Panic("unmarshaling error blob: ", err)
	}

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		buf := bytes.NewBuffer(blob.GetZlibData())
		r, err := zlib.NewReader(buf)
		if err != nil {
			log.Panic("zlib error: ", err)
		}
		raw := make([]byte, blob.GetRawSize())
		io.ReadFull(r, raw)
		err = proto.Unmarshal(raw, block)
		if err != nil {
			log.Panic("unmarshaling error: ", err)
		}
	}
}
<|endoftext|>


<commit_before>package storm

import (
	"github.com/asdine/storm"
	sq "github.com/asdine/storm/q"
	"github.com/leeola/errors"
	kq "github.com/leeola/kala/q"
	"github.com/leeola/kala/q/operators"
)

func ConvertQuery(db storm.DB, kq kq.Query) (storm.Query, error) {
	var ms []sq.Matcher

	if !structs.IsZero(kq.Constraint) {
		m, err := ConvertConstraint(kq.Constraint)
		if err != nil {
			return nil, err
		}
		ms = append(ms, m)
	}

	// TODO(leeola): Storm may not accept an empty set of constraints, so we may need to
	// reject kala queries without constraints.
	stormQuery := storm.Select(ms...)

	if kq.SortBy != "" {
		stormQuery.OrderBy(kq.SortBy)
	}
	if kq.SortDescending {
		stormQuery.Reverse()
	}
	if kq.Skip != 0 {
		stormQuery.Skip(kq.Skip)
	}
	if kq.Limit != 0 {
		stormQuery.Limit(kq.Limit)
	}

	return stormQuery, nil
}

func ConvertConstraint(c kq.Constraint) (sq.Matcher, error) {
	switch c.Operator {
	case operators.Equal:
		return sq.Eq(c.Field, c.Value)

	case operators.GreaterThan:
		return sq.Gt(c.Field, c.Value)

	case operators.GreaterThanOrEqual:
		return sq.Gte(c.Field, c.Value)

	case operators.In:
		return sq.In(c.Field, c.Value)

	case operators.LessThan:
		return sq.Lt(c.Field, c.Value)

	case operators.LessThanOrEqual:
		return sq.Lte(c.Field, c.Value)

	case operators.Not:
		return sq.Not(c.Field, c.Value)

	case operators.Regex:
		return sq.Re(c.Field, c.Value)

	case operators.And, operators.Or:
		matchers := make([]sq.Matcher, len(c.Constraints))
		for i, subc := range c.Constraints {
			m, err := ConvertConstraint(subc)
			if err != nil {
				return err
			}
			matchers[i] = m
		}

		if c.Operator == operators.And {
			return sq.And(matchers...)
		} else {
			return sq.Or(matchers...)
		}
	default:
		return errors.Errorf("unsupported operator: %s", c.Operator)
	}
}
<commit_msg>feat: more prototyping fixes<commit_after>package storm

import (
	"github.com/asdine/storm"
	sq "github.com/asdine/storm/q"
	"github.com/fatih/structs"
	"github.com/leeola/errors"
	kq "github.com/leeola/kala/q"
	ops "github.com/leeola/kala/q/operators"
)

func ConvertQuery(db storm.DB, kq kq.Query) (storm.Query, error) {
	var ms []sq.Matcher

	if !structs.IsZero(kq.Constraint) {
		m, err := ConvertConstraint(kq.Constraint)
		if err != nil {
			return nil, err
		}
		ms = append(ms, m)
	}

	// TODO(leeola): Storm may not accept an empty set of constraints, so we may need to
	// reject kala queries without constraints.
	stormQuery := db.Select(ms...)

	if kq.SortBy != "" {
		stormQuery.OrderBy(kq.SortBy)
	}
	if kq.SortDescending {
		stormQuery.Reverse()
	}
	if kq.SkipBy != 0 {
		stormQuery.Skip(kq.SkipBy)
	}
	if kq.LimitBy != 0 {
		stormQuery.Limit(kq.LimitBy)
	}

	return stormQuery, nil
}

func ConvertConstraint(c kq.Constraint) (sq.Matcher, error) {
	switch c.Operator {
	case ops.Equal:
		return sq.Eq(c.Field, c.Value), nil

	case ops.GreaterThan:
		return sq.Gt(c.Field, c.Value), nil

	case ops.GreaterThanOrEqual:
		return sq.Gte(c.Field, c.Value), nil

	case ops.In:
		return sq.In(c.Field, c.Value), nil

	case ops.LessThan:
		return sq.Lt(c.Field, c.Value), nil

	case ops.LessThanOrEqual:
		return sq.Lte(c.Field, c.Value), nil

	case ops.Regex:
		v, ok := c.Value.(string)
		if !ok {
			return nil, errors.New("unexpected Regex field value")
		}

		return sq.Re(c.Field, v), nil

	case ops.And, ops.Or, ops.Not:
		matchers := make([]sq.Matcher, len(c.Constraints))
		for i, subc := range c.Constraints {
			m, err := ConvertConstraint(subc)
			if err != nil {
				return nil, err
			}
			matchers[i] = m
		}

		switch c.Operator {
		case ops.And:
			return sq.And(matchers...), nil
		case ops.Or:
			return sq.Or(matchers...), nil
		case ops.Not:
			return sq.Not(matchers...), nil
		default:
			// should never happen
			return nil, errors.Errorf(
				"unexpected operator inside And/Or/Not case: %s", c.Operator)
		}

	default:
		return nil, errors.Errorf("unsupported operator: %s", c.Operator)
	}
}
<|endoftext|>


<commit_before>package conf_test

import (
	"encoding/json"
	"os"
	"path/filepath"
	"testing"

	"github.com/golang/protobuf/proto"
	"v2ray.com/core/app/dns"
	"v2ray.com/core/common"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/platform"
	"v2ray.com/core/common/platform/filesystem"
	. "v2ray.com/core/infra/conf"
)

func init() {
	wd, err := os.Getwd()
	common.Must(err)

	common.Must(filesystem.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(wd, "..", "..", "release", "config", "geoip.dat")))
	common.Must(filesystem.CopyFile(platform.GetAssetLocation("geosite.dat"), filepath.Join(wd, "..", "..", "release", "config", "geosite.dat")))
}
func TestDnsConfigParsing(t *testing.T) {
	geositePath := platform.GetAssetLocation("geosite.dat")
	defer func() {
		os.Remove(geositePath)
	}()

	parserCreator := func() func(string) (proto.Message, error) {
		return func(s string) (proto.Message, error) {
			config := new(DnsConfig)
			if err := json.Unmarshal([]byte(s), config); err != nil {
				return nil, err
			}
			return config.Build()
		}
	}

	runMultiTestCase(t, []TestCase{
		{
			Input: `{
				"servers": [{
					"address": "8.8.8.8",
					"port": 5353,
					"domains": ["domain:v2ray.com"]
				}],
				"hosts": {
					"v2ray.com": "127.0.0.1",
					"geosite:tld-cn": "10.0.0.1",
					"domain:example.com": "google.com"
				},
				"clientIp": "10.0.0.1"
			}`,
			Parser: parserCreator(),
			Output: &dns.Config{
				NameServer: []*dns.NameServer{
					{
						Address: &net.Endpoint{
							Address: &net.IPOrDomain{
								Address: &net.IPOrDomain_Ip{
									Ip: []byte{8, 8, 8, 8},
								},
							},
							Network: net.Network_UDP,
							Port:    5353,
						},
						PrioritizedDomain: []*dns.NameServer_PriorityDomain{
							{
								Type:   dns.DomainMatchingType_Subdomain,
								Domain: "v2ray.com",
							},
						},
					},
				},
				StaticHosts: []*dns.Config_HostMapping{
					{
						Type:          dns.DomainMatchingType_Subdomain,
						Domain:        "example.com",
						ProxiedDomain: "google.com",
					},
					{
						Type:   dns.DomainMatchingType_Subdomain,
						Domain: "cn",
						Ip:     [][]byte{{10, 0, 0, 1}},
					},
					{
						Type:   dns.DomainMatchingType_Subdomain,
						Domain: "xn--fiqs8s",
						Ip:     [][]byte{{10, 0, 0, 1}},
					},
					{
						Type:   dns.DomainMatchingType_Full,
						Domain: "v2ray.com",
						Ip:     [][]byte{{127, 0, 0, 1}},
					},
				},
				ClientIp: []byte{10, 0, 0, 1},
			},
		},
	})
}
<commit_msg>remove geosite test case due to inconsistant data. fixes #1557<commit_after>package conf_test

import (
	"encoding/json"
	"os"
	"path/filepath"
	"testing"

	"github.com/golang/protobuf/proto"
	"v2ray.com/core/app/dns"
	"v2ray.com/core/common"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/platform"
	"v2ray.com/core/common/platform/filesystem"
	. "v2ray.com/core/infra/conf"
)

func init() {
	wd, err := os.Getwd()
	common.Must(err)

	common.Must(filesystem.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(wd, "..", "..", "release", "config", "geoip.dat")))
	common.Must(filesystem.CopyFile(platform.GetAssetLocation("geosite.dat"), filepath.Join(wd, "..", "..", "release", "config", "geosite.dat")))
}
func TestDnsConfigParsing(t *testing.T) {
	geositePath := platform.GetAssetLocation("geosite.dat")
	defer func() {
		os.Remove(geositePath)
	}()

	parserCreator := func() func(string) (proto.Message, error) {
		return func(s string) (proto.Message, error) {
			config := new(DnsConfig)
			if err := json.Unmarshal([]byte(s), config); err != nil {
				return nil, err
			}
			return config.Build()
		}
	}

	runMultiTestCase(t, []TestCase{
		{
			Input: `{
				"servers": [{
					"address": "8.8.8.8",
					"port": 5353,
					"domains": ["domain:v2ray.com"]
				}],
				"hosts": {
					"v2ray.com": "127.0.0.1",
					"domain:example.com": "google.com"
				},
				"clientIp": "10.0.0.1"
			}`,
			Parser: parserCreator(),
			Output: &dns.Config{
				NameServer: []*dns.NameServer{
					{
						Address: &net.Endpoint{
							Address: &net.IPOrDomain{
								Address: &net.IPOrDomain_Ip{
									Ip: []byte{8, 8, 8, 8},
								},
							},
							Network: net.Network_UDP,
							Port:    5353,
						},
						PrioritizedDomain: []*dns.NameServer_PriorityDomain{
							{
								Type:   dns.DomainMatchingType_Subdomain,
								Domain: "v2ray.com",
							},
						},
					},
				},
				StaticHosts: []*dns.Config_HostMapping{
					{
						Type:          dns.DomainMatchingType_Subdomain,
						Domain:        "example.com",
						ProxiedDomain: "google.com",
					},
					{
						Type:   dns.DomainMatchingType_Full,
						Domain: "v2ray.com",
						Ip:     [][]byte{{127, 0, 0, 1}},
					},
				},
				ClientIp: []byte{10, 0, 0, 1},
			},
		},
	})
}
<|endoftext|>


<commit_before>// Copyright 2016 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// Package syntax 提供对代码块的语法进行解析
package syntax

import (
	"log"

	"github.com/caixw/apidoc/locale"
	"github.com/caixw/apidoc/types"
	"github.com/caixw/apidoc/vars"

	"github.com/issue9/is"
)

// Input 输入的数据
type Input struct {
	File  string
	Line  int
	Data  []rune
	Error *log.Logger
	Warn  *log.Logger
}

// Parse 分析一段代码，并将结果保存到 d 中。
func Parse(d *types.Doc, input *Input) {
	l := newLexer(input.Data)

	for {
		switch {
		case l.matchTag(vars.APIDoc):
			if err := l.scanAPIDoc(d); err != nil {
				err.File = input.File
				err.Line += input.Line
				input.Error.Println(err)
			}
			return
		case l.matchTag(vars.API):
			if err := l.scanAPI(d); err != nil {
				err.File = input.File
				err.Line += input.Line
				input.Error.Println(err)
			}
			return
		case l.match(vars.API):
			l.backup()
			input.Warn.Println(locale.Sprintf(locale.ErrUnknownTag, l.readWord()))
		default:
			if l.atEOF() {
				return
			}
			l.pos++ // 去掉无用的字符。
		}
	} // end for
}

// 解析 @apidoc 及其子标签
//
// @apidoc title of doc
// @apiVersion 2.0
// @apiBaseURL https://api.caixw.io
// @apiLicense MIT https://opensource.org/licenses/MIT
//
// @apiContent
// content1
// content2
func (l *lexer) scanAPIDoc(d *types.Doc) *types.SyntaxError {
	if len(d.Title) > 0 || len(d.Version) > 0 {
		return l.syntaxError(locale.ErrDuplicateTag, vars.APIDoc)
	}

	t := l.readTag()
	d.Title = t.readLine()
	if len(d.Title) == 0 {
		return l.syntaxError(locale.ErrTagArgNotEnough, vars.APIDoc)
	}
	if !t.atEOF() {
		return l.syntaxError(locale.ErrTagArgTooMuch, vars.APIDoc)
	}

	for {
		switch {
		case l.matchTag(vars.APIVersion):
			t := l.readTag()
			d.Version = t.readLine()
			if len(d.Version) == 0 {
				return t.syntaxError(locale.ErrTagArgNotEnough, vars.APIVersion)
			}
			if !t.atEOF() {
				return t.syntaxError(locale.ErrTagArgTooMuch, vars.APIVersion)
			}
		case l.matchTag(vars.APIBaseURL):
			t := l.readTag()
			d.BaseURL = t.readLine()
			if len(d.BaseURL) == 0 {
				return t.syntaxError(locale.ErrTagArgNotEnough, vars.APIBaseURL)
			}
			if !t.atEOF() {
				return t.syntaxError(locale.ErrTagArgTooMuch, vars.APIBaseURL)
			}
		case l.matchTag(vars.APILicense):
			t := l.readTag()
			d.LicenseName = t.readWord()
			d.LicenseURL = t.readLine()
			if len(d.LicenseName) == 0 {
				return t.syntaxError(locale.ErrTagArgNotEnough, vars.APILicense)
			}
			if len(d.LicenseURL) > 0 && !is.URL(d.LicenseURL) {
				return t.syntaxError(locale.ErrSecondArgMustURL)
			}
			if !t.atEOF() {
				return t.syntaxError(locale.ErrTagArgTooMuch, vars.APILicense)
			}
		case l.matchTag(vars.APIContent):
			d.Content = string(l.data[l.pos:])
		case l.match(vars.API): // 不认识的标签
			l.backup()
			return l.syntaxError(locale.ErrUnknownTag, l.readWord())
		default:
			if l.atEOF() {
				return nil
			}
			l.pos++ // 去掉无用的字符。
		}
	} // end for
}

// 解析 @api 及其子标签
func (l *lexer) scanAPI(d *types.Doc) (err *types.SyntaxError) {
	api := &types.API{}
	t := l.readTag()
	api.Method = t.readWord()
	api.URL = t.readWord()
	api.Summary = t.readLine()

	if len(api.Method) == 0 || len(api.URL) == 0 || len(api.Summary) == 0 {
		return t.syntaxError(locale.ErrTagArgNotEnough, vars.API)
	}

	api.Description = t.readEnd()
LOOP:
	for {
		switch {
		case l.matchTag(vars.APIIgnore):
			return nil
		case l.matchTag(vars.APIGroup):
			err = l.scanGroup(api)
		case l.matchTag(vars.APIQuery):
			err = l.scanAPIQueries(api)
		case l.matchTag(vars.APIParam):
			err = l.scanAPIParams(api)
		case l.matchTag(vars.APIRequest):
			err = l.scanAPIRequest(api)
		case l.matchTag(vars.APIError):
			api.Error, err = l.scanResponse(vars.APIError)
		case l.matchTag(vars.APISuccess):
			api.Success, err = l.scanResponse(vars.APISuccess)
		case l.match(vars.API): // 不认识的标签
			l.backup()
			return l.syntaxError(locale.ErrUnknownTag, l.readWord())
		default:
			if l.atEOF() {
				break LOOP
			}
			l.pos++ // 去掉无用的字符。
		}

		if err != nil {
			return err
		}
	}

	if api.Success == nil {
		return &types.SyntaxError{Message: locale.ErrSuccessNotEmpty}
	}

	if len(api.Group) == 0 {
		api.Group = vars.DefaultGroupName
	}

	d.NewAPI(api)
	return nil
}

func (l *lexer) scanGroup(api *types.API) *types.SyntaxError {
	t := l.readTag()

	api.Group = t.readWord()
	if len(api.Group) == 0 {
		return t.syntaxError(locale.ErrTagArgNotEnough, vars.APIGroup)
	}

	if !t.atEOF() {
		t.syntaxError(locale.ErrTagArgTooMuch, vars.APIGroup)
	}

	return nil
}

func (l *lexer) scanAPIQueries(api *types.API) *types.SyntaxError {
	if api.Queries == nil {
		api.Queries = make([]*types.Param, 0, 1)
	}

	p, err := l.scanAPIParam(vars.APIQuery)
	if err != nil {
		return err
	}
	api.Queries = append(api.Queries, p)
	return nil
}

func (l *lexer) scanAPIParams(api *types.API) *types.SyntaxError {
	if api.Params == nil {
		api.Params = make([]*types.Param, 0, 1)
	}

	p, err := l.scanAPIParam(vars.APIParam)
	if err != nil {
		return err
	}
	api.Params = append(api.Params, p)
	return nil
}

// 解析 @apiRequest 及其子标签
func (l *lexer) scanAPIRequest(api *types.API) *types.SyntaxError {
	t := l.readTag()
	r := &types.Request{
		Type:     t.readLine(),
		Headers:  map[string]string{},
		Params:   []*types.Param{},
		Examples: []*types.Example{},
	}
	if !t.atEOF() {
		return t.syntaxError(locale.ErrTagArgTooMuch, vars.APIRequest)
	}

LOOP:
	for {
		switch {
		case l.matchTag(vars.APIHeader):
			t := l.readTag()
			key := t.readWord()
			val := t.readLine()
			if len(key) == 0 || len(val) == 0 {
				return t.syntaxError(locale.ErrTagArgNotEnough, vars.APIHeader)
			}
			if !t.atEOF() {
				return t.syntaxError(locale.ErrTagArgTooMuch, vars.APIHeader)
			}
			r.Headers[string(key)] = string(val)
		case l.matchTag(vars.APIParam):
			p, err := l.scanAPIParam(vars.APIParam)
			if err != nil {
				return err
			}
			r.Params = append(r.Params, p)
		case l.matchTag(vars.APIExample):
			e, err := l.scanAPIExample()
			if err != nil {
				return err
			}
			r.Examples = append(r.Examples, e)
		case l.match(vars.API): // 其它 api*，退出。
			l.backup()
			break LOOP
		default:
			if l.atEOF() {
				break LOOP
			}
			l.pos++ // 去掉无用的字符。

		} // end switch
	} // end for

	api.Request = r
	return nil
}

// 解析 @apiSuccess 或是 @apiError 及其子标签。
func (l *lexer) scanResponse(tagName string) (*types.Response, *types.SyntaxError) {
	tag := l.readTag()
	resp := &types.Response{
		Code:     tag.readWord(),
		Summary:  tag.readLine(),
		Headers:  map[string]string{},
		Params:   []*types.Param{},
		Examples: []*types.Example{},
	}

	if len(resp.Code) == 0 || len(resp.Summary) == 0 {
		return nil, tag.syntaxError(locale.ErrTagArgNotEnough, tagName)
	}
	if !tag.atEOF() {
		return nil, tag.syntaxError(locale.ErrTagArgTooMuch, tagName)
	}

LOOP:
	for {
		switch {
		case l.matchTag(vars.APIHeader):
			t := l.readTag()
			key := t.readWord()
			val := t.readLine()
			if len(key) == 0 || len(val) == 0 {
				return nil, t.syntaxError(locale.ErrTagArgNotEnough, vars.APIHeader)
			}
			if !t.atEOF() {
				return nil, t.syntaxError(locale.ErrTagArgTooMuch, vars.APIHeader)
			}
			resp.Headers[key] = val
		case l.matchTag(vars.APIParam):
			p, err := l.scanAPIParam(vars.APIParam)
			if err != nil {
				return nil, err
			}
			resp.Params = append(resp.Params, p)
		case l.matchTag(vars.APIExample):
			e, err := l.scanAPIExample()
			if err != nil {
				return nil, err
			}
			resp.Examples = append(resp.Examples, e)
		case l.match(vars.API): // 其它 api*，退出。
			l.backup()
			break LOOP
		default:
			if l.atEOF() {
				break LOOP
			}
			l.pos++ // 去掉无用的字符。
		}
	}

	return resp, nil
}

// 解析 @apiExample 标签
func (l *lexer) scanAPIExample() (*types.Example, *types.SyntaxError) {
	tag := l.readTag()
	example := &types.Example{
		Type: tag.readWord(),
		Code: tag.readEnd(),
	}

	if len(example.Type) == 0 || len(example.Code) == 0 {
		return nil, tag.syntaxError(locale.ErrTagArgNotEnough, vars.APIExample)
	}

	return example, nil
}

// 解析 @apiParam 标签
func (l *lexer) scanAPIParam(tagName string) (*types.Param, *types.SyntaxError) {
	p := &types.Param{}

	tag := l.readTag()
	p.Name = tag.readWord()
	p.Type = tag.readWord()
	p.Summary = tag.readEnd()
	if len(p.Name) == 0 || len(p.Type) == 0 || len(p.Summary) == 0 {
		return nil, tag.syntaxError(locale.ErrTagArgNotEnough, tagName)
	}
	return p, nil
}
<commit_msg>碰到未知标签，将其发送到警告通道<commit_after>// Copyright 2016 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// Package syntax 提供对代码块的语法进行解析
package syntax

import (
	"log"

	"github.com/caixw/apidoc/locale"
	"github.com/caixw/apidoc/types"
	"github.com/caixw/apidoc/vars"

	"github.com/issue9/is"
)

// Input 输入的数据
type Input struct {
	File  string
	Line  int
	Data  []rune
	Error *log.Logger
	Warn  *log.Logger
}

// Parse 分析一段代码，并将结果保存到 d 中。
func Parse(d *types.Doc, input *Input) {
	l := newLexer(input.Data)

	for {
		switch {
		case l.matchTag(vars.APIDoc):
			if err := l.scanAPIDoc(d); err != nil {
				err.File = input.File
				err.Line += input.Line
				input.Error.Println(err)
			}
			return
		case l.matchTag(vars.API):
			if err := l.scanAPI(d); err != nil {
				err.File = input.File
				err.Line += input.Line
				input.Error.Println(err)
			}
			return
		case l.match(vars.API):
			l.backup()
			input.Warn.Println(locale.Sprintf(locale.ErrUnknownTag, l.readWord()))
			l.readTag() // 指针移到下一个标签处
		default:
			if l.atEOF() {
				return
			}
			l.pos++ // 去掉无用的字符。
		}
	} // end for
}

// 解析 @apidoc 及其子标签
//
// @apidoc title of doc
// @apiVersion 2.0
// @apiBaseURL https://api.caixw.io
// @apiLicense MIT https://opensource.org/licenses/MIT
//
// @apiContent
// content1
// content2
func (l *lexer) scanAPIDoc(d *types.Doc) *types.SyntaxError {
	if len(d.Title) > 0 || len(d.Version) > 0 {
		return l.syntaxError(locale.ErrDuplicateTag, vars.APIDoc)
	}

	t := l.readTag()
	d.Title = t.readLine()
	if len(d.Title) == 0 {
		return l.syntaxError(locale.ErrTagArgNotEnough, vars.APIDoc)
	}
	if !t.atEOF() {
		return l.syntaxError(locale.ErrTagArgTooMuch, vars.APIDoc)
	}

	for {
		switch {
		case l.matchTag(vars.APIVersion):
			t := l.readTag()
			d.Version = t.readLine()
			if len(d.Version) == 0 {
				return t.syntaxError(locale.ErrTagArgNotEnough, vars.APIVersion)
			}
			if !t.atEOF() {
				return t.syntaxError(locale.ErrTagArgTooMuch, vars.APIVersion)
			}
		case l.matchTag(vars.APIBaseURL):
			t := l.readTag()
			d.BaseURL = t.readLine()
			if len(d.BaseURL) == 0 {
				return t.syntaxError(locale.ErrTagArgNotEnough, vars.APIBaseURL)
			}
			if !t.atEOF() {
				return t.syntaxError(locale.ErrTagArgTooMuch, vars.APIBaseURL)
			}
		case l.matchTag(vars.APILicense):
			t := l.readTag()
			d.LicenseName = t.readWord()
			d.LicenseURL = t.readLine()
			if len(d.LicenseName) == 0 {
				return t.syntaxError(locale.ErrTagArgNotEnough, vars.APILicense)
			}
			if len(d.LicenseURL) > 0 && !is.URL(d.LicenseURL) {
				return t.syntaxError(locale.ErrSecondArgMustURL)
			}
			if !t.atEOF() {
				return t.syntaxError(locale.ErrTagArgTooMuch, vars.APILicense)
			}
		case l.matchTag(vars.APIContent):
			d.Content = string(l.data[l.pos:])
		case l.match(vars.API): // 不认识的标签
			l.backup()
			return l.syntaxError(locale.ErrUnknownTag, l.readWord())
		default:
			if l.atEOF() {
				return nil
			}
			l.pos++ // 去掉无用的字符。
		}
	} // end for
}

// 解析 @api 及其子标签
func (l *lexer) scanAPI(d *types.Doc) (err *types.SyntaxError) {
	api := &types.API{}
	t := l.readTag()
	api.Method = t.readWord()
	api.URL = t.readWord()
	api.Summary = t.readLine()

	if len(api.Method) == 0 || len(api.URL) == 0 || len(api.Summary) == 0 {
		return t.syntaxError(locale.ErrTagArgNotEnough, vars.API)
	}

	api.Description = t.readEnd()
LOOP:
	for {
		switch {
		case l.matchTag(vars.APIIgnore):
			return nil
		case l.matchTag(vars.APIGroup):
			err = l.scanGroup(api)
		case l.matchTag(vars.APIQuery):
			err = l.scanAPIQueries(api)
		case l.matchTag(vars.APIParam):
			err = l.scanAPIParams(api)
		case l.matchTag(vars.APIRequest):
			err = l.scanAPIRequest(api)
		case l.matchTag(vars.APIError):
			api.Error, err = l.scanResponse(vars.APIError)
		case l.matchTag(vars.APISuccess):
			api.Success, err = l.scanResponse(vars.APISuccess)
		case l.match(vars.API): // 不认识的标签
			l.backup()
			return l.syntaxError(locale.ErrUnknownTag, l.readWord())
		default:
			if l.atEOF() {
				break LOOP
			}
			l.pos++ // 去掉无用的字符。
		}

		if err != nil {
			return err
		}
	}

	if api.Success == nil {
		return &types.SyntaxError{Message: locale.ErrSuccessNotEmpty}
	}

	if len(api.Group) == 0 {
		api.Group = vars.DefaultGroupName
	}

	d.NewAPI(api)
	return nil
}

func (l *lexer) scanGroup(api *types.API) *types.SyntaxError {
	t := l.readTag()

	api.Group = t.readWord()
	if len(api.Group) == 0 {
		return t.syntaxError(locale.ErrTagArgNotEnough, vars.APIGroup)
	}

	if !t.atEOF() {
		t.syntaxError(locale.ErrTagArgTooMuch, vars.APIGroup)
	}

	return nil
}

func (l *lexer) scanAPIQueries(api *types.API) *types.SyntaxError {
	if api.Queries == nil {
		api.Queries = make([]*types.Param, 0, 1)
	}

	p, err := l.scanAPIParam(vars.APIQuery)
	if err != nil {
		return err
	}
	api.Queries = append(api.Queries, p)
	return nil
}

func (l *lexer) scanAPIParams(api *types.API) *types.SyntaxError {
	if api.Params == nil {
		api.Params = make([]*types.Param, 0, 1)
	}

	p, err := l.scanAPIParam(vars.APIParam)
	if err != nil {
		return err
	}
	api.Params = append(api.Params, p)
	return nil
}

// 解析 @apiRequest 及其子标签
func (l *lexer) scanAPIRequest(api *types.API) *types.SyntaxError {
	t := l.readTag()
	r := &types.Request{
		Type:     t.readLine(),
		Headers:  map[string]string{},
		Params:   []*types.Param{},
		Examples: []*types.Example{},
	}
	if !t.atEOF() {
		return t.syntaxError(locale.ErrTagArgTooMuch, vars.APIRequest)
	}

LOOP:
	for {
		switch {
		case l.matchTag(vars.APIHeader):
			t := l.readTag()
			key := t.readWord()
			val := t.readLine()
			if len(key) == 0 || len(val) == 0 {
				return t.syntaxError(locale.ErrTagArgNotEnough, vars.APIHeader)
			}
			if !t.atEOF() {
				return t.syntaxError(locale.ErrTagArgTooMuch, vars.APIHeader)
			}
			r.Headers[string(key)] = string(val)
		case l.matchTag(vars.APIParam):
			p, err := l.scanAPIParam(vars.APIParam)
			if err != nil {
				return err
			}
			r.Params = append(r.Params, p)
		case l.matchTag(vars.APIExample):
			e, err := l.scanAPIExample()
			if err != nil {
				return err
			}
			r.Examples = append(r.Examples, e)
		case l.match(vars.API): // 其它 api*，退出。
			l.backup()
			break LOOP
		default:
			if l.atEOF() {
				break LOOP
			}
			l.pos++ // 去掉无用的字符。

		} // end switch
	} // end for

	api.Request = r
	return nil
}

// 解析 @apiSuccess 或是 @apiError 及其子标签。
func (l *lexer) scanResponse(tagName string) (*types.Response, *types.SyntaxError) {
	tag := l.readTag()
	resp := &types.Response{
		Code:     tag.readWord(),
		Summary:  tag.readLine(),
		Headers:  map[string]string{},
		Params:   []*types.Param{},
		Examples: []*types.Example{},
	}

	if len(resp.Code) == 0 || len(resp.Summary) == 0 {
		return nil, tag.syntaxError(locale.ErrTagArgNotEnough, tagName)
	}
	if !tag.atEOF() {
		return nil, tag.syntaxError(locale.ErrTagArgTooMuch, tagName)
	}

LOOP:
	for {
		switch {
		case l.matchTag(vars.APIHeader):
			t := l.readTag()
			key := t.readWord()
			val := t.readLine()
			if len(key) == 0 || len(val) == 0 {
				return nil, t.syntaxError(locale.ErrTagArgNotEnough, vars.APIHeader)
			}
			if !t.atEOF() {
				return nil, t.syntaxError(locale.ErrTagArgTooMuch, vars.APIHeader)
			}
			resp.Headers[key] = val
		case l.matchTag(vars.APIParam):
			p, err := l.scanAPIParam(vars.APIParam)
			if err != nil {
				return nil, err
			}
			resp.Params = append(resp.Params, p)
		case l.matchTag(vars.APIExample):
			e, err := l.scanAPIExample()
			if err != nil {
				return nil, err
			}
			resp.Examples = append(resp.Examples, e)
		case l.match(vars.API): // 其它 api*，退出。
			l.backup()
			break LOOP
		default:
			if l.atEOF() {
				break LOOP
			}
			l.pos++ // 去掉无用的字符。
		}
	}

	return resp, nil
}

// 解析 @apiExample 标签
func (l *lexer) scanAPIExample() (*types.Example, *types.SyntaxError) {
	tag := l.readTag()
	example := &types.Example{
		Type: tag.readWord(),
		Code: tag.readEnd(),
	}

	if len(example.Type) == 0 || len(example.Code) == 0 {
		return nil, tag.syntaxError(locale.ErrTagArgNotEnough, vars.APIExample)
	}

	return example, nil
}

// 解析 @apiParam 标签
func (l *lexer) scanAPIParam(tagName string) (*types.Param, *types.SyntaxError) {
	p := &types.Param{}

	tag := l.readTag()
	p.Name = tag.readWord()
	p.Type = tag.readWord()
	p.Summary = tag.readEnd()
	if len(p.Name) == 0 || len(p.Type) == 0 || len(p.Summary) == 0 {
		return nil, tag.syntaxError(locale.ErrTagArgNotEnough, tagName)
	}
	return p, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"
	"net/url"
	"os"
	"strings"
	"syscall"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/i18n"
)

// Lists
const (
	listFormatCSV   = "csv"
	listFormatJSON  = "json"
	listFormatTable = "table"
	listFormatYAML  = "yaml"
)

// Progress tracking
type ProgressRenderer struct {
	Format string

	maxLength int
}

func (p *ProgressRenderer) Done(msg string) {
	if msg != "" {
		msg += "\n"
	}

	if len(msg) > p.maxLength {
		p.maxLength = len(msg)
	} else {
		fmt.Printf("\r%s", strings.Repeat(" ", p.maxLength))
	}

	fmt.Print("\r")
	fmt.Print(msg)
}

func (p *ProgressRenderer) Update(status string) {
	msg := "%s"
	if p.Format != "" {
		msg = p.Format
	}

	msg = fmt.Sprintf("\r"+msg, status)

	if len(msg) > p.maxLength {
		p.maxLength = len(msg)
	} else {
		fmt.Printf("\r%s", strings.Repeat(" ", p.maxLength))
	}

	fmt.Print(msg)
}

func (p *ProgressRenderer) UpdateProgress(progress lxd.ProgressData) {
	p.Update(progress.Text)
}

func (p *ProgressRenderer) UpdateOp(op api.Operation) {
	if op.Metadata == nil {
		return
	}

	for _, key := range []string{"fs_progress", "download_progress"} {
		value, ok := op.Metadata[key]
		if ok {
			p.Update(value.(string))
			break
		}
	}
}

type StringList [][]string

func (a StringList) Len() int {
	return len(a)
}

func (a StringList) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a StringList) Less(i, j int) bool {
	x := 0
	for x = range a[i] {
		if a[i][x] != a[j][x] {
			break
		}
	}

	if a[i][x] == "" {
		return false
	}

	if a[j][x] == "" {
		return true
	}

	return a[i][x] < a[j][x]
}

// Container name sorting
type byName [][]string

func (a byName) Len() int {
	return len(a)
}

func (a byName) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a byName) Less(i, j int) bool {
	if a[i][0] == "" {
		return false
	}

	if a[j][0] == "" {
		return true
	}

	return a[i][0] < a[j][0]
}

// Storage volume sorting
type byNameAndType [][]string

func (a byNameAndType) Len() int {
	return len(a)
}

func (a byNameAndType) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a byNameAndType) Less(i, j int) bool {
	if a[i][0] != a[j][0] {
		return a[i][0] < a[j][0]
	}

	if a[i][1] == "" {
		return false
	}

	if a[j][1] == "" {
		return true
	}

	return a[i][1] < a[j][1]
}

// Batch operations
type batchResult struct {
	err  error
	name string
}

func runBatch(names []string, action func(name string) error) []batchResult {
	chResult := make(chan batchResult, len(names))

	for _, name := range names {
		go func(name string) {
			chResult <- batchResult{action(name), name}
		}(name)
	}

	results := []batchResult{}
	for range names {
		results = append(results, <-chResult)
	}

	return results
}

// summaryLine returns the first line of the help text. Conventionally, this
// should be a one-line command summary, potentially followed by a longer
// explanation.
func summaryLine(usage string) string {
	for _, line := range strings.Split(usage, "\n") {
		if strings.HasPrefix(line, "Usage:") {
			continue
		}

		if len(line) == 0 {
			continue
		}

		return strings.TrimSuffix(line, ".")
	}

	return i18n.G("Missing summary.")
}

// Used to return a user friendly error
func getLocalErr(err error) error {
	t, ok := err.(*url.Error)
	if !ok {
		return nil
	}

	u, ok := t.Err.(*net.OpError)
	if !ok {
		return nil
	}

	if u.Op == "dial" && u.Net == "unix" {
		var lxdErr error

		sysErr, ok := u.Err.(*os.SyscallError)
		if ok {
			lxdErr = sysErr.Err
		} else {
			// syscall.Errno may be returned on some systems, e.g. CentOS
			lxdErr, ok = u.Err.(syscall.Errno)
			if !ok {
				return nil
			}
		}

		switch lxdErr {
		case syscall.ENOENT, syscall.ECONNREFUSED, syscall.EACCES:
			return lxdErr
		}
	}

	return nil
}

// Add a device to a container
func containerDeviceAdd(client lxd.ContainerServer, name string, devName string, dev map[string]string) error {
	// Get the container entry
	container, etag, err := client.GetContainer(name)
	if err != nil {
		return err
	}

	// Check if the device already exists
	_, ok := container.Devices[devName]
	if ok {
		return fmt.Errorf(i18n.G("Device already exists: %s"), devName)
	}

	container.Devices[devName] = dev

	op, err := client.UpdateContainer(name, container.Writable(), etag)
	if err != nil {
		return err
	}

	return op.Wait()
}

// Add a device to a profile
func profileDeviceAdd(client lxd.ContainerServer, name string, devName string, dev map[string]string) error {
	// Get the profile entry
	profile, profileEtag, err := client.GetProfile(name)
	if err != nil {
		return err
	}

	// Check if the device already exists
	_, ok := profile.Devices[devName]
	if ok {
		return fmt.Errorf(i18n.G("Device already exists: %s"), devName)
	}

	// Add the device to the container
	profile.Devices[devName] = dev

	err = client.UpdateProfile(name, profile.Writable(), profileEtag)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>lxc: Add plumbing for operation cancelation<commit_after>package main

import (
	"fmt"
	"net"
	"net/url"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/i18n"
)

// Lists
const (
	listFormatCSV   = "csv"
	listFormatJSON  = "json"
	listFormatTable = "table"
	listFormatYAML  = "yaml"
)

// Progress tracking
type ProgressRenderer struct {
	Format string

	maxLength int
	wait      time.Time
}

func (p *ProgressRenderer) Done(msg string) {
	if msg != "" {
		msg += "\n"
	}

	if len(msg) > p.maxLength {
		p.maxLength = len(msg)
	} else {
		fmt.Printf("\r%s", strings.Repeat(" ", p.maxLength))
	}

	fmt.Print("\r")
	fmt.Print(msg)
}

func (p *ProgressRenderer) Update(status string) {
	timeout := p.wait.Sub(time.Now())
	if timeout.Seconds() > 0 {
		time.Sleep(timeout)
	}

	msg := "%s"
	if p.Format != "" {
		msg = p.Format
	}

	msg = fmt.Sprintf("\r"+msg, status)

	if len(msg) > p.maxLength {
		p.maxLength = len(msg)
	} else {
		fmt.Printf("\r%s", strings.Repeat(" ", p.maxLength))
	}

	fmt.Print(msg)
}

func (p *ProgressRenderer) Warn(status string, timeout time.Duration) {
	p.wait = time.Now().Add(timeout)
	msg := fmt.Sprintf("\r%s", status)

	if len(msg) > p.maxLength {
		p.maxLength = len(msg)
	} else {
		fmt.Printf("\r%s", strings.Repeat(" ", p.maxLength))
	}

	fmt.Print(msg)
}

func (p *ProgressRenderer) UpdateProgress(progress lxd.ProgressData) {
	p.Update(progress.Text)
}

func (p *ProgressRenderer) UpdateOp(op api.Operation) {
	if op.Metadata == nil {
		return
	}

	for _, key := range []string{"fs_progress", "download_progress"} {
		value, ok := op.Metadata[key]
		if ok {
			p.Update(value.(string))
			break
		}
	}
}

type StringList [][]string

func (a StringList) Len() int {
	return len(a)
}

func (a StringList) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a StringList) Less(i, j int) bool {
	x := 0
	for x = range a[i] {
		if a[i][x] != a[j][x] {
			break
		}
	}

	if a[i][x] == "" {
		return false
	}

	if a[j][x] == "" {
		return true
	}

	return a[i][x] < a[j][x]
}

// Container name sorting
type byName [][]string

func (a byName) Len() int {
	return len(a)
}

func (a byName) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a byName) Less(i, j int) bool {
	if a[i][0] == "" {
		return false
	}

	if a[j][0] == "" {
		return true
	}

	return a[i][0] < a[j][0]
}

// Storage volume sorting
type byNameAndType [][]string

func (a byNameAndType) Len() int {
	return len(a)
}

func (a byNameAndType) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a byNameAndType) Less(i, j int) bool {
	if a[i][0] != a[j][0] {
		return a[i][0] < a[j][0]
	}

	if a[i][1] == "" {
		return false
	}

	if a[j][1] == "" {
		return true
	}

	return a[i][1] < a[j][1]
}

// Batch operations
type batchResult struct {
	err  error
	name string
}

func runBatch(names []string, action func(name string) error) []batchResult {
	chResult := make(chan batchResult, len(names))

	for _, name := range names {
		go func(name string) {
			chResult <- batchResult{action(name), name}
		}(name)
	}

	results := []batchResult{}
	for range names {
		results = append(results, <-chResult)
	}

	return results
}

// summaryLine returns the first line of the help text. Conventionally, this
// should be a one-line command summary, potentially followed by a longer
// explanation.
func summaryLine(usage string) string {
	for _, line := range strings.Split(usage, "\n") {
		if strings.HasPrefix(line, "Usage:") {
			continue
		}

		if len(line) == 0 {
			continue
		}

		return strings.TrimSuffix(line, ".")
	}

	return i18n.G("Missing summary.")
}

// Used to return a user friendly error
func getLocalErr(err error) error {
	t, ok := err.(*url.Error)
	if !ok {
		return nil
	}

	u, ok := t.Err.(*net.OpError)
	if !ok {
		return nil
	}

	if u.Op == "dial" && u.Net == "unix" {
		var lxdErr error

		sysErr, ok := u.Err.(*os.SyscallError)
		if ok {
			lxdErr = sysErr.Err
		} else {
			// syscall.Errno may be returned on some systems, e.g. CentOS
			lxdErr, ok = u.Err.(syscall.Errno)
			if !ok {
				return nil
			}
		}

		switch lxdErr {
		case syscall.ENOENT, syscall.ECONNREFUSED, syscall.EACCES:
			return lxdErr
		}
	}

	return nil
}

// Add a device to a container
func containerDeviceAdd(client lxd.ContainerServer, name string, devName string, dev map[string]string) error {
	// Get the container entry
	container, etag, err := client.GetContainer(name)
	if err != nil {
		return err
	}

	// Check if the device already exists
	_, ok := container.Devices[devName]
	if ok {
		return fmt.Errorf(i18n.G("Device already exists: %s"), devName)
	}

	container.Devices[devName] = dev

	op, err := client.UpdateContainer(name, container.Writable(), etag)
	if err != nil {
		return err
	}

	return op.Wait()
}

// Add a device to a profile
func profileDeviceAdd(client lxd.ContainerServer, name string, devName string, dev map[string]string) error {
	// Get the profile entry
	profile, profileEtag, err := client.GetProfile(name)
	if err != nil {
		return err
	}

	// Check if the device already exists
	_, ok := profile.Devices[devName]
	if ok {
		return fmt.Errorf(i18n.G("Device already exists: %s"), devName)
	}

	// Add the device to the container
	profile.Devices[devName] = dev

	err = client.UpdateProfile(name, profile.Writable(), profileEtag)
	if err != nil {
		return err
	}

	return nil
}

// Wait for an operation and cancel it on SIGINT/SIGTERM
func cancelableWait(op *lxd.RemoteOperation, progress *ProgressRenderer) error {
	// Signal handling
	chSignal := make(chan os.Signal)
	signal.Notify(chSignal, os.Interrupt)

	// Operation handling
	chOperation := make(chan error)
	go func() {
		chOperation <- op.Wait()
		close(chOperation)
	}()

	count := 0
	for {
		select {
		case err := <-chOperation:
			return err
		case <-chSignal:
			err := op.CancelTarget()
			if err == nil {
				return fmt.Errorf(i18n.G("Remote operation canceled by user"))
			} else {
				count++

				if count == 3 {
					return fmt.Errorf(i18n.G("User signaled us three times, exiting. The remote operation will keep running."))
				}

				if progress != nil {
					progress.Warn(fmt.Sprintf(i18n.G("%v (interrupt two more times to force)"), err), time.Second*5)
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"
	"net/url"
	"os"
	"sort"
	"strings"
	"syscall"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/i18n"
)

// Lists
const (
	listFormatCSV   = "csv"
	listFormatJSON  = "json"
	listFormatTable = "table"
	listFormatYAML  = "yaml"
)

type stringList [][]string

func (a stringList) Len() int {
	return len(a)
}

func (a stringList) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a stringList) Less(i, j int) bool {
	x := 0
	for x = range a[i] {
		if a[i][x] != a[j][x] {
			break
		}
	}

	if a[i][x] == "" {
		return false
	}

	if a[j][x] == "" {
		return true
	}

	return a[i][x] < a[j][x]
}

// Container name sorting
type byName [][]string

func (a byName) Len() int {
	return len(a)
}

func (a byName) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a byName) Less(i, j int) bool {
	if a[i][0] == "" {
		return false
	}

	if a[j][0] == "" {
		return true
	}

	return a[i][0] < a[j][0]
}

// Storage volume sorting
type byNameAndType [][]string

func (a byNameAndType) Len() int {
	return len(a)
}

func (a byNameAndType) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a byNameAndType) Less(i, j int) bool {
	if a[i][0] != a[j][0] {
		return a[i][0] < a[j][0]
	}

	if a[i][1] == "" {
		return false
	}

	if a[j][1] == "" {
		return true
	}

	return a[i][1] < a[j][1]
}

// Batch operations
type batchResult struct {
	err  error
	name string
}

func runBatch(names []string, action func(name string) error) []batchResult {
	chResult := make(chan batchResult, len(names))

	for _, name := range names {
		go func(name string) {
			chResult <- batchResult{action(name), name}
		}(name)
	}

	results := []batchResult{}
	for range names {
		results = append(results, <-chResult)
	}

	return results
}

// summaryLine returns the first line of the help text. Conventionally, this
// should be a one-line command summary, potentially followed by a longer
// explanation.
func summaryLine(usage string) string {
	for _, line := range strings.Split(usage, "\n") {
		if strings.HasPrefix(line, "Usage:") {
			continue
		}

		if len(line) == 0 {
			continue
		}

		return strings.TrimSuffix(line, ".")
	}

	return i18n.G("Missing summary.")
}

// Used to return a user friendly error
func getLocalErr(err error) error {
	t, ok := err.(*url.Error)
	if !ok {
		return nil
	}

	u, ok := t.Err.(*net.OpError)
	if !ok {
		return nil
	}

	if u.Op == "dial" && u.Net == "unix" {
		var lxdErr error

		sysErr, ok := u.Err.(*os.SyscallError)
		if ok {
			lxdErr = sysErr.Err
		} else {
			// syscall.Errno may be returned on some systems, e.g. CentOS
			lxdErr, ok = u.Err.(syscall.Errno)
			if !ok {
				return nil
			}
		}

		switch lxdErr {
		case syscall.ENOENT, syscall.ECONNREFUSED, syscall.EACCES:
			return lxdErr
		}
	}

	return nil
}

// Add a device to a container
func containerDeviceAdd(client lxd.ContainerServer, name string, devName string, dev map[string]string) error {
	// Get the container entry
	container, etag, err := client.GetContainer(name)
	if err != nil {
		return err
	}

	// Check if the device already exists
	_, ok := container.Devices[devName]
	if ok {
		return fmt.Errorf(i18n.G("Device already exists: %s"), devName)
	}

	container.Devices[devName] = dev

	op, err := client.UpdateContainer(name, container.Writable(), etag)
	if err != nil {
		return err
	}

	return op.Wait()
}

// Add a device to a profile
func profileDeviceAdd(client lxd.ContainerServer, name string, devName string, dev map[string]string) error {
	// Get the profile entry
	profile, profileEtag, err := client.GetProfile(name)
	if err != nil {
		return err
	}

	// Check if the device already exists
	_, ok := profile.Devices[devName]
	if ok {
		return fmt.Errorf(i18n.G("Device already exists: %s"), devName)
	}

	// Add the device to the container
	profile.Devices[devName] = dev

	err = client.UpdateProfile(name, profile.Writable(), profileEtag)
	if err != nil {
		return err
	}

	return nil
}

// Create the specified image alises, updating those that already exist
func ensureImageAliases(client lxd.ContainerServer, aliases []api.ImageAlias, fingerprint string) error {
	if len(aliases) == 0 {
		return nil
	}

	names := make([]string, len(aliases))
	for i, alias := range aliases {
		names[i] = alias.Name
	}
	sort.Strings(names)

	resp, err := client.GetImageAliases()
	if err != nil {
		return err
	}

	// Delete existing aliases that match provided ones
	for _, alias := range GetExistingAliases(names, resp) {
		err := client.DeleteImageAlias(alias.Name)
		if err != nil {
			fmt.Println(fmt.Sprintf(i18n.G("Failed to remove alias %s"), alias.Name))
		}
	}
	// Create new aliases
	for _, alias := range aliases {
		aliasPost := api.ImageAliasesPost{}
		aliasPost.Name = alias.Name
		aliasPost.Target = fingerprint
		err := client.CreateImageAlias(aliasPost)
		if err != nil {
			fmt.Println(fmt.Sprintf(i18n.G("Failed to create alias %s"), alias.Name))
		}
	}
	return nil
}

// GetExistingAliases returns the intersection between a list of aliases and all the existing ones.
func GetExistingAliases(aliases []string, allAliases []api.ImageAliasesEntry) []api.ImageAliasesEntry {
	existing := []api.ImageAliasesEntry{}
	for _, alias := range allAliases {
		name := alias.Name
		pos := sort.SearchStrings(aliases, name)
		if pos < len(aliases) && aliases[pos] == name {
			existing = append(existing, alias)
		}
	}
	return existing
}
<commit_msg>lxc/utils: Drop unused functions<commit_after>package main

import (
	"fmt"
	"sort"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/i18n"
)

// Lists
const (
	listFormatCSV   = "csv"
	listFormatJSON  = "json"
	listFormatTable = "table"
	listFormatYAML  = "yaml"
)

type stringList [][]string

func (a stringList) Len() int {
	return len(a)
}

func (a stringList) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a stringList) Less(i, j int) bool {
	x := 0
	for x = range a[i] {
		if a[i][x] != a[j][x] {
			break
		}
	}

	if a[i][x] == "" {
		return false
	}

	if a[j][x] == "" {
		return true
	}

	return a[i][x] < a[j][x]
}

// Container name sorting
type byName [][]string

func (a byName) Len() int {
	return len(a)
}

func (a byName) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a byName) Less(i, j int) bool {
	if a[i][0] == "" {
		return false
	}

	if a[j][0] == "" {
		return true
	}

	return a[i][0] < a[j][0]
}

// Storage volume sorting
type byNameAndType [][]string

func (a byNameAndType) Len() int {
	return len(a)
}

func (a byNameAndType) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a byNameAndType) Less(i, j int) bool {
	if a[i][0] != a[j][0] {
		return a[i][0] < a[j][0]
	}

	if a[i][1] == "" {
		return false
	}

	if a[j][1] == "" {
		return true
	}

	return a[i][1] < a[j][1]
}

// Batch operations
type batchResult struct {
	err  error
	name string
}

func runBatch(names []string, action func(name string) error) []batchResult {
	chResult := make(chan batchResult, len(names))

	for _, name := range names {
		go func(name string) {
			chResult <- batchResult{action(name), name}
		}(name)
	}

	results := []batchResult{}
	for range names {
		results = append(results, <-chResult)
	}

	return results
}

// Add a device to a container
func containerDeviceAdd(client lxd.ContainerServer, name string, devName string, dev map[string]string) error {
	// Get the container entry
	container, etag, err := client.GetContainer(name)
	if err != nil {
		return err
	}

	// Check if the device already exists
	_, ok := container.Devices[devName]
	if ok {
		return fmt.Errorf(i18n.G("Device already exists: %s"), devName)
	}

	container.Devices[devName] = dev

	op, err := client.UpdateContainer(name, container.Writable(), etag)
	if err != nil {
		return err
	}

	return op.Wait()
}

// Add a device to a profile
func profileDeviceAdd(client lxd.ContainerServer, name string, devName string, dev map[string]string) error {
	// Get the profile entry
	profile, profileEtag, err := client.GetProfile(name)
	if err != nil {
		return err
	}

	// Check if the device already exists
	_, ok := profile.Devices[devName]
	if ok {
		return fmt.Errorf(i18n.G("Device already exists: %s"), devName)
	}

	// Add the device to the container
	profile.Devices[devName] = dev

	err = client.UpdateProfile(name, profile.Writable(), profileEtag)
	if err != nil {
		return err
	}

	return nil
}

// Create the specified image alises, updating those that already exist
func ensureImageAliases(client lxd.ContainerServer, aliases []api.ImageAlias, fingerprint string) error {
	if len(aliases) == 0 {
		return nil
	}

	names := make([]string, len(aliases))
	for i, alias := range aliases {
		names[i] = alias.Name
	}
	sort.Strings(names)

	resp, err := client.GetImageAliases()
	if err != nil {
		return err
	}

	// Delete existing aliases that match provided ones
	for _, alias := range GetExistingAliases(names, resp) {
		err := client.DeleteImageAlias(alias.Name)
		if err != nil {
			fmt.Println(fmt.Sprintf(i18n.G("Failed to remove alias %s"), alias.Name))
		}
	}
	// Create new aliases
	for _, alias := range aliases {
		aliasPost := api.ImageAliasesPost{}
		aliasPost.Name = alias.Name
		aliasPost.Target = fingerprint
		err := client.CreateImageAlias(aliasPost)
		if err != nil {
			fmt.Println(fmt.Sprintf(i18n.G("Failed to create alias %s"), alias.Name))
		}
	}
	return nil
}

// GetExistingAliases returns the intersection between a list of aliases and all the existing ones.
func GetExistingAliases(aliases []string, allAliases []api.ImageAliasesEntry) []api.ImageAliasesEntry {
	existing := []api.ImageAliasesEntry{}
	for _, alias := range allAliases {
		name := alias.Name
		pos := sort.SearchStrings(aliases, name)
		if pos < len(aliases) && aliases[pos] == name {
			existing = append(existing, alias)
		}
	}
	return existing
}
<|endoftext|>


<commit_before>// Copyright 2015 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !js

package audio

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"github.com/timshannon/go-openal/openal"
	"log"
	"runtime"
	"time"
)

func toBytes(l, r []int16) []byte {
	if len(l) != len(r) {
		panic("len(l) must equal to len(r)")
	}
	b := &bytes.Buffer{}
	for i := 0; i < len(l); i++ {
		if err := binary.Write(b, binary.LittleEndian, []int16{l[i], r[i]}); err != nil {
			panic(err)
		}
	}
	return b.Bytes()
}

func initialize() {
	ch := make(chan struct{})
	go func() {
		runtime.LockOSThread()

		device := openal.OpenDevice("")
		context := device.CreateContext()
		context.Activate()

		if alErr := openal.GetError(); alErr != 0 {
			log.Printf("OpenAL initialize error: %d", alErr)
			close(ch)
			// Graceful ending: Audio is not available on Travis CI.
			return
		}

		audioEnabled = true
		sources := openal.NewSources(MaxChannel)
		close(ch)

		emptyBytes := make([]byte, 4*bufferSize)

		for _, source := range sources {
			const bufferNum = 16
			buffers := openal.NewBuffers(bufferNum)
			for _, buffer := range buffers {
				buffer.SetData(openal.FormatStereo16, emptyBytes, SampleRate)
				source.QueueBuffer(buffer)
			}
			source.Play()
			if alErr := openal.GetError(); alErr != 0 {
				panic(fmt.Sprintf("OpenAL error: %d", alErr))
			}
		}

		for {
			oneProcessed := false
			for channel, source := range sources {
				processed := source.BuffersProcessed()
				if processed == 0 {
					continue
				}
				oneProcessed = true
				buffers := make([]openal.Buffer, processed)
				source.UnqueueBuffers(buffers)
				for _, buffer := range buffers {
					l, r := loadChannelBuffer(channel)
					b := toBytes(l, r)
					buffer.SetData(openal.FormatStereo16, b, SampleRate)
					source.QueueBuffer(buffer)
				}
			}
			if !oneProcessed {
				time.Sleep(1)
			}
		}
	}()
	<-ch
}

func start() {
	// Do nothing
}
<commit_msg>audio: Bug fix: Reduce delay for desktops<commit_after>// Copyright 2015 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !js

package audio

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"github.com/timshannon/go-openal/openal"
	"log"
	"runtime"
	"time"
)

func toBytes(l, r []int16) []byte {
	if len(l) != len(r) {
		panic("len(l) must equal to len(r)")
	}
	b := &bytes.Buffer{}
	for i := 0; i < len(l); i++ {
		if err := binary.Write(b, binary.LittleEndian, []int16{l[i], r[i]}); err != nil {
			panic(err)
		}
	}
	return b.Bytes()
}

func initialize() {
	ch := make(chan struct{})
	go func() {
		runtime.LockOSThread()

		device := openal.OpenDevice("")
		context := device.CreateContext()
		context.Activate()

		if alErr := openal.GetError(); alErr != 0 {
			log.Printf("OpenAL initialize error: %d", alErr)
			close(ch)
			// Graceful ending: Audio is not available on Travis CI.
			return
		}

		audioEnabled = true
		sources := openal.NewSources(MaxChannel)
		close(ch)

		emptyBytes := make([]byte, 4*bufferSize)

		for _, source := range sources {
			const bufferNum = 4
			buffers := openal.NewBuffers(bufferNum)
			for _, buffer := range buffers {
				buffer.SetData(openal.FormatStereo16, emptyBytes, SampleRate)
				source.QueueBuffer(buffer)
			}
			source.Play()
			if alErr := openal.GetError(); alErr != 0 {
				panic(fmt.Sprintf("OpenAL error: %d", alErr))
			}
		}

		for {
			oneProcessed := false
			for channel, source := range sources {
				processed := source.BuffersProcessed()
				if processed == 0 {
					continue
				}

				oneProcessed = true
				buffers := make([]openal.Buffer, processed)
				source.UnqueueBuffers(buffers)
				for _, buffer := range buffers {
					l, r := loadChannelBuffer(channel)
					b := toBytes(l, r)
					buffer.SetData(openal.FormatStereo16, b, SampleRate)
					source.QueueBuffer(buffer)
				}
				if source.State() == openal.Stopped {
					source.Rewind()
					source.Play()
				}
			}
			if !oneProcessed {
				time.Sleep(1)
			}
		}
	}()
	<-ch
}

func start() {
	// Do nothing
}
<|endoftext|>


<commit_before>/*
Copyright 2020 ceph-csi authors.

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

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

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

package util

import (
	"fmt"
	"testing"

	"github.com/container-storage-interface/spec/lib/go/csi"
)

func checkError(t *testing.T, msg string, err error) {
	if err == nil {
		t.Errorf(msg)
	}
}

func checkAndReportError(t *testing.T, msg string, err error) {
	if err != nil {
		t.Errorf("%s (%v)", msg, err)
	}
}

// TestFindPoolAndTopology also tests MatchTopologyForPool.
func TestFindPoolAndTopology(t *testing.T) {
	t.Parallel()
	var err error
	var label1 = "region"
	var label2 = "zone"
	var l1Value1 = "R1"
	var l1Value2 = "R2"
	var l2Value1 = "Z1"
	var l2Value2 = "Z2"
	var pool1 = "PoolA"
	var pool2 = "PoolB"
	var topologyPrefix = "prefix"
	var emptyTopoPools = []TopologyConstrainedPool{}
	var emptyPoolNameTopoPools = []TopologyConstrainedPool{
		{
			DomainSegments: []topologySegment{
				{
					DomainLabel: label1,
					DomainValue: l1Value1,
				},
				{
					DomainLabel: label2,
					DomainValue: l2Value1,
				},
			},
		},
	}
	var emptyDomainsInTopoPools = []TopologyConstrainedPool{
		{
			PoolName: pool1,
		},
	}
	var partialDomainsInTopoPools = []TopologyConstrainedPool{
		{
			PoolName: pool1,
			DomainSegments: []topologySegment{
				{
					DomainLabel: label1,
					DomainValue: l1Value1,
				},
			},
		},
	}
	var differentDomainsInTopoPools = []TopologyConstrainedPool{
		{
			PoolName: pool1,
			DomainSegments: []topologySegment{
				{
					DomainLabel: label1 + "fuzz1",
					DomainValue: l1Value1,
				},
				{
					DomainLabel: label2,
					DomainValue: l2Value1,
				},
			},
		},
		{
			PoolName: pool2,
			DomainSegments: []topologySegment{
				{
					DomainLabel: label1,
					DomainValue: l1Value2,
				},
				{
					DomainLabel: label2,
					DomainValue: l2Value2 + "fuzz1",
				},
			},
		},
	}
	var validSingletonTopoPools = []TopologyConstrainedPool{
		{
			PoolName: pool1,
			DomainSegments: []topologySegment{
				{
					DomainLabel: label1,
					DomainValue: l1Value1,
				},
				{
					DomainLabel: label2,
					DomainValue: l2Value1,
				},
			},
		},
	}
	var validMultipleTopoPools = []TopologyConstrainedPool{
		{
			PoolName: pool1,
			DomainSegments: []topologySegment{
				{
					DomainLabel: label1,
					DomainValue: l1Value1,
				},
				{
					DomainLabel: label2,
					DomainValue: l2Value1,
				},
			},
		},
		{
			PoolName: pool2,
			DomainSegments: []topologySegment{
				{
					DomainLabel: label1,
					DomainValue: l1Value2,
				},
				{
					DomainLabel: label2,
					DomainValue: l2Value2,
				},
			},
		},
	}
	var emptyAccReq = csi.TopologyRequirement{}
	var emptySegmentAccReq = csi.TopologyRequirement{
		Requisite: []*csi.Topology{
			{},
			{},
		},
	}
	var partialHigherSegmentAccReq = csi.TopologyRequirement{
		Preferred: []*csi.Topology{
			{
				Segments: map[string]string{
					topologyPrefix + "/" + label1: l1Value1,
				},
			},
		},
	}
	var partialLowerSegmentAccReq = csi.TopologyRequirement{
		Preferred: []*csi.Topology{
			{
				Segments: map[string]string{
					topologyPrefix + "/" + label2: l2Value1,
				},
			},
		},
	}
	var differentSegmentAccReq = csi.TopologyRequirement{
		Requisite: []*csi.Topology{
			{
				Segments: map[string]string{
					topologyPrefix + "/" + label1 + "fuzz2": l1Value1,
					topologyPrefix + "/" + label2:           l2Value1,
				},
			},
			{
				Segments: map[string]string{
					topologyPrefix + "/" + label1: l1Value2,
					topologyPrefix + "/" + label2: l2Value2 + "fuzz2",
				},
			},
		},
	}
	var validAccReq = csi.TopologyRequirement{
		Requisite: []*csi.Topology{
			{
				Segments: map[string]string{
					topologyPrefix + "/" + label1: l1Value1,
					topologyPrefix + "/" + label2: l2Value1,
				},
			},
			{
				Segments: map[string]string{
					topologyPrefix + "/" + label1: l1Value2,
					topologyPrefix + "/" + label2: l2Value2,
				},
			},
		},
		Preferred: []*csi.Topology{
			{
				Segments: map[string]string{
					topologyPrefix + "/" + label1: l1Value1,
					topologyPrefix + "/" + label2: l2Value1,
				},
			},
			{
				Segments: map[string]string{
					topologyPrefix + "/" + label1: l1Value2,
					topologyPrefix + "/" + label2: l2Value2,
				},
			},
		},
	}

	checkOutput := func(err error, poolName string, topoSegment map[string]string) error {
		if err != nil {
			return fmt.Errorf("expected success, got err (%v)", err)
		}
		if poolName != pool1 || !(len(topoSegment) == 2) &&
			topoSegment[topologyPrefix+"/"+label1] == l1Value1 &&
			topoSegment[topologyPrefix+"/"+label2] == l2Value1 {
			return fmt.Errorf("expected poolName (%s) and topoSegment (%s %s), got (%s) and (%v)", pool1,
				topologyPrefix+"/"+label1+l1Value1, topologyPrefix+"/"+label2+l2Value1,
				poolName, topoSegment)
		}
		return nil
	}
	// Test nil values
	_, _, _, err = FindPoolAndTopology(nil, nil)
	checkAndReportError(t, "expected success due to nil in-args", err)

	poolName, _, _, err := FindPoolAndTopology(&validMultipleTopoPools, nil)
	if err != nil || poolName != "" {
		t.Errorf("expected success due to nil accessibility requirements (err - %v) (poolName - %s)", err, poolName)
	}

	poolName, _, _, err = FindPoolAndTopology(nil, &validAccReq)
	if err != nil || poolName != "" {
		t.Errorf("expected success due to nil topology pools (err - %v) (poolName - %s)", err, poolName)
	}

	// Test valid accessibility requirement, with invalid topology pools values
	_, _, _, err = FindPoolAndTopology(&emptyTopoPools, &validAccReq)
	checkError(t, "expected failure due to empty topology pools", err)

	_, _, _, err = FindPoolAndTopology(&emptyPoolNameTopoPools, &validAccReq)
	checkError(t, "expected failure due to missing pool name in topology pools", err)

	_, _, _, err = FindPoolAndTopology(&differentDomainsInTopoPools, &validAccReq)
	checkError(t, "expected failure due to mismatching domains in topology pools", err)

	// Test valid topology pools, with invalid accessibility requirements
	_, _, _, err = FindPoolAndTopology(&validMultipleTopoPools, &emptyAccReq)
	checkError(t, "expected failure due to empty accessibility requirements", err)

	_, _, _, err = FindPoolAndTopology(&validSingletonTopoPools, &emptySegmentAccReq)
	checkError(t, "expected failure due to empty segments in accessibility requirements", err)

	_, _, _, err = FindPoolAndTopology(&validMultipleTopoPools, &partialHigherSegmentAccReq)
	checkError(t, "expected failure due to partial segments in accessibility requirements", err)

	_, _, _, err = FindPoolAndTopology(&validSingletonTopoPools, &partialLowerSegmentAccReq)
	checkError(t, "expected failure due to partial segments in accessibility requirements", err)

	_, _, _, err = FindPoolAndTopology(&validMultipleTopoPools, &partialLowerSegmentAccReq)
	checkError(t, "expected failure due to partial segments in accessibility requirements", err)

	_, _, _, err = FindPoolAndTopology(&validMultipleTopoPools, &differentSegmentAccReq)
	checkError(t, "expected failure due to mismatching segments in accessibility requirements", err)

	// Test success cases
	// If a pool is a superset of domains (either empty domain labels or partial), it can be selected
	poolName, _, topoSegment, err := FindPoolAndTopology(&emptyDomainsInTopoPools, &validAccReq)
	err = checkOutput(err, poolName, topoSegment)
	checkAndReportError(t, "expected success got:", err)

	poolName, _, topoSegment, err = FindPoolAndTopology(&partialDomainsInTopoPools, &validAccReq)
	err = checkOutput(err, poolName, topoSegment)
	checkAndReportError(t, "expected success got:", err)

	// match in a singleton topology pools
	poolName, _, topoSegment, err = FindPoolAndTopology(&validSingletonTopoPools, &validAccReq)
	err = checkOutput(err, poolName, topoSegment)
	checkAndReportError(t, "expected success got:", err)

	// match first in multiple topology pools
	poolName, _, topoSegment, err = FindPoolAndTopology(&validMultipleTopoPools, &validAccReq)
	err = checkOutput(err, poolName, topoSegment)
	checkAndReportError(t, "expected success got:", err)

	// match non-first in multiple topology pools
	switchPoolOrder := []TopologyConstrainedPool{}
	switchPoolOrder = append(switchPoolOrder, validMultipleTopoPools[1], validMultipleTopoPools[0])
	poolName, _, topoSegment, err = FindPoolAndTopology(&switchPoolOrder, &validAccReq)
	err = checkOutput(err, poolName, topoSegment)
	checkAndReportError(t, "expected success got:", err)

	// test valid dataPool return
	for i := range switchPoolOrder {
		switchPoolOrder[i].DataPoolName = "ec-" + switchPoolOrder[i].PoolName
	}
	poolName, dataPoolName, topoSegment, err := FindPoolAndTopology(&switchPoolOrder, &validAccReq)
	err = checkOutput(err, poolName, topoSegment)
	checkAndReportError(t, "expected success got:", err)
	if dataPoolName != "ec-"+poolName {
		t.Errorf("expected data pool to be named ec-%s, got %s", poolName, dataPoolName)
	}

	// TEST: MatchTopologyForPool
	// check for non-existent pool
	_, err = MatchTopologyForPool(&validMultipleTopoPools, &validAccReq, pool1+"fuzz")
	if err == nil {
		t.Errorf("expected failure due to non-existent pool name (%s) got success", pool1+"fuzz")
	}

	// check for existing pool
	topoSegment, err = MatchTopologyForPool(&validMultipleTopoPools, &validAccReq, pool1)
	err = checkOutput(err, pool1, topoSegment)
	checkAndReportError(t, "expected success got:", err)
}

/*
// TODO: To test GetTopologyFromDomainLabels we need it to accept a k8s client interface, to mock k8sGetNdeLabels output
func TestGetTopologyFromDomainLabels(t *testing.T) {
	fakeNodes := v1.Node{
		ObjectMeta: metav1.ObjectMeta{
			Name: "worker1",
			Labels: map[string]string{
				"prefix/region": "R1",
				"prefix/zone":   "Z1",
			},
		},
	}

	client := fake.NewSimpleClientset(&fakeNodes)

	_, err := k8sGetNodeLabels(client, "nodeName")
	if err == nil {
		t.Error("Expected error due to invalid node name, got success")
	}

	labels, err := k8sGetNodeLabels(client, "worker1")
	if err != nil {
		t.Errorf("Expected success, got err (%v)", err)
	}
	t.Errorf("Read labels (%v)", labels)
}*/
<commit_msg>cleanup: Adds t.Helper() to test helper function<commit_after>/*
Copyright 2020 ceph-csi authors.

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

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

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

package util

import (
	"fmt"
	"testing"

	"github.com/container-storage-interface/spec/lib/go/csi"
)

func checkError(t *testing.T, msg string, err error) {
	t.Helper()
	if err == nil {
		t.Errorf(msg)
	}
}

func checkAndReportError(t *testing.T, msg string, err error) {
	t.Helper()
	if err != nil {
		t.Errorf("%s (%v)", msg, err)
	}
}

// TestFindPoolAndTopology also tests MatchTopologyForPool.
func TestFindPoolAndTopology(t *testing.T) {
	t.Parallel()
	var err error
	var label1 = "region"
	var label2 = "zone"
	var l1Value1 = "R1"
	var l1Value2 = "R2"
	var l2Value1 = "Z1"
	var l2Value2 = "Z2"
	var pool1 = "PoolA"
	var pool2 = "PoolB"
	var topologyPrefix = "prefix"
	var emptyTopoPools = []TopologyConstrainedPool{}
	var emptyPoolNameTopoPools = []TopologyConstrainedPool{
		{
			DomainSegments: []topologySegment{
				{
					DomainLabel: label1,
					DomainValue: l1Value1,
				},
				{
					DomainLabel: label2,
					DomainValue: l2Value1,
				},
			},
		},
	}
	var emptyDomainsInTopoPools = []TopologyConstrainedPool{
		{
			PoolName: pool1,
		},
	}
	var partialDomainsInTopoPools = []TopologyConstrainedPool{
		{
			PoolName: pool1,
			DomainSegments: []topologySegment{
				{
					DomainLabel: label1,
					DomainValue: l1Value1,
				},
			},
		},
	}
	var differentDomainsInTopoPools = []TopologyConstrainedPool{
		{
			PoolName: pool1,
			DomainSegments: []topologySegment{
				{
					DomainLabel: label1 + "fuzz1",
					DomainValue: l1Value1,
				},
				{
					DomainLabel: label2,
					DomainValue: l2Value1,
				},
			},
		},
		{
			PoolName: pool2,
			DomainSegments: []topologySegment{
				{
					DomainLabel: label1,
					DomainValue: l1Value2,
				},
				{
					DomainLabel: label2,
					DomainValue: l2Value2 + "fuzz1",
				},
			},
		},
	}
	var validSingletonTopoPools = []TopologyConstrainedPool{
		{
			PoolName: pool1,
			DomainSegments: []topologySegment{
				{
					DomainLabel: label1,
					DomainValue: l1Value1,
				},
				{
					DomainLabel: label2,
					DomainValue: l2Value1,
				},
			},
		},
	}
	var validMultipleTopoPools = []TopologyConstrainedPool{
		{
			PoolName: pool1,
			DomainSegments: []topologySegment{
				{
					DomainLabel: label1,
					DomainValue: l1Value1,
				},
				{
					DomainLabel: label2,
					DomainValue: l2Value1,
				},
			},
		},
		{
			PoolName: pool2,
			DomainSegments: []topologySegment{
				{
					DomainLabel: label1,
					DomainValue: l1Value2,
				},
				{
					DomainLabel: label2,
					DomainValue: l2Value2,
				},
			},
		},
	}
	var emptyAccReq = csi.TopologyRequirement{}
	var emptySegmentAccReq = csi.TopologyRequirement{
		Requisite: []*csi.Topology{
			{},
			{},
		},
	}
	var partialHigherSegmentAccReq = csi.TopologyRequirement{
		Preferred: []*csi.Topology{
			{
				Segments: map[string]string{
					topologyPrefix + "/" + label1: l1Value1,
				},
			},
		},
	}
	var partialLowerSegmentAccReq = csi.TopologyRequirement{
		Preferred: []*csi.Topology{
			{
				Segments: map[string]string{
					topologyPrefix + "/" + label2: l2Value1,
				},
			},
		},
	}
	var differentSegmentAccReq = csi.TopologyRequirement{
		Requisite: []*csi.Topology{
			{
				Segments: map[string]string{
					topologyPrefix + "/" + label1 + "fuzz2": l1Value1,
					topologyPrefix + "/" + label2:           l2Value1,
				},
			},
			{
				Segments: map[string]string{
					topologyPrefix + "/" + label1: l1Value2,
					topologyPrefix + "/" + label2: l2Value2 + "fuzz2",
				},
			},
		},
	}
	var validAccReq = csi.TopologyRequirement{
		Requisite: []*csi.Topology{
			{
				Segments: map[string]string{
					topologyPrefix + "/" + label1: l1Value1,
					topologyPrefix + "/" + label2: l2Value1,
				},
			},
			{
				Segments: map[string]string{
					topologyPrefix + "/" + label1: l1Value2,
					topologyPrefix + "/" + label2: l2Value2,
				},
			},
		},
		Preferred: []*csi.Topology{
			{
				Segments: map[string]string{
					topologyPrefix + "/" + label1: l1Value1,
					topologyPrefix + "/" + label2: l2Value1,
				},
			},
			{
				Segments: map[string]string{
					topologyPrefix + "/" + label1: l1Value2,
					topologyPrefix + "/" + label2: l2Value2,
				},
			},
		},
	}

	checkOutput := func(err error, poolName string, topoSegment map[string]string) error {
		if err != nil {
			return fmt.Errorf("expected success, got err (%v)", err)
		}
		if poolName != pool1 || !(len(topoSegment) == 2) &&
			topoSegment[topologyPrefix+"/"+label1] == l1Value1 &&
			topoSegment[topologyPrefix+"/"+label2] == l2Value1 {
			return fmt.Errorf("expected poolName (%s) and topoSegment (%s %s), got (%s) and (%v)", pool1,
				topologyPrefix+"/"+label1+l1Value1, topologyPrefix+"/"+label2+l2Value1,
				poolName, topoSegment)
		}
		return nil
	}
	// Test nil values
	_, _, _, err = FindPoolAndTopology(nil, nil)
	checkAndReportError(t, "expected success due to nil in-args", err)

	poolName, _, _, err := FindPoolAndTopology(&validMultipleTopoPools, nil)
	if err != nil || poolName != "" {
		t.Errorf("expected success due to nil accessibility requirements (err - %v) (poolName - %s)", err, poolName)
	}

	poolName, _, _, err = FindPoolAndTopology(nil, &validAccReq)
	if err != nil || poolName != "" {
		t.Errorf("expected success due to nil topology pools (err - %v) (poolName - %s)", err, poolName)
	}

	// Test valid accessibility requirement, with invalid topology pools values
	_, _, _, err = FindPoolAndTopology(&emptyTopoPools, &validAccReq)
	checkError(t, "expected failure due to empty topology pools", err)

	_, _, _, err = FindPoolAndTopology(&emptyPoolNameTopoPools, &validAccReq)
	checkError(t, "expected failure due to missing pool name in topology pools", err)

	_, _, _, err = FindPoolAndTopology(&differentDomainsInTopoPools, &validAccReq)
	checkError(t, "expected failure due to mismatching domains in topology pools", err)

	// Test valid topology pools, with invalid accessibility requirements
	_, _, _, err = FindPoolAndTopology(&validMultipleTopoPools, &emptyAccReq)
	checkError(t, "expected failure due to empty accessibility requirements", err)

	_, _, _, err = FindPoolAndTopology(&validSingletonTopoPools, &emptySegmentAccReq)
	checkError(t, "expected failure due to empty segments in accessibility requirements", err)

	_, _, _, err = FindPoolAndTopology(&validMultipleTopoPools, &partialHigherSegmentAccReq)
	checkError(t, "expected failure due to partial segments in accessibility requirements", err)

	_, _, _, err = FindPoolAndTopology(&validSingletonTopoPools, &partialLowerSegmentAccReq)
	checkError(t, "expected failure due to partial segments in accessibility requirements", err)

	_, _, _, err = FindPoolAndTopology(&validMultipleTopoPools, &partialLowerSegmentAccReq)
	checkError(t, "expected failure due to partial segments in accessibility requirements", err)

	_, _, _, err = FindPoolAndTopology(&validMultipleTopoPools, &differentSegmentAccReq)
	checkError(t, "expected failure due to mismatching segments in accessibility requirements", err)

	// Test success cases
	// If a pool is a superset of domains (either empty domain labels or partial), it can be selected
	poolName, _, topoSegment, err := FindPoolAndTopology(&emptyDomainsInTopoPools, &validAccReq)
	err = checkOutput(err, poolName, topoSegment)
	checkAndReportError(t, "expected success got:", err)

	poolName, _, topoSegment, err = FindPoolAndTopology(&partialDomainsInTopoPools, &validAccReq)
	err = checkOutput(err, poolName, topoSegment)
	checkAndReportError(t, "expected success got:", err)

	// match in a singleton topology pools
	poolName, _, topoSegment, err = FindPoolAndTopology(&validSingletonTopoPools, &validAccReq)
	err = checkOutput(err, poolName, topoSegment)
	checkAndReportError(t, "expected success got:", err)

	// match first in multiple topology pools
	poolName, _, topoSegment, err = FindPoolAndTopology(&validMultipleTopoPools, &validAccReq)
	err = checkOutput(err, poolName, topoSegment)
	checkAndReportError(t, "expected success got:", err)

	// match non-first in multiple topology pools
	switchPoolOrder := []TopologyConstrainedPool{}
	switchPoolOrder = append(switchPoolOrder, validMultipleTopoPools[1], validMultipleTopoPools[0])
	poolName, _, topoSegment, err = FindPoolAndTopology(&switchPoolOrder, &validAccReq)
	err = checkOutput(err, poolName, topoSegment)
	checkAndReportError(t, "expected success got:", err)

	// test valid dataPool return
	for i := range switchPoolOrder {
		switchPoolOrder[i].DataPoolName = "ec-" + switchPoolOrder[i].PoolName
	}
	poolName, dataPoolName, topoSegment, err := FindPoolAndTopology(&switchPoolOrder, &validAccReq)
	err = checkOutput(err, poolName, topoSegment)
	checkAndReportError(t, "expected success got:", err)
	if dataPoolName != "ec-"+poolName {
		t.Errorf("expected data pool to be named ec-%s, got %s", poolName, dataPoolName)
	}

	// TEST: MatchTopologyForPool
	// check for non-existent pool
	_, err = MatchTopologyForPool(&validMultipleTopoPools, &validAccReq, pool1+"fuzz")
	if err == nil {
		t.Errorf("expected failure due to non-existent pool name (%s) got success", pool1+"fuzz")
	}

	// check for existing pool
	topoSegment, err = MatchTopologyForPool(&validMultipleTopoPools, &validAccReq, pool1)
	err = checkOutput(err, pool1, topoSegment)
	checkAndReportError(t, "expected success got:", err)
}

/*
// TODO: To test GetTopologyFromDomainLabels we need it to accept a k8s client interface, to mock k8sGetNdeLabels output
func TestGetTopologyFromDomainLabels(t *testing.T) {
	fakeNodes := v1.Node{
		ObjectMeta: metav1.ObjectMeta{
			Name: "worker1",
			Labels: map[string]string{
				"prefix/region": "R1",
				"prefix/zone":   "Z1",
			},
		},
	}

	client := fake.NewSimpleClientset(&fakeNodes)

	_, err := k8sGetNodeLabels(client, "nodeName")
	if err == nil {
		t.Error("Expected error due to invalid node name, got success")
	}

	labels, err := k8sGetNodeLabels(client, "worker1")
	if err != nil {
		t.Errorf("Expected success, got err (%v)", err)
	}
	t.Errorf("Read labels (%v)", labels)
}*/
<|endoftext|>


<commit_before>// +build new

package gototo

import (
	"encoding/json"
	zmq "github.com/JeremyOT/gozmq"
	"log"
	"runtime"
)

var logger *log.Logger

func SetLogger(l *log.Logger) {
	logger = l
}

func writeLog(message ...interface{}) {
	if logger != nil {
		logger.Println(message)
	} else {
		println(message)
	}
}

var registeredWorkerFunctions map[string]WorkerFunction = make(map[string]WorkerFunction)
var activeTimeout = 1
var passiveTimeout = 100

type WorkerFunction func(interface{}) interface{}

func RunRouter(routerAddress, dealerAddress string, routerBind, dealerBind bool) error {
	context, _ := zmq.NewContext()
	defer context.Close()
	router, _ := context.NewSocket(zmq.ROUTER)
	defer router.Close()
	if routerBind {
		router.Bind(routerAddress)
	} else {
		router.Connect(routerAddress)
	}
	dealer, _ := context.NewSocket(zmq.DEALER)
	defer dealer.Close()
	if dealerBind {
		dealer.Bind(dealerAddress)
	} else {
		dealer.Connect(dealerAddress)
	}
	return zmq.Device(zmq.QUEUE, router, dealer)
}

func RegisterWorkerFunction(name string, workerFunction WorkerFunction) {
	registeredWorkerFunctions[name] = workerFunction
}

func callworker(responseChannel chan [][]byte, message [][]byte, parameters interface{}, workerFunction WorkerFunction) {
	response := workerFunction(parameters)
	responseData, _ := json.Marshal(response)
	message[len(message)-1] = responseData
	responseChannel <- message
}

func RunWorker(address string, numWorkers int, quit chan int, wait chan int) {
	defer func() { close(wait) }()
	context, _ := zmq.NewContext()
	defer context.Close()
	socket, _ := context.NewSocket(zmq.ROUTER)
	defer socket.Close()
	socket.SetSockOptInt(zmq.RCVTIMEO, passiveTimeout)
	socket.Bind(address)
	runningWorkers := 0
	responseChannel := make(chan [][]byte)
	sendResponse := func(response [][]byte) {
			runningWorkers -= 1
			socket.SendMultipart(response, 0)
			if runningWorkers == 0 {
				socket.SetSockOptInt(zmq.RCVTIMEO, passiveTimeout)
			}
	}
	for {
		if runningWorkers == numWorkers {
			select {
				case response := <-responseChannel:
					sendResponse(response)
				case <- quit:
					return
			}
			continue
		}
		select {
		case <- quit:
			return
		case response := <-responseChannel:
			sendResponse(response)
			break
		default:
			message, err := socket.RecvMultipart(0)
			if err != nil {
				break
			}
			data := map[string]interface{}{}
			json.Unmarshal(message[len(message)-1], &data)
			if data == nil {
				writeLog("Received invalid message")
				break
			}
			workerFunction := registeredWorkerFunctions[data["method"].(string)]
			if workerFunction == nil {
				writeLog("Unregistered worker function:", data["method"].(string))
				break
			}
			if runningWorkers == 0 {
				socket.SetSockOptInt(zmq.RCVTIMEO, activeTimeout)
			}
			runningWorkers += 1
			go callworker(responseChannel, message, data["parameters"], workerFunction)
		}
	}
}

func RunWorkerServer(routerAddress, internalAddress string, routerBind bool, count int) {
	if count <= 0 {
		count = runtime.NumCPU()
	}
	quit := make(chan int)
	wait := make(chan int)
	RunWorker(routerAddress, count, quit, wait)
	<-wait
}
<commit_msg>gofmt<commit_after>// +build new

package gototo

import (
	"encoding/json"
	zmq "github.com/JeremyOT/gozmq"
	"log"
	"runtime"
)

var logger *log.Logger

func SetLogger(l *log.Logger) {
	logger = l
}

func writeLog(message ...interface{}) {
	if logger != nil {
		logger.Println(message)
	} else {
		println(message)
	}
}

var registeredWorkerFunctions map[string]WorkerFunction = make(map[string]WorkerFunction)
var activeTimeout = 1
var passiveTimeout = 100

type WorkerFunction func(interface{}) interface{}

func RunRouter(routerAddress, dealerAddress string, routerBind, dealerBind bool) error {
	context, _ := zmq.NewContext()
	defer context.Close()
	router, _ := context.NewSocket(zmq.ROUTER)
	defer router.Close()
	if routerBind {
		router.Bind(routerAddress)
	} else {
		router.Connect(routerAddress)
	}
	dealer, _ := context.NewSocket(zmq.DEALER)
	defer dealer.Close()
	if dealerBind {
		dealer.Bind(dealerAddress)
	} else {
		dealer.Connect(dealerAddress)
	}
	return zmq.Device(zmq.QUEUE, router, dealer)
}

func RegisterWorkerFunction(name string, workerFunction WorkerFunction) {
	registeredWorkerFunctions[name] = workerFunction
}

func callworker(responseChannel chan [][]byte, message [][]byte, parameters interface{}, workerFunction WorkerFunction) {
	response := workerFunction(parameters)
	responseData, _ := json.Marshal(response)
	message[len(message)-1] = responseData
	responseChannel <- message
}

func RunWorker(address string, numWorkers int, quit chan int, wait chan int) {
	defer func() { close(wait) }()
	context, _ := zmq.NewContext()
	defer context.Close()
	socket, _ := context.NewSocket(zmq.ROUTER)
	defer socket.Close()
	socket.SetSockOptInt(zmq.RCVTIMEO, passiveTimeout)
	socket.Bind(address)
	runningWorkers := 0
	responseChannel := make(chan [][]byte)
	sendResponse := func(response [][]byte) {
		runningWorkers -= 1
		socket.SendMultipart(response, 0)
		if runningWorkers == 0 {
			socket.SetSockOptInt(zmq.RCVTIMEO, passiveTimeout)
		}
	}
	for {
		if runningWorkers == numWorkers {
			select {
			case response := <-responseChannel:
				sendResponse(response)
			case <-quit:
				return
			}
			continue
		}
		select {
		case <-quit:
			return
		case response := <-responseChannel:
			sendResponse(response)
			break
		default:
			message, err := socket.RecvMultipart(0)
			if err != nil {
				break
			}
			data := map[string]interface{}{}
			json.Unmarshal(message[len(message)-1], &data)
			if data == nil {
				writeLog("Received invalid message")
				break
			}
			workerFunction := registeredWorkerFunctions[data["method"].(string)]
			if workerFunction == nil {
				writeLog("Unregistered worker function:", data["method"].(string))
				break
			}
			if runningWorkers == 0 {
				socket.SetSockOptInt(zmq.RCVTIMEO, activeTimeout)
			}
			runningWorkers += 1
			go callworker(responseChannel, message, data["parameters"], workerFunction)
		}
	}
}

func RunWorkerServer(routerAddress, internalAddress string, routerBind bool, count int) {
	if count <= 0 {
		count = runtime.NumCPU()
	}
	quit := make(chan int)
	wait := make(chan int)
	RunWorker(routerAddress, count, quit, wait)
	<-wait
}
<|endoftext|>