<commit_before>/*
The MIT License (MIT)

Copyright (c) 2016 Ivan Dejanovic

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 lexer

import (
	"errors"
	"fmt"
	"mlpl/types"
	"strconv"
)

type lexBuffer struct {
	token  types.Token
	index  int
	tokens []types.Token
}

func syntaxError(token types.Token) {
	fmt.Printf("Syntax error at line %d, unexpected token -> ", token.Lineno)

	switch token.TokenType {
	case types.IF, types.THEN, types.ELSE, types.END, types.REPEAT, types.UNTIL, types.READ, types.WRITE:
		fmt.Printf("reserved word: %s\n", token.TokenString)
	case types.ASSIGN:
		fmt.Printf(":=\n")
	case types.LT:
		fmt.Printf("<\n")
	case types.EQ:
		fmt.Printf("=\n")
	case types.LPAREN:
		fmt.Printf("(\n")
	case types.RPAREN:
		fmt.Printf(")\n")
	case types.SEMI:
		fmt.Printf(";\n")
	case types.PLUS:
		fmt.Printf("+\n")
	case types.MINUS:
		fmt.Printf("-\n")
	case types.TIMES:
		fmt.Printf("*\n")
	case types.OVER:
		fmt.Printf("/\n")
	case types.ENDFILE:
		fmt.Printf("EOF\n")
	case types.NUM:
		fmt.Printf("NUM, name= %s\n", token.TokenString)
	case types.ID:
		fmt.Printf("ID, name= %s\n", token.TokenString)
	case types.ERROR:
		fmt.Printf("ERROR: %s\n", token.TokenString)
	default:
		// Should never happen.
		fmt.Printf("Unknown token: %d\n", token.TokenType)
	}

	err := errors.New("Aborting.")
	if err != nil {
		panic(err)
	}
}

func newStmtNode(kind types.StmtKind, lineno int) *types.TreeNode {
	node := new(types.TreeNode)

	node.Children = make([]*types.TreeNode, 0, 0)
	node.Sibling = nil
	node.Node = types.StmtK
	node.Stmt = kind
	node.Lineno = lineno

	return node
}

func newExpNode(kind types.ExpKind, lineno int) *types.TreeNode {
	node := new(types.TreeNode)

	node.Children = make([]*types.TreeNode, 0, 0)
	node.Sibling = nil
	node.Node = types.ExpK
	node.Exp = kind
	node.Lineno = lineno

	return node
}

func (buffer *lexBuffer) nextToken() {
	if buffer.index < len(buffer.tokens) {
		buffer.index += 1
		buffer.token = buffer.tokens[buffer.index]
	}
}

func (buffer *lexBuffer) match(expected types.TokenType) {
	if buffer.token.TokenType == expected {
		buffer.nextToken()
	} else {
		syntaxError(buffer.token)
	}
}

func (buffer *lexBuffer) factor() *types.TreeNode {
	var node *types.TreeNode
	var err error

	switch buffer.token.TokenType {
	case types.NUM:
		node = newExpNode(types.ConstK, buffer.token.Lineno)
		if buffer.token.TokenType == types.NUM {
			node.Val, err = strconv.Atoi(buffer.token.TokenString)
			if err != nil {
				panic(err)
			}
		}
		buffer.match(types.NUM)
	case types.ID:
		node = newExpNode(types.IdK, buffer.token.Lineno)
		if buffer.token.TokenType == types.ID {
			node.Name = buffer.token.TokenString
		}
		buffer.match(types.ID)
	case types.STRING:
		node = newExpNode(types.StringK, buffer.token.Lineno)
		node.ValString = buffer.token.TokenString
		buffer.match(types.STRING)
	case types.LPAREN:
		buffer.match(types.LPAREN)
		node = buffer.exp()
		buffer.match(types.RPAREN)
	default:
		syntaxError(buffer.token)
	}

	return node
}

func (buffer *lexBuffer) term() *types.TreeNode {
	node := buffer.factor()

	for buffer.token.TokenType == types.TIMES || buffer.token.TokenType == types.OVER {
		p := newExpNode(types.OpK, buffer.token.Lineno)
		p.Children = append(p.Children, node)
		p.Op = buffer.token.TokenType
		node = p
		buffer.match(buffer.token.TokenType)
		node.Children = append(node.Children, buffer.factor())
	}

	return node
}

func (buffer *lexBuffer) simpleExp() *types.TreeNode {
	node := buffer.term()

	for buffer.token.TokenType == types.PLUS || buffer.token.TokenType == types.MINUS {
		p := newExpNode(types.OpK, buffer.token.Lineno)
		p.Children = append(p.Children, node)
		p.Op = buffer.token.TokenType
		node = p
		buffer.match(buffer.token.TokenType)
		node.Children = append(node.Children, buffer.term())
	}

	return node
}

func (buffer *lexBuffer) exp() *types.TreeNode {
	node := buffer.simpleExp()

	if buffer.token.TokenType == types.LT || buffer.token.TokenType == types.EQ {
		p := newExpNode(types.OpK, buffer.token.Lineno)
		p.Children = append(p.Children, node)
		p.Op = buffer.token.TokenType
		node = p
		buffer.match(buffer.token.TokenType)
		node.Children = append(node.Children, buffer.simpleExp())
	}

	return node
}

func (buffer *lexBuffer) ifStmt() *types.TreeNode {
	node := newStmtNode(types.IfK, buffer.token.Lineno)

	buffer.match(types.IF)
	node.Children = append(node.Children, buffer.exp())
	buffer.match(types.THEN)
	node.Children = append(node.Children, buffer.stmtSequence())
	if buffer.token.TokenType == types.ELSE {
		buffer.match(types.ELSE)
		node.Children = append(node.Children, buffer.stmtSequence())
	}

	return node
}

func (buffer *lexBuffer) repeatStmt() *types.TreeNode {
	node := newStmtNode(types.RepeatK, buffer.token.Lineno)

	buffer.match(types.REPEAT)
	node.Children = append(node.Children, buffer.stmtSequence())
	buffer.match(types.UNTIL)
	node.Children = append(node.Children, buffer.exp())

	return node
}

func (buffer *lexBuffer) assignStmt() *types.TreeNode {
	node := newStmtNode(types.AssignK, buffer.token.Lineno)

	if buffer.token.TokenType == types.ID {
		node.Name = buffer.token.TokenString
	}
	buffer.match(types.ID)
	buffer.match(types.ASSIGN)
	node.Children = append(node.Children, buffer.exp())

	return node
}

func (buffer *lexBuffer) readStmt() *types.TreeNode {
	node := newStmtNode(types.ReadK, buffer.token.Lineno)

	buffer.match(types.READ)
	if buffer.token.TokenType == types.ID {
		node.Name = buffer.token.TokenString
	}
	buffer.match(types.ID)

	return node
}

func (buffer *lexBuffer) writeStmt() *types.TreeNode {
	node := newStmtNode(types.WriteK, buffer.token.Lineno)

	buffer.match(types.WRITE)
	node.Children = append(node.Children, buffer.exp())

	return node
}

func (buffer *lexBuffer) statement() *types.TreeNode {
	var node *types.TreeNode

	switch buffer.token.TokenType {
	case types.IF:
		node = buffer.ifStmt()
	case types.REPEAT:
		node = buffer.repeatStmt()
	case types.ID:
		node = buffer.assignStmt()
	case types.READ:
		node = buffer.readStmt()
	case types.WRITE:
		node = buffer.writeStmt()
	default:
		syntaxError(buffer.token)
	}

	return node
}

func (buffer *lexBuffer) stmtSequence() *types.TreeNode {
	node := buffer.statement()
	p := node

	for buffer.token.TokenType != types.ENDFILE &&
		buffer.token.TokenType != types.END &&
		buffer.token.TokenType != types.ELSE &&
		buffer.token.TokenType != types.UNTIL {
		buffer.match(types.SEMI)
		q := buffer.statement()
		if q != nil {
			if node == nil {
				p = q
				node = p
			} else {
				// Now p cannot be nil either.
				p.Sibling = q
				p = q
			}
		}
	}

	return node
}

func (buffer *lexBuffer) lexSequence() *types.TreeNode {
	var node, p, q *types.TreeNode = nil, nil, nil

	for buffer.token.TokenType != types.ENDFILE {
		if buffer.token.TokenType == types.END ||
			buffer.token.TokenType == types.ELSE ||
			buffer.token.TokenType == types.UNTIL {
			buffer.nextToken()
		}
		if buffer.token.TokenType == types.ENDFILE {
			break
		}
		p = buffer.stmtSequence()

		if node == nil {
			node = p
		} else {
			q = node

			for q.Sibling != nil {
				q = q.Sibling
			}
			q.Sibling = p
		}
	}

	return node
}

func Lex(tokens []types.Token) *types.TreeNode {
	buffer := &lexBuffer{tokens[0], 0, tokens}

	return buffer.lexSequence()
}
<commit_msg>Updated lexer to use locale package.<commit_after>/*
The MIT License (MIT)

Copyright (c) 2016 Ivan Dejanovic

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 lexer

import (
	"errors"
	"fmt"
	"mlpl/locale"
	"mlpl/types"
	"strconv"
)

type lexBuffer struct {
	token  types.Token
	index  int
	tokens []types.Token
}

func syntaxError(token types.Token) {
	fmt.Printf(locale.Locale.LexerSyntaxError, token.Lineno)

	switch token.TokenType {
	case types.IF, types.THEN, types.ELSE, types.END, types.REPEAT, types.UNTIL, types.READ, types.WRITE:
		fmt.Printf(locale.Locale.LexerReservedWordError, token.TokenString)
	case types.ASSIGN:
		fmt.Printf(locale.Locale.LexerAssignError)
	case types.LT:
		fmt.Printf(locale.Locale.LexerLTError)
	case types.EQ:
		fmt.Printf(locale.Locale.LexerEQError)
	case types.LPAREN:
		fmt.Printf(locale.Locale.LexerLPARENError)
	case types.RPAREN:
		fmt.Printf(locale.Locale.LexerRPARENError)
	case types.SEMI:
		fmt.Printf(locale.Locale.LexerSEMIError)
	case types.PLUS:
		fmt.Printf(locale.Locale.LexerPLUSError)
	case types.MINUS:
		fmt.Printf(locale.Locale.LexerMINUSError)
	case types.TIMES:
		fmt.Printf(locale.Locale.LexerTIMESError)
	case types.OVER:
		fmt.Printf(locale.Locale.LexerOVERError)
	case types.ENDFILE:
		fmt.Printf(locale.Locale.LexerENDFILEError)
	case types.NUM:
		fmt.Printf(locale.Locale.LexerNUMError, token.TokenString)
	case types.ID:
		fmt.Printf(locale.Locale.LexerIDError, token.TokenString)
	case types.ERROR:
		fmt.Printf(locale.Locale.LexerERRORError, token.TokenString)
	default:
		// Should never happen.
		fmt.Printf(locale.Locale.LexerDEFAULTError, token.TokenType)
	}

	err := errors.New(locale.Locale.LexerABORTINGError)
	if err != nil {
		panic(err)
	}
}

func newStmtNode(kind types.StmtKind, lineno int) *types.TreeNode {
	node := new(types.TreeNode)

	node.Children = make([]*types.TreeNode, 0, 0)
	node.Sibling = nil
	node.Node = types.StmtK
	node.Stmt = kind
	node.Lineno = lineno

	return node
}

func newExpNode(kind types.ExpKind, lineno int) *types.TreeNode {
	node := new(types.TreeNode)

	node.Children = make([]*types.TreeNode, 0, 0)
	node.Sibling = nil
	node.Node = types.ExpK
	node.Exp = kind
	node.Lineno = lineno

	return node
}

func (buffer *lexBuffer) nextToken() {
	if buffer.index < len(buffer.tokens) {
		buffer.index += 1
		buffer.token = buffer.tokens[buffer.index]
	}
}

func (buffer *lexBuffer) match(expected types.TokenType) {
	if buffer.token.TokenType == expected {
		buffer.nextToken()
	} else {
		syntaxError(buffer.token)
	}
}

func (buffer *lexBuffer) factor() *types.TreeNode {
	var node *types.TreeNode
	var err error

	switch buffer.token.TokenType {
	case types.NUM:
		node = newExpNode(types.ConstK, buffer.token.Lineno)
		if buffer.token.TokenType == types.NUM {
			node.Val, err = strconv.Atoi(buffer.token.TokenString)
			if err != nil {
				panic(err)
			}
		}
		buffer.match(types.NUM)
	case types.ID:
		node = newExpNode(types.IdK, buffer.token.Lineno)
		if buffer.token.TokenType == types.ID {
			node.Name = buffer.token.TokenString
		}
		buffer.match(types.ID)
	case types.STRING:
		node = newExpNode(types.StringK, buffer.token.Lineno)
		node.ValString = buffer.token.TokenString
		buffer.match(types.STRING)
	case types.LPAREN:
		buffer.match(types.LPAREN)
		node = buffer.exp()
		buffer.match(types.RPAREN)
	default:
		syntaxError(buffer.token)
	}

	return node
}

func (buffer *lexBuffer) term() *types.TreeNode {
	node := buffer.factor()

	for buffer.token.TokenType == types.TIMES || buffer.token.TokenType == types.OVER {
		p := newExpNode(types.OpK, buffer.token.Lineno)
		p.Children = append(p.Children, node)
		p.Op = buffer.token.TokenType
		node = p
		buffer.match(buffer.token.TokenType)
		node.Children = append(node.Children, buffer.factor())
	}

	return node
}

func (buffer *lexBuffer) simpleExp() *types.TreeNode {
	node := buffer.term()

	for buffer.token.TokenType == types.PLUS || buffer.token.TokenType == types.MINUS {
		p := newExpNode(types.OpK, buffer.token.Lineno)
		p.Children = append(p.Children, node)
		p.Op = buffer.token.TokenType
		node = p
		buffer.match(buffer.token.TokenType)
		node.Children = append(node.Children, buffer.term())
	}

	return node
}

func (buffer *lexBuffer) exp() *types.TreeNode {
	node := buffer.simpleExp()

	if buffer.token.TokenType == types.LT || buffer.token.TokenType == types.EQ {
		p := newExpNode(types.OpK, buffer.token.Lineno)
		p.Children = append(p.Children, node)
		p.Op = buffer.token.TokenType
		node = p
		buffer.match(buffer.token.TokenType)
		node.Children = append(node.Children, buffer.simpleExp())
	}

	return node
}

func (buffer *lexBuffer) ifStmt() *types.TreeNode {
	node := newStmtNode(types.IfK, buffer.token.Lineno)

	buffer.match(types.IF)
	node.Children = append(node.Children, buffer.exp())
	buffer.match(types.THEN)
	node.Children = append(node.Children, buffer.stmtSequence())
	if buffer.token.TokenType == types.ELSE {
		buffer.match(types.ELSE)
		node.Children = append(node.Children, buffer.stmtSequence())
	}

	return node
}

func (buffer *lexBuffer) repeatStmt() *types.TreeNode {
	node := newStmtNode(types.RepeatK, buffer.token.Lineno)

	buffer.match(types.REPEAT)
	node.Children = append(node.Children, buffer.stmtSequence())
	buffer.match(types.UNTIL)
	node.Children = append(node.Children, buffer.exp())

	return node
}

func (buffer *lexBuffer) assignStmt() *types.TreeNode {
	node := newStmtNode(types.AssignK, buffer.token.Lineno)

	if buffer.token.TokenType == types.ID {
		node.Name = buffer.token.TokenString
	}
	buffer.match(types.ID)
	buffer.match(types.ASSIGN)
	node.Children = append(node.Children, buffer.exp())

	return node
}

func (buffer *lexBuffer) readStmt() *types.TreeNode {
	node := newStmtNode(types.ReadK, buffer.token.Lineno)

	buffer.match(types.READ)
	if buffer.token.TokenType == types.ID {
		node.Name = buffer.token.TokenString
	}
	buffer.match(types.ID)

	return node
}

func (buffer *lexBuffer) writeStmt() *types.TreeNode {
	node := newStmtNode(types.WriteK, buffer.token.Lineno)

	buffer.match(types.WRITE)
	node.Children = append(node.Children, buffer.exp())

	return node
}

func (buffer *lexBuffer) statement() *types.TreeNode {
	var node *types.TreeNode

	switch buffer.token.TokenType {
	case types.IF:
		node = buffer.ifStmt()
	case types.REPEAT:
		node = buffer.repeatStmt()
	case types.ID:
		node = buffer.assignStmt()
	case types.READ:
		node = buffer.readStmt()
	case types.WRITE:
		node = buffer.writeStmt()
	default:
		syntaxError(buffer.token)
	}

	return node
}

func (buffer *lexBuffer) stmtSequence() *types.TreeNode {
	node := buffer.statement()
	p := node

	for buffer.token.TokenType != types.ENDFILE &&
		buffer.token.TokenType != types.END &&
		buffer.token.TokenType != types.ELSE &&
		buffer.token.TokenType != types.UNTIL {
		buffer.match(types.SEMI)
		q := buffer.statement()
		if q != nil {
			if node == nil {
				p = q
				node = p
			} else {
				// Now p cannot be nil either.
				p.Sibling = q
				p = q
			}
		}
	}

	return node
}

func (buffer *lexBuffer) lexSequence() *types.TreeNode {
	var node, p, q *types.TreeNode = nil, nil, nil

	for buffer.token.TokenType != types.ENDFILE {
		if buffer.token.TokenType == types.END ||
			buffer.token.TokenType == types.ELSE ||
			buffer.token.TokenType == types.UNTIL {
			buffer.nextToken()
		}
		if buffer.token.TokenType == types.ENDFILE {
			break
		}
		p = buffer.stmtSequence()

		if node == nil {
			node = p
		} else {
			q = node

			for q.Sibling != nil {
				q = q.Sibling
			}
			q.Sibling = p
		}
	}

	return node
}

func Lex(tokens []types.Token) *types.TreeNode {
	buffer := &lexBuffer{tokens[0], 0, tokens}

	return buffer.lexSequence()
}
<|endoftext|>


<commit_before>package router551

import (
	"github.com/go51/container551"
	"github.com/go51/string551"
	"regexp"
	"runtime"
)

type routerMethod int

const (
	GET     routerMethod = 1
	POST    routerMethod = 2
	PUT     routerMethod = 4
	DELETE  routerMethod = 8
	COMMAND routerMethod = 16
	UNKNOWN routerMethod = 32
)

func (rm routerMethod) String() string {
	switch rm {
	case GET:
		return "GET"
	case POST:
		return "POST"
	case PUT:
		return "PUT"
	case DELETE:
		return "DELETE"
	case COMMAND:
		return "COMMAND"
	}
	return "UNKNOWN"
}

type ActionFunc func(c *container551.Container) interface{}

type Router struct {
	get     map[string]*route
	post    map[string]*route
	put     map[string]*route
	delete  map[string]*route
	command map[string]*route
}

var routerInstance *Router

func Load() *Router {
	if routerInstance != nil {
		return routerInstance
	}

	routerInstance = &Router{
		get:     make(map[string]*route, 0),
		post:    make(map[string]*route, 0),
		put:     make(map[string]*route, 0),
		delete:  make(map[string]*route, 0),
		command: make(map[string]*route, 0),
	}

	return routerInstance
}

type route struct {
	name        string
	pattern     string
	regex       *regexp.Regexp
	packageName string
	action      ActionFunc
	keys        []string
}

func (r *route) Name() string {
	return r.name
}

func (r *route) Keys() []string {
	return r.keys
}

func (r *route) Action() ActionFunc {
	return r.action
}

func (r *Router) Add(method routerMethod, name, pattern string, action ActionFunc) {

	pc, _, _, _ := runtime.Caller(1)
	path := runtime.FuncForPC(pc).Name()

	route := &route{
		name:        name,
		pattern:     pattern,
		regex:       nil,
		packageName: r.getPackageName(path),
		action:      action,
		keys:        []string{},
	}

	if method != COMMAND {
		route.keys = r.getKeys(pattern)
		regexPattern := pattern
		for i := 0; i < len(route.keys); i++ {
			regexPattern = string551.Replace(regexPattern, ":"+route.keys[i]+":", ".*")
		}
		route.regex = regexp.MustCompile(regexPattern)
	}

	if method&GET == GET {
		r.addGet(name, route)
	}
	if method&POST == POST {
		r.addPost(name, route)
	}
	if method&PUT == PUT {
		r.addPut(name, route)
	}
	if method&DELETE == DELETE {
		r.addDelete(name, route)
	}
	if method&COMMAND == COMMAND {
		r.addCommand(name, route)
	}
}

func (r *Router) addGet(name string, route *route) {
	r.get[name] = route
}

func (r *Router) addPost(name string, route *route) {
	r.post[name] = route
}

func (r *Router) addPut(name string, route *route) {
	r.put[name] = route
}

func (r *Router) addDelete(name string, route *route) {
	r.delete[name] = route
}

func (r *Router) addCommand(name string, route *route) {
	r.command[name] = route
}

func (r *Router) getKeys(pattern string) []string {
	keys := []string{}

	patternBytes := string551.StringToBytes(pattern)
	coron := false

	keyBytes := []byte{}

	for i := 0; i < len(patternBytes); i++ {
		if patternBytes[i] == 0x3A { // 0x3A => ":"
			if !coron {
				keyBytes = []byte{}
				coron = true
			} else {
				keys = append(keys, string551.BytesToString(keyBytes))
				coron = false
			}
		} else {
			if coron {
				keyBytes = append(keyBytes, patternBytes[i])
			}
		}
	}

	return keys
}

var regexpControllerPattern *regexp.Regexp

func (r *Router) getPackageName(path string) string {
	if regexpControllerPattern == nil {
		regexpControllerPattern = regexp.MustCompile(`\/\w+\.`)
	}

	packageName := regexpControllerPattern.FindString(path)

	return packageName[1 : len(packageName)-1]
}

func (r *Router) FindRouteByName(method, name string) *route {
	if method == GET.String() {
		return r.get[name]
	}
	if method == POST.String() {
		return r.post[name]
	}
	if method == PUT.String() {
		return r.put[name]
	}
	if method == DELETE.String() {
		return r.delete[name]
	}
	if method == COMMAND.String() {
		return r.command[name]
	}

	return nil
}

func (r *Router) FindRouteByPathMatch(method, path string) *route {
	routes := r.getRoutes(method)

	for _, route := range routes {
		if route.pattern == path {
			return route
		}
	}

	for _, route := range routes {
		if route.pattern == "/" {
			continue
		}
		if route.regex.MatchString(path) {
			return route
		}
	}

	return nil
}

func (r *Router) getRoutes(method string) map[string]*route {
	switch method {
	case GET.String():
		return r.get
	case POST.String():
		return r.post
	case PUT.String():
		return r.put
	case DELETE.String():
		return r.delete
	case COMMAND.String():
		return r.command
	}

	return make(map[string]*route)
}
<commit_msg>refs #17 route にパッケージ名を返すメソッドを実装する<commit_after>package router551

import (
	"github.com/go51/container551"
	"github.com/go51/string551"
	"regexp"
	"runtime"
)

type routerMethod int

const (
	GET     routerMethod = 1
	POST    routerMethod = 2
	PUT     routerMethod = 4
	DELETE  routerMethod = 8
	COMMAND routerMethod = 16
	UNKNOWN routerMethod = 32
)

func (rm routerMethod) String() string {
	switch rm {
	case GET:
		return "GET"
	case POST:
		return "POST"
	case PUT:
		return "PUT"
	case DELETE:
		return "DELETE"
	case COMMAND:
		return "COMMAND"
	}
	return "UNKNOWN"
}

type ActionFunc func(c *container551.Container) interface{}

type Router struct {
	get     map[string]*route
	post    map[string]*route
	put     map[string]*route
	delete  map[string]*route
	command map[string]*route
}

var routerInstance *Router

func Load() *Router {
	if routerInstance != nil {
		return routerInstance
	}

	routerInstance = &Router{
		get:     make(map[string]*route, 0),
		post:    make(map[string]*route, 0),
		put:     make(map[string]*route, 0),
		delete:  make(map[string]*route, 0),
		command: make(map[string]*route, 0),
	}

	return routerInstance
}

type route struct {
	name        string
	pattern     string
	regex       *regexp.Regexp
	packageName string
	action      ActionFunc
	keys        []string
}

func (r *route) Name() string {
	return r.name
}

func (r *route) Keys() []string {
	return r.keys
}

func (r *route) Action() ActionFunc {
	return r.action
}

func (r *route) PackageName() string {
	return r.packageName
}

func (r *Router) Add(method routerMethod, name, pattern string, action ActionFunc) {

	pc, _, _, _ := runtime.Caller(1)
	path := runtime.FuncForPC(pc).Name()

	route := &route{
		name:        name,
		pattern:     pattern,
		regex:       nil,
		packageName: r.getPackageName(path),
		action:      action,
		keys:        []string{},
	}

	if method != COMMAND {
		route.keys = r.getKeys(pattern)
		regexPattern := pattern
		for i := 0; i < len(route.keys); i++ {
			regexPattern = string551.Replace(regexPattern, ":"+route.keys[i]+":", ".*")
		}
		route.regex = regexp.MustCompile(regexPattern)
	}

	if method&GET == GET {
		r.addGet(name, route)
	}
	if method&POST == POST {
		r.addPost(name, route)
	}
	if method&PUT == PUT {
		r.addPut(name, route)
	}
	if method&DELETE == DELETE {
		r.addDelete(name, route)
	}
	if method&COMMAND == COMMAND {
		r.addCommand(name, route)
	}
}

func (r *Router) addGet(name string, route *route) {
	r.get[name] = route
}

func (r *Router) addPost(name string, route *route) {
	r.post[name] = route
}

func (r *Router) addPut(name string, route *route) {
	r.put[name] = route
}

func (r *Router) addDelete(name string, route *route) {
	r.delete[name] = route
}

func (r *Router) addCommand(name string, route *route) {
	r.command[name] = route
}

func (r *Router) getKeys(pattern string) []string {
	keys := []string{}

	patternBytes := string551.StringToBytes(pattern)
	coron := false

	keyBytes := []byte{}

	for i := 0; i < len(patternBytes); i++ {
		if patternBytes[i] == 0x3A { // 0x3A => ":"
			if !coron {
				keyBytes = []byte{}
				coron = true
			} else {
				keys = append(keys, string551.BytesToString(keyBytes))
				coron = false
			}
		} else {
			if coron {
				keyBytes = append(keyBytes, patternBytes[i])
			}
		}
	}

	return keys
}

var regexpControllerPattern *regexp.Regexp

func (r *Router) getPackageName(path string) string {
	if regexpControllerPattern == nil {
		regexpControllerPattern = regexp.MustCompile(`\/\w+\.`)
	}

	packageName := regexpControllerPattern.FindString(path)

	return packageName[1 : len(packageName)-1]
}

func (r *Router) FindRouteByName(method, name string) *route {
	if method == GET.String() {
		return r.get[name]
	}
	if method == POST.String() {
		return r.post[name]
	}
	if method == PUT.String() {
		return r.put[name]
	}
	if method == DELETE.String() {
		return r.delete[name]
	}
	if method == COMMAND.String() {
		return r.command[name]
	}

	return nil
}

func (r *Router) FindRouteByPathMatch(method, path string) *route {
	routes := r.getRoutes(method)

	for _, route := range routes {
		if route.pattern == path {
			return route
		}
	}

	for _, route := range routes {
		if route.pattern == "/" {
			continue
		}
		if route.regex.MatchString(path) {
			return route
		}
	}

	return nil
}

func (r *Router) getRoutes(method string) map[string]*route {
	switch method {
	case GET.String():
		return r.get
	case POST.String():
		return r.post
	case PUT.String():
		return r.put
	case DELETE.String():
		return r.delete
	case COMMAND.String():
		return r.command
	}

	return make(map[string]*route)
}
<|endoftext|>


<commit_before>/*
RpcClient for Go RPC Servers
Copyright (C) ITsysCOM GmbH

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package rpcclient

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	//"log/syslog"
	"math"
	"math/rand"
	"net"
	"net/http"
	"net/rpc"
	"net/rpc/jsonrpc"
	"reflect"
	"strings"
	"sync"
	"syscall"
	"time"
)

const (
	JSON_RPC       = "json"
	JSON_HTTP      = "http_jsonrpc"
	GOB_RPC        = "gob"
	INTERNAL_RPC   = "*internal"
	POOL_FIRST     = "first"
	POOL_RANDOM    = "random"
	POOL_NEXT      = "next"
	POOL_BROADCAST = "broadcast"
)

var (
	ErrReqUnsynchronized       = errors.New("REQ_UNSYNCHRONIZED")
	ErrUnsupporteServiceMethod = errors.New("UNSUPPORTED_SERVICE_METHOD")
	ErrWrongArgsType           = errors.New("WRONG_ARGS_TYPE")
	ErrWrongReplyType          = errors.New("WRONG_REPLY_TYPE")
	ErrDisconnected            = errors.New("DISCONNECTED")
	ErrReplyTimeout            = errors.New("REPLY_TIMEOUT")
	ErrFailedReconnect         = errors.New("FAILED_RECONNECT")
	//logger                     *syslog.Writer
)

func init() {
	//logger, _ = syslog.New(syslog.LOG_INFO, "RPCClient") // If we need to report anything to syslog
}

// successive Fibonacci numbers.
func Fib() func() time.Duration {
	a, b := 0, 1
	return func() time.Duration {
		a, b = b, a+b
		return time.Duration(a*10) * time.Millisecond
	}
}

func NewRpcClient(transport, addr string, connectAttempts, reconnects int, connTimeout, replyTimeout time.Duration, codec string, internalConn RpcClientConnection) (*RpcClient, error) {
	var err error
	rpcClient := &RpcClient{transport: transport, address: addr, reconnects: reconnects,
		connTimeout: connTimeout, replyTimeout: replyTimeout, codec: codec, connection: internalConn, connMux: new(sync.Mutex)}
	delay := Fib()
	for i := 0; i < connectAttempts; i++ {
		err = rpcClient.connect()
		if err == nil { //Connected so no need to reiterate
			break
		}
		time.Sleep(delay())
	}
	return rpcClient, err
}

type RpcClient struct {
	transport    string
	address      string
	reconnects   int
	connTimeout  time.Duration
	replyTimeout time.Duration
	codec        string // JSON_RPC or GOB_RPC
	connection   RpcClientConnection
	connMux      *sync.Mutex
}

func (self *RpcClient) connect() (err error) {
	self.connMux.Lock()
	defer self.connMux.Unlock()
	if self.codec == INTERNAL_RPC {
		return nil
	} else if self.codec == JSON_HTTP {
		self.connection = &HttpJsonRpcClient{httpClient: new(http.Client), url: self.address}
		return
	}
	// RPC compliant connections here, manually create connection to timeout
	netconn, err := net.DialTimeout(self.transport, self.address, self.connTimeout)
	if err != nil {
		self.connection = nil // So we don't wrap nil into the interface
		return err
	}
	if self.codec == JSON_RPC {
		self.connection = jsonrpc.NewClient(netconn)
	} else {
		self.connection = rpc.NewClient(netconn)
	}
	return
}

func (self *RpcClient) disconnect() (err error) {
	switch self.codec {
	case INTERNAL_RPC, JSON_HTTP:
	default:
		if self.connection == nil {
			return nil
		}
		self.connMux.Lock()
		self.connection.(*rpc.Client).Close()
		self.connection = nil
		self.connMux.Unlock()
	}
	return nil
}

func (self *RpcClient) reconnect() (err error) {
	if self.reconnects == 0 {
		return nil // no reconnects
	}
	self.disconnect()            // make sure we have cleared the connection so it can be garbage collected
	if self.codec == JSON_HTTP { // http client has automatic reconnects in place
		return self.connect()
	}
	i := 0
	delay := Fib()
	for {
		i++
		if self.reconnects != -1 && i > self.reconnects { // Maximum reconnects reached, -1 for infinite reconnects
			break
		}
		if err = self.connect(); err == nil { // No error on connect, succcess
			return nil
		}
		time.Sleep(delay()) // Cound not reconnect, retry
	}
	return ErrFailedReconnect
}

func (self *RpcClient) Call(serviceMethod string, args interface{}, reply interface{}) (err error) {
	if args == nil {
		return fmt.Errorf("nil rpc in argument method: %s in: %v out: %v", serviceMethod, args, reply)
	}
	if self.connection == nil {
		err = ErrDisconnected
	} else {
		errChan := make(chan error, 1)
		go func(serviceMethod string, args interface{}, reply interface{}) {
			errChan <- self.connection.Call(serviceMethod, args, reply)
		}(serviceMethod, args, reply)
		select {
		case err = <-errChan:
		case <-time.After(self.replyTimeout):
			err = ErrReplyTimeout
		}
	}
	if isNetworkError(err) && err != ErrReplyTimeout && self.reconnects != 0 { // ReplyTimeout should not reconnect since it creates loop
		if errReconnect := self.reconnect(); errReconnect != nil {
			return err
		} else { // Run command after reconnect
			return self.connection.Call(serviceMethod, args, reply)
		}
	}
	return err
}

// Connection used in RpcClient, as interface so we can combine the rpc.RpcClient with http one or websocket
type RpcClientConnection interface {
	Call(string, interface{}, interface{}) error
}

// Response received for
type JsonRpcResponse struct {
	Id     uint64
	Result *json.RawMessage
	Error  interface{}
}

type HttpJsonRpcClient struct {
	httpClient *http.Client
	id         uint64
	url        string
}

func (self *HttpJsonRpcClient) Call(serviceMethod string, args interface{}, reply interface{}) error {
	self.id += 1
	id := self.id
	data, err := json.Marshal(map[string]interface{}{
		"method": serviceMethod,
		"id":     self.id,
		"params": [1]interface{}{args},
	})
	if err != nil {
		return err
	}
	resp, err := self.httpClient.Post(self.url, "application/json", ioutil.NopCloser(strings.NewReader(string(data)))) // Closer so we automatically have close after response
	if err != nil {
		return err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	var jsonRsp JsonRpcResponse
	err = json.Unmarshal(body, &jsonRsp)
	if err != nil {
		return err
	}
	if jsonRsp.Id != id {
		return ErrReqUnsynchronized
	}
	if jsonRsp.Error != nil || jsonRsp.Result == nil {
		x, ok := jsonRsp.Error.(string)
		if !ok {
			return fmt.Errorf("invalid error %v", jsonRsp.Error)
		}
		if x == "" {
			x = "unspecified error"
		}
		return errors.New(x)
	}
	return json.Unmarshal(*jsonRsp.Result, reply)
}

type RpcClientPool struct {
	transmissionType string
	connections      []RpcClientConnection
	counter          int
	replyTimeout     time.Duration
}

func NewRpcClientPool(transmissionType string, replyTimeout time.Duration) *RpcClientPool {
	return &RpcClientPool{transmissionType: transmissionType, replyTimeout: replyTimeout}
}

func (pool *RpcClientPool) AddClient(rcc RpcClientConnection) {
	if rcc != nil {
		pool.connections = append(pool.connections, rcc)
	}
}

func (pool *RpcClientPool) Call(serviceMethod string, args interface{}, reply interface{}) (err error) {
	switch pool.transmissionType {
	case POOL_BROADCAST:
		replyChan := make(chan *rpcReplyError, len(pool.connections))
		for _, rc := range pool.connections {
			go func(conn RpcClientConnection) {
				// make a new pointer of the same type
				rpl := reflect.New(reflect.TypeOf(reflect.ValueOf(reply).Elem().Interface()))
				err := conn.Call(serviceMethod, args, rpl.Interface())
				if !isNetworkError(err) {
					replyChan <- &rpcReplyError{reply: rpl.Interface(), err: err}
				}
			}(rc)
		}
		//get first response with timeout
		var re *rpcReplyError
		select {
		case re = <-replyChan:
		case <-time.After(pool.replyTimeout):
			return ErrReplyTimeout
		}
		// put received value in the orig reply
		reflect.ValueOf(reply).Elem().Set(reflect.ValueOf(re.reply).Elem())
		return re.err
	case POOL_FIRST:
		for _, rc := range pool.connections {
			err = rc.Call(serviceMethod, args, reply)
			if isNetworkError(err) {
				continue
			}
			return
		}
	case POOL_NEXT:
		ln := len(pool.connections)
		rrIndexes := roundIndex(int(math.Mod(float64(pool.counter), float64(ln))), ln)
		pool.counter++
		for _, index := range rrIndexes {
			err = pool.connections[index].Call(serviceMethod, args, reply)
			if isNetworkError(err) {
				continue
			}
			return
		}
	case POOL_RANDOM:
		rand.Seed(time.Now().UnixNano())
		randomIndex := rand.Perm(len(pool.connections))
		for _, index := range randomIndex {
			err = pool.connections[index].Call(serviceMethod, args, reply)
			if isNetworkError(err) {
				continue
			}
			return
		}
	}
	return
}

type rpcReplyError struct {
	reply interface{}
	err   error
}

// generates round robin indexes for a slice of length max
// starting from index start
func roundIndex(start, max int) []int {
	if start < 0 {
		start = 0
	}
	result := make([]int, max)
	for i := 0; i < max; i++ {
		if start+i < max {
			result[i] = start + i
		} else {
			result[i] = int(math.Abs(float64(max - (start + i))))
		}
	}
	return result
}

func isNetworkError(err error) bool {
	if err == nil {
		return false
	}
	if operr, ok := err.(*net.OpError); ok && strings.HasSuffix(operr.Err.Error(), syscall.ECONNRESET.Error()) { // connection reset
		return true
	}
	return err == rpc.ErrShutdown ||
		err == ErrReqUnsynchronized ||
		err == ErrDisconnected ||
		err == ErrReplyTimeout ||
		strings.HasPrefix(err.Error(), "rpc: can't find service")
}
<commit_msg>Better locking to avoid reads of dynamic connection establishment<commit_after>/*
RpcClient for Go RPC Servers
Copyright (C) ITsysCOM GmbH

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package rpcclient

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log/syslog"
	"math"
	"math/rand"
	"net"
	"net/http"
	"net/rpc"
	"net/rpc/jsonrpc"
	"reflect"
	"strings"
	"sync"
	"syscall"
	"time"
)

const (
	JSON_RPC       = "json"
	JSON_HTTP      = "http_jsonrpc"
	GOB_RPC        = "gob"
	INTERNAL_RPC   = "*internal"
	POOL_FIRST     = "first"
	POOL_RANDOM    = "random"
	POOL_NEXT      = "next"
	POOL_BROADCAST = "broadcast"
)

var (
	ErrReqUnsynchronized       = errors.New("REQ_UNSYNCHRONIZED")
	ErrUnsupporteServiceMethod = errors.New("UNSUPPORTED_SERVICE_METHOD")
	ErrWrongArgsType           = errors.New("WRONG_ARGS_TYPE")
	ErrWrongReplyType          = errors.New("WRONG_REPLY_TYPE")
	ErrDisconnected            = errors.New("DISCONNECTED")
	ErrReplyTimeout            = errors.New("REPLY_TIMEOUT")
	ErrFailedReconnect         = errors.New("FAILED_RECONNECT")
	logger                     *syslog.Writer
)

func init() {
	logger, _ = syslog.New(syslog.LOG_INFO, "RPCClient") // If we need to report anything to syslog
}

// successive Fibonacci numbers.
func Fib() func() time.Duration {
	a, b := 0, 1
	return func() time.Duration {
		a, b = b, a+b
		return time.Duration(a*10) * time.Millisecond
	}
}

func NewRpcClient(transport, addr string, connectAttempts, reconnects int, connTimeout, replyTimeout time.Duration, codec string, internalConn RpcClientConnection) (*RpcClient, error) {
	var err error
	rpcClient := &RpcClient{transport: transport, address: addr, reconnects: reconnects,
		connTimeout: connTimeout, replyTimeout: replyTimeout, codec: codec, connection: internalConn}
	delay := Fib()
	for i := 0; i < connectAttempts; i++ {
		err = rpcClient.connect()
		if err == nil { //Connected so no need to reiterate
			break
		}
		time.Sleep(delay())
	}
	return rpcClient, err
}

type RpcClient struct {
	transport    string
	address      string
	reconnects   int
	connTimeout  time.Duration
	replyTimeout time.Duration
	codec        string // JSON_RPC or GOB_RPC
	connection   RpcClientConnection
	connMux      sync.RWMutex // protects connection
}

func (self *RpcClient) connect() (err error) {
	self.connMux.Lock()
	defer self.connMux.Unlock()
	if self.codec == INTERNAL_RPC {
		return
	} else if self.codec == JSON_HTTP {
		self.connection = &HttpJsonRpcClient{httpClient: new(http.Client), url: self.address}
		return
	}
	// RPC compliant connections here, manually create connection to timeout
	netconn, err := net.DialTimeout(self.transport, self.address, self.connTimeout)
	if err != nil {
		self.connection = nil // So we don't wrap nil into the interface
		return err
	}
	if self.codec == JSON_RPC {
		self.connection = jsonrpc.NewClient(netconn)
	} else {
		self.connection = rpc.NewClient(netconn)
	}
	return
}

func (self *RpcClient) isConnected() bool {
	self.connMux.RLock()
	defer self.connMux.RUnlock()
	return self.connection != nil
}

func (self *RpcClient) disconnect() (err error) {
	switch self.codec {
	case INTERNAL_RPC, JSON_HTTP:
	default:
		if !self.isConnected() {
			return nil
		}
		self.connMux.Lock()
		self.connection.(*rpc.Client).Close()
		self.connection = nil
		self.connMux.Unlock()
	}
	return nil
}

func (self *RpcClient) reconnect() (err error) {
	if self.reconnects == 0 {
		return nil // no reconnects
	}
	self.disconnect()            // make sure we have cleared the connection so it can be garbage collected
	if self.codec == JSON_HTTP { // http client has automatic reconnects in place
		return self.connect()
	}
	i := 0
	delay := Fib()
	for {
		i++
		if self.reconnects != -1 && i > self.reconnects { // Maximum reconnects reached, -1 for infinite reconnects
			break
		}
		if err = self.connect(); err == nil { // No error on connect, succcess
			return nil
		}
		time.Sleep(delay()) // Cound not reconnect, retry
	}
	return ErrFailedReconnect
}

func (self *RpcClient) Call(serviceMethod string, args interface{}, reply interface{}) (err error) {
	if args == nil {
		return fmt.Errorf("nil rpc in argument method: %s in: %v out: %v", serviceMethod, args, reply)
	}
	if !self.isConnected() {
		err = ErrDisconnected
	} else {
		errChan := make(chan error, 1)
		go func(serviceMethod string, args interface{}, reply interface{}) {
			self.connMux.RLock()
			defer self.connMux.RUnlock()
			errChan <- self.connection.Call(serviceMethod, args, reply)
		}(serviceMethod, args, reply)
		select {
		case err = <-errChan:
		case <-time.After(self.replyTimeout):
			err = ErrReplyTimeout
		}
	}
	if isNetworkError(err) && err != ErrReplyTimeout && self.reconnects != 0 { // ReplyTimeout should not reconnect since it creates loop
		if errReconnect := self.reconnect(); errReconnect != nil {
			return err
		} else { // Run command after reconnect
			self.connMux.RLock()
			defer self.connMux.RUnlock()
			return self.connection.Call(serviceMethod, args, reply)
		}
	}
	return err
}

// Connection used in RpcClient, as interface so we can combine the rpc.RpcClient with http one or websocket
type RpcClientConnection interface {
	Call(string, interface{}, interface{}) error
}

// Response received for
type JsonRpcResponse struct {
	Id     uint64
	Result *json.RawMessage
	Error  interface{}
}

type HttpJsonRpcClient struct {
	httpClient *http.Client
	id         uint64
	url        string
}

func (self *HttpJsonRpcClient) Call(serviceMethod string, args interface{}, reply interface{}) error {
	self.id += 1
	id := self.id
	data, err := json.Marshal(map[string]interface{}{
		"method": serviceMethod,
		"id":     self.id,
		"params": [1]interface{}{args},
	})
	if err != nil {
		return err
	}
	resp, err := self.httpClient.Post(self.url, "application/json", ioutil.NopCloser(strings.NewReader(string(data)))) // Closer so we automatically have close after response
	if err != nil {
		return err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	var jsonRsp JsonRpcResponse
	err = json.Unmarshal(body, &jsonRsp)
	if err != nil {
		return err
	}
	if jsonRsp.Id != id {
		return ErrReqUnsynchronized
	}
	if jsonRsp.Error != nil || jsonRsp.Result == nil {
		x, ok := jsonRsp.Error.(string)
		if !ok {
			return fmt.Errorf("invalid error %v", jsonRsp.Error)
		}
		if x == "" {
			x = "unspecified error"
		}
		return errors.New(x)
	}
	return json.Unmarshal(*jsonRsp.Result, reply)
}

type RpcClientPool struct {
	transmissionType string
	connections      []RpcClientConnection
	counter          int
	replyTimeout     time.Duration
}

func NewRpcClientPool(transmissionType string, replyTimeout time.Duration) *RpcClientPool {
	return &RpcClientPool{transmissionType: transmissionType, replyTimeout: replyTimeout}
}

func (pool *RpcClientPool) AddClient(rcc RpcClientConnection) {
	if rcc != nil {
		pool.connections = append(pool.connections, rcc)
	}
}

func (pool *RpcClientPool) Call(serviceMethod string, args interface{}, reply interface{}) (err error) {
	switch pool.transmissionType {
	case POOL_BROADCAST:
		replyChan := make(chan *rpcReplyError, len(pool.connections))
		for _, rc := range pool.connections {
			go func(conn RpcClientConnection) {
				// make a new pointer of the same type
				rpl := reflect.New(reflect.TypeOf(reflect.ValueOf(reply).Elem().Interface()))
				err := conn.Call(serviceMethod, args, rpl.Interface())
				if !isNetworkError(err) {
					replyChan <- &rpcReplyError{reply: rpl.Interface(), err: err}
				}
			}(rc)
		}
		//get first response with timeout
		var re *rpcReplyError
		select {
		case re = <-replyChan:
		case <-time.After(pool.replyTimeout):
			return ErrReplyTimeout
		}
		// put received value in the orig reply
		reflect.ValueOf(reply).Elem().Set(reflect.ValueOf(re.reply).Elem())
		return re.err
	case POOL_FIRST:
		for _, rc := range pool.connections {
			err = rc.Call(serviceMethod, args, reply)
			if isNetworkError(err) {
				continue
			}
			return
		}
	case POOL_NEXT:
		ln := len(pool.connections)
		rrIndexes := roundIndex(int(math.Mod(float64(pool.counter), float64(ln))), ln)
		pool.counter++
		for _, index := range rrIndexes {
			err = pool.connections[index].Call(serviceMethod, args, reply)
			if isNetworkError(err) {
				continue
			}
			return
		}
	case POOL_RANDOM:
		rand.Seed(time.Now().UnixNano())
		randomIndex := rand.Perm(len(pool.connections))
		for _, index := range randomIndex {
			err = pool.connections[index].Call(serviceMethod, args, reply)
			if isNetworkError(err) {
				continue
			}
			return
		}
	}
	return
}

type rpcReplyError struct {
	reply interface{}
	err   error
}

// generates round robin indexes for a slice of length max
// starting from index start
func roundIndex(start, max int) []int {
	if start < 0 {
		start = 0
	}
	result := make([]int, max)
	for i := 0; i < max; i++ {
		if start+i < max {
			result[i] = start + i
		} else {
			result[i] = int(math.Abs(float64(max - (start + i))))
		}
	}
	return result
}

func isNetworkError(err error) bool {
	if err == nil {
		return false
	}
	if operr, ok := err.(*net.OpError); ok && strings.HasSuffix(operr.Err.Error(), syscall.ECONNRESET.Error()) { // connection reset
		return true
	}
	return err == rpc.ErrShutdown ||
		err == ErrReqUnsynchronized ||
		err == ErrDisconnected ||
		err == ErrReplyTimeout ||
		strings.HasPrefix(err.Error(), "rpc: can't find service")
}
<|endoftext|>


<commit_before>package logentries_goclient

import (
	"net/http"
	"encoding/json"
	"io/ioutil"
	"bytes"
)

type HttpClient struct {
	httpClient    *http.Client
}

func (httpClient *HttpClient) Get(url string, headers map[string]string, out interface{}) (*http.Response, error) {
	if req, err := httpClient.prepareRequest(http.MethodGet, url, headers, nil); err != nil {
		return nil, err
	} else {
		return httpClient.performRequest(req, out)
	}
}

func (httpClient *HttpClient) Post(url string, headers map[string]string, in interface{}, out interface{}) (*http.Response, error) {
	if req, err := httpClient.prepareRequest(http.MethodPost, url, headers, in); err != nil {
		return nil, err
	} else {
		return httpClient.performRequest(req, out)
	}
}

func (httpClient *HttpClient) Put(url string, headers map[string]string, in interface{}, out interface{}) (*http.Response, error) {
	if req, err := httpClient.prepareRequest(http.MethodPut, url, headers, in); err != nil {
		return nil, err
	} else {
		return httpClient.performRequest(req, out)
	}
}

func (httpClient *HttpClient) Delete(url string, headers map[string]string) (*http.Response, error) {
	if req, err := httpClient.prepareRequest(http.MethodDelete, url, headers, nil); err != nil {
		return nil, err
	} else {
		return httpClient.performRequest(req, nil)
	}
}

func (httpClient *HttpClient) prepareRequest(method, url string, headers map[string]string, in interface{}) (*http.Request, error) {

	var body []byte
	var err error
	if in != nil {
		body, err = json.Marshal(in)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, url, bytes.NewBufferString(string(body)))
	if err != nil {
		return nil, err
	}
	for key, value := range headers {
		req.Header.Set(key, value)
	}
	return req, nil
}

func (httpClient *HttpClient) performRequest(request *http.Request, out interface{}) (*http.Response, error) {
	resp, err := httpClient.httpClient.Do(request)
	defer resp.Body.Close()

	if err != nil {
		return nil, err
	}

	if out != nil {
		var body []byte
		if body, err = ioutil.ReadAll(resp.Body); err != nil {
			return nil, err
		}
		if err = json.Unmarshal(body, &out); err != nil {
			return nil, err
		}
	}
	return resp, nil
}<commit_msg>Fix bug where put/post req did not have proper content type<commit_after>package logentries_goclient

import (
	"net/http"
	"encoding/json"
	"io/ioutil"
	"strings"
	"fmt"
)

type HttpClient struct {
	httpClient    *http.Client
}

func (httpClient *HttpClient) Get(url string, headers map[string]string, out interface{}) (*http.Response, error) {
	if req, err := httpClient.prepareRequest(http.MethodGet, url, headers, nil); err != nil {
		return nil, err
	} else {
		return httpClient.performRequest(req, out)
	}
}

func (httpClient *HttpClient) Post(url string, headers map[string]string, in interface{}, out interface{}) (*http.Response, error) {
	headers["Content-Type"] = "application/json"
	if req, err := httpClient.prepareRequest(http.MethodPost, url, headers, in); err != nil {
		return nil, err
	} else {
		return httpClient.performRequest(req, out)
	}
}

func (httpClient *HttpClient) Put(url string, headers map[string]string, in interface{}, out interface{}) (*http.Response, error) {
	headers["Content-Type"] = "application/json"
	if req, err := httpClient.prepareRequest(http.MethodPut, url, headers, in); err != nil {
		return nil, err
	} else {
		return httpClient.performRequest(req, out)
	}
}

func (httpClient *HttpClient) Delete(url string, headers map[string]string) (*http.Response, error) {
	if req, err := httpClient.prepareRequest(http.MethodDelete, url, headers, nil); err != nil {
		return nil, err
	} else {
		return httpClient.performRequest(req, nil)
	}
}

func (httpClient *HttpClient) prepareRequest(method, url string, headers map[string]string, in interface{}) (*http.Request, error) {

	var body []byte
	var err error
	if in != nil {
		body, err = json.Marshal(in)
		fmt.Println(string(body))
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, url, strings.NewReader(string(body)))
	if err != nil {
		return nil, err
	}
	for key, value := range headers {
		req.Header.Set(key, value)
	}
	return req, nil
}

func (httpClient *HttpClient) performRequest(req *http.Request, out interface{}) (*http.Response, error) {
	resp, err := httpClient.httpClient.Do(req)
	defer resp.Body.Close()

	if err != nil {
		return nil, fmt.Errorf("request %s %s %s failed. Response Error [%s]: '%s'", req.Method, req.URL, req.Proto, resp.Status, err.Error())
	}

	if out != nil {
		var body []byte
		if body, err = ioutil.ReadAll(resp.Body); err != nil {
			return nil, err
		}
		if err = json.Unmarshal(body, &out); err != nil {
			return nil, fmt.Errorf("unable to unmarshal response body ['%s'] for request = '%s %s %s'. Response = '%s'",  err.Error(), req.Method, req.URL, req.Proto, resp.Status)
		}
	}
	return resp, nil
}<|endoftext|>


<commit_before>package wikidump

import (
	"bytes"
	"golang.org/x/text/unicode/norm"
	"html"
	"regexp"
	"strings"
	"unicode"
	"unicode/utf8"
)

var (
	starttag = regexp.MustCompile(`^<[a-z][^>]*>`)
	endtag   = regexp.MustCompile(`^</[a-z]+>`)
)

// Get rid of tables, template calls, quasi-XML. Throws away their content.
//
// Assumes tables, templates and tags are properly nested, except for spurious
// end-of-{table,template,element} tags, which are ignored.
func Cleanup(s string) string {
	var depth int
	output := bytes.NewBuffer(make([]byte, 0, len(s)))

	for {
		next := strings.IndexAny(s, "{}|<")
		if next == -1 {
			if depth == 0 {
				output.WriteString(s)
			}
			break
		}

		if depth == 0 {
			output.WriteString(s[:next])
		}
		s = s[next:]

		var skip int
		if strings.HasPrefix(s, "{{") || strings.HasPrefix(s, "{|") {
			depth++
			skip = 2
		} else if strings.HasPrefix(s, "|}") || strings.HasPrefix(s, "}}") {
			if depth > 0 {
				depth--
			}
			skip = 2
		} else if s[0] != '<' {
			// This case prevents regexp matching for a 20% speedup.
			skip = 1
		} else if span := starttag.FindStringIndex(s); span != nil {
			depth++
			skip = span[1]
		} else if span := endtag.FindStringIndex(s); span != nil {
			if depth > 0 {
				depth--
			}
			skip = span[1]
		} else {
			skip = 1
		}

		// If skip == 1, we didn't find a tag/table marker.
		if skip == 1 && depth == 0 && len(s) > 0 {
			output.WriteByte(s[0])
		}
		s = s[skip:]
	}
	return norm.NFC.String(html.UnescapeString(output.String()))
}

// A link to the article Target with anchor text Anchor.
type Link struct {
	Anchor, Target string
}

var (
	linkRE     = regexp.MustCompile(`(\w*)\[\[([^]]+)\]\](\w*)`)
	whitespace = regexp.MustCompile(`[\s_]+`)
)

func normSpace(s string) string {
	s = whitespace.ReplaceAllLiteralString(s, " ")
	return strings.TrimSpace(s)
}

// Extract all the wikilinks from s. Returns a frequency table.
func ExtractLinks(s string) map[Link]int {
	freq := make(map[Link]int)

	for _, candidate := range linkRE.FindAllStringSubmatch(s, -1) {
		before, l, after := candidate[1], candidate[2], candidate[3]

		var target, anchor string
		if pipe := strings.IndexByte(l, '|'); pipe != -1 {
			target, anchor = l[:pipe], l[pipe+1:]
		} else {
			target = l
			anchor = l
		}

		// If the anchor contains a colon, assume it's a file or category link.
		// XXX Maybe skip matches for `:\s`? Proper solution would parse the
		// dump to find non-main namespace prefixes.
		if strings.IndexByte(target, ':') != -1 {
			continue
		}

		// Remove section links.
		if hash := strings.IndexByte(target, '#'); hash == 0 {
			continue
		} else if hash != -1 {
			target = target[:hash]
		}

		// Normalize to the format used in <redirect> elements:
		// uppercase first character, spaces instead of underscores.
		target = normSpace(target)
		first, size := utf8.DecodeRuneInString(target)
		// XXX Upper case or title case? Should look up the difference...
		if unicode.IsLower(first) {
			target = string(unicode.ToUpper(first)) + target[size:]
		}

		anchor = before + anchor + after
		freq[Link{anchor, target}]++
	}
	return freq
}
<commit_msg>Speed up link extraction by some 20%.<commit_after>package wikidump

import (
	"bytes"
	"golang.org/x/text/unicode/norm"
	"html"
	"regexp"
	"strings"
	"unicode"
	"unicode/utf8"
)

var (
	starttag = regexp.MustCompile(`^<[a-z][^>]*>`)
	endtag   = regexp.MustCompile(`^</[a-z]+>`)
)

// Get rid of tables, template calls, quasi-XML. Throws away their content.
//
// Assumes tables, templates and tags are properly nested, except for spurious
// end-of-{table,template,element} tags, which are ignored.
func Cleanup(s string) string {
	var depth int
	output := bytes.NewBuffer(make([]byte, 0, len(s)))

	for {
		next := strings.IndexAny(s, "{}|<")
		if next == -1 {
			if depth == 0 {
				output.WriteString(s)
			}
			break
		}

		if depth == 0 {
			output.WriteString(s[:next])
		}
		s = s[next:]

		var skip int
		if strings.HasPrefix(s, "{{") || strings.HasPrefix(s, "{|") {
			depth++
			skip = 2
		} else if strings.HasPrefix(s, "|}") || strings.HasPrefix(s, "}}") {
			if depth > 0 {
				depth--
			}
			skip = 2
		} else if s[0] != '<' {
			// This case prevents regexp matching for a 20% speedup.
			skip = 1
		} else if span := starttag.FindStringIndex(s); span != nil {
			depth++
			skip = span[1]
		} else if span := endtag.FindStringIndex(s); span != nil {
			if depth > 0 {
				depth--
			}
			skip = span[1]
		} else {
			skip = 1
		}

		// If skip == 1, we didn't find a tag/table marker.
		if skip == 1 && depth == 0 && len(s) > 0 {
			output.WriteByte(s[0])
		}
		s = s[skip:]
	}
	return norm.NFC.String(html.UnescapeString(output.String()))
}

// A link to the article Target with anchor text Anchor.
type Link struct {
	Anchor, Target string
}

func normSpace(s string) string {
	parts := strings.FieldsFunc(s, func(c rune) bool {
		return c == '_' || unicode.IsSpace(c)
	})
	return strings.Join(parts, " ")
}

var linkRE = regexp.MustCompile(`\w*\[\[[^]]+\]\]\w*`)

// Extract all the wikilinks from s. Returns a frequency table.
func ExtractLinks(s string) map[Link]int {
	freq := make(map[Link]int)

	for _, candidate := range linkRE.FindAllStringSubmatch(s, -1) {
		// Parse complex links like "foo[[bar|baz]]quux". We used to do this
		// with capturing groups in linkRE, but those are *slow*.
		text := candidate[0]
		openbrack := strings.IndexByte(text, '[')
		closebrack := strings.LastIndex(text, "]")
		before := text[:openbrack]
		after := text[closebrack+1:]
		mid := text[openbrack+2:closebrack-1]

		var target, anchor string
		if pipe := strings.IndexByte(mid, '|'); pipe != -1 {
			target, anchor = mid[:pipe], mid[pipe+1:]
		} else {
			target = mid
			anchor = mid
		}

		// If the anchor contains a colon, assume it's a file or category link.
		// XXX Maybe skip matches for `:\s`? Proper solution would parse the
		// dump to find non-main namespace prefixes.
		if strings.IndexByte(target, ':') != -1 {
			continue
		}

		// Remove section links.
		if hash := strings.IndexByte(target, '#'); hash == 0 {
			continue
		} else if hash != -1 {
			target = target[:hash]
		}

		// Normalize to the format used in <redirect> elements:
		// uppercase first character, spaces instead of underscores.
		target = normSpace(target)
		first, size := utf8.DecodeRuneInString(target)
		// XXX Upper case or title case? Should look up the difference...
		if unicode.IsLower(first) {
			target = string(unicode.ToUpper(first)) + target[size:]
		}

		anchor = before + anchor + after
		freq[Link{anchor, target}]++
	}
	return freq
}
<|endoftext|>


<commit_before>package main

import (
    "image/png"
    "os"
    "io"
    "bytes"
    "log"
    "functorama.com/demo/libgodelbrot"
)

func main() {
    var input io.Reader = os.Stdin
    var output io.Writer = os.Stdout

    buff := bytes.Buffer{}
    count, readErr := buff.ReadFrom(input)

    if readErr != nil {
        log.Fatal("Read error after ", count, "bytes:", readErr)
    }

    desc, jsonErr := libgodelbrot.FromJSON(buff.Bytes())

    if jsonErr != nil {
        log.Fatal("Error decoding JSON:", jsonErr)
    }

    picture, renderErr := libgodelbrot.Render(desc)

    if renderErr != nil {
        log.Fatal("Render errror:", renderErr)
    }

    encodeErr := png.Encode(output, picture)

    if encodeErr != nil {
        log.Fatal("Encoding error:", encodeErr)
    }
}<commit_msg>Use all available cores<commit_after>package main

import (
    "image/png"
    "os"
    "io"
    "bytes"
    "log"
    "runtime"
    "functorama.com/demo/libgodelbrot"
)

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())

    var input io.Reader = os.Stdin
    var output io.Writer = os.Stdout

    buff := bytes.Buffer{}
    count, readErr := buff.ReadFrom(input)

    if readErr != nil {
        log.Fatal("Read error after ", count, "bytes:", readErr)
    }

    desc, jsonErr := libgodelbrot.FromJSON(buff.Bytes())

    if jsonErr != nil {
        log.Fatal("Error decoding JSON:", jsonErr)
    }

    picture, renderErr := libgodelbrot.Render(desc)

    if renderErr != nil {
        log.Fatal("Render errror:", renderErr)
    }

    encodeErr := png.Encode(output, picture)

    if encodeErr != nil {
        log.Fatal("Encoding error:", encodeErr)
    }
}<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/boltdb/bolt"
)

const bucketName = "rsvps"

var (
	db *bolt.DB

	dbFile   = flag.String("db", "./rsvp.boltdb", "database file")
	add      = flag.Bool("add", false, "add an rsvp to the database")
	list     = flag.Bool("list", false, "list rsvp's in the database")
	name     = flag.String("name", "", "name of person to add to database")
	email    = flag.String("email", "", "email of person to add to database")
	httpServ = flag.String("http", "", "start http server ([host]:port)")
	rootDir  = flag.String("root", "./", "root directory for http server")
)

func main() {
	var err error

	flag.Parse()
	if !*add && !*list && *httpServ == "" {
		fmt.Println("Error: one of the following must be given: -add, -list, -http")
		flag.Usage()
		return
	}

	if *add && (*name == "" || *email == "") {
		fmt.Println("Error: both name and email must be given for adding to the database")
		flag.Usage()
		return
	}

	db, err = bolt.Open(*dbFile, 0600, nil)
	if err != nil {
		fmt.Println("Failed to open Database:", err)
		return
	}
	defer db.Close()

	db.Update(func(tx *bolt.Tx) (err error) {
		_, err = tx.CreateBucketIfNotExists([]byte("rsvps"))
		if err != nil {
			fmt.Println("Failed to create bucket:", err)
		}

		return err
	})

	if *add {
		if err := addRsvp(*name, *email); err != nil {
			fmt.Println("Failed to add RSVP:", err)
		}
	} else if *list {
		listRsvp(os.Stdout)
	} else if *httpServ != "" {
		echoServer()
	}
}
<commit_msg>split our arg handling to lower cyclo complexity<commit_after>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/boltdb/bolt"
)

const bucketName = "rsvps"

var (
	db *bolt.DB

	dbFile   = flag.String("db", "./rsvp.boltdb", "database file")
	add      = flag.Bool("add", false, "add an rsvp to the database")
	list     = flag.Bool("list", false, "list rsvp's in the database")
	name     = flag.String("name", "", "name of person to add to database")
	email    = flag.String("email", "", "email of person to add to database")
	httpServ = flag.String("http", "", "start http server ([host]:port)")
	rootDir  = flag.String("root", "./", "root directory for http server")
)

func parseArguments() {
	flag.Parse()

	if !*add && !*list && *httpServ == "" {
		fmt.Println("Error: one of the following must be given: -add, -list, -http")
		flag.Usage()
		os.Exit(1)
	}

	if *add && (*name == "" || *email == "") {
		fmt.Println("Error: both name and email must be given for adding to the database")
		flag.Usage()
		os.Exit(1)
	}
}

func main() {
	var err error

	parseArguments()

	db, err = bolt.Open(*dbFile, 0600, nil)
	if err != nil {
		fmt.Println("Failed to open Database:", err)
		return
	}
	defer db.Close()

	db.Update(func(tx *bolt.Tx) (err error) {
		_, err = tx.CreateBucketIfNotExists([]byte("rsvps"))
		if err != nil {
			fmt.Println("Failed to create bucket:", err)
		}

		return err
	})

	if *add {
		if err := addRsvp(*name, *email); err != nil {
			fmt.Println("Failed to add RSVP:", err)
		}
	} else if *list {
		listRsvp(os.Stdout)
	} else if *httpServ != "" {
		echoServer()
	}
}
<|endoftext|>


<commit_before>package common

import (
	"context"
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
)

type StepCreateEncryptedAMICopy struct {
	image             *ec2.Image
	KeyID             string
	EncryptBootVolume bool
	Name              string
	AMIMappings       []BlockDevice
}

func (s *StepCreateEncryptedAMICopy) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
	ec2conn := state.Get("ec2").(*ec2.EC2)
	ui := state.Get("ui").(packer.Ui)
	kmsKeyId := s.KeyID

	// Encrypt boot not set, so skip step
	if !s.EncryptBootVolume {
		if kmsKeyId != "" {
			log.Printf("Ignoring KMS Key ID: %s, encrypted=false", kmsKeyId)
		}
		return multistep.ActionContinue
	}

	ui.Say("Creating Encrypted AMI Copy")

	amis := state.Get("amis").(map[string]string)
	var region, id string
	if amis != nil {
		for region, id = range amis {
			break // There is only ever one region:ami pair in this map
		}
	}

	ui.Say(fmt.Sprintf("Copying AMI: %s(%s)", region, id))

	if kmsKeyId != "" {
		ui.Say(fmt.Sprintf("Encrypting with KMS Key ID: %s", kmsKeyId))
	}

	copyOpts := &ec2.CopyImageInput{
		Name:          &s.Name, // Try to overwrite existing AMI
		SourceImageId: aws.String(id),
		SourceRegion:  aws.String(region),
		Encrypted:     aws.Bool(true),
		KmsKeyId:      aws.String(kmsKeyId),
	}

	copyResp, err := ec2conn.CopyImage(copyOpts)
	if err != nil {
		err := fmt.Errorf("Error copying AMI: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Wait for the copy to become ready
	ui.Say("Waiting for AMI copy to become ready...")
	if err := WaitUntilAMIAvailable(ctx, ec2conn, *copyResp.ImageId); err != nil {
		err := fmt.Errorf("Error waiting for AMI Copy: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Get the encrypted AMI image, we need the new snapshot id's
	encImagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{aws.String(*copyResp.ImageId)}})
	if err != nil {
		err := fmt.Errorf("Error searching for AMI: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}
	encImage := encImagesResp.Images[0]
	var encSnapshots []string
	for _, blockDevice := range encImage.BlockDeviceMappings {
		if blockDevice.Ebs != nil && blockDevice.Ebs.SnapshotId != nil {
			encSnapshots = append(encSnapshots, *blockDevice.Ebs.SnapshotId)
		}
	}

	// Get the unencrypted AMI image
	unencImagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{aws.String(id)}})
	if err != nil {
		err := fmt.Errorf("Error searching for AMI: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}
	unencImage := unencImagesResp.Images[0]

	// Remove unencrypted AMI
	ui.Say("Deregistering unencrypted AMI")
	deregisterOpts := &ec2.DeregisterImageInput{ImageId: aws.String(id)}
	if _, err := ec2conn.DeregisterImage(deregisterOpts); err != nil {
		ui.Error(fmt.Sprintf("Error deregistering AMI, may still be around: %s", err))
		return multistep.ActionHalt
	}

	// Remove associated unencrypted snapshot(s)
	ui.Say("Deleting unencrypted snapshots")
	snapshots := state.Get("snapshots").(map[string][]string)

OuterLoop:
	for _, blockDevice := range unencImage.BlockDeviceMappings {
		if blockDevice.Ebs != nil && blockDevice.Ebs.SnapshotId != nil {
			// If this packer run didn't create it, then don't delete it
			for _, origDevice := range s.AMIMappings {
				if origDevice.SnapshotId == *blockDevice.Ebs.SnapshotId {
					ui.Message(fmt.Sprintf("Keeping Snapshot ID: %s", *blockDevice.Ebs.SnapshotId))
					continue OuterLoop
				}
			}

			ui.Message(fmt.Sprintf("Deleting Snapshot ID: %s", *blockDevice.Ebs.SnapshotId))
			deleteSnapOpts := &ec2.DeleteSnapshotInput{
				SnapshotId: aws.String(*blockDevice.Ebs.SnapshotId),
			}
			if _, err := ec2conn.DeleteSnapshot(deleteSnapOpts); err != nil {
				ui.Error(fmt.Sprintf("Error deleting snapshot, may still be around: %s", err))
				return multistep.ActionHalt
			}
		}
	}

	// Replace original AMI ID with Encrypted ID in state
	amis[region] = *copyResp.ImageId
	snapshots[region] = encSnapshots
	state.Put("amis", amis)
	state.Put("snapshots", snapshots)

	imagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{copyResp.ImageId}})
	if err != nil {
		err := fmt.Errorf("Error searching for AMI: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}
	s.image = imagesResp.Images[0]

	return multistep.ActionContinue
}

func (s *StepCreateEncryptedAMICopy) Cleanup(state multistep.StateBag) {
	if s.image == nil {
		return
	}

	_, cancelled := state.GetOk(multistep.StateCancelled)
	_, halted := state.GetOk(multistep.StateHalted)
	if !cancelled && !halted {
		return
	}

	ec2conn := state.Get("ec2").(*ec2.EC2)
	ui := state.Get("ui").(packer.Ui)

	ui.Say("Deregistering the AMI because cancellation or error...")
	deregisterOpts := &ec2.DeregisterImageInput{ImageId: s.image.ImageId}
	if _, err := ec2conn.DeregisterImage(deregisterOpts); err != nil {
		ui.Error(fmt.Sprintf("Error deregistering AMI, may still be around: %s", err))
		return
	}
}
<commit_msg>move snapshot deletion to cleanup; this allows people with tag-specific permissions to perform snapshot deletion operation<commit_after>package common

import (
	"context"
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
)

type StepCreateEncryptedAMICopy struct {
	image             *ec2.Image
	KeyID             string
	EncryptBootVolume bool
	Name              string
	AMIMappings       []BlockDevice
	ToDelete          []*string
}

func (s *StepCreateEncryptedAMICopy) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
	ec2conn := state.Get("ec2").(*ec2.EC2)
	ui := state.Get("ui").(packer.Ui)
	kmsKeyId := s.KeyID

	// Encrypt boot not set, so skip step
	if !s.EncryptBootVolume {
		if kmsKeyId != "" {
			log.Printf("Ignoring KMS Key ID: %s, encrypted=false", kmsKeyId)
		}
		return multistep.ActionContinue
	}

	ui.Say("Creating Encrypted AMI Copy")

	amis := state.Get("amis").(map[string]string)
	var region, id string
	if amis != nil {
		for region, id = range amis {
			break // There is only ever one region:ami pair in this map
		}
	}

	ui.Say(fmt.Sprintf("Copying AMI: %s(%s)", region, id))

	if kmsKeyId != "" {
		ui.Say(fmt.Sprintf("Encrypting with KMS Key ID: %s", kmsKeyId))
	}

	copyOpts := &ec2.CopyImageInput{
		Name:          &s.Name, // Try to overwrite existing AMI
		SourceImageId: aws.String(id),
		SourceRegion:  aws.String(region),
		Encrypted:     aws.Bool(true),
		KmsKeyId:      aws.String(kmsKeyId),
	}

	copyResp, err := ec2conn.CopyImage(copyOpts)
	if err != nil {
		err := fmt.Errorf("Error copying AMI: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Wait for the copy to become ready
	ui.Say("Waiting for AMI copy to become ready...")
	if err := WaitUntilAMIAvailable(ctx, ec2conn, *copyResp.ImageId); err != nil {
		err := fmt.Errorf("Error waiting for AMI Copy: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Get the encrypted AMI image, we need the new snapshot id's
	encImagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{aws.String(*copyResp.ImageId)}})
	if err != nil {
		err := fmt.Errorf("Error searching for AMI: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}
	encImage := encImagesResp.Images[0]
	var encSnapshots []string
	for _, blockDevice := range encImage.BlockDeviceMappings {
		if blockDevice.Ebs != nil && blockDevice.Ebs.SnapshotId != nil {
			encSnapshots = append(encSnapshots, *blockDevice.Ebs.SnapshotId)
		}
	}

	// Get the unencrypted AMI image
	unencImagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{aws.String(id)}})
	if err != nil {
		err := fmt.Errorf("Error searching for AMI: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}
	unencImage := unencImagesResp.Images[0]

	// Remove unencrypted AMI
	ui.Say("Deregistering unencrypted AMI")
	deregisterOpts := &ec2.DeregisterImageInput{ImageId: aws.String(id)}
	if _, err := ec2conn.DeregisterImage(deregisterOpts); err != nil {
		ui.Error(fmt.Sprintf("Error deregistering AMI, may still be around: %s", err))
		return multistep.ActionHalt
	}

	// Figure out which unencrypted snapshot(s) to delete
	snapshots := state.Get("snapshots").(map[string][]string)

OuterLoop:
	for _, blockDevice := range unencImage.BlockDeviceMappings {
		if blockDevice.Ebs != nil && blockDevice.Ebs.SnapshotId != nil {
			// If this packer run didn't create it, then don't delete it
			for _, origDevice := range s.AMIMappings {
				if origDevice.SnapshotId == *blockDevice.Ebs.SnapshotId {
					ui.Message(fmt.Sprintf("Keeping Snapshot ID: %s", *blockDevice.Ebs.SnapshotId))
					continue OuterLoop
				}
			}
			s.ToDelete = append(s.ToDelete, aws.String(*blockDevice.Ebs.SnapshotId))
		}
	}

	// Replace original AMI ID with Encrypted ID in state
	amis[region] = *copyResp.ImageId
	snapshots[region] = encSnapshots
	state.Put("amis", amis)
	state.Put("snapshots", snapshots)

	imagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{copyResp.ImageId}})
	if err != nil {
		err := fmt.Errorf("Error searching for AMI: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}
	s.image = imagesResp.Images[0]

	return multistep.ActionContinue
}

func (s *StepCreateEncryptedAMICopy) Cleanup(state multistep.StateBag) {
	if s.image == nil {
		return
	}

	_, cancelled := state.GetOk(multistep.StateCancelled)
	_, halted := state.GetOk(multistep.StateHalted)
	if !cancelled && !halted {
		return
	}

	ec2conn := state.Get("ec2").(*ec2.EC2)
	ui := state.Get("ui").(packer.Ui)

	ui.Say("Deregistering the AMI because cancellation or error...")
	deregisterOpts := &ec2.DeregisterImageInput{ImageId: s.image.ImageId}
	if _, err := ec2conn.DeregisterImage(deregisterOpts); err != nil {
		ui.Error(fmt.Sprintf("Error deregistering AMI, may still be around: %s", err))
		return
	}

	ui.Say("Deleting unencrypted snapshots")
	for _, snap := range s.ToDelete {
		ui.Message(fmt.Sprintf("Deleting Snapshot ID: %s", *snap))
		deleteSnapOpts := &ec2.DeleteSnapshotInput{
			SnapshotId: snap,
		}
		if _, err := ec2conn.DeleteSnapshot(deleteSnapOpts); err != nil {
			ui.Error(fmt.Sprintf("Error deleting snapshot, may still be around: %s", err))
			return
		}
	}
}
<|endoftext|>


<commit_before>package client

import (
	"encoding/base64"
	"encoding/json"
	"io"
	"net/http"
	"net/url"
	"strconv"

	"golang.org/x/net/context"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
)

// ImageBuild sends request to the daemon to build images.
// The Body in the response implement an io.ReadCloser and it's up to the caller to
// close it.
func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
	query, err := cli.imageBuildOptionsToQuery(options)
	if err != nil {
		return types.ImageBuildResponse{}, err
	}

	headers := http.Header(make(map[string][]string))
	buf, err := json.Marshal(options.AuthConfigs)
	if err != nil {
		return types.ImageBuildResponse{}, err
	}
	headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
	headers.Set("Content-Type", "application/x-tar")

	serverResp, err := cli.postRaw(ctx, "/build", query, buildContext, headers)
	if err != nil {
		return types.ImageBuildResponse{}, err
	}

	osType := getDockerOS(serverResp.header.Get("Server"))

	return types.ImageBuildResponse{
		Body:   serverResp.body,
		OSType: osType,
	}, nil
}

func (cli *Client) imageBuildOptionsToQuery(options types.ImageBuildOptions) (url.Values, error) {
	query := url.Values{
		"t":           options.Tags,
		"securityopt": options.SecurityOpt,
	}
	if options.SuppressOutput {
		query.Set("q", "1")
	}
	if options.RemoteContext != "" {
		query.Set("remote", options.RemoteContext)
	}
	if options.NoCache {
		query.Set("nocache", "1")
	}
	if options.Remove {
		query.Set("rm", "1")
	} else {
		query.Set("rm", "0")
	}

	if options.ForceRemove {
		query.Set("forcerm", "1")
	}

	if options.PullParent {
		query.Set("pull", "1")
	}

	if options.Squash {
		if err := cli.NewVersionError("1.25", "squash"); err != nil {
			return query, err
		}
		query.Set("squash", "1")
	}

	if !container.Isolation.IsDefault(options.Isolation) {
		query.Set("isolation", string(options.Isolation))
	}

	query.Set("cpusetcpus", options.CPUSetCPUs)
	query.Set("networkmode", options.NetworkMode)
	query.Set("cpusetmems", options.CPUSetMems)
	query.Set("cpushares", strconv.FormatInt(options.CPUShares, 10))
	query.Set("cpuquota", strconv.FormatInt(options.CPUQuota, 10))
	query.Set("cpuperiod", strconv.FormatInt(options.CPUPeriod, 10))
	query.Set("memory", strconv.FormatInt(options.Memory, 10))
	query.Set("memswap", strconv.FormatInt(options.MemorySwap, 10))
	query.Set("cgroupparent", options.CgroupParent)
	query.Set("shmsize", strconv.FormatInt(options.ShmSize, 10))
	query.Set("dockerfile", options.Dockerfile)

	ulimitsJSON, err := json.Marshal(options.Ulimits)
	if err != nil {
		return query, err
	}
	query.Set("ulimits", string(ulimitsJSON))

	buildArgsJSON, err := json.Marshal(options.BuildArgs)
	if err != nil {
		return query, err
	}
	query.Set("buildargs", string(buildArgsJSON))

	labelsJSON, err := json.Marshal(options.Labels)
	if err != nil {
		return query, err
	}
	query.Set("labels", string(labelsJSON))

	cacheFromJSON, err := json.Marshal(options.CacheFrom)
	if err != nil {
		return query, err
	}
	query.Set("cachefrom", string(cacheFromJSON))

	return query, nil
}
<commit_msg>Add --add-host for docker build<commit_after>package client

import (
	"encoding/base64"
	"encoding/json"
	"io"
	"net/http"
	"net/url"
	"strconv"

	"golang.org/x/net/context"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
)

// ImageBuild sends request to the daemon to build images.
// The Body in the response implement an io.ReadCloser and it's up to the caller to
// close it.
func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
	query, err := cli.imageBuildOptionsToQuery(options)
	if err != nil {
		return types.ImageBuildResponse{}, err
	}

	headers := http.Header(make(map[string][]string))
	buf, err := json.Marshal(options.AuthConfigs)
	if err != nil {
		return types.ImageBuildResponse{}, err
	}
	headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
	headers.Set("Content-Type", "application/x-tar")

	serverResp, err := cli.postRaw(ctx, "/build", query, buildContext, headers)
	if err != nil {
		return types.ImageBuildResponse{}, err
	}

	osType := getDockerOS(serverResp.header.Get("Server"))

	return types.ImageBuildResponse{
		Body:   serverResp.body,
		OSType: osType,
	}, nil
}

func (cli *Client) imageBuildOptionsToQuery(options types.ImageBuildOptions) (url.Values, error) {
	query := url.Values{
		"t":           options.Tags,
		"securityopt": options.SecurityOpt,
		"extrahosts":  options.ExtraHosts,
	}
	if options.SuppressOutput {
		query.Set("q", "1")
	}
	if options.RemoteContext != "" {
		query.Set("remote", options.RemoteContext)
	}
	if options.NoCache {
		query.Set("nocache", "1")
	}
	if options.Remove {
		query.Set("rm", "1")
	} else {
		query.Set("rm", "0")
	}

	if options.ForceRemove {
		query.Set("forcerm", "1")
	}

	if options.PullParent {
		query.Set("pull", "1")
	}

	if options.Squash {
		if err := cli.NewVersionError("1.25", "squash"); err != nil {
			return query, err
		}
		query.Set("squash", "1")
	}

	if !container.Isolation.IsDefault(options.Isolation) {
		query.Set("isolation", string(options.Isolation))
	}

	query.Set("cpusetcpus", options.CPUSetCPUs)
	query.Set("networkmode", options.NetworkMode)
	query.Set("cpusetmems", options.CPUSetMems)
	query.Set("cpushares", strconv.FormatInt(options.CPUShares, 10))
	query.Set("cpuquota", strconv.FormatInt(options.CPUQuota, 10))
	query.Set("cpuperiod", strconv.FormatInt(options.CPUPeriod, 10))
	query.Set("memory", strconv.FormatInt(options.Memory, 10))
	query.Set("memswap", strconv.FormatInt(options.MemorySwap, 10))
	query.Set("cgroupparent", options.CgroupParent)
	query.Set("shmsize", strconv.FormatInt(options.ShmSize, 10))
	query.Set("dockerfile", options.Dockerfile)

	ulimitsJSON, err := json.Marshal(options.Ulimits)
	if err != nil {
		return query, err
	}
	query.Set("ulimits", string(ulimitsJSON))

	buildArgsJSON, err := json.Marshal(options.BuildArgs)
	if err != nil {
		return query, err
	}
	query.Set("buildargs", string(buildArgsJSON))

	labelsJSON, err := json.Marshal(options.Labels)
	if err != nil {
		return query, err
	}
	query.Set("labels", string(labelsJSON))

	cacheFromJSON, err := json.Marshal(options.CacheFrom)
	if err != nil {
		return query, err
	}
	query.Set("cachefrom", string(cacheFromJSON))

	return query, nil
}
<|endoftext|>


<commit_before>package s2

import (
	"fmt"
	"math"

	"code.google.com/p/gos2/s1"
)

// LatLng represents a point on the unit sphere as a pair of angles.
type LatLng struct {
	Lat, Lng s1.Angle
}

func LatLngFromDegrees(lat, lng float64) LatLng {
	return LatLng{s1.Angle(lat) * s1.Degree, s1.Angle(lng) * s1.Degree}
}

// IsValid returns true iff the LatLng is normalized, with Lat ∈ [-π/2,π/2] and Lng ∈ [-π,π].
func (ll LatLng) IsValid() bool {
	return math.Abs(ll.Lat.Radians()) <= math.Pi/2 && math.Abs(ll.Lng.Radians()) <= math.Pi
}

func (ll LatLng) String() string { return fmt.Sprintf("[%v, %v]", ll.Lat, ll.Lng) }

// Distance returns the angle between two LatLngs.
func (ll LatLng) Distance(ll2 LatLng) s1.Angle {
	// Haversine formula, as used in C++ S2LatLng::GetDistance.
	lat1, lat2 := ll.Lat.Radians(), ll2.Lat.Radians()
	lng1, lng2 := ll.Lng.Radians(), ll2.Lng.Radians()
	dlat := math.Sin(0.5 * (lat2 - lat1))
	dlng := math.Sin(0.5 * (lng2 - lng1))
	x := dlat*dlat + dlng*dlng*math.Cos(lat1)*math.Cos(lat2)
	return s1.Angle(2*math.Atan2(math.Sqrt(x), math.Sqrt(math.Max(0, 1-x)))) * s1.Radian
}

// NOTE(mikeperrow): The C++ implementation publicly exposes latitude/longitude
// functions. Let's see if that's really necessary before exposing the same functionality.

func latitude(p Point) s1.Angle {
	return s1.Angle(math.Atan2(p.Z, math.Sqrt(p.X*p.X+p.Y*p.Y))) * s1.Radian
}

func longitude(p Point) s1.Angle {
	return s1.Angle(math.Atan2(p.Y, p.X)) * s1.Radian
}

// PointFromLatLng returns an Point for the given LatLng.
func PointFromLatLng(ll LatLng) Point {
	phi := ll.Lat.Radians()
	theta := ll.Lng.Radians()
	cosphi := math.Cos(phi)
	return PointFromCoords(math.Cos(theta)*cosphi, math.Sin(theta)*cosphi, math.Sin(phi))
}

// LatLngFromPoint returns an LatLng for a given Point.
func LatLngFromPoint(p Point) LatLng {
	return LatLng{latitude(p), longitude(p)}
}

// BUG(dsymonds): The major differences from the C++ version are:
//   - normalization
<commit_msg>Add a comment to LatLngFromDegrees.<commit_after>package s2

import (
	"fmt"
	"math"

	"code.google.com/p/gos2/s1"
)

// LatLng represents a point on the unit sphere as a pair of angles.
type LatLng struct {
	Lat, Lng s1.Angle
}

// LatLngFromDegrees returns a LatLng for the coordinates given in degrees.
func LatLngFromDegrees(lat, lng float64) LatLng {
	return LatLng{s1.Angle(lat) * s1.Degree, s1.Angle(lng) * s1.Degree}
}

// IsValid returns true iff the LatLng is normalized, with Lat ∈ [-π/2,π/2] and Lng ∈ [-π,π].
func (ll LatLng) IsValid() bool {
	return math.Abs(ll.Lat.Radians()) <= math.Pi/2 && math.Abs(ll.Lng.Radians()) <= math.Pi
}

func (ll LatLng) String() string { return fmt.Sprintf("[%v, %v]", ll.Lat, ll.Lng) }

// Distance returns the angle between two LatLngs.
func (ll LatLng) Distance(ll2 LatLng) s1.Angle {
	// Haversine formula, as used in C++ S2LatLng::GetDistance.
	lat1, lat2 := ll.Lat.Radians(), ll2.Lat.Radians()
	lng1, lng2 := ll.Lng.Radians(), ll2.Lng.Radians()
	dlat := math.Sin(0.5 * (lat2 - lat1))
	dlng := math.Sin(0.5 * (lng2 - lng1))
	x := dlat*dlat + dlng*dlng*math.Cos(lat1)*math.Cos(lat2)
	return s1.Angle(2*math.Atan2(math.Sqrt(x), math.Sqrt(math.Max(0, 1-x)))) * s1.Radian
}

// NOTE(mikeperrow): The C++ implementation publicly exposes latitude/longitude
// functions. Let's see if that's really necessary before exposing the same functionality.

func latitude(p Point) s1.Angle {
	return s1.Angle(math.Atan2(p.Z, math.Sqrt(p.X*p.X+p.Y*p.Y))) * s1.Radian
}

func longitude(p Point) s1.Angle {
	return s1.Angle(math.Atan2(p.Y, p.X)) * s1.Radian
}

// PointFromLatLng returns an Point for the given LatLng.
func PointFromLatLng(ll LatLng) Point {
	phi := ll.Lat.Radians()
	theta := ll.Lng.Radians()
	cosphi := math.Cos(phi)
	return PointFromCoords(math.Cos(theta)*cosphi, math.Sin(theta)*cosphi, math.Sin(phi))
}

// LatLngFromPoint returns an LatLng for a given Point.
func LatLngFromPoint(p Point) LatLng {
	return LatLng{latitude(p), longitude(p)}
}

// BUG(dsymonds): The major differences from the C++ version are:
//   - normalization
<|endoftext|>


<commit_before>// +build appengine

package memcache

import (
	"time"

	"appengine"
	"appengine/memcache"

	"gnd.la/cache/driver"
	"gnd.la/config"
)

type memcacheDriver struct {
	c appengine.Context
}

func (c *memcacheDriver) Set(key string, b []byte, timeout int) error {
	item := &memcache.Item{Key: key, Value: b, Expiration: time.Duration(timeout) * time.Second}
	return memcache.Set(c.c, item)
}

func (c *memcacheDriver) Get(key string) ([]byte, error) {
	item, err := memcache.Get(c.c, key)
	if err != nil && err != memcache.ErrCacheMiss {
		return nil, err
	}
	if item != nil {
		return item.Value, nil
	}
	return nil, nil
}

func (c *memcacheDriver) GetMulti(keys []string) (map[string][]byte, error) {
	results, err := memcache.GetMulti(c.c, keys)
	if err != nil && err != memcache.ErrCacheMiss {
		return nil, err
	}
	value := make(map[string][]byte, len(results))
	for k, v := range results {
		value[k] = v.Value
	}
	return value, nil
}

func (c *memcacheDriver) Delete(key string) error {
	err := memcache.Delete(c.c, key)
	if err != nil && err != memcache.ErrCacheMiss {
		return err
	}
	return nil
}

func (c *memcacheDriver) Connection() interface{} {
	return c
}

func (c *memcacheDriver) SetContext(ctx appengine.Context) {
	c.c = ctx
}

func (c *memcacheDriver) Close() error {
	return nil
}

func memcacheOpener(_ *config.URL) (driver.Driver, error) {
	return &memcacheDriver{}, nil
}

func init() {
	driver.Register("memcache", memcacheOpener)
}
<commit_msg>Add missing method to App Engine memcache driver<commit_after>// +build appengine

package memcache

import (
	"time"

	"appengine"
	"appengine/memcache"

	"gnd.la/cache/driver"
	"gnd.la/config"
)

type memcacheDriver struct {
	c appengine.Context
}

func (c *memcacheDriver) Set(key string, b []byte, timeout int) error {
	item := &memcache.Item{Key: key, Value: b, Expiration: time.Duration(timeout) * time.Second}
	return memcache.Set(c.c, item)
}

func (c *memcacheDriver) Get(key string) ([]byte, error) {
	item, err := memcache.Get(c.c, key)
	if err != nil && err != memcache.ErrCacheMiss {
		return nil, err
	}
	if item != nil {
		return item.Value, nil
	}
	return nil, nil
}

func (c *memcacheDriver) GetMulti(keys []string) (map[string][]byte, error) {
	results, err := memcache.GetMulti(c.c, keys)
	if err != nil && err != memcache.ErrCacheMiss {
		return nil, err
	}
	value := make(map[string][]byte, len(results))
	for k, v := range results {
		value[k] = v.Value
	}
	return value, nil
}

func (c *memcacheDriver) Delete(key string) error {
	err := memcache.Delete(c.c, key)
	if err != nil && err != memcache.ErrCacheMiss {
		return err
	}
	return nil
}

func (c *memcacheDriver) Connection() interface{} {
	return c
}

func (c *memcacheDriver) Flush() error {
	return memcache.Flush(c.c)
}

func (c *memcacheDriver) SetContext(ctx appengine.Context) {
	c.c = ctx
}

func (c *memcacheDriver) Close() error {
	return nil
}

func memcacheOpener(_ *config.URL) (driver.Driver, error) {
	return &memcacheDriver{}, nil
}

func init() {
	driver.Register("memcache", memcacheOpener)
}
<|endoftext|>


<commit_before>// +build freebsd

package mem

import (
	"context"
	"errors"
	"os/exec"
	"strconv"
	"strings"

	"golang.org/x/sys/unix"
)

func VirtualMemory() (*VirtualMemoryStat, error) {
	return VirtualMemoryWithContext(context.Background())
}

func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
	pageSize, err := unix.SysctlUint32("vm.stats.vm.v_page_size")
	if err != nil {
		return nil, err
	}
	pageCount, err := unix.SysctlUint32("vm.stats.vm.v_page_count")
	if err != nil {
		return nil, err
	}
	free, err := unix.SysctlUint32("vm.stats.vm.v_free_count")
	if err != nil {
		return nil, err
	}
	active, err := unix.SysctlUint32("vm.stats.vm.v_active_count")
	if err != nil {
		return nil, err
	}
	inactive, err := unix.SysctlUint32("vm.stats.vm.v_inactive_count")
	if err != nil {
		return nil, err
	}
	cached, err := unix.SysctlUint32("vm.stats.vm.v_cache_count")
	if err != nil {
		return nil, err
	}
	buffers, err := unix.SysctlUint32("vfs.bufspace")
	if err != nil {
		return nil, err
	}
	wired, err := unix.SysctlUint32("vm.stats.vm.v_wire_count")
	if err != nil {
		return nil, err
	}

	p := uint64(pageSize)
	ret := &VirtualMemoryStat{
		Total:    uint64(pageCount) * p,
		Free:     uint64(free) * p,
		Active:   uint64(active) * p,
		Inactive: uint64(inactive) * p,
		Cached:   uint64(cached) * p,
		Buffers:  uint64(buffers),
		Wired:    uint64(wired) * p,
	}

	ret.Available = ret.Inactive + ret.Cached + ret.Free
	ret.Used = ret.Total - ret.Available
	ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0

	return ret, nil
}

// Return swapinfo
// FreeBSD can have multiple swap devices. but use only first device
func SwapMemory() (*SwapMemoryStat, error) {
	return SwapMemoryWithContext(context.Background())
}

func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
	swapinfo, err := exec.LookPath("swapinfo")
	if err != nil {
		return nil, err
	}

	out, err := invoke.Command(swapinfo)
	if err != nil {
		return nil, err
	}
	for _, line := range strings.Split(string(out), "\n") {
		values := strings.Fields(line)
		// skip title line
		if len(values) == 0 || values[0] == "Device" {
			continue
		}

		u := strings.Replace(values[4], "%", "", 1)
		total_v, err := strconv.ParseUint(values[1], 10, 64)
		if err != nil {
			return nil, err
		}
		used_v, err := strconv.ParseUint(values[2], 10, 64)
		if err != nil {
			return nil, err
		}
		free_v, err := strconv.ParseUint(values[3], 10, 64)
		if err != nil {
			return nil, err
		}
		up_v, err := strconv.ParseFloat(u, 64)
		if err != nil {
			return nil, err
		}

		return &SwapMemoryStat{
			Total:       total_v,
			Used:        used_v,
			Free:        free_v,
			UsedPercent: up_v,
		}, nil
	}

	return nil, errors.New("no swap devices found")
}
<commit_msg>Eliminate call to swapinfo on FreeBSD<commit_after>// +build freebsd

package mem

import (
	"context"
	"errors"
	"unsafe"

	"golang.org/x/sys/unix"
)

func VirtualMemory() (*VirtualMemoryStat, error) {
	return VirtualMemoryWithContext(context.Background())
}

func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
	pageSize, err := unix.SysctlUint32("vm.stats.vm.v_page_size")
	if err != nil {
		return nil, err
	}
	pageCount, err := unix.SysctlUint32("vm.stats.vm.v_page_count")
	if err != nil {
		return nil, err
	}
	free, err := unix.SysctlUint32("vm.stats.vm.v_free_count")
	if err != nil {
		return nil, err
	}
	active, err := unix.SysctlUint32("vm.stats.vm.v_active_count")
	if err != nil {
		return nil, err
	}
	inactive, err := unix.SysctlUint32("vm.stats.vm.v_inactive_count")
	if err != nil {
		return nil, err
	}
	cached, err := unix.SysctlUint32("vm.stats.vm.v_cache_count")
	if err != nil {
		return nil, err
	}
	buffers, err := unix.SysctlUint32("vfs.bufspace")
	if err != nil {
		return nil, err
	}
	wired, err := unix.SysctlUint32("vm.stats.vm.v_wire_count")
	if err != nil {
		return nil, err
	}

	p := uint64(pageSize)
	ret := &VirtualMemoryStat{
		Total:    uint64(pageCount) * p,
		Free:     uint64(free) * p,
		Active:   uint64(active) * p,
		Inactive: uint64(inactive) * p,
		Cached:   uint64(cached) * p,
		Buffers:  uint64(buffers),
		Wired:    uint64(wired) * p,
	}

	ret.Available = ret.Inactive + ret.Cached + ret.Free
	ret.Used = ret.Total - ret.Available
	ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0

	return ret, nil
}

// Return swapinfo
func SwapMemory() (*SwapMemoryStat, error) {
	return SwapMemoryWithContext(context.Background())
}

// Constants from vm/vm_param.h
// nolint: golint
const (
	XSWDEV_VERSION = 1
)

// Types from vm/vm_param.h
type xswdev struct {
	Version uint32 // Version is the version
	Dev     uint32 // Dev is the device identifier
	Flags   int32  // Flags is the swap flags applied to the device
	NBlks   int32  // NBlks is the total number of blocks
	Used    int32  // Used is the number of blocks used
}

func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
	// FreeBSD can have multiple swap devices so we total them up
	i, err := unix.SysctlUint32("vm.nswapdev")
	if err != nil {
		return nil, err
	}

	if i == 0 {
		return nil, errors.New("no swap devices found")
	}

	c := int(i)

	i, err = unix.SysctlUint32("vm.stats.vm.v_page_size")
	if err != nil {
		return nil, err
	}
	pageSize := uint64(i)

	var buf []byte
	s := &SwapMemoryStat{}
	for n := 0; n < c; n++ {
		buf, err = unix.SysctlRaw("vm.swap_info", n)
		if err != nil {
			return nil, err
		}

		xsw := (*xswdev)(unsafe.Pointer(&buf[0]))
		if xsw.Version != XSWDEV_VERSION {
			return nil, errors.New("xswdev version mismatch")
		}
		s.Total += uint64(xsw.NBlks)
		s.Used += uint64(xsw.Used)
	}

	if s.Total != 0 {
		s.UsedPercent = float64(s.Used) / float64(s.Total) * 100
	}
	s.Total *= pageSize
	s.Used *= pageSize
	s.Free = s.Total - s.Used

	return s, nil
}
<|endoftext|>


<commit_before>package tartrans

import (
	"context"
	"io/ioutil"
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"

	"go.polydawn.net/rio/fs"
	"go.polydawn.net/rio/fs/osfs"
	"go.polydawn.net/rio/fsOp"
	"go.polydawn.net/rio/testutil"
	"go.polydawn.net/timeless-api"
	"go.polydawn.net/timeless-api/rio"
)

/*
	Tests against pre-generated, known fixtures of tar binary blobs.

	These tests allow us to cover compat with other tar impls, compression, etc.
*/
func TestTarFixtureUnpack(t *testing.T) {
	Convey("Tar transmat: unpacking of fixtures", t, func() {
		testutil.WithTmpdir(func(tmpDir fs.AbsolutePath) {
			Convey("Unpack a fixture from gnu tar which includes a base dir", func() {
				wareID := api.WareID{"tar", "5y6NvK6GBPQ6CcuNyJyWtSrMAJQ4LVrAcZSoCRAzMSk5o53pkTYiieWyRivfvhZwhZ"}
				gotWareID, err := Unpack(
					context.Background(),
					wareID,
					tmpDir.String(),
					api.FilesetFilters{},
					[]api.WarehouseAddr{"file://./fixtures/tar_withBase.tgz"},
					rio.Monitor{},
				)
				So(err, ShouldBeNil)
				So(gotWareID, ShouldResemble, wareID)

				fmeta, reader, err := fsOp.ScanFile(osfs.New(tmpDir), fs.MustRelPath("ab"))
				So(err, ShouldBeNil)
				So(fmeta.Name, ShouldResemble, fs.MustRelPath("ab"))
				So(fmeta.Type, ShouldResemble, fs.Type_File)
				So(fmeta.Mtime.UTC(), ShouldResemble, time.Date(2015, 05, 30, 19, 53, 35, 0, time.UTC))
				body, err := ioutil.ReadAll(reader)
				So(string(body), ShouldResemble, "")

				fmeta, reader, err = fsOp.ScanFile(osfs.New(tmpDir), fs.MustRelPath("bc"))
				So(err, ShouldBeNil)
				So(fmeta.Name, ShouldResemble, fs.MustRelPath("bc"))
				So(fmeta.Type, ShouldResemble, fs.Type_Dir)
				So(fmeta.Mtime.UTC(), ShouldResemble, time.Date(2015, 05, 30, 19, 53, 35, 0, time.UTC))
				So(reader, ShouldBeNil)
			})
			Convey("Unpack a fixture from gnu tar which lacks a base dir", func() {
				wareID := api.WareID{"tar", "2RLHdc3am6tMCFy56vfcHm5kWLoAtYBfiaQcq17vDm1tEzQn9CC6tcF2yzpAJvehPC"}
				gotWareID, err := Unpack(
					context.Background(),
					wareID,
					tmpDir.String(),
					api.FilesetFilters{},
					[]api.WarehouseAddr{"file://./fixtures/tar_sansBase.tgz"},
					rio.Monitor{},
				)
				So(err, ShouldBeNil)
				So(gotWareID, ShouldResemble, wareID)
			})
		})
	})
}
<commit_msg>Assert UID and GID.<commit_after>package tartrans

import (
	"context"
	"io/ioutil"
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"

	"go.polydawn.net/rio/fs"
	"go.polydawn.net/rio/fs/osfs"
	"go.polydawn.net/rio/fsOp"
	"go.polydawn.net/rio/testutil"
	"go.polydawn.net/timeless-api"
	"go.polydawn.net/timeless-api/rio"
)

/*
	Tests against pre-generated, known fixtures of tar binary blobs.

	These tests allow us to cover compat with other tar impls, compression, etc.
*/
func TestTarFixtureUnpack(t *testing.T) {
	Convey("Tar transmat: unpacking of fixtures", t, func() {
		testutil.WithTmpdir(func(tmpDir fs.AbsolutePath) {
			Convey("Unpack a fixture from gnu tar which includes a base dir", func() {
				wareID := api.WareID{"tar", "5y6NvK6GBPQ6CcuNyJyWtSrMAJQ4LVrAcZSoCRAzMSk5o53pkTYiieWyRivfvhZwhZ"}
				gotWareID, err := Unpack(
					context.Background(),
					wareID,
					tmpDir.String(),
					api.FilesetFilters{},
					[]api.WarehouseAddr{"file://./fixtures/tar_withBase.tgz"},
					rio.Monitor{},
				)
				So(err, ShouldBeNil)
				So(gotWareID, ShouldResemble, wareID)

				fmeta, reader, err := fsOp.ScanFile(osfs.New(tmpDir), fs.MustRelPath("ab"))
				So(err, ShouldBeNil)
				So(fmeta.Name, ShouldResemble, fs.MustRelPath("ab"))
				So(fmeta.Type, ShouldResemble, fs.Type_File)
				So(fmeta.Uid, ShouldEqual, 7000)
				So(fmeta.Gid, ShouldEqual, 7000)
				So(fmeta.Mtime.UTC(), ShouldResemble, time.Date(2015, 05, 30, 19, 53, 35, 0, time.UTC))
				body, err := ioutil.ReadAll(reader)
				So(string(body), ShouldResemble, "")

				fmeta, reader, err = fsOp.ScanFile(osfs.New(tmpDir), fs.MustRelPath("bc"))
				So(err, ShouldBeNil)
				So(fmeta.Name, ShouldResemble, fs.MustRelPath("bc"))
				So(fmeta.Type, ShouldResemble, fs.Type_Dir)
				So(fmeta.Mtime.UTC(), ShouldResemble, time.Date(2015, 05, 30, 19, 53, 35, 0, time.UTC))
				So(reader, ShouldBeNil)
			})
			Convey("Unpack a fixture from gnu tar which lacks a base dir", func() {
				wareID := api.WareID{"tar", "2RLHdc3am6tMCFy56vfcHm5kWLoAtYBfiaQcq17vDm1tEzQn9CC6tcF2yzpAJvehPC"}
				gotWareID, err := Unpack(
					context.Background(),
					wareID,
					tmpDir.String(),
					api.FilesetFilters{},
					[]api.WarehouseAddr{"file://./fixtures/tar_sansBase.tgz"},
					rio.Monitor{},
				)
				So(err, ShouldBeNil)
				So(gotWareID, ShouldResemble, wareID)
			})
		})
	})
}
<|endoftext|>