<commit_before>// Package pomodoro contains functionality for timing work tasks and calling a user-supplied callback on end or cancel.
// This is currently Discord-specific, due to the "NotifyInfo" struct being strongly typed, rather than an interface{}.
// I prefer this for now, until other concrete use cases have been decided upon.
//
// Copyright 2017 Sean A. Pfeifer
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//		http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pomodoro

import (
	"sync"
	"time"
)

// Pomodoro represents a single Pomodoro instance, which can be started and stopped.
// There were a few options I considered when designing this:
//
// • A sync.Mutex on each Pomodoro, which is locked and unlocked internally to maintain state.
//
// • Use channels to notify the Pomodoro of events, which handles the state management on its own goroutine
//
// I chose the channel option.  This was to avoid the risks of issues related to locking if the code is
// expanded upon(user error), as well as generally to make it more idiomatic Go.
type Pomodoro struct {
	workDuration time.Duration // The duration for a regular Pomodoro work cycle
	onWorkEnd    TaskCallback
	notifyInfo   NotifyInfo

	cancelChan chan struct{} // A channel to interrupt our wait if this Pomodoro is cancelled first
	cancel     sync.Once     // To ensure we only close the cancelChan once
}

// TaskCallback is the type of function that will be called upon Pomodoro task completion.  These may be called in a separate
// goroutine, and thus should be made goroutine-safe.
//
// It receives the NotifyInfo and a boolean to tell the receiver whether the task completed (true), or was cancelled (false).
type TaskCallback func(info NotifyInfo, completed bool)

// NotifyInfo contains the necessary information to notify the creating user upon ending the Pomodoro.
type NotifyInfo struct {
	Title     string // The title of the work task
	UserID    string // The UserID to notify
	GuildID   string // The Guild (Discord server) that the user created the Pomodoro on
	ChannelID string // The Channel to notify with the state of the Pomodoro
}

// NewPomodoro creates a new Pomodoro and starts it, similar to time.NewTimer. "Start" functionality
// is intentionally omitted to prevent double-starting.
//
// onWorkEnd is called after the Pomodoro has been completed or cancelled.
func NewPomodoro(workDuration time.Duration, onWorkEnd TaskCallback, notify NotifyInfo) *Pomodoro {
	pom := &Pomodoro{
		workDuration,
		onWorkEnd,
		notify,
		make(chan struct{}),
		sync.Once{},
	}

	go pom.performPom()

	return pom
}

// Cancel is used to cancel a current work cycle. This uses "sync.Once" so we prevent a panic if, for whatever
// reason, the caller is able to call Cancel more than once.
//
// This method is goroutine-safe, and will cancel a Pomodoro only once (multiple calls are OK).
func (pom *Pomodoro) Cancel() {
	pom.cancel.Do(func() {
		close(pom.cancelChan)
	})
}

func (pom *Pomodoro) performPom() {
	workTimer := time.NewTimer(pom.workDuration)

	select {
	case <-workTimer.C:
		go pom.onWorkEnd(pom.notifyInfo, true)
	case <-pom.cancelChan:
		workTimer.Stop()
		go pom.onWorkEnd(pom.notifyInfo, false)
	}
}

// ChannelPomMap is a map-like structure that has goroutine-safe operations to create Pomodoros on individual channels.
type ChannelPomMap struct {
	sync.Mutex
	channelToPom map[string]*Pomodoro
}

// NewChannelPomMap creates a ChannelPomMap and prepares it to be used.
func NewChannelPomMap() ChannelPomMap {
	return ChannelPomMap{channelToPom: make(map[string]*Pomodoro)}
}

// CreateIfEmpty will create and start a Pomodoro on the given channel if one does not already exist.
// The Pomodoro will be removed from the map when its work is complete, or it is cancelled.
//
// This method is goroutine-safe.
func (m *ChannelPomMap) CreateIfEmpty(duration time.Duration, onWorkEnd TaskCallback, notify NotifyInfo) bool {
	m.Lock()
	defer m.Unlock()

	wasCreated := false
	if _, exists := m.channelToPom[notify.ChannelID]; !exists {
		// Ensure we remove the Pomodoro from the map when it completes
		doneInMap := func(notif NotifyInfo, completed bool) {
			// Note that this call is done so we use the mutex. The cancellation will never trigger "onWorkEnd", since the "performPom"
			// goroutine will already be complete by this point.
			m.RemoveIfExists(notif.ChannelID)
			onWorkEnd(notif, completed)
		}

		m.channelToPom[notify.ChannelID] = NewPomodoro(duration, doneInMap, notify)
		wasCreated = true
	}

	return wasCreated
}

// RemoveIfExists will stop and remove a Pomodoro from the given channel if one exists.  Note that this will perform cancellation of
// the Pomodoro if it is running and call the onWorkEnded callback.
//
// It returns a boolean representing whether the Pomodoro was removed.
//
// This method is goroutine-safe.
func (m *ChannelPomMap) RemoveIfExists(channel string) bool {
	m.Lock()
	defer m.Unlock()

	wasRemoved := false
	if p, exists := m.channelToPom[channel]; exists {
		delete(m.channelToPom, channel)
		p.Cancel()
		wasRemoved = true
	}

	return wasRemoved
}
<commit_msg>Moved ChannelPomMap's previously-embedded Mutex into an attribute, as it should not be exported.<commit_after>// Package pomodoro contains functionality for timing work tasks and calling a user-supplied callback on end or cancel.
// This is currently Discord-specific, due to the "NotifyInfo" struct being strongly typed, rather than an interface{}.
// I prefer this for now, until other concrete use cases have been decided upon.
//
// Copyright 2017 Sean A. Pfeifer
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//		http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pomodoro

import (
	"sync"
	"time"
)

// Pomodoro represents a single Pomodoro instance, which can be started and stopped.
// There were a few options I considered when designing this:
//
// • A sync.Mutex on each Pomodoro, which is locked and unlocked internally to maintain state.
//
// • Use channels to notify the Pomodoro of events, which handles the state management on its own goroutine
//
// I chose the channel option.  This was to avoid the risks of issues related to locking if the code is
// expanded upon(user error), as well as generally to make it more idiomatic Go.
type Pomodoro struct {
	workDuration time.Duration // The duration for a regular Pomodoro work cycle
	onWorkEnd    TaskCallback
	notifyInfo   NotifyInfo

	cancelChan chan struct{} // A channel to interrupt our wait if this Pomodoro is cancelled first
	cancel     sync.Once     // To ensure we only close the cancelChan once
}

// TaskCallback is the type of function that will be called upon Pomodoro task completion.  These may be called in a separate
// goroutine, and thus should be made goroutine-safe.
//
// It receives the NotifyInfo and a boolean to tell the receiver whether the task completed (true), or was cancelled (false).
type TaskCallback func(info NotifyInfo, completed bool)

// NotifyInfo contains the necessary information to notify the creating user upon ending the Pomodoro.
type NotifyInfo struct {
	Title     string // The title of the work task
	UserID    string // The UserID to notify
	GuildID   string // The Guild (Discord server) that the user created the Pomodoro on
	ChannelID string // The Channel to notify with the state of the Pomodoro
}

// NewPomodoro creates a new Pomodoro and starts it, similar to time.NewTimer. "Start" functionality
// is intentionally omitted to prevent double-starting.
//
// onWorkEnd is called after the Pomodoro has been completed or cancelled.
func NewPomodoro(workDuration time.Duration, onWorkEnd TaskCallback, notify NotifyInfo) *Pomodoro {
	pom := &Pomodoro{
		workDuration,
		onWorkEnd,
		notify,
		make(chan struct{}),
		sync.Once{},
	}

	go pom.performPom()

	return pom
}

// Cancel is used to cancel a current work cycle. This uses "sync.Once" so we prevent a panic if, for whatever
// reason, the caller is able to call Cancel more than once.
//
// This method is goroutine-safe, and will cancel a Pomodoro only once (multiple calls are OK).
func (pom *Pomodoro) Cancel() {
	pom.cancel.Do(func() {
		close(pom.cancelChan)
	})
}

func (pom *Pomodoro) performPom() {
	workTimer := time.NewTimer(pom.workDuration)

	select {
	case <-workTimer.C:
		go pom.onWorkEnd(pom.notifyInfo, true)
	case <-pom.cancelChan:
		workTimer.Stop()
		go pom.onWorkEnd(pom.notifyInfo, false)
	}
}

// ChannelPomMap is a map-like structure that has goroutine-safe operations to create Pomodoros on individual channels.
type ChannelPomMap struct {
	mutex        sync.Mutex
	channelToPom map[string]*Pomodoro
}

// NewChannelPomMap creates a ChannelPomMap and prepares it to be used.
func NewChannelPomMap() ChannelPomMap {
	return ChannelPomMap{channelToPom: make(map[string]*Pomodoro)}
}

// CreateIfEmpty will create and start a Pomodoro on the given channel if one does not already exist.
// The Pomodoro will be removed from the map when its work is complete, or it is cancelled.
//
// This method is goroutine-safe.
func (m *ChannelPomMap) CreateIfEmpty(duration time.Duration, onWorkEnd TaskCallback, notify NotifyInfo) bool {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	wasCreated := false
	if _, exists := m.channelToPom[notify.ChannelID]; !exists {
		// Ensure we remove the Pomodoro from the map when it completes
		doneInMap := func(notif NotifyInfo, completed bool) {
			// Note that this call is done so we use the mutex. The cancellation will never trigger "onWorkEnd", since the "performPom"
			// goroutine will already be complete by this point.
			m.RemoveIfExists(notif.ChannelID)
			onWorkEnd(notif, completed)
		}

		m.channelToPom[notify.ChannelID] = NewPomodoro(duration, doneInMap, notify)
		wasCreated = true
	}

	return wasCreated
}

// RemoveIfExists will stop and remove a Pomodoro from the given channel if one exists.  Note that this will perform cancellation of
// the Pomodoro if it is running and call the onWorkEnded callback.
//
// It returns a boolean representing whether the Pomodoro was removed.
//
// This method is goroutine-safe.
func (m *ChannelPomMap) RemoveIfExists(channel string) bool {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	wasRemoved := false
	if p, exists := m.channelToPom[channel]; exists {
		delete(m.channelToPom, channel)
		p.Cancel()
		wasRemoved = true
	}

	return wasRemoved
}
<|endoftext|>


<commit_before>package postgres

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"unicode"

	"github.com/caarlos0/env"
	"github.com/jmoiron/sqlx"
	// Used pg drive on sqlx
	_ "github.com/lib/pq"

	"database/sql"

	"github.com/nuveo/prest/api"
	"github.com/nuveo/prest/config"
)

const (
	pageNumberKey   = "_page"
	pageSizeKey     = "_page_size"
	defaultPageSize = 10
)

// Conn connect on PostgreSQL
// Used sqlx
func Conn() (db *sqlx.DB) {
	cfg := config.Prest{}
	env.Parse(&cfg)

	db, err := sqlx.Connect("postgres", fmt.Sprintf("user=%s dbname=%s host=%s port=%v sslmode=disable", cfg.PGUser, cfg.PGDatabase, cfg.PGHost, cfg.PGPort))
	if err != nil {
		panic(fmt.Sprintf("Unable to connection to database: %v\n", err))
	}
	return
}

// chkInvaidIdentifier return true if identifier is invalid
func chkInvaidIdentifier(identifer string) bool {
	if len(identifer) > 63 ||
		unicode.IsDigit([]rune(identifer)[0]) {
		return true
	}

	for _, v := range identifer {
		if !unicode.IsLetter(v) &&
			!unicode.IsDigit(v) &&
			v != '_' &&
			v != '.' {
			return true
		}
	}
	return false
}

// WhereByRequest create interface for queries + where
func WhereByRequest(r *http.Request, initialPlaceholderID int) (whereSyntax string, values []interface{}, err error) {
	whereKey := []string{}
	whereValues := []string{}

	u, _ := url.Parse(r.URL.String())
	pid := initialPlaceholderID
	for key, val := range u.Query() {
		if !strings.HasPrefix(key, "_") {
			keyInfo := strings.Split(key, ":")
			if len(keyInfo) > 1 {
				switch keyInfo[1] {
				case "jsonb":
					jsonField := strings.Split(keyInfo[0], "->>")
					if chkInvaidIdentifier(jsonField[0]) ||
						chkInvaidIdentifier(jsonField[1]) {
						err = errors.New("Invalid identifier")
						return
					}
					whereKey = append(whereKey, fmt.Sprintf("%s->>'%s'=$%d", jsonField[0], jsonField[1], pid))
					whereValues = append(whereValues, val[0])
				default:
					if chkInvaidIdentifier(keyInfo[0]) {
						err = errors.New("Invalid identifier")
						return
					}
					whereKey = append(whereKey, fmt.Sprintf("%s=$%d", keyInfo[0], pid))
					whereValues = append(whereValues, val[0])
				}
				continue
			}
			if chkInvaidIdentifier(key) {
				err = errors.New("Invalid identifier")
				return
			}

			whereKey = append(whereKey, fmt.Sprintf("%s=$%d", key, pid))
			whereValues = append(whereValues, val[0])

			pid++
		}
	}

	for i := 0; i < len(whereKey); i++ {
		if whereSyntax == "" {
			whereSyntax += whereKey[i]
		} else {
			whereSyntax += " AND " + whereKey[i]
		}

		values = append(values, whereValues[i])
	}

	return
}

// Query process queries
func Query(SQL string, params ...interface{}) (jsonData []byte, err error) {
	db := Conn()

	prepare, err := db.Prepare(SQL)

	if err != nil {
		return
	}

	rows, err := prepare.Query(params...)
	if err != nil {
		return
	}
	defer rows.Close()

	columns, err := rows.Columns()
	if err != nil {
		return
	}

	count := len(columns)
	tableData := make([]map[string]interface{}, 0)
	values := make([]interface{}, count)
	valuePtrs := make([]interface{}, count)
	for rows.Next() {
		for i := 0; i < count; i++ {
			valuePtrs[i] = &values[i]
		}
		rows.Scan(valuePtrs...)
		entry := make(map[string]interface{})
		for i, col := range columns {
			var v interface{}
			val := values[i]
			b, ok := val.([]byte)
			if ok {
				v = string(b)
			} else {
				v = val
			}
			entry[col] = v
		}
		tableData = append(tableData, entry)
	}
	jsonData, err = json.Marshal(tableData)

	return
}

// PaginateIfPossible func
func PaginateIfPossible(r *http.Request) (paginatedQuery string, err error) {
	u, _ := url.Parse(r.URL.String())
	values := u.Query()
	if _, ok := values[pageNumberKey]; !ok {
		paginatedQuery = ""
		return
	}
	pageNumber, err := strconv.Atoi(values[pageNumberKey][0])
	if err != nil {
		return
	}
	pageSize := defaultPageSize
	if size, ok := values[pageSizeKey]; ok {
		pageSize, err = strconv.Atoi(size[0])
		if err != nil {
			return
		}
	}
	paginatedQuery = fmt.Sprintf("LIMIT %d OFFSET(%d - 1) * %d", pageSize, pageNumber, pageSize)
	return
}

// Insert execute insert sql into a table
func Insert(database, schema, table string, body api.Request) (jsonData []byte, err error) {

	if chkInvaidIdentifier(database) ||
		chkInvaidIdentifier(schema) ||
		chkInvaidIdentifier(table) {
		err = errors.New("Insert: Invalid identifier")
		return
	}

	var result sql.Result
	var rowsAffected int64

	fields := make([]string, 0)
	values := make([]interface{}, 0)
	for key, value := range body.Data {
		if chkInvaidIdentifier(key) {
			err = errors.New("Insert: Invalid identifier")
			return
		}
		fields = append(fields, key)
		values = append(values, value)
	}

	colsName := strings.Join(fields, ", ")
	colPlaceholder := ""
	for i := 1; i < len(values)+1; i++ {
		if colPlaceholder != "" {
			colPlaceholder += ","
		}
		colPlaceholder += fmt.Sprintf("$%d", i)
	}

	sql := fmt.Sprintf("INSERT INTO %s.%s.%s (%s) VALUES (%s)", database, schema, table, colsName, colPlaceholder)

	db := Conn()
	stmt, err := db.Prepare(sql)
	if err != nil {
		return
	}

	valuesAux := make([]interface{}, 0, len(values))

	for i := 0; i < len(values); i++ {
		valuesAux = append(valuesAux, values[i])
	}

	result, err = stmt.Exec(valuesAux...)
	if err != nil {
		return
	}

	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return
	}

	data := make(map[string]interface{})
	data["rows_affected"] = rowsAffected
	jsonData, err = json.Marshal(data)
	return
}

// Delete execute delete sql into a table
func Delete(database, schema, table, where string, whereValues []interface{}) (jsonData []byte, err error) {
	var result sql.Result
	var rowsAffected int64

	if chkInvaidIdentifier(database) ||
		chkInvaidIdentifier(schema) ||
		chkInvaidIdentifier(table) {
		err = errors.New("Delete: Invalid identifier")
		return
	}

	sql := fmt.Sprintf("DELETE FROM %s.%s.%s", database, schema, table)
	if where != "" {
		sql = fmt.Sprint(
			sql,
			" WHERE ",
			where)
	}

	db := Conn()
	result, err = db.Exec(sql, whereValues...)
	if err != nil {
		return
	}
	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return
	}

	data := make(map[string]interface{})
	data["rows_affected"] = rowsAffected
	jsonData, err = json.Marshal(data)
	return
}

// Update execute update sql into a table
func Update(database, schema, table, where string, whereValues []interface{}, body api.Request) (jsonData []byte, err error) {

	if chkInvaidIdentifier(database) ||
		chkInvaidIdentifier(schema) ||
		chkInvaidIdentifier(table) {
		err = errors.New("Update: Invalid identifier")
		return
	}

	var result sql.Result
	var rowsAffected int64

	fields := []string{}
	values := make([]interface{}, 0)
	pid := len(whereValues) + 1 // placeholder id
	for key, value := range body.Data {
		fields = append(fields, fmt.Sprintf("%s=$%d", key, pid))
		values = append(values, value)
		pid++
	}
	setSyntax := strings.Join(fields, ", ")

	sql := fmt.Sprintf("UPDATE %s.%s.%s SET %s", database, schema, table, setSyntax)

	if where != "" {
		sql = fmt.Sprint(
			sql,
			" WHERE ",
			where)
		values = append(whereValues, values...)
	}

	db := Conn()
	stmt, err := db.Prepare(sql)
	if err != nil {
		return
	}

	valuesAux := make([]interface{}, 0, len(values))

	for i := 0; i < len(values); i++ {
		valuesAux = append(valuesAux, values[i])
	}

	result, err = stmt.Exec(valuesAux...)
	if err != nil {
		return
	}

	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return
	}

	data := make(map[string]interface{})
	data["rows_affected"] = rowsAffected
	jsonData, err = json.Marshal(data)
	return
}
<commit_msg>config file implementation<commit_after>package postgres

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"unicode"

	"github.com/jmoiron/sqlx"
	// Used pg drive on sqlx
	_ "github.com/lib/pq"

	"database/sql"

	"github.com/nuveo/prest/api"
	"github.com/nuveo/prest/config"
)

const (
	pageNumberKey   = "_page"
	pageSizeKey     = "_page_size"
	defaultPageSize = 10
)

// Conn connect on PostgreSQL
// Used sqlx
func Conn() (db *sqlx.DB) {
	cfg := config.Prest{}
	config.Parse(&cfg)

	db, err := sqlx.Connect("postgres", fmt.Sprintf("user=%s dbname=%s host=%s port=%v sslmode=disable", cfg.PGUser, cfg.PGDatabase, cfg.PGHost, cfg.PGPort))
	if err != nil {
		panic(fmt.Sprintf("Unable to connection to database: %v\n", err))
	}
	return
}

// chkInvaidIdentifier return true if identifier is invalid
func chkInvaidIdentifier(identifer string) bool {
	if len(identifer) > 63 ||
		unicode.IsDigit([]rune(identifer)[0]) {
		return true
	}

	for _, v := range identifer {
		if !unicode.IsLetter(v) &&
			!unicode.IsDigit(v) &&
			v != '_' &&
			v != '.' {
			return true
		}
	}
	return false
}

// WhereByRequest create interface for queries + where
func WhereByRequest(r *http.Request, initialPlaceholderID int) (whereSyntax string, values []interface{}, err error) {
	whereKey := []string{}
	whereValues := []string{}

	u, _ := url.Parse(r.URL.String())
	pid := initialPlaceholderID
	for key, val := range u.Query() {
		if !strings.HasPrefix(key, "_") {
			keyInfo := strings.Split(key, ":")
			if len(keyInfo) > 1 {
				switch keyInfo[1] {
				case "jsonb":
					jsonField := strings.Split(keyInfo[0], "->>")
					if chkInvaidIdentifier(jsonField[0]) ||
						chkInvaidIdentifier(jsonField[1]) {
						err = errors.New("Invalid identifier")
						return
					}
					whereKey = append(whereKey, fmt.Sprintf("%s->>'%s'=$%d", jsonField[0], jsonField[1], pid))
					whereValues = append(whereValues, val[0])
				default:
					if chkInvaidIdentifier(keyInfo[0]) {
						err = errors.New("Invalid identifier")
						return
					}
					whereKey = append(whereKey, fmt.Sprintf("%s=$%d", keyInfo[0], pid))
					whereValues = append(whereValues, val[0])
				}
				continue
			}
			if chkInvaidIdentifier(key) {
				err = errors.New("Invalid identifier")
				return
			}

			whereKey = append(whereKey, fmt.Sprintf("%s=$%d", key, pid))
			whereValues = append(whereValues, val[0])

			pid++
		}
	}

	for i := 0; i < len(whereKey); i++ {
		if whereSyntax == "" {
			whereSyntax += whereKey[i]
		} else {
			whereSyntax += " AND " + whereKey[i]
		}

		values = append(values, whereValues[i])
	}

	return
}

// Query process queries
func Query(SQL string, params ...interface{}) (jsonData []byte, err error) {
	db := Conn()

	prepare, err := db.Prepare(SQL)

	if err != nil {
		return
	}

	rows, err := prepare.Query(params...)
	if err != nil {
		return
	}
	defer rows.Close()

	columns, err := rows.Columns()
	if err != nil {
		return
	}

	count := len(columns)
	tableData := make([]map[string]interface{}, 0)
	values := make([]interface{}, count)
	valuePtrs := make([]interface{}, count)
	for rows.Next() {
		for i := 0; i < count; i++ {
			valuePtrs[i] = &values[i]
		}
		rows.Scan(valuePtrs...)
		entry := make(map[string]interface{})
		for i, col := range columns {
			var v interface{}
			val := values[i]
			b, ok := val.([]byte)
			if ok {
				v = string(b)
			} else {
				v = val
			}
			entry[col] = v
		}
		tableData = append(tableData, entry)
	}
	jsonData, err = json.Marshal(tableData)

	return
}

// PaginateIfPossible func
func PaginateIfPossible(r *http.Request) (paginatedQuery string, err error) {
	u, _ := url.Parse(r.URL.String())
	values := u.Query()
	if _, ok := values[pageNumberKey]; !ok {
		paginatedQuery = ""
		return
	}
	pageNumber, err := strconv.Atoi(values[pageNumberKey][0])
	if err != nil {
		return
	}
	pageSize := defaultPageSize
	if size, ok := values[pageSizeKey]; ok {
		pageSize, err = strconv.Atoi(size[0])
		if err != nil {
			return
		}
	}
	paginatedQuery = fmt.Sprintf("LIMIT %d OFFSET(%d - 1) * %d", pageSize, pageNumber, pageSize)
	return
}

// Insert execute insert sql into a table
func Insert(database, schema, table string, body api.Request) (jsonData []byte, err error) {

	if chkInvaidIdentifier(database) ||
		chkInvaidIdentifier(schema) ||
		chkInvaidIdentifier(table) {
		err = errors.New("Insert: Invalid identifier")
		return
	}

	var result sql.Result
	var rowsAffected int64

	fields := make([]string, 0)
	values := make([]interface{}, 0)
	for key, value := range body.Data {
		if chkInvaidIdentifier(key) {
			err = errors.New("Insert: Invalid identifier")
			return
		}
		fields = append(fields, key)
		values = append(values, value)
	}

	colsName := strings.Join(fields, ", ")
	colPlaceholder := ""
	for i := 1; i < len(values)+1; i++ {
		if colPlaceholder != "" {
			colPlaceholder += ","
		}
		colPlaceholder += fmt.Sprintf("$%d", i)
	}

	sql := fmt.Sprintf("INSERT INTO %s.%s.%s (%s) VALUES (%s)", database, schema, table, colsName, colPlaceholder)

	db := Conn()
	stmt, err := db.Prepare(sql)
	if err != nil {
		return
	}

	valuesAux := make([]interface{}, 0, len(values))

	for i := 0; i < len(values); i++ {
		valuesAux = append(valuesAux, values[i])
	}

	result, err = stmt.Exec(valuesAux...)
	if err != nil {
		return
	}

	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return
	}

	data := make(map[string]interface{})
	data["rows_affected"] = rowsAffected
	jsonData, err = json.Marshal(data)
	return
}

// Delete execute delete sql into a table
func Delete(database, schema, table, where string, whereValues []interface{}) (jsonData []byte, err error) {
	var result sql.Result
	var rowsAffected int64

	if chkInvaidIdentifier(database) ||
		chkInvaidIdentifier(schema) ||
		chkInvaidIdentifier(table) {
		err = errors.New("Delete: Invalid identifier")
		return
	}

	sql := fmt.Sprintf("DELETE FROM %s.%s.%s", database, schema, table)
	if where != "" {
		sql = fmt.Sprint(
			sql,
			" WHERE ",
			where)
	}

	db := Conn()
	result, err = db.Exec(sql, whereValues...)
	if err != nil {
		return
	}
	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return
	}

	data := make(map[string]interface{})
	data["rows_affected"] = rowsAffected
	jsonData, err = json.Marshal(data)
	return
}

// Update execute update sql into a table
func Update(database, schema, table, where string, whereValues []interface{}, body api.Request) (jsonData []byte, err error) {

	if chkInvaidIdentifier(database) ||
		chkInvaidIdentifier(schema) ||
		chkInvaidIdentifier(table) {
		err = errors.New("Update: Invalid identifier")
		return
	}

	var result sql.Result
	var rowsAffected int64

	fields := []string{}
	values := make([]interface{}, 0)
	pid := len(whereValues) + 1 // placeholder id
	for key, value := range body.Data {
		fields = append(fields, fmt.Sprintf("%s=$%d", key, pid))
		values = append(values, value)
		pid++
	}
	setSyntax := strings.Join(fields, ", ")

	sql := fmt.Sprintf("UPDATE %s.%s.%s SET %s", database, schema, table, setSyntax)

	if where != "" {
		sql = fmt.Sprint(
			sql,
			" WHERE ",
			where)
		values = append(whereValues, values...)
	}

	db := Conn()
	stmt, err := db.Prepare(sql)
	if err != nil {
		return
	}

	valuesAux := make([]interface{}, 0, len(values))

	for i := 0; i < len(values); i++ {
		valuesAux = append(valuesAux, values[i])
	}

	result, err = stmt.Exec(valuesAux...)
	if err != nil {
		return
	}

	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return
	}

	data := make(map[string]interface{})
	data["rows_affected"] = rowsAffected
	jsonData, err = json.Marshal(data)
	return
}
<|endoftext|>


<commit_before>package swarm

import (
	"context"
	"errors"
	"sync"

	"github.com/libp2p/go-libp2p-core/network"
	"github.com/libp2p/go-libp2p-core/peer"
)

// TODO: change this text when we fix the bug
var errDialCanceled = errors.New("dial was aborted internally, likely due to https://git.io/Je2wW")

// DialFunc is the type of function expected by DialSync.
type DialWorkerFunc func(context.Context, peer.ID, <-chan DialRequest)

// NewDialSync constructs a new DialSync
func NewDialSync(worker DialWorkerFunc) *DialSync {
	return &DialSync{
		dials:      make(map[peer.ID]*activeDial),
		dialWorker: worker,
	}
}

// DialSync is a dial synchronization helper that ensures that at most one dial
// to any given peer is active at any given time.
type DialSync struct {
	dials      map[peer.ID]*activeDial
	dialsLk    sync.Mutex
	dialWorker DialWorkerFunc
}

type activeDial struct {
	id     peer.ID
	refCnt int

	ctx    context.Context
	cancel func()

	reqch chan DialRequest

	ds *DialSync
}

func (ad *activeDial) incref() {
	ad.refCnt++
}

func (ad *activeDial) decref() {
	ad.ds.dialsLk.Lock()
	ad.refCnt--
	if ad.refCnt == 0 {
		ad.cancel()
		close(ad.reqch)
		delete(ad.ds.dials, ad.id)
	}
	ad.ds.dialsLk.Unlock()
}

func (ad *activeDial) dial(ctx context.Context, p peer.ID) (*Conn, error) {
	dialCtx := ad.ctx

	if forceDirect, reason := network.GetForceDirectDial(ctx); forceDirect {
		dialCtx = network.WithForceDirectDial(dialCtx, reason)
	}
	if simConnect, reason := network.GetSimultaneousConnect(ctx); simConnect {
		dialCtx = network.WithSimultaneousConnect(dialCtx, reason)
	}

	resch := make(chan DialResponse, 1)
	select {
	case ad.reqch <- DialRequest{Ctx: dialCtx, Resch: resch}:
	case <-ctx.Done():
		return nil, ctx.Err()
	}

	select {
	case res := <-resch:
		return res.Conn, res.Err
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

func (ds *DialSync) getActiveDial(p peer.ID) *activeDial {
	ds.dialsLk.Lock()
	defer ds.dialsLk.Unlock()

	actd, ok := ds.dials[p]
	if !ok {
		// This code intentionally uses the background context. Otherwise, if the first call
		// to Dial is canceled, subsequent dial calls will also be canceled.
		// XXX: this also breaks direct connection logic. We will need to pipe the
		// information through some other way.
		adctx, cancel := context.WithCancel(context.Background())
		actd = &activeDial{
			id:     p,
			ctx:    adctx,
			cancel: cancel,
			reqch:  make(chan DialRequest),
			ds:     ds,
		}
		ds.dials[p] = actd

		go ds.dialWorker(adctx, p, actd.reqch)
	}

	// increase ref count before dropping dialsLk
	actd.incref()

	return actd
}

// DialLock initiates a dial to the given peer if there are none in progress
// then waits for the dial to that peer to complete.
func (ds *DialSync) DialLock(ctx context.Context, p peer.ID) (*Conn, error) {
	ad := ds.getActiveDial(p)
	defer ad.decref()

	return ad.dial(ctx, p)
}
<commit_msg>nuke incref, it's useless<commit_after>package swarm

import (
	"context"
	"errors"
	"sync"

	"github.com/libp2p/go-libp2p-core/network"
	"github.com/libp2p/go-libp2p-core/peer"
)

// TODO: change this text when we fix the bug
var errDialCanceled = errors.New("dial was aborted internally, likely due to https://git.io/Je2wW")

// DialFunc is the type of function expected by DialSync.
type DialWorkerFunc func(context.Context, peer.ID, <-chan DialRequest)

// NewDialSync constructs a new DialSync
func NewDialSync(worker DialWorkerFunc) *DialSync {
	return &DialSync{
		dials:      make(map[peer.ID]*activeDial),
		dialWorker: worker,
	}
}

// DialSync is a dial synchronization helper that ensures that at most one dial
// to any given peer is active at any given time.
type DialSync struct {
	dials      map[peer.ID]*activeDial
	dialsLk    sync.Mutex
	dialWorker DialWorkerFunc
}

type activeDial struct {
	id     peer.ID
	refCnt int

	ctx    context.Context
	cancel func()

	reqch chan DialRequest

	ds *DialSync
}

func (ad *activeDial) decref() {
	ad.ds.dialsLk.Lock()
	ad.refCnt--
	if ad.refCnt == 0 {
		ad.cancel()
		close(ad.reqch)
		delete(ad.ds.dials, ad.id)
	}
	ad.ds.dialsLk.Unlock()
}

func (ad *activeDial) dial(ctx context.Context, p peer.ID) (*Conn, error) {
	dialCtx := ad.ctx

	if forceDirect, reason := network.GetForceDirectDial(ctx); forceDirect {
		dialCtx = network.WithForceDirectDial(dialCtx, reason)
	}
	if simConnect, reason := network.GetSimultaneousConnect(ctx); simConnect {
		dialCtx = network.WithSimultaneousConnect(dialCtx, reason)
	}

	resch := make(chan DialResponse, 1)
	select {
	case ad.reqch <- DialRequest{Ctx: dialCtx, Resch: resch}:
	case <-ctx.Done():
		return nil, ctx.Err()
	}

	select {
	case res := <-resch:
		return res.Conn, res.Err
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

func (ds *DialSync) getActiveDial(p peer.ID) *activeDial {
	ds.dialsLk.Lock()
	defer ds.dialsLk.Unlock()

	actd, ok := ds.dials[p]
	if !ok {
		// This code intentionally uses the background context. Otherwise, if the first call
		// to Dial is canceled, subsequent dial calls will also be canceled.
		// XXX: this also breaks direct connection logic. We will need to pipe the
		// information through some other way.
		adctx, cancel := context.WithCancel(context.Background())
		actd = &activeDial{
			id:     p,
			ctx:    adctx,
			cancel: cancel,
			reqch:  make(chan DialRequest),
			ds:     ds,
		}
		ds.dials[p] = actd

		go ds.dialWorker(adctx, p, actd.reqch)
	}

	// increase ref count before dropping dialsLk
	actd.refCnt++

	return actd
}

// DialLock initiates a dial to the given peer if there are none in progress
// then waits for the dial to that peer to complete.
func (ds *DialSync) DialLock(ctx context.Context, p peer.ID) (*Conn, error) {
	ad := ds.getActiveDial(p)
	defer ad.decref()

	return ad.dial(ctx, p)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"strings"
)

const (
	// DefaultHeight is the default height of an Image
	DefaultHeight = 500
	// DefaultWidth is the default width of an Image
	DefaultWidth = 500
)

var (
	Black = Color{0, 0, 0}
	White = Color{255, 255, 255}
)

type Color struct {
	r byte
	g byte
	b byte
}

// Image represents an image
type Image struct {
	frame  [][]Color
	height int
	width  int
}

// NewImage returns a new Image with the given height and width
func NewImage(height, width int) *Image {
	frame := make([][]Color, height)
	for i := 0; i < height; i++ {
		frame[i] = make([]Color, width)
	}
	image := &Image{
		frame:  frame,
		height: height,
		width:  width,
	}
	return image
}

// DrawLines draws all lines onto the Image
func (image *Image) DrawLines(em *Matrix, c Color) error {
	if em.cols < 2 {
		return errors.New("2 or more points are required for drawing")
	}
	for i := 0; i < em.cols-1; i += 2 {
		p0 := em.GetColumn(i)
		p1 := em.GetColumn(i + 1)
		image.DrawLine(p0[0], p0[1], p1[0], p1[1], c)
	}
	return nil
}

// DrawPolygons draws all polygons onto the Image
func (image *Image) DrawPolygons(em *Matrix, c Color) error {
	if em.cols < 3 {
		return errors.New("3 or more points are required for drawing")
	}
	for i := 0; i < em.cols-2; i += 3 {
		p0 := em.GetColumn(i)
		p1 := em.GetColumn(i + 1)
		p2 := em.GetColumn(i + 2)
		if isVisible(p0, p1, p2) {
			image.DrawLine(p0[0], p0[1], p1[0], p1[1], c)
			image.DrawLine(p1[0], p1[1], p2[0], p2[1], c)
			image.DrawLine(p2[0], p2[1], p0[0], p0[1], c)
		}
	}
	return nil
}

// DrawLine draws a single line onto the Image
func (image *Image) DrawLine(x1, y1, x2, y2 float64, c Color) {
	if x1 > x2 {
		x1, x2 = x2, x1
		y1, y2 = y2, y1
	}

	A := 2 * (y2 - y1)
	B := 2 * -(x2 - x1)
	m := A / -B
	if m >= 0 {
		if m <= 1 {
			image.drawOctant1(x1, y1, x2, y2, A, B, c)
		} else {
			image.drawOctant2(x1, y1, x2, y2, A, B, c)
		}
	} else {
		if m < -1 {
			image.drawOctant7(x1, y1, x2, y2, A, B, c)
		} else {
			image.drawOctant8(x1, y1, x2, y2, A, B, c)
		}
	}
}

func (image *Image) drawOctant1(x1, y1, x2, y2, A, B float64, c Color) {
	d := A + B/2
	for x1 <= x2 {
		image.set(int(x1), int(y1), c)
		if d > 0 {
			y1++
			d += B
		}
		x1++
		d += A
	}
}

func (image *Image) drawOctant2(x1, y1, x2, y2, A, B float64, c Color) {
	d := A/2 + B
	for y1 <= y2 {
		image.set(int(x1), int(y1), c)
		if d < 0 {
			x1++
			d += A
		}
		y1++
		d += B
	}
}

func (image *Image) drawOctant7(x1, y1, x2, y2, A, B float64, c Color) {
	d := A/2 + B
	for y1 >= y2 {
		image.set(int(x1), int(y1), c)
		if d > 0 {
			x1++
			d += A
		}
		y1--
		d -= B
	}
}

func (image *Image) drawOctant8(x1, y1, x2, y2, A, B float64, c Color) {
	d := A - B/2
	for x1 <= x2 {
		image.set(int(x1), int(y1), c)
		if d < 0 {
			y1--
			d -= B
		}
		x1++
		d += A
	}
}

// Fill completely fills the Image with a single color
func (image *Image) Fill(c Color) {
	for y := 0; y < image.height; y++ {
		for x := 0; x < image.width; x++ {
			image.set(x, y, c)
		}
	}
}

func (image *Image) set(x, y int, c Color) {
	if (x < 0 || x >= image.width) || (y < 0 || y >= image.height) {
		return
	}
	// Plot so that the y coodinate is the row, and the x coordinate is the column
	image.frame[y][x] = c
}

// SavePpm will save the Image as a ppm
func (image *Image) SavePpm(name string) error {
	f, err := os.Create(name)
	if err != nil {
		return err
	}
	defer f.Close()

	w := bufio.NewWriter(f)
	defer w.Flush()

	fmt.Fprintln(w, "P3", image.width, image.height, 255)
	for y := 0; y < image.height; y++ {
		// Adjust y coordinate that the origin is the bottom left
		adjustedY := image.height - y - 1
		for x := 0; x < image.width; x++ {
			color := image.frame[adjustedY][x]
			fmt.Fprintln(w, color.r, color.g, color.b)
		}
	}
	return nil
}

// Save will save an Image into a given format
func (image *Image) Save(name string) error {
	index := strings.Index(name, ".")
	extension := ".ppm"
	if index == -1 {
		extension = ".png"
	} else {
		extension = name[index:]
		name = name[:index]
	}

	if extension == ".ppm" {
		// save as ppm without converting
		err := image.SavePpm(fmt.Sprint(name, ".ppm"))
		return err
	}

	ppm := fmt.Sprint(name, "-tmp.ppm")
	err := image.SavePpm(ppm)
	if err != nil {
		return err
	}
	defer os.Remove(ppm)
	err = exec.Command("convert", ppm, fmt.Sprint(name, extension)).Run()
	return err
}

// Display displays the Image
func (image *Image) Display() error {
	filename := "tmp.ppm"
	err := image.SavePpm(filename)
	if err != nil {
		return err
	}
	defer os.Remove(filename)

	err = exec.Command("display", filename).Run()
	return err
}

// MakeAnimation converts individual frames to a gif
func MakeAnimation(basename string) error {
	path := fmt.Sprintf("%s/%s*", FramesDirectory, basename)
	gif := fmt.Sprintf("%s.gif", basename)
	err := exec.Command("convert", "-delay", "3", path, gif).Run()
	return err
}

func isVisible(p0, p1, p2 []float64) bool {
	a := []float64{p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]}
	b := []float64{p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2]}
	normal := CrossProduct(a, b)
	return normal[2] > 0
}
<commit_msg>Change ppm specification to P6<commit_after>package main

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"strings"
)

const (
	// DefaultHeight is the default height of an Image
	DefaultHeight = 500
	// DefaultWidth is the default width of an Image
	DefaultWidth = 500
)

var (
	Black = Color{0, 0, 0}
	White = Color{255, 255, 255}
)

type Color struct {
	r int
	g int
	b int
}

// Image represents an image
type Image struct {
	frame  [][]Color
	height int
	width  int
}

// NewImage returns a new Image with the given height and width
func NewImage(height, width int) *Image {
	frame := make([][]Color, height)
	for i := 0; i < height; i++ {
		frame[i] = make([]Color, width)
	}
	image := &Image{
		frame:  frame,
		height: height,
		width:  width,
	}
	return image
}

// DrawLines draws all lines onto the Image
func (image *Image) DrawLines(em *Matrix, c Color) error {
	if em.cols < 2 {
		return errors.New("2 or more points are required for drawing")
	}
	for i := 0; i < em.cols-1; i += 2 {
		p0 := em.GetColumn(i)
		p1 := em.GetColumn(i + 1)
		image.DrawLine(p0[0], p0[1], p1[0], p1[1], c)
	}
	return nil
}

// DrawPolygons draws all polygons onto the Image
func (image *Image) DrawPolygons(em *Matrix, c Color) error {
	if em.cols < 3 {
		return errors.New("3 or more points are required for drawing")
	}
	for i := 0; i < em.cols-2; i += 3 {
		p0 := em.GetColumn(i)
		p1 := em.GetColumn(i + 1)
		p2 := em.GetColumn(i + 2)
		if isVisible(p0, p1, p2) {
			image.DrawLine(p0[0], p0[1], p1[0], p1[1], c)
			image.DrawLine(p1[0], p1[1], p2[0], p2[1], c)
			image.DrawLine(p2[0], p2[1], p0[0], p0[1], c)
		}
	}
	return nil
}

// DrawLine draws a single line onto the Image
func (image *Image) DrawLine(x1, y1, x2, y2 float64, c Color) {
	if x1 > x2 {
		x1, x2 = x2, x1
		y1, y2 = y2, y1
	}

	A := 2 * (y2 - y1)
	B := 2 * -(x2 - x1)
	m := A / -B
	if m >= 0 {
		if m <= 1 {
			image.drawOctant1(x1, y1, x2, y2, A, B, c)
		} else {
			image.drawOctant2(x1, y1, x2, y2, A, B, c)
		}
	} else {
		if m < -1 {
			image.drawOctant7(x1, y1, x2, y2, A, B, c)
		} else {
			image.drawOctant8(x1, y1, x2, y2, A, B, c)
		}
	}
}

func (image *Image) drawOctant1(x1, y1, x2, y2, A, B float64, c Color) {
	d := A + B/2
	for x1 <= x2 {
		image.set(int(x1), int(y1), c)
		if d > 0 {
			y1++
			d += B
		}
		x1++
		d += A
	}
}

func (image *Image) drawOctant2(x1, y1, x2, y2, A, B float64, c Color) {
	d := A/2 + B
	for y1 <= y2 {
		image.set(int(x1), int(y1), c)
		if d < 0 {
			x1++
			d += A
		}
		y1++
		d += B
	}
}

func (image *Image) drawOctant7(x1, y1, x2, y2, A, B float64, c Color) {
	d := A/2 + B
	for y1 >= y2 {
		image.set(int(x1), int(y1), c)
		if d > 0 {
			x1++
			d += A
		}
		y1--
		d -= B
	}
}

func (image *Image) drawOctant8(x1, y1, x2, y2, A, B float64, c Color) {
	d := A - B/2
	for x1 <= x2 {
		image.set(int(x1), int(y1), c)
		if d < 0 {
			y1--
			d -= B
		}
		x1++
		d += A
	}
}

// Fill completely fills the Image with a single color
func (image *Image) Fill(c Color) {
	for y := 0; y < image.height; y++ {
		for x := 0; x < image.width; x++ {
			image.set(x, y, c)
		}
	}
}

func (image *Image) set(x, y int, c Color) {
	if (x < 0 || x >= image.width) || (y < 0 || y >= image.height) {
		return
	}
	// Plot so that the y coodinate is the row, and the x coordinate is the column
	image.frame[y][x] = c
}

// SavePpm will save the Image as a ppm
func (image *Image) SavePpm(name string) error {
	f, err := os.Create(name)
	if err != nil {
		return err
	}
	defer f.Close()

	w := bufio.NewWriter(f)
	defer w.Flush()

	fmt.Fprintln(w, "P6", image.width, image.height, 255)
	pixel := make([]byte, 3)
	for y := 0; y < image.height; y++ {
		// Adjust y coordinate that the origin is the bottom left
		adjustedY := image.height - y - 1
		for x := 0; x < image.width; x++ {
			color := image.frame[adjustedY][x]
			pixel[0], pixel[1], pixel[2] = byte(color.r), byte(color.g), byte(color.b)
			w.Write(pixel)
		}
	}
	return nil
}

// Save will save an Image into a given format
func (image *Image) Save(name string) error {
	index := strings.Index(name, ".")
	extension := ".ppm"
	if index == -1 {
		extension = ".png"
	} else {
		extension = name[index:]
		name = name[:index]
	}

	if extension == ".ppm" {
		// save as ppm without converting
		err := image.SavePpm(fmt.Sprint(name, ".ppm"))
		return err
	}

	ppm := fmt.Sprint(name, "-tmp.ppm")
	err := image.SavePpm(ppm)
	if err != nil {
		return err
	}
	defer os.Remove(ppm)
	err = exec.Command("convert", ppm, fmt.Sprint(name, extension)).Run()
	return err
}

// Display displays the Image
func (image *Image) Display() error {
	filename := "tmp.ppm"
	err := image.SavePpm(filename)
	if err != nil {
		return err
	}
	defer os.Remove(filename)

	err = exec.Command("display", filename).Run()
	return err
}

// MakeAnimation converts individual frames to a gif
func MakeAnimation(basename string) error {
	path := fmt.Sprintf("%s/%s*", FramesDirectory, basename)
	gif := fmt.Sprintf("%s.gif", basename)
	err := exec.Command("convert", "-delay", "3", path, gif).Run()
	return err
}

func isVisible(p0, p1, p2 []float64) bool {
	a := []float64{p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]}
	b := []float64{p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2]}
	normal := CrossProduct(a, b)
	return normal[2] > 0
}
<|endoftext|>


<commit_before>/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * promise/functions.go                                   *
 *                                                        *
 * some functions of promise for Go.                      *
 *                                                        *
 * LastModified: Aug 14, 2016                             *
 * Author: Ma Bingyao <andot@hprose.com>                  *
 *                                                        *
\**********************************************************/

package promise

import (
	"errors"
	"sync/atomic"
	"time"
)

// Create creates a Promise object containing the result of asynchronously
// calling computation.
//
// If calling computation returns error, the returned Promise is rejected with
// the error.
//
// If calling computation returns a Promise object, completion of the created
// Promise will wait until the returned Promise completes, and will then
// complete with the same result.
//
// If calling computation returns a non-Promise value, the returned Promise is
// completed with that value.
func Create(computation func() (interface{}, error)) Promise {
	promise := New()
	go call(promise, computation)
	return promise
}

// Sync creates a Promise object containing the result of immediately calling
// computation.
//
// If calling computation returns error, the returned Promise is rejected with
// the error.
//
// If calling computation returns a Promise object, completion of the created
// Promise will wait until the returned Promise completes, and will then
// complete with the same result.
//
// If calling computation returns a non-Promise value, the returned Promise is
// completed with that value.
func Sync(computation func() (interface{}, error)) Promise {
	promise := New()
	call(promise, computation)
	return promise
}

// Delayed creates a Promise object with the given value after a delay.
//
// If the value is a Callable function, it will be executed after the given
// duration has passed, and the Promise object is completed with the result.
func Delayed(duration time.Duration, value interface{}) Promise {
	promise := New()
	go func() {
		time.Sleep(duration)
		switch computation := value.(type) {
		case func() (interface{}, error):
			call(promise, computation)
		case func():
			call1(promise, computation)
		default:
			promise.Resolve(value)
		}
	}()
	return promise
}

// All function returns a promise that resolves when all of the promises in the
// iterable argument have resolved, or rejects with the reason of the first
// passed promise that rejects.
func All(iterable ...interface{}) Promise {
	count := int64(len(iterable))
	if count == 0 {
		return Resolve(nil)
	}
	result := make([]interface{}, count)
	promise := New()
	for index, value := range iterable {
		ToPromise(value).Then(func(index int) func(value interface{}) {
			return func(value interface{}) {
				result[index] = value
				if atomic.AddInt64(&count, -1) == 0 {
					promise.Resolve(result)
				}
			}
		}(index), promise.Reject)
	}
	return promise
}

// Race function returns a promise that resolves or rejects as soon as one of
// the promises in the iterable resolves or rejects, with the value or reason
// from that promise.
func Race(iterable ...interface{}) Promise {
	promise := New()
	for _, value := range iterable {
		ToPromise(value).Fill(promise)
	}
	return promise
}

// Any function is a competitive race that allows one winner.
//
// The returned promise will fulfill as soon as any one of the input promises
// fulfills, with the value of the fulfilled input promise.
//
// Or reject with a IllegalArgumentError if the input array is empty--i.e. it
// is impossible to have one winner.
//
// Or reject with an array of all the rejection reasons, if the input array is
// non-empty, and all input promises reject.
func Any(iterable ...interface{}) Promise {
	count := int64(len(iterable))
	if count == 0 {
		return Reject(IllegalArgumentError("any(): array must not be empty"))
	}
	promise := New()
	for _, value := range iterable {
		ToPromise(value).Then(promise.Resolve, func() {
			if atomic.AddInt64(&count, -1) == 0 {
				promise.Reject(errors.New("any(): all promises failed"))
			}
		})
	}
	return promise
}

// Each function executes a provided function once per element of iterable.
//
// The callback parameter is a function to execute for each element:
//
//		func(index int, value interface{})
//
// index: The index of the current element being processed.
//
// value: The current element being processed.
//
// If any of the promises in iterable is rejected, the callback will not be
// executed. the returned promise will be rejected with the rejection reason
// of the first promise that was rejected.
func Each(callback func(int, interface{}), iterable ...interface{}) Promise {
	return All(iterable...).Then(func(a interface{}) {
		if a == nil {
			return
		}
		iterable := a.([]interface{})
		for index, value := range iterable {
			callback(index, value)
		}
	})
}

// Every function tests whether all elements in the iterable pass the test
// implemented by the provided function.
//
// The callback parameter is a function to test for each element:
//
//		func(index int, value interface{}) bool
//
// index: The index of the current element being processed.
//
// value: The current element being processed.
//
// If any of the promises in iterable is rejected, the callback will not be
// executed. the returned promise will be rejected with the rejection reason
// of the first promise that was rejected.
func Every(callback func(int, interface{}) bool, iterable ...interface{}) Promise {
	return All(iterable...).Then(func(a interface{}) (interface{}, error) {
		if a == nil {
			return true, nil
		}
		iterable := a.([]interface{})
		for index, value := range iterable {
			if !callback(index, value) {
				return false, nil
			}
		}
		return true, nil
	})
}
<commit_msg>Added Some function.<commit_after>/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * promise/functions.go                                   *
 *                                                        *
 * some functions of promise for Go.                      *
 *                                                        *
 * LastModified: Aug 14, 2016                             *
 * Author: Ma Bingyao <andot@hprose.com>                  *
 *                                                        *
\**********************************************************/

package promise

import (
	"errors"
	"sync/atomic"
	"time"
)

// Create creates a Promise object containing the result of asynchronously
// calling computation.
//
// If calling computation returns error, the returned Promise is rejected with
// the error.
//
// If calling computation returns a Promise object, completion of the created
// Promise will wait until the returned Promise completes, and will then
// complete with the same result.
//
// If calling computation returns a non-Promise value, the returned Promise is
// completed with that value.
func Create(computation func() (interface{}, error)) Promise {
	promise := New()
	go call(promise, computation)
	return promise
}

// Sync creates a Promise object containing the result of immediately calling
// computation.
//
// If calling computation returns error, the returned Promise is rejected with
// the error.
//
// If calling computation returns a Promise object, completion of the created
// Promise will wait until the returned Promise completes, and will then
// complete with the same result.
//
// If calling computation returns a non-Promise value, the returned Promise is
// completed with that value.
func Sync(computation func() (interface{}, error)) Promise {
	promise := New()
	call(promise, computation)
	return promise
}

// Delayed creates a Promise object with the given value after a delay.
//
// If the value is a Callable function, it will be executed after the given
// duration has passed, and the Promise object is completed with the result.
func Delayed(duration time.Duration, value interface{}) Promise {
	promise := New()
	go func() {
		time.Sleep(duration)
		switch computation := value.(type) {
		case func() (interface{}, error):
			call(promise, computation)
		case func():
			call1(promise, computation)
		default:
			promise.Resolve(value)
		}
	}()
	return promise
}

// All function returns a promise that resolves when all of the promises in the
// iterable argument have resolved, or rejects with the reason of the first
// passed promise that rejects.
func All(iterable ...interface{}) Promise {
	count := int64(len(iterable))
	if count == 0 {
		return Resolve(nil)
	}
	result := make([]interface{}, count)
	promise := New()
	for index, value := range iterable {
		ToPromise(value).Then(func(index int) func(value interface{}) {
			return func(value interface{}) {
				result[index] = value
				if atomic.AddInt64(&count, -1) == 0 {
					promise.Resolve(result)
				}
			}
		}(index), promise.Reject)
	}
	return promise
}

// Race function returns a promise that resolves or rejects as soon as one of
// the promises in the iterable resolves or rejects, with the value or reason
// from that promise.
func Race(iterable ...interface{}) Promise {
	promise := New()
	for _, value := range iterable {
		ToPromise(value).Fill(promise)
	}
	return promise
}

// Any function is a competitive race that allows one winner.
//
// The returned promise will fulfill as soon as any one of the input promises
// fulfills, with the value of the fulfilled input promise.
//
// Or reject with a IllegalArgumentError if the input array is empty--i.e. it
// is impossible to have one winner.
//
// Or reject with an array of all the rejection reasons, if the input array is
// non-empty, and all input promises reject.
func Any(iterable ...interface{}) Promise {
	count := int64(len(iterable))
	if count == 0 {
		return Reject(IllegalArgumentError("any(): array must not be empty"))
	}
	promise := New()
	for _, value := range iterable {
		ToPromise(value).Then(promise.Resolve, func() {
			if atomic.AddInt64(&count, -1) == 0 {
				promise.Reject(errors.New("any(): all promises failed"))
			}
		})
	}
	return promise
}

// Each function executes a provided function once per element of iterable.
//
// The callback parameter is a function to execute for each element:
//
//		func(index int, value interface{})
//
// index: The index of the current element being processed.
//
// value: The current element being processed.
//
// If any of the promises in iterable is rejected, the callback will not be
// executed. the returned promise will be rejected with the rejection reason
// of the first promise that was rejected.
func Each(callback func(int, interface{}), iterable ...interface{}) Promise {
	return All(iterable...).Then(func(a interface{}) {
		if a == nil {
			return
		}
		iterable := a.([]interface{})
		for index, value := range iterable {
			callback(index, value)
		}
	})
}

// Every function tests whether all elements in the iterable pass the test
// implemented by the provided function.
//
// The callback parameter is a function to test for each element:
//
//		func(index int, value interface{}) bool
//
// index: The index of the current element being processed.
//
// value: The current element being processed.
//
// If any of the promises in iterable is rejected, the callback will not be
// executed. the returned promise will be rejected with the rejection reason
// of the first promise that was rejected.
//
// The returned promise will fulfill with false if the callback function returns
// false for any iterable element immediately. Otherwise, if callback returned
// a true value for all elements, Every will return promise fulfill with true.
//
// If iterable is empty, The returned promise will fulfill with true.
func Every(callback func(int, interface{}) bool, iterable ...interface{}) Promise {
	return All(iterable...).Then(func(a interface{}) (interface{}, error) {
		if a == nil {
			return true, nil
		}
		iterable := a.([]interface{})
		for index, value := range iterable {
			if !callback(index, value) {
				return false, nil
			}
		}
		return true, nil
	})
}

// Some function tests whether some element in the array passes the test
// implemented by the provided function.
//
// The callback parameter is a function to test for each element:
//
//		func(index int, value interface{}) bool
//
// index: The index of the current element being processed.
//
// value: The current element being processed.
//
// If any of the promises in iterable is rejected, the callback will not be
// executed. the returned promise will be rejected with the rejection reason
// of the first promise that was rejected.
//
// The returned promise will fulfill with true if the callback function returns
// true for any iterable element immediately. Otherwise, if callback returned a
// false value for all elements, Some will return promise fulfill with false.
//
// If iterable is empty, The returned promise will fulfill with false.
func Some(callback func(int, interface{}) bool, iterable ...interface{}) Promise {
	return All(iterable...).Then(func(a interface{}) (interface{}, error) {
		if a == nil {
			return false, nil
		}
		iterable := a.([]interface{})
		for index, value := range iterable {
			if callback(index, value) {
				return true, nil
			}
		}
		return false, nil
	})
}
<|endoftext|>


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

// Package kzip implements the kzip compilation storage file format.
//
// The package exports two types of interest: A kzip.Reader can be used to read
// the contents of an existing kzip archive, and a kzip.Writer can be used to
// construct a new kzip archive.
//
// Reading an Archive:
//
//   r, err := kzip.NewReader(file, size)
//   ...
//
//   // Look up a compilation record by its digest.
//   unit, err := r.Lookup(unitDigest)
//   ...
//
//   // Scan all the compilation records stored.
//   err := r.Scan(func(unit *kzip.Unit) error {
//      if hasInterestingProperty(unit) {
//         doStuffWith(unit)
//      }
//      return nil
//   })
//
//   // Open a reader for a stored file.
//   rc, err := r.Open(fileDigest)
//   ...
//   defer rc.Close()
//
//   // Read the complete contents of a stored file.
//   bits, err := r.ReadAll(fileDigest)
//   ...
//
// Writing an Archive:
//
//   w, err := kzip.NewWriter(file)
//   ...
//
//   // Add a compilation record and (optional) index data.
//   udigest, err := w.AddUnit(unit, nil)
//   ...
//
//   // Add file contents.
//   fdigest, err := w.AddFile(file)
//   ...
//
package kzip

import (
	"archive/zip"
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"sync"
	"time"

	"kythe.io/kythe/go/platform/kcd/kythe"

	"github.com/golang/protobuf/jsonpb"

	apb "kythe.io/kythe/proto/analysis_go_proto"

	// These are common detail messages used by Kythe compilations, and
	// required for JSON (un)marshaling to work.
	_ "kythe.io/kythe/proto/buildinfo_go_proto"
	_ "kythe.io/kythe/proto/cxx_go_proto"
	_ "kythe.io/kythe/proto/go_go_proto"
	_ "kythe.io/kythe/proto/java_go_proto"
)

// A Reader permits reading and scanning compilation records and file contents
// stored in a .kzip archive. The Lookup and Scan methods are mutually safe for
// concurrent use by multiple goroutines.
type Reader struct {
	zip *zip.Reader

	// The archives written by this library always use "root/" for the root
	// directory, but it's not required by the spec. Use whatever name the
	// archive actually specifies in the leading directory.
	root string
}

// NewReader constructs a new Reader that consumes zip data from r, whose total
// size in bytes is given.
func NewReader(r io.ReaderAt, size int64) (*Reader, error) {
	archive, err := zip.NewReader(r, size)
	if err != nil {
		return nil, err
	}
	if len(archive.File) == 0 {
		return nil, errors.New("archive is empty")
	} else if fi := archive.File[0].FileInfo(); !fi.IsDir() {
		return nil, errors.New("archive root is not a directory")
	}
	return &Reader{
		zip:  archive,
		root: archive.File[0].Name,
	}, nil
}

func (r *Reader) unitPath(digest string) string { return path.Join(r.root, "units", digest) }
func (r *Reader) filePath(digest string) string { return path.Join(r.root, "files", digest) }

// ErrDigestNotFound is returned when a requested compilation unit or file
// digest is not found.
var ErrDigestNotFound = errors.New("digest not found")

// ErrUnitExists is returned by AddUnit when adding the same compilation
// multiple times.
var ErrUnitExists = errors.New("unit already exists")

func readUnit(digest string, f *zip.File) (*Unit, error) {
	rc, err := f.Open()
	if err != nil {
		return nil, err
	}
	defer rc.Close()

	var msg apb.IndexedCompilation
	if err := jsonpb.Unmarshal(rc, &msg); err != nil {
		return nil, err
	}
	return &Unit{
		Digest: digest,
		Proto:  msg.Unit,
		Index:  msg.Index,
	}, nil
}

// Lookup returns the specified compilation from the archive, if it exists.  If
// the requested digest is not in the archive, ErrDigestNotFound is returned.
func (r *Reader) Lookup(unitDigest string) (*Unit, error) {
	needle := r.unitPath(unitDigest)
	for _, f := range r.zip.File {
		if f.Name == needle {
			return readUnit(unitDigest, f)
		}
	}
	return nil, ErrDigestNotFound
}

// Scan scans all the compilations stored in the archive, and invokes f for
// each compilation record. If f reports an error, the scan is terminated and
// that error is propagated to the caller of Scan.
func (r *Reader) Scan(f func(*Unit) error) error {
	prefix := r.unitPath("") + "/"
	for _, file := range r.zip.File {
		if !strings.HasPrefix(file.Name, prefix) {
			continue // skip root, files
		}
		digest := strings.TrimPrefix(file.Name, prefix)
		unit, err := readUnit(digest, file)
		if err != nil {
			return err
		}
		if err := f(unit); err != nil {
			return err
		}
	}
	return nil
}

// Open opens a reader on the contents of the specified file digest.  If the
// requested digest is not in the archive, ErrDigestNotFound is returned.  The
// caller must close the reader when it is no longer needed.
func (r *Reader) Open(fileDigest string) (io.ReadCloser, error) {
	needle := r.filePath(fileDigest)
	for _, f := range r.zip.File {
		if f.Name == needle {
			return f.Open()
		}
	}
	return nil, ErrDigestNotFound
}

// ReadAll returns the complete contents of the file with the specified digest.
// It is a convenience wrapper for Open followed by ioutil.ReadAll.
func (r *Reader) ReadAll(fileDigest string) ([]byte, error) {
	f, err := r.Open(fileDigest)
	if err == nil {
		defer f.Close()
		return ioutil.ReadAll(f)
	}
	return nil, err
}

// A Unit represents a compilation record read from a kzip archive.
type Unit struct {
	Digest string
	Proto  *apb.CompilationUnit
	Index  *apb.IndexedCompilation_Index
}

// A Writer permits construction of a .kzip archive.
type Writer struct {
	mu  sync.Mutex
	zip *zip.Writer
	fd  map[string]bool // file digests already written
	ud  map[string]bool // unit digests already written
}

// NewWriter constructs a new empty Writer that delivers output to w.  The
// AddUnit and AddFile methods are safe for use by concurrent goroutines.
func NewWriter(w io.Writer) (*Writer, error) {
	archive := zip.NewWriter(w)
	// Create an entry for the root directory, which must be first.
	root := &zip.FileHeader{
		Name:    "root/",
		Comment: "kzip root directory",
	}
	root.SetMode(os.ModeDir | 0755)
	root.SetModTime(time.Now())
	if _, err := archive.CreateHeader(root); err != nil {
		return nil, err
	}
	archive.SetComment("Kythe kzip archive")

	return &Writer{
		zip: archive,
		fd:  make(map[string]bool),
		ud:  make(map[string]bool),
	}, nil
}

// toJSON defines the encoding format for compilation messages.
var toJSON = &jsonpb.Marshaler{OrigName: true}

// AddUnit adds a new compilation record to be added to the archive, returning
// the hex-encoded SHA256 digest of the unit's contents. It is legal for index
// to be nil, in which case no index terms will be added.
//
// If the same compilation is added multiple times, AddUnit returns the digest
// of the duplicated compilation along with ErrUnitExists to all callers after
// the first. The existing unit is not modified.
func (w *Writer) AddUnit(cu *apb.CompilationUnit, index *apb.IndexedCompilation_Index) (string, error) {
	unit := kythe.Unit{Proto: cu}
	unit.Canonicalize()
	hash := sha256.New()
	unit.Digest(hash)
	digest := hex.EncodeToString(hash.Sum(nil))

	w.mu.Lock()
	defer w.mu.Unlock()
	if _, ok := w.ud[digest]; ok {
		return digest, ErrUnitExists
	}

	f, err := w.zip.CreateHeader(newFileHeader("root", "units", digest))
	if err != nil {
		return "", err
	}
	if err := toJSON.Marshal(f, &apb.IndexedCompilation{
		Unit:  unit.Proto,
		Index: index,
	}); err != nil {
		return "", err
	}
	w.ud[digest] = true
	return digest, nil
}

// AddFile copies the complete contents of r into the archive as a new file
// entry, returning the hex-encoded SHA256 digest of the file's contents.
func (w *Writer) AddFile(r io.Reader) (string, error) {
	// Buffer the file contents and compute their digest.
	// We have to do this ahead of time, because we have to provide the name of
	// the file before we can start writing its contents.
	var buf bytes.Buffer
	hash := sha256.New()
	if _, err := io.Copy(io.MultiWriter(hash, &buf), r); err != nil {
		return "", err
	}
	digest := hex.EncodeToString(hash.Sum(nil))

	w.mu.Lock()
	defer w.mu.Unlock()
	if _, ok := w.fd[digest]; ok {
		return digest, nil // already written
	}

	f, err := w.zip.CreateHeader(newFileHeader("root", "files", digest))
	if err != nil {
		return "", err
	}
	if _, err := io.Copy(f, &buf); err != nil {
		return "", err
	}
	w.fd[digest] = true
	return digest, nil
}

// Close closes the writer, flushing any remaining unwritten data out to the
// underlying zip file. It is safe to close w arbitrarily many times; all calls
// after the first will report nil.
func (w *Writer) Close() error {
	w.mu.Lock()
	defer w.mu.Unlock()
	if w.zip != nil {
		err := w.zip.Close()
		w.zip = nil
		return err
	}
	return nil
}

func newFileHeader(parts ...string) *zip.FileHeader {
	fh := &zip.FileHeader{Name: path.Join(parts...), Method: zip.Deflate}
	fh.SetMode(0600)
	fh.SetModTime(time.Now())
	return fh
}

// Scan is a convenience function that creates a *Reader from f and invokes its
// Scan method with the given callback. Each invocation of scan is passed the
// reader associated with f, along with the current compilation unit.
func Scan(f File, scan func(*Reader, *Unit) error) error {
	size, err := f.Seek(0, io.SeekEnd)
	if err != nil {
		return fmt.Errorf("getting file size: %v", err)
	}
	r, err := NewReader(f, size)
	if err != nil {
		return err
	}
	return r.Scan(func(unit *Unit) error {
		return scan(r, unit)
	})
}

// A File represents the file capabilities needed to scan a kzip file.
type File interface {
	io.ReaderAt
	io.Seeker
}
<commit_msg>kzip: Use binary search to speed up reads and scans. (#105)<commit_after>/*
 * Copyright 2018 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Package kzip implements the kzip compilation storage file format.
//
// The package exports two types of interest: A kzip.Reader can be used to read
// the contents of an existing kzip archive, and a kzip.Writer can be used to
// construct a new kzip archive.
//
// Reading an Archive:
//
//   r, err := kzip.NewReader(file, size)
//   ...
//
//   // Look up a compilation record by its digest.
//   unit, err := r.Lookup(unitDigest)
//   ...
//
//   // Scan all the compilation records stored.
//   err := r.Scan(func(unit *kzip.Unit) error {
//      if hasInterestingProperty(unit) {
//         doStuffWith(unit)
//      }
//      return nil
//   })
//
//   // Open a reader for a stored file.
//   rc, err := r.Open(fileDigest)
//   ...
//   defer rc.Close()
//
//   // Read the complete contents of a stored file.
//   bits, err := r.ReadAll(fileDigest)
//   ...
//
// Writing an Archive:
//
//   w, err := kzip.NewWriter(file)
//   ...
//
//   // Add a compilation record and (optional) index data.
//   udigest, err := w.AddUnit(unit, nil)
//   ...
//
//   // Add file contents.
//   fdigest, err := w.AddFile(file)
//   ...
//
package kzip

import (
	"archive/zip"
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"sort"
	"strings"
	"sync"
	"time"

	"kythe.io/kythe/go/platform/kcd/kythe"

	"github.com/golang/protobuf/jsonpb"

	apb "kythe.io/kythe/proto/analysis_go_proto"

	// These are common detail messages used by Kythe compilations, and
	// required for JSON (un)marshaling to work.
	_ "kythe.io/kythe/proto/buildinfo_go_proto"
	_ "kythe.io/kythe/proto/cxx_go_proto"
	_ "kythe.io/kythe/proto/go_go_proto"
	_ "kythe.io/kythe/proto/java_go_proto"
)

// A Reader permits reading and scanning compilation records and file contents
// stored in a .kzip archive. The Lookup and Scan methods are mutually safe for
// concurrent use by multiple goroutines.
type Reader struct {
	zip *zip.Reader

	// The archives written by this library always use "root/" for the root
	// directory, but it's not required by the spec. Use whatever name the
	// archive actually specifies in the leading directory.
	root string
}

// NewReader constructs a new Reader that consumes zip data from r, whose total
// size in bytes is given.
func NewReader(r io.ReaderAt, size int64) (*Reader, error) {
	archive, err := zip.NewReader(r, size)
	if err != nil {
		return nil, err
	}
	// Order the files in the archive by path, so we can binary search.
	sort.Slice(archive.File, func(i, j int) bool {
		return archive.File[i].Name < archive.File[j].Name
	})

	if len(archive.File) == 0 {
		return nil, errors.New("archive is empty")
	} else if fi := archive.File[0].FileInfo(); !fi.IsDir() {
		return nil, errors.New("archive root is not a directory")
	}

	return &Reader{
		zip:  archive,
		root: archive.File[0].Name,
	}, nil
}

func (r *Reader) unitPath(digest string) string { return path.Join(r.root, "units", digest) }
func (r *Reader) filePath(digest string) string { return path.Join(r.root, "files", digest) }

// ErrDigestNotFound is returned when a requested compilation unit or file
// digest is not found.
var ErrDigestNotFound = errors.New("digest not found")

// ErrUnitExists is returned by AddUnit when adding the same compilation
// multiple times.
var ErrUnitExists = errors.New("unit already exists")

func readUnit(digest string, f *zip.File) (*Unit, error) {
	rc, err := f.Open()
	if err != nil {
		return nil, err
	}
	defer rc.Close()

	var msg apb.IndexedCompilation
	if err := jsonpb.Unmarshal(rc, &msg); err != nil {
		return nil, err
	}
	return &Unit{
		Digest: digest,
		Proto:  msg.Unit,
		Index:  msg.Index,
	}, nil
}

// firstIndex returns the first index in the archive's file list whose path is
// greater than or equal to with prefix, or -1 if no such index exists.
func (r *Reader) firstIndex(prefix string) int {
	fs := r.zip.File
	n := sort.Search(len(fs), func(i int) bool {
		return fs[i].Name >= prefix
	})
	if n >= len(fs) {
		return -1
	}
	return n
}

// Lookup returns the specified compilation from the archive, if it exists.  If
// the requested digest is not in the archive, ErrDigestNotFound is returned.
func (r *Reader) Lookup(unitDigest string) (*Unit, error) {
	needle := r.unitPath(unitDigest)
	if pos := r.firstIndex(needle); pos >= 0 {
		if f := r.zip.File[pos]; f.Name == needle {
			return readUnit(unitDigest, f)
		}
	}
	return nil, ErrDigestNotFound
}

// Scan scans all the compilations stored in the archive, and invokes f for
// each compilation record. If f reports an error, the scan is terminated and
// that error is propagated to the caller of Scan.
func (r *Reader) Scan(f func(*Unit) error) error {
	prefix := r.unitPath("") + "/"
	pos := r.firstIndex(prefix)
	if pos < 0 {
		return nil
	}
	for _, file := range r.zip.File[pos:] {
		if !strings.HasPrefix(file.Name, prefix) {
			break
		}
		digest := strings.TrimPrefix(file.Name, prefix)
		unit, err := readUnit(digest, file)
		if err != nil {
			return err
		}
		if err := f(unit); err != nil {
			return err
		}
	}
	return nil
}

// Open opens a reader on the contents of the specified file digest.  If the
// requested digest is not in the archive, ErrDigestNotFound is returned.  The
// caller must close the reader when it is no longer needed.
func (r *Reader) Open(fileDigest string) (io.ReadCloser, error) {
	needle := r.filePath(fileDigest)
	if pos := r.firstIndex(needle); pos >= 0 {
		if f := r.zip.File[pos]; f.Name == needle {
			return f.Open()
		}
	}
	return nil, ErrDigestNotFound
}

// ReadAll returns the complete contents of the file with the specified digest.
// It is a convenience wrapper for Open followed by ioutil.ReadAll.
func (r *Reader) ReadAll(fileDigest string) ([]byte, error) {
	f, err := r.Open(fileDigest)
	if err == nil {
		defer f.Close()
		return ioutil.ReadAll(f)
	}
	return nil, err
}

// A Unit represents a compilation record read from a kzip archive.
type Unit struct {
	Digest string
	Proto  *apb.CompilationUnit
	Index  *apb.IndexedCompilation_Index
}

// A Writer permits construction of a .kzip archive.
type Writer struct {
	mu  sync.Mutex
	zip *zip.Writer
	fd  map[string]bool // file digests already written
	ud  map[string]bool // unit digests already written
}

// NewWriter constructs a new empty Writer that delivers output to w.  The
// AddUnit and AddFile methods are safe for use by concurrent goroutines.
func NewWriter(w io.Writer) (*Writer, error) {
	archive := zip.NewWriter(w)
	// Create an entry for the root directory, which must be first.
	root := &zip.FileHeader{
		Name:    "root/",
		Comment: "kzip root directory",
	}
	root.SetMode(os.ModeDir | 0755)
	root.SetModTime(time.Now())
	if _, err := archive.CreateHeader(root); err != nil {
		return nil, err
	}
	archive.SetComment("Kythe kzip archive")

	return &Writer{
		zip: archive,
		fd:  make(map[string]bool),
		ud:  make(map[string]bool),
	}, nil
}

// toJSON defines the encoding format for compilation messages.
var toJSON = &jsonpb.Marshaler{OrigName: true}

// AddUnit adds a new compilation record to be added to the archive, returning
// the hex-encoded SHA256 digest of the unit's contents. It is legal for index
// to be nil, in which case no index terms will be added.
//
// If the same compilation is added multiple times, AddUnit returns the digest
// of the duplicated compilation along with ErrUnitExists to all callers after
// the first. The existing unit is not modified.
func (w *Writer) AddUnit(cu *apb.CompilationUnit, index *apb.IndexedCompilation_Index) (string, error) {
	unit := kythe.Unit{Proto: cu}
	unit.Canonicalize()
	hash := sha256.New()
	unit.Digest(hash)
	digest := hex.EncodeToString(hash.Sum(nil))

	w.mu.Lock()
	defer w.mu.Unlock()
	if _, ok := w.ud[digest]; ok {
		return digest, ErrUnitExists
	}

	f, err := w.zip.CreateHeader(newFileHeader("root", "units", digest))
	if err != nil {
		return "", err
	}
	if err := toJSON.Marshal(f, &apb.IndexedCompilation{
		Unit:  unit.Proto,
		Index: index,
	}); err != nil {
		return "", err
	}
	w.ud[digest] = true
	return digest, nil
}

// AddFile copies the complete contents of r into the archive as a new file
// entry, returning the hex-encoded SHA256 digest of the file's contents.
func (w *Writer) AddFile(r io.Reader) (string, error) {
	// Buffer the file contents and compute their digest.
	// We have to do this ahead of time, because we have to provide the name of
	// the file before we can start writing its contents.
	var buf bytes.Buffer
	hash := sha256.New()
	if _, err := io.Copy(io.MultiWriter(hash, &buf), r); err != nil {
		return "", err
	}
	digest := hex.EncodeToString(hash.Sum(nil))

	w.mu.Lock()
	defer w.mu.Unlock()
	if _, ok := w.fd[digest]; ok {
		return digest, nil // already written
	}

	f, err := w.zip.CreateHeader(newFileHeader("root", "files", digest))
	if err != nil {
		return "", err
	}
	if _, err := io.Copy(f, &buf); err != nil {
		return "", err
	}
	w.fd[digest] = true
	return digest, nil
}

// Close closes the writer, flushing any remaining unwritten data out to the
// underlying zip file. It is safe to close w arbitrarily many times; all calls
// after the first will report nil.
func (w *Writer) Close() error {
	w.mu.Lock()
	defer w.mu.Unlock()
	if w.zip != nil {
		err := w.zip.Close()
		w.zip = nil
		return err
	}
	return nil
}

func newFileHeader(parts ...string) *zip.FileHeader {
	fh := &zip.FileHeader{Name: path.Join(parts...), Method: zip.Deflate}
	fh.SetMode(0600)
	fh.SetModTime(time.Now())
	return fh
}

// Scan is a convenience function that creates a *Reader from f and invokes its
// Scan method with the given callback. Each invocation of scan is passed the
// reader associated with f, along with the current compilation unit.
func Scan(f File, scan func(*Reader, *Unit) error) error {
	size, err := f.Seek(0, io.SeekEnd)
	if err != nil {
		return fmt.Errorf("getting file size: %v", err)
	}
	r, err := NewReader(f, size)
	if err != nil {
		return err
	}
	return r.Scan(func(unit *Unit) error {
		return scan(r, unit)
	})
}

// A File represents the file capabilities needed to scan a kzip file.
type File interface {
	io.ReaderAt
	io.Seeker
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/rand"
	"flag"
	"fmt"
	"log"
	"math"
	"math/big"
	"strconv"
	"strings"
)

var (
	startingCards = flag.Int("starting_cards", 5, "Number of cards each player should start with.")
	numGames      = flag.Int("num_games", 1000, "Number of games that will be played.")
	numTests      = flag.Int("num_tests", 100, "Number of tests to be performed.")
	ais           = flag.String("ais", "avoidJChooseSuit,avoidJChooseSuit", "AI algorithms to be used by each player separated by comma. The first player is the main one.")
	randomStart   = flag.Bool("random_start", true, "Defines who starts randomly. If false, the first player always starts.")
	decks         = flag.Int("decks", 1, "Number of card decks to be used.")
	debug         = flag.Bool("debug", false, "Print debug information")
)

func main() {
	flag.Parse()
	var results []int
	total := 0
	for i := 0; i < *numTests; i++ {
		res := runGame()
		results = append(results, res)
		total += res
	}
	avg := float64(total) / float64(*numTests)
	variance := 0.0
	for _, res := range results {
		term := float64(res) - avg
		variance += term * term
	}
	variance /= float64(*numTests)
	fmt.Printf("%v+-%v\n", avg, math.Sqrt(variance))
}

func runGame() int {
	w := 0
	for i := 0; i < *numGames; i++ {
		g := newGame()
		if g.play() == 0 {
			w++
		}
	}
	return w
}

type game struct {
	players []*player
	playing int
	deck    *deck
	top     *card
	asked   suit
	order   int
	garbage []*card
}

func newGame() *game {
	g := &game{}
	if *randomStart {
		if *debug {
			fmt.Println("random_start")
		}
		g.playing = randInt(2)
	}
	g.deck = newDeck()
	for _, aiName := range strings.Split(*ais, ",") {
		g.players = append(g.players, newPlayer(aiName))
	}
	for nc := 0; nc < *startingCards; nc++ {
		for _, player := range g.players {
			player.addCard(g.getCard())
		}
	}
	g.top = g.getCard()
	g.order = 1
	return g
}

func (g *game) play() int {
	for {
		top, asked := g.player().play(g.top, g.asked, g.deck)
		if *debug {
			fmt.Println(g, top, asked)
		}
		if top == nil {
			g.addCard()
			g.next()
			continue
		}
		isPlayValid(top, asked, g.top, g.asked)
		if g.end() {
			return g.playing
		}
		g.garbage = append(g.garbage, g.top)
		g.top, g.asked = top, asked
		switch g.top.n {
		case 1:
			g.next()
		case 12:
			if len(g.players) == 2 {
				g.next()
			} else {
				g.order *= -1
			}
		case 7:
			g.next()
			g.addCard()
			g.addCard()
		}
		g.next()
	}
}

func (g *game) player() *player {
	return g.players[g.playing]
}

func (g *game) next() {
	g.playing = (g.playing + g.order + len(g.players)) % len(g.players)
}

func (g *game) getCard() *card {
	c := g.deck.getCard()
	if c != nil {
		return c
	}
	if g.garbage == nil {
		log.Fatal("empty garbage")
	}
	g.deck.cs = g.garbage
	g.deck.shuffle()
	g.garbage = nil
	return g.deck.getCard()
}

func (g *game) addCard() {
	g.player().addCard(g.getCard())
}

func (g *game) end() bool {
	return g.player().end()
}

func (g *game) String() string {
	var b bytes.Buffer
	b.WriteString(fmt.Sprintf("-> %v %v %v %v %v %v ", len(g.deck.cs), len(g.garbage), g.playing, g.top, g.asked, g.order))
	for _, p := range g.players {
		b.WriteString(p.String())
		b.WriteString(" ")
	}
	return b.String()
}

type deck struct {
	cs []*card
}

func newDeck() *deck {
	d := &deck{}
	for n := 1; n <= 13; n++ {
		for s := spades; s <= clubs; s++ {
			for i := 0; i < *decks; i++ {
				d.cs = append(d.cs, &card{n, s})
			}
		}
	}
	d.shuffle()
	return d
}

func (d *deck) shuffle() {
	l := len(d.cs)
	s := make([]*card, l)
	for i, c := range d.cs {
		r := randInt(i + 1)
		s[i] = s[r]
		s[r] = c
	}
	d.cs = s
}

func (d *deck) getCard() *card {
	if len(d.cs) == 0 {
		return nil
	}
	c := d.cs[0]
	d.cs = d.cs[1:]
	return c
}

type card struct {
	n int
	s suit
}

func (c *card) String() string {
	return strconv.Itoa(c.n) + "/" + strconv.Itoa(int(c.s))
}

type suit int

const (
	noSuit suit = iota
	spades
	hearts
	diamonds
	clubs
)

type player struct {
	cs []*card
	ai ai
}

func newPlayer(aiName string) *player {
	imp, ok := aiImplementation[aiName]
	if !ok {
		log.Fatal("Unexisting ai: ", aiName)
	}
	return &player{nil, imp}
}

func (p *player) addCard(c *card) {
	p.cs = append(p.cs, c)
}

func (p *player) play(top *card, asked suit, d *deck) (*card, suit) {
	i, s := p.ai(p.cs, top, asked, d)
	if i == -1 {
		return nil, noSuit
	}
	c := p.cs[i]
	p.cs = append(p.cs[:i], p.cs[i+1:]...)
	if c.n != 11 {
		return c, noSuit
	}
	return c, s
}

func (p *player) end() bool {
	return len(p.cs) == 0
}

func (p *player) String() string {
	return fmt.Sprintf("%v", p.cs)
}

// ai returns the index of the card in cs that should be played, -1 if there's
// no card to play, and suit if the card is a 11.
type ai func(cs []*card, top *card, asked suit, d *deck) (int, suit)

var aiImplementation = map[string]ai{
	"avoidJChooseSuit": avoidJChooseSuitAI,
	"aFirst":           aFirstAI,
	"first":            firstAI,
	"avoidJ":           avoidJAI,
	"chooseSuit":       chooseSuitAI,
	"onlyFirst":        onlyFirstAI,
	"onlyBuy":          onlyBuyAI,
}

func avoidJChooseSuitAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	is := validIndexes(cs, top, asked)
	if len(is) == 0 {
		return -1, noSuit
	}
	if n := findNumber(cs, is, 11, true); n != -1 {
		return n, noSuit
	}
	return is[0], mostPopularSuit(cs)
}

func aFirstAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	is := validIndexes(cs, top, asked)
	if len(is) == 0 {
		return -1, noSuit
	}
	if n := findNumber(cs, is, 1, false); n != -1 {
		return n, noSuit
	}
	if n := findNumber(cs, is, 11, true); n != -1 {
		return n, noSuit
	}
	return is[0], mostPopularSuit(cs)
}

func firstAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	is := validIndexes(cs, top, asked)
	if len(is) == 0 {
		return -1, noSuit
	}
	return is[0], suit(randInt(4) + 1)
}

func avoidJAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	is := validIndexes(cs, top, asked)
	if len(is) == 0 {
		return -1, noSuit
	}
	if n := findNumber(cs, is, 11, true); n != -1 {
		return n, noSuit
	}
	return is[0], suit(randInt(4) + 1)
}

func chooseSuitAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	is := validIndexes(cs, top, asked)
	if len(is) == 0 {
		return -1, noSuit
	}
	if cs[is[0]].n == 11 {
		return is[0], mostPopularSuit(cs)
	}
	return is[0], noSuit
}

func onlyFirstAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	if cs[0].n == 11 {
		return 0, spades
	}
	if asked != noSuit {
		if cs[0].s == asked {
			return 0, noSuit
		}
		return -1, noSuit
	}
	if cs[0].n == top.n || cs[0].s == top.s {
		return 0, noSuit
	}
	return -1, noSuit
}

func onlyBuyAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	return -1, noSuit
}

func validIndexes(cs []*card, top *card, asked suit) []int {
	var is []int
	for i, c := range cs {
		if c.n == 11 {
			is = append(is, i)
			continue
		}
		if asked != noSuit {
			if c.s == asked {
				is = append(is, i)
			}
			continue
		}
		if c.s == top.s || c.n == top.n {
			is = append(is, i)
		}
	}
	return is
}

func mostPopularSuit(cs []*card) suit {
	suits := make(map[suit]int)
	mps := noSuit
	for _, c := range cs {
		if c.n == 11 {
			continue
		}
		suits[c.s]++
		if suits[c.s] > suits[mps] {
			mps = c.s
		}
	}
	if mps == noSuit {
		return suit(randInt(4) + 1)
	}
	return mps
}

func findNumber(cs []*card, is []int, number int, inverse bool) int {
	for _, i := range is {
		if (cs[i].n != number) == inverse {
			return i
		}
	}
	return -1
}

func isPlayValid(newc *card, newa suit, oldc *card, olda suit) {
	if newc.n == 11 {
		if newa == noSuit {
			log.Fatal("newc.n = 11, newa = noSuit")
		}
		return
	}
	if newa != noSuit {
		log.Fatalf("newc.n = %v, newa = %v", newc.n, newa)
	}
	if olda != noSuit {
		if newc.s != olda {
			log.Fatalf("newc.s = %v, olda = %v", newc.s, olda)
		}
		return
	}
	if newc.n != oldc.n && newc.s != newc.s {
		log.Fatalf("newc = %v, oldc = %v", newc, oldc)
	}
}

// randInt returns a random number from 0 to max - 1.
func randInt(max int) int {
	b, err := rand.Int(rand.Reader, big.NewInt(int64(max)))
	if err != nil {
		log.Fatal(err)
	}
	return int(b.Int64())
}
<commit_msg>qFirst<commit_after>package main

import (
	"bytes"
	"crypto/rand"
	"flag"
	"fmt"
	"log"
	"math"
	"math/big"
	"strconv"
	"strings"
)

var (
	startingCards = flag.Int("starting_cards", 5, "Number of cards each player should start with.")
	numGames      = flag.Int("num_games", 1000, "Number of games that will be played.")
	numTests      = flag.Int("num_tests", 100, "Number of tests to be performed.")
	ais           = flag.String("ais", "avoidJChooseSuit,avoidJChooseSuit", "AI algorithms to be used by each player separated by comma. The first player is the main one.")
	randomStart   = flag.Bool("random_start", true, "Defines who starts randomly. If false, the first player always starts.")
	decks         = flag.Int("decks", 1, "Number of card decks to be used.")
	debug         = flag.Bool("debug", false, "Print debug information")
)

func main() {
	flag.Parse()
	var results []int
	total := 0
	for i := 0; i < *numTests; i++ {
		res := runGame()
		results = append(results, res)
		total += res
	}
	avg := float64(total) / float64(*numTests)
	variance := 0.0
	for _, res := range results {
		term := float64(res) - avg
		variance += term * term
	}
	variance /= float64(*numTests)
	fmt.Printf("%v+-%v\n", avg, math.Sqrt(variance))
}

func runGame() int {
	w := 0
	for i := 0; i < *numGames; i++ {
		g := newGame()
		if g.play() == 0 {
			w++
		}
	}
	return w
}

type game struct {
	players []*player
	playing int
	deck    *deck
	top     *card
	asked   suit
	order   int
	garbage []*card
}

func newGame() *game {
	g := &game{}
	if *randomStart {
		if *debug {
			fmt.Println("random_start")
		}
		g.playing = randInt(2)
	}
	g.deck = newDeck()
	for _, aiName := range strings.Split(*ais, ",") {
		g.players = append(g.players, newPlayer(aiName))
	}
	for nc := 0; nc < *startingCards; nc++ {
		for _, player := range g.players {
			player.addCard(g.getCard())
		}
	}
	g.top = g.getCard()
	g.order = 1
	return g
}

func (g *game) play() int {
	for {
		top, asked := g.player().play(g.top, g.asked, g.deck)
		if *debug {
			fmt.Println(g, top, asked)
		}
		if top == nil {
			g.addCard()
			g.next()
			continue
		}
		isPlayValid(top, asked, g.top, g.asked)
		if g.end() {
			return g.playing
		}
		g.garbage = append(g.garbage, g.top)
		g.top, g.asked = top, asked
		switch g.top.n {
		case 1:
			g.next()
		case 12:
			if len(g.players) == 2 {
				g.next()
			} else {
				g.order *= -1
			}
		case 7:
			g.next()
			g.addCard()
			g.addCard()
		}
		g.next()
	}
}

func (g *game) player() *player {
	return g.players[g.playing]
}

func (g *game) next() {
	g.playing = (g.playing + g.order + len(g.players)) % len(g.players)
}

func (g *game) getCard() *card {
	c := g.deck.getCard()
	if c != nil {
		return c
	}
	if g.garbage == nil {
		log.Fatal("empty garbage")
	}
	g.deck.cs = g.garbage
	g.deck.shuffle()
	g.garbage = nil
	return g.deck.getCard()
}

func (g *game) addCard() {
	g.player().addCard(g.getCard())
}

func (g *game) end() bool {
	return g.player().end()
}

func (g *game) String() string {
	var b bytes.Buffer
	b.WriteString(fmt.Sprintf("-> %v %v %v %v %v %v ", len(g.deck.cs), len(g.garbage), g.playing, g.top, g.asked, g.order))
	for _, p := range g.players {
		b.WriteString(p.String())
		b.WriteString(" ")
	}
	return b.String()
}

type deck struct {
	cs []*card
}

func newDeck() *deck {
	d := &deck{}
	for n := 1; n <= 13; n++ {
		for s := spades; s <= clubs; s++ {
			for i := 0; i < *decks; i++ {
				d.cs = append(d.cs, &card{n, s})
			}
		}
	}
	d.shuffle()
	return d
}

func (d *deck) shuffle() {
	l := len(d.cs)
	s := make([]*card, l)
	for i, c := range d.cs {
		r := randInt(i + 1)
		s[i] = s[r]
		s[r] = c
	}
	d.cs = s
}

func (d *deck) getCard() *card {
	if len(d.cs) == 0 {
		return nil
	}
	c := d.cs[0]
	d.cs = d.cs[1:]
	return c
}

type card struct {
	n int
	s suit
}

func (c *card) String() string {
	return strconv.Itoa(c.n) + "/" + strconv.Itoa(int(c.s))
}

type suit int

const (
	noSuit suit = iota
	spades
	hearts
	diamonds
	clubs
)

type player struct {
	cs []*card
	ai ai
}

func newPlayer(aiName string) *player {
	imp, ok := aiImplementation[aiName]
	if !ok {
		log.Fatal("Unexisting ai: ", aiName)
	}
	return &player{nil, imp}
}

func (p *player) addCard(c *card) {
	p.cs = append(p.cs, c)
}

func (p *player) play(top *card, asked suit, d *deck) (*card, suit) {
	i, s := p.ai(p.cs, top, asked, d)
	if i == -1 {
		return nil, noSuit
	}
	c := p.cs[i]
	p.cs = append(p.cs[:i], p.cs[i+1:]...)
	if c.n != 11 {
		return c, noSuit
	}
	return c, s
}

func (p *player) end() bool {
	return len(p.cs) == 0
}

func (p *player) String() string {
	return fmt.Sprintf("%v", p.cs)
}

// ai returns the index of the card in cs that should be played, -1 if there's
// no card to play, and suit if the card is a 11.
type ai func(cs []*card, top *card, asked suit, d *deck) (int, suit)

var aiImplementation = map[string]ai{
	"avoidJChooseSuit": avoidJChooseSuitAI,
	"aFirst":           aFirstAI,
	"qFirst":           qFirstAI,
	"first":            firstAI,
	"avoidJ":           avoidJAI,
	"chooseSuit":       chooseSuitAI,
	"onlyFirst":        onlyFirstAI,
	"onlyBuy":          onlyBuyAI,
}

func avoidJChooseSuitAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	is := validIndexes(cs, top, asked)
	if len(is) == 0 {
		return -1, noSuit
	}
	if n := findNumber(cs, is, 11, true); n != -1 {
		return n, noSuit
	}
	return is[0], mostPopularSuit(cs)
}

func aFirstAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	is := validIndexes(cs, top, asked)
	if len(is) == 0 {
		return -1, noSuit
	}
	if n := findNumber(cs, is, 1, false); n != -1 {
		return n, noSuit
	}
	if n := findNumber(cs, is, 11, true); n != -1 {
		return n, noSuit
	}
	return is[0], mostPopularSuit(cs)
}

func qFirstAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	is := validIndexes(cs, top, asked)
	if len(is) == 0 {
		return -1, noSuit
	}
	if n := findNumber(cs, is, 12, false); n != -1 {
		return n, noSuit
	}
	if n := findNumber(cs, is, 11, true); n != -1 {
		return n, noSuit
	}
	return is[0], mostPopularSuit(cs)
}

func firstAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	is := validIndexes(cs, top, asked)
	if len(is) == 0 {
		return -1, noSuit
	}
	return is[0], suit(randInt(4) + 1)
}

func avoidJAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	is := validIndexes(cs, top, asked)
	if len(is) == 0 {
		return -1, noSuit
	}
	if n := findNumber(cs, is, 11, true); n != -1 {
		return n, noSuit
	}
	return is[0], suit(randInt(4) + 1)
}

func chooseSuitAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	is := validIndexes(cs, top, asked)
	if len(is) == 0 {
		return -1, noSuit
	}
	if cs[is[0]].n == 11 {
		return is[0], mostPopularSuit(cs)
	}
	return is[0], noSuit
}

func onlyFirstAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	if cs[0].n == 11 {
		return 0, spades
	}
	if asked != noSuit {
		if cs[0].s == asked {
			return 0, noSuit
		}
		return -1, noSuit
	}
	if cs[0].n == top.n || cs[0].s == top.s {
		return 0, noSuit
	}
	return -1, noSuit
}

func onlyBuyAI(cs []*card, top *card, asked suit, d *deck) (int, suit) {
	return -1, noSuit
}

func validIndexes(cs []*card, top *card, asked suit) []int {
	var is []int
	for i, c := range cs {
		if c.n == 11 {
			is = append(is, i)
			continue
		}
		if asked != noSuit {
			if c.s == asked {
				is = append(is, i)
			}
			continue
		}
		if c.s == top.s || c.n == top.n {
			is = append(is, i)
		}
	}
	return is
}

func mostPopularSuit(cs []*card) suit {
	suits := make(map[suit]int)
	mps := noSuit
	for _, c := range cs {
		if c.n == 11 {
			continue
		}
		suits[c.s]++
		if suits[c.s] > suits[mps] {
			mps = c.s
		}
	}
	if mps == noSuit {
		return suit(randInt(4) + 1)
	}
	return mps
}

func findNumber(cs []*card, is []int, number int, inverse bool) int {
	for _, i := range is {
		if (cs[i].n != number) == inverse {
			return i
		}
	}
	return -1
}

func isPlayValid(newc *card, newa suit, oldc *card, olda suit) {
	if newc.n == 11 {
		if newa == noSuit {
			log.Fatal("newc.n = 11, newa = noSuit")
		}
		return
	}
	if newa != noSuit {
		log.Fatalf("newc.n = %v, newa = %v", newc.n, newa)
	}
	if olda != noSuit {
		if newc.s != olda {
			log.Fatalf("newc.s = %v, olda = %v", newc.s, olda)
		}
		return
	}
	if newc.n != oldc.n && newc.s != newc.s {
		log.Fatalf("newc = %v, oldc = %v", newc, oldc)
	}
}

// randInt returns a random number from 0 to max - 1.
func randInt(max int) int {
	b, err := rand.Int(rand.Reader, big.NewInt(int64(max)))
	if err != nil {
		log.Fatal(err)
	}
	return int(b.Int64())
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"github.com/bradfitz/gomemcache/memcache"
	"github.com/montanaflynn/stats"
	"time"
	"encoding/binary"
	"strconv"
	"crypto/rand"
)

func main() {
	for {
		measure(1000)
	}
}

func measure(n int) {
	mc := memcache.New("169.254.10.0:11211")
	timeToSet := make([]float64, n)
	timeToGet := make([]float64, n)
	for i := 0; i < n; i++ {
		timeToSet[i], timeToGet[i] = measureSetAndGetTime(mc)
		if i > 0 && timeToSet[i] < 0 {
			timeToSet[i] = timeToSet[i - 1]
		}
		if i > 0 && timeToGet[i] < 0 {
			timeToGet[i] = timeToGet[i - 1]
		}
	}
	fmt.Printf("Set min: %.2f, max: %.2f, median: %.2f, 95%%: %.2f, 99%%: %.2f \n",
		stats.Min(timeToSet), stats.Max(timeToSet), stats.Median(timeToSet),
		stats.Percentile(timeToSet, 95), stats.Percentile(timeToSet, 99))
	fmt.Printf("Get min: %.2f, max: %.2f, median: %.2f, 95%%: %.2f, 99%%: %.2f \n",
		stats.Min(timeToGet), stats.Max(timeToGet), stats.Median(timeToGet),
		stats.Percentile(timeToGet, 95), stats.Percentile(timeToGet, 99))
}

func measureSetAndGetTime(mc *memcache.Client) (float64, float64) {

	// create key and value
	k := rand12Chars(1) // 12 bytes
	v := []byte(rand12Chars(85)) // 1020 bytes

	// test setting
	start := time.Now().Nanosecond()
	mc.Set(&memcache.Item{Key: k, Value: v})
	timeToSet := float64((time.Now().Nanosecond() - start)) / 1000.0

	// test getting
	start = time.Now().Nanosecond()
	it, err := mc.Get(k)
	if err != nil {
		fmt.Printf("%v\n", err)
	 	return -1, -1
	}
	timeToGet := float64((time.Now().Nanosecond() - start)) / 1000.0

	// validate the value
	if !bytes.Equal(it.Value, v) {
		fmt.Printf("Wrong value: %v\n", it.Value)
	 	return -1, -1
	}
	return timeToSet, timeToGet
}

func rand12Chars(l int) string {
	var buf bytes.Buffer
	for i := 0; i < l; i++ {
		var n uint64
		binary.Read(rand.Reader, binary.LittleEndian, &n)
		buf.WriteString(strconv.FormatUint(n, 36))
	}
	return buf.String()
}
<commit_msg>Added goroutine support<commit_after>package main

import (
	"bytes"
	"fmt"
	"github.com/bradfitz/gomemcache/memcache"
	"github.com/montanaflynn/stats"
	"time"
	"encoding/binary"
	"strconv"
	"crypto/rand"
	"runtime"
)

func main() {
	initGRs := runtime.NumGoroutine()
        maxGRs := 1
	for {
		for runtime.NumGoroutine() < initGRs + maxGRs {
			go measure(1000)
		}
	}
}

func measure(n int) {
	mc := memcache.New("169.254.10.0:11211")
	timeToSet := make([]float64, n)
	timeToGet := make([]float64, n)
	for i := 0; i < n; i++ {
		timeToSet[i], timeToGet[i] = measureSetAndGetTime(mc)
		if i > 0 && timeToSet[i] < 0 {
			timeToSet[i] = timeToSet[i - 1]
		}
		if i > 0 && timeToGet[i] < 0 {
			timeToGet[i] = timeToGet[i - 1]
		}
	}
	fmt.Printf("Set min: %.2f, max: %.2f, median: %.2f, 95%%: %.2f, 99%%: %.2f \n",
		stats.Min(timeToSet), stats.Max(timeToSet), stats.Median(timeToSet),
		stats.Percentile(timeToSet, 95), stats.Percentile(timeToSet, 99))
	fmt.Printf("Get min: %.2f, max: %.2f, median: %.2f, 95%%: %.2f, 99%%: %.2f \n",
		stats.Min(timeToGet), stats.Max(timeToGet), stats.Median(timeToGet),
		stats.Percentile(timeToGet, 95), stats.Percentile(timeToGet, 99))
}

func measureSetAndGetTime(mc *memcache.Client) (float64, float64) {

	// create key and value
	k := rand12Chars(1) // 12 bytes
	v := []byte(rand12Chars(85)) // 1020 bytes

	// test setting
	start := time.Now().Nanosecond()
	mc.Set(&memcache.Item{Key: k, Value: v})
	timeToSet := float64((time.Now().Nanosecond() - start)) / 1000.0

	// test getting
	start = time.Now().Nanosecond()
	it, err := mc.Get(k)
	if err != nil {
		fmt.Printf("%v\n", err)
	 	return -1, -1
	}
	timeToGet := float64((time.Now().Nanosecond() - start)) / 1000.0

	// validate the value
	if !bytes.Equal(it.Value, v) {
		fmt.Printf("Wrong value: %v\n", it.Value)
	 	return -1, -1
	}
	return timeToSet, timeToGet
}

func rand12Chars(l int) string {
	var buf bytes.Buffer
	for i := 0; i < l; i++ {
		var n uint64
		binary.Read(rand.Reader, binary.LittleEndian, &n)
		buf.WriteString(strconv.FormatUint(n, 36))
	}
	return buf.String()
}
<|endoftext|>