<commit_before>package dskvs

import (
	"bytes"
	"encoding/json"
	"testing"
)

type TestData struct {
	Descr string
}

func TestCreatingStore(t *testing.T) {
	store, err := NewStore("./db")
	if err != nil {
		t.Fatalf("Error creating store", err)
	}

	err = store.Load()
	if err != nil {
		t.Fatalf("Error loading store", err)
	}

	err = store.Close()
	if err != nil {
		t.Fatalf("Error closing store", err)
	}
}

func TestGet(t *testing.T) {

	store, err := NewStore("./db")
	if err != nil {
		t.Fatalf("Error creating store", err)
	}

	err = store.Load()
	if err != nil {
		t.Fatalf("Error loading store", err)
	}

	expected, err := json.Marshal(TestData{"the great and epics"})
	if err != nil {
		t.Fatal("Error with test data", err)
	}

	store.Put("artist/daftpunk", expected)

	actual, err := store.Get("artist/daftpunk")
	if err != nil {
		t.Fatalf("Error getting data back", err)
	}

	if !bytes.Equal(expected, actual) {
		t.Fatalf("Expected <%s> but was <%s>",
			expected,
			actual)
	}

	err = store.Close()
	if err != nil {
		t.Fatalf("Error closing store", err)
	}
}
<commit_msg>Test for proper working of map in single-thread context.<commit_after>package dskvs

import (
	"bytes"
	"encoding/json"
	"strconv"
	"testing"
)

type Data struct {
	Descr string
}

/*
	Boilerplate
*/

func setUp(t *testing.T) *Store {
	store, err := NewStore("./db")
	if err != nil {
		t.Fatalf("Error creating store", err)
	}

	err = store.Load()
	if err != nil {
		t.Fatalf("Error loading store", err)
	}
	return store
}

func tearDown(store *Store, t *testing.T) {
	err := store.Close()
	if err != nil {
		t.Fatalf("Error closing store", err)
	}
}

func getData(d Data, t *testing.T) []byte {
	dataBytes, err := json.Marshal(d)
	if err != nil {
		t.Fatal("Error with test data", err)
	}
	return dataBytes
}

/*
	Test cases
*/

func TestCreatingStore(t *testing.T) {
	store := setUp(t)
	tearDown(store, t)
}

func TestSingleOperation(t *testing.T) {

	store := setUp(t)
	defer tearDown(store, t)

	key := "artist/daftpunk"
	expected := getData(Data{"The peak of awesome"}, t)

	err := store.Put(key, expected)
	if err != nil {
		t.Fatalf("Error putting data in", err)
	}

	actual, err := store.Get(key)
	if err != nil {
		t.Fatalf("Error getting data back", err)
	}

	if !bytes.Equal(expected, actual) {
		t.Fatalf("Expected <%s> but was <%s>",
			expected,
			actual)
	}

	err = store.Delete(key)
	if err != nil {
		t.Fatalf("Error deleting data we just put", err)
	}
	_, err = store.Get(key)
	if err == nil {
		t.Fatalf("Expected to receive KeyError but no error")
	}
	switch err := err.(type) {
	case KeyError:
		// Expected
		break
	default:
		t.Fatalf("Expected to receive KeyError but got <%s>", err)
	}

}

func TestMultipleOperations(t *testing.T) {

	store := setUp(t)
	defer tearDown(store, t)

	var key string
	var expected []byte
	var actual []byte
	for i := int(0); i < 10; i++ {
		key = "artist/daftpunk" + strconv.Itoa(i)
		expected = getData(Data{"The peak of awesome" + strconv.Itoa(i)}, t)

		err := store.Put(key, expected)
		if err != nil {
			t.Errorf("Error putting data in", err)
		}
		actual, err = store.Get(key)
		if err != nil {
			t.Errorf("Error getting data back", err)
		}

		if !bytes.Equal(expected, actual) {
			t.Errorf("Expected <%s> but was <%s>",
				expected,
				actual)
		}
	}

	err := store.DeleteAll("artist")
	if err != nil {
		t.Fatal("Error deleting key", err)
	}
	for i := int(0); i < 10; i++ {
		key = "artist/daftpunk" + strconv.Itoa(i)
		_, err = store.Get(key)
		if err == nil {
			t.Fatalf("Expected to receive KeyError, but got no error at all")
		}
		switch err := err.(type) {
		case KeyError:
			// Expected
			break
		default:
			t.Fatalf("Expected to receive KeyError but got <%s>", err)
		}
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright 2013 Nan Deng
 *
 * 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 msgcache

import (
	"encoding/json"
	"fmt"
	"github.com/garyburd/redigo/redis"
	"github.com/uniqush/uniqush-conn/proto"
	"math/rand"
	"time"
)

type redisMessageCache struct {
	pool *redis.Pool
}

func NewRedisMessageCache(addr, password string, db int) Cache {
	if len(addr) == 0 {
		addr = "localhost:6379"
	}
	if db < 0 {
		db = 0
	}

	dial := func() (redis.Conn, error) {
		c, err := redis.Dial("tcp", addr)
		if err != nil {
			return nil, err
		}
		if len(password) > 0 {
			if _, err := c.Do("AUTH", password); err != nil {
				c.Close()
				return nil, err
			}
		}
		if _, err := c.Do("SELECT", db); err != nil {
			c.Close()
			return nil, err
		}
		return c, err
	}
	testOnBorrow := func(c redis.Conn, t time.Time) error {
		_, err := c.Do("PING")
		return err
	}

	pool := &redis.Pool{
		MaxIdle:      3,
		IdleTimeout:  240 * time.Second,
		Dial:         dial,
		TestOnBorrow: testOnBorrow,
	}

	ret := new(redisMessageCache)
	ret.pool = pool
	return ret
}

func randomId() string {
	return fmt.Sprintf("%v-%v-%v", time.Now().UnixNano(), rand.Int63(), rand.Int63())
}

func (self *redisMessageCache) CacheMessage(service, username string, msg *proto.Message, ttl time.Duration) (id string, err error) {
	id = randomId()
	err = self.set(service, username, id, msg, ttl)
	if err != nil {
		id = ""
		return
	}
	return
}

func msgKey(service, username, id string) string {
	return fmt.Sprintf("mcache:%v:%v:%v", service, username, id)
}

func msgQueueKey(service, username string) string {
	return fmt.Sprintf("mqueue:%v:%v", service, username)
}

func msgMarshal(msg *proto.Message) (data []byte, err error) {
	data, err = json.Marshal(msg)
	return
}

func msgUnmarshal(data []byte) (msg *proto.Message, err error) {
	msg = new(proto.Message)
	err = json.Unmarshal(data, msg)
	if err != nil {
		msg = nil
		return
	}
	return
}

func (self *redisMessageCache) set(service, username, id string, msg *proto.Message, ttl time.Duration) error {
	key := msgKey(service, username, id)
	conn := self.pool.Get()
	defer conn.Close()

	data, err := msgMarshal(msg)
	if err != nil {
		return err
	}
	err = conn.Send("MULTI")
	if err != nil {
		return err
	}

	if ttl.Seconds() <= 0.0 {
		err = conn.Send("SET", key, data)
	} else {
		err = conn.Send("SETEX", key, int64(ttl.Seconds()), data)
	}
	if err != nil {
		conn.Do("DISCARD")
		return err
	}
	msgQK := msgQueueKey(service, username)
	err = conn.Send("SADD", msgQK, id)
	if err != nil {
		conn.Do("DISCARD")
		return err
	}
	_, err = conn.Do("EXEC")
	if err != nil {
		return err
	}
	return nil
}

func (self *redisMessageCache) Get(service, username, id string) (msg *proto.Message, err error) {
	key := msgKey(service, username, id)
	conn := self.pool.Get()
	defer conn.Close()

	reply, err := conn.Do("GET", key)
	if err != nil {
		return
	}
	if reply == nil {
		return
	}
	data, err := redis.Bytes(reply, err)
	if err != nil {
		return
	}
	msg, err = msgUnmarshal(data)
	return
}

func (self *redisMessageCache) Del(service, username, id string) error {
	key := msgKey(service, username, id)
	conn := self.pool.Get()
	defer conn.Close()

	err := conn.Send("MULTI")
	if err != nil {
		return err
	}
	err = conn.Send("DEL", key)
	if err != nil {
		conn.Do("DISCARD")
		return err
	}
	msgQK := msgQueueKey(service, username)
	err = conn.Send("SREM", msgQK, id)
	if err != nil {
		conn.Do("DISCARD")
		return err
	}
	_, err = conn.Do("EXEC")
	if err != nil {
		return err
	}
	return nil
}

func (self *redisMessageCache) GetThenDel(service, username, id string) (msg *proto.Message, err error) {
	key := msgKey(service, username, id)
	conn := self.pool.Get()
	defer conn.Close()

	err = conn.Send("MULTI")
	if err != nil {
		return
	}
	err = conn.Send("GET", key)
	if err != nil {
		conn.Do("DISCARD")
		return
	}
	err = conn.Send("DEL", key)
	if err != nil {
		conn.Do("DISCARD")
		return
	}
	msgQK := msgQueueKey(service, username)
	err = conn.Send("SREM", msgQK, id)
	if err != nil {
		conn.Do("DISCARD")
		return
	}
	reply, err := conn.Do("EXEC")
	if err != nil {
		return
	}

	bulkReply, err := redis.Values(reply, err)
	if err != nil {
		return
	}
	if len(bulkReply) != 3 {
		return
	}
	if bulkReply[0] == nil {
		return
	}
	data, err := redis.Bytes(bulkReply[0], err)
	if err != nil {
		return
	}
	if len(data) == 0 {
		return
	}
	msg, err = msgUnmarshal(data)
	return
}

func (self *redisMessageCache) GetAllIds(service, username string) (ids []string, err error) {
	msgQK := msgQueueKey(service, username)
	conn := self.pool.Get()
	defer conn.Close()

	reply, err := conn.Do("SMEMBERS", msgQK)
	if err != nil {
		return
	}
	bulkReply, err := redis.Values(reply, err)
	if err != nil {
		return
	}
	n := len(bulkReply)
	if n == 0 {
		return
	}
	idshadow := make([]string, n)
	for i := 0; i < n; i++ {
		idshadow[i], err = redis.String(bulkReply[i], nil)
		if err != nil {
			return
		}
	}
	ids = idshadow
	return
}
<commit_msg>return the message ids according to insersion order<commit_after>/*
 * Copyright 2013 Nan Deng
 *
 * 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 msgcache

import (
	"encoding/json"
	"fmt"
	"github.com/garyburd/redigo/redis"
	"github.com/uniqush/uniqush-conn/proto"
	"math/rand"
	"time"
)

type redisMessageCache struct {
	pool *redis.Pool
}

func NewRedisMessageCache(addr, password string, db int) Cache {
	if len(addr) == 0 {
		addr = "localhost:6379"
	}
	if db < 0 {
		db = 0
	}

	dial := func() (redis.Conn, error) {
		c, err := redis.Dial("tcp", addr)
		if err != nil {
			return nil, err
		}
		if len(password) > 0 {
			if _, err := c.Do("AUTH", password); err != nil {
				c.Close()
				return nil, err
			}
		}
		if _, err := c.Do("SELECT", db); err != nil {
			c.Close()
			return nil, err
		}
		return c, err
	}
	testOnBorrow := func(c redis.Conn, t time.Time) error {
		_, err := c.Do("PING")
		return err
	}

	pool := &redis.Pool{
		MaxIdle:      3,
		IdleTimeout:  240 * time.Second,
		Dial:         dial,
		TestOnBorrow: testOnBorrow,
	}

	ret := new(redisMessageCache)
	ret.pool = pool
	return ret
}

func randomId() string {
	return fmt.Sprintf("%v-%v-%v", time.Now().UnixNano(), rand.Int63(), rand.Int63())
}

func (self *redisMessageCache) CacheMessage(service, username string, msg *proto.Message, ttl time.Duration) (id string, err error) {
	id = randomId()
	err = self.set(service, username, id, msg, ttl)
	if err != nil {
		id = ""
		return
	}
	return
}

func msgKey(service, username, id string) string {
	return fmt.Sprintf("mcache:%v:%v:%v", service, username, id)
}

func msgQueueKey(service, username string) string {
	return fmt.Sprintf("mqueue:%v:%v", service, username)
}

func msgWeightKey(service, username, id string) string {
	return fmt.Sprintf("w_mcache:%v:%v:%v", service, username, id)
}

func msgWeightPattern(service, username string) string {
	return fmt.Sprintf("w_mcache:%v:%v:", service, username)
}

func counterKey(service, username string) string {
	return "msgCounter"
}

func msgMarshal(msg *proto.Message) (data []byte, err error) {
	data, err = json.Marshal(msg)
	return
}

func msgUnmarshal(data []byte) (msg *proto.Message, err error) {
	msg = new(proto.Message)
	err = json.Unmarshal(data, msg)
	if err != nil {
		msg = nil
		return
	}
	return
}

func (self *redisMessageCache) set(service, username, id string, msg *proto.Message, ttl time.Duration) error {
	key := msgKey(service, username, id)
	conn := self.pool.Get()
	defer conn.Close()

	data, err := msgMarshal(msg)
	if err != nil {
		return err
	}

	reply, err := conn.Do("INCR", counterKey(service, username))
	if err != nil {
		return err
	}

	weight, err := redis.Int64(reply, err)
	if err != nil {
		return err
	}
	wkey := msgWeightKey(service, username, id)

	err = conn.Send("MULTI")
	if err != nil {
		return err
	}

	if ttl.Seconds() <= 0.0 {
		err = conn.Send("SET", key, data)
		if err != nil {
			conn.Do("DISCARD")
			return err
		}
		err = conn.Send("SET", wkey, weight)
	} else {
		err = conn.Send("SETEX", key, int64(ttl.Seconds()), data)
		if err != nil {
			conn.Do("DISCARD")
			return err
		}
		err = conn.Send("SETEX", wkey, int64(ttl.Seconds()), weight)
	}
	if err != nil {
		conn.Do("DISCARD")
		return err
	}
	msgQK := msgQueueKey(service, username)
	err = conn.Send("SADD", msgQK, id)
	if err != nil {
		conn.Do("DISCARD")
		return err
	}
	_, err = conn.Do("EXEC")
	if err != nil {
		return err
	}
	return nil
}

func (self *redisMessageCache) Get(service, username, id string) (msg *proto.Message, err error) {
	key := msgKey(service, username, id)
	conn := self.pool.Get()
	defer conn.Close()

	reply, err := conn.Do("GET", key)
	if err != nil {
		return
	}
	if reply == nil {
		return
	}
	data, err := redis.Bytes(reply, err)
	if err != nil {
		return
	}
	msg, err = msgUnmarshal(data)
	return
}

func (self *redisMessageCache) Del(service, username, id string) error {
	key := msgKey(service, username, id)
	wkey := msgWeightKey(service, username, id)
	conn := self.pool.Get()
	defer conn.Close()

	err := conn.Send("MULTI")
	if err != nil {
		return err
	}
	err = conn.Send("DEL", key)
	if err != nil {
		conn.Do("DISCARD")
		return err
	}
	err = conn.Send("DEL", wkey)
	if err != nil {
		conn.Do("DISCARD")
		return err
	}
	msgQK := msgQueueKey(service, username)
	err = conn.Send("SREM", msgQK, id)
	if err != nil {
		conn.Do("DISCARD")
		return err
	}
	_, err = conn.Do("EXEC")
	if err != nil {
		return err
	}
	return nil
}

func (self *redisMessageCache) GetThenDel(service, username, id string) (msg *proto.Message, err error) {
	key := msgKey(service, username, id)
	wkey := msgWeightKey(service, username, id)
	conn := self.pool.Get()
	defer conn.Close()

	err = conn.Send("MULTI")
	if err != nil {
		return
	}
	err = conn.Send("GET", key)
	if err != nil {
		conn.Do("DISCARD")
		return
	}
	err = conn.Send("DEL", key)
	if err != nil {
		conn.Do("DISCARD")
		return
	}
	err = conn.Send("DEL", wkey)
	if err != nil {
		conn.Do("DISCARD")
		return
	}
	msgQK := msgQueueKey(service, username)
	err = conn.Send("SREM", msgQK, id)
	if err != nil {
		conn.Do("DISCARD")
		return
	}
	reply, err := conn.Do("EXEC")
	if err != nil {
		return
	}

	bulkReply, err := redis.Values(reply, err)
	if err != nil {
		return
	}
	if len(bulkReply) != 4 {
		return
	}
	if bulkReply[0] == nil {
		return
	}
	data, err := redis.Bytes(bulkReply[0], err)
	if err != nil {
		return
	}
	if len(data) == 0 {
		return
	}
	msg, err = msgUnmarshal(data)
	return
}

func (self *redisMessageCache) GetAllIds(service, username string) (ids []string, err error) {
	msgQK := msgQueueKey(service, username)
	conn := self.pool.Get()
	defer conn.Close()

	reply, err := conn.Do("SORT", msgQK, "BY", msgWeightPattern(service, username))
	if err != nil {
		return
	}
	bulkReply, err := redis.Values(reply, err)
	if err != nil {
		return
	}
	n := len(bulkReply)
	if n == 0 {
		return
	}
	idshadow := make([]string, n)
	for i := 0; i < n; i++ {
		idshadow[i], err = redis.String(bulkReply[i], nil)
		if err != nil {
			return
		}
	}
	ids = idshadow
	return
}
<|endoftext|>


<commit_before>package multiplier

//
// Copyright 2017 Pedro Salgado
//
// 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.
//

import (
	"fmt"
	"os"
	"sync"

	"github.com/steenzout/go-resque"
)

// Consumer Multiplier message consumer.
type Consumer struct {
	*resque.BaseConsumer
}

// NewConsumer returns a new Consumer pointer.
func NewConsumer() *Consumer {
	return &Consumer{
		resque.NewConsumer(),
	}
}

// Consume returns the multiplication of the given arguments.
func (c *Consumer) Consume(args ...resque.JobArgument) (interface{}, error) {
	fmt.Printf("[multiplier/consumer] DEBUG args %v\n", args)

	job := NewJobFromArgs(args...)
	output := job.Arg1 * job.Arg2

	fmt.Printf("[multiplier/consumer] DEBUG %.0f * %.0f = %.0f\n", job.Arg1, job.Arg2, output)

	return output, nil
}

// Run routine that prints the results from the Multiplier queue.
func (c *Consumer) Run(wg *sync.WaitGroup, chanOut chan float64, chanExit <-chan bool) {
	defer wg.Done()

	chanJob := make(chan *resque.Job, 1)
	chanErr := make(chan error, 1)
	chanQuit := make(chan bool, 1)

	// channel to signal this go routine is ready to process the next message
	chanNext := make(chan bool, 1)

	var localWG sync.WaitGroup

	localWG.Add(1)
	go c.Subscribe(&localWG, chanJob, chanErr, chanNext, chanQuit)
	defer func() {
		fmt.Println("[multiplier/consumer.go] INFO waiting for subscription to timeout")
		localWG.Wait()
		close(chanNext)
		close(chanJob)
		close(chanErr)
		close(chanQuit)
	}()

	// signal ready to receive messages
	chanNext <- true

	for {
		select {
		case <-chanExit:
			chanQuit <- true
			fmt.Println("multiplier/consumer.go: exit")
			return

		case err := <-chanErr:
			fmt.Fprintf(os.Stderr, "[multiplier/consumer] ERROR %s\n", err.Error())
			chanNext <- true

		case job := <-chanJob:
			fmt.Printf("[multiplier/consumer] got job %s\n", job)
			out, err := c.Consume(job.Args...)
			if err != nil {
				fmt.Fprintf(os.Stderr, "[multiplier] ERROR %s\n", err.Error())
				chanNext <- true
				continue
			}
			fmt.Printf("[multiplier/consumer] INFO %0.2f\n", out)
			chanOut <- out.(float64)
			chanNext <- true
		}
	}
}
<commit_msg>multiplier/consumer.go: adjust float format.<commit_after>package multiplier

//
// Copyright 2017 Pedro Salgado
//
// 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.
//

import (
	"fmt"
	"os"
	"sync"

	"github.com/steenzout/go-resque"
)

// Consumer Multiplier message consumer.
type Consumer struct {
	*resque.BaseConsumer
}

// NewConsumer returns a new Consumer pointer.
func NewConsumer() *Consumer {
	return &Consumer{
		resque.NewConsumer(),
	}
}

// Consume returns the multiplication of the given arguments.
func (c *Consumer) Consume(args ...resque.JobArgument) (interface{}, error) {
	fmt.Printf("[multiplier/consumer] DEBUG args %v\n", args)

	job := NewJobFromArgs(args...)
	output := job.Arg1 * job.Arg2

	fmt.Printf("[multiplier/consumer] DEBUG %f * %f = %f\n", job.Arg1, job.Arg2, output)

	return output, nil
}

// Run routine that prints the results from the Multiplier queue.
func (c *Consumer) Run(wg *sync.WaitGroup, chanOut chan float64, chanExit <-chan bool) {
	defer wg.Done()

	chanJob := make(chan *resque.Job, 1)
	chanErr := make(chan error, 1)
	chanQuit := make(chan bool, 1)

	// channel to signal this go routine is ready to process the next message
	chanNext := make(chan bool, 1)

	var localWG sync.WaitGroup

	localWG.Add(1)
	go c.Subscribe(&localWG, chanJob, chanErr, chanNext, chanQuit)
	defer func() {
		fmt.Println("[multiplier/consumer.go] INFO waiting for subscription to timeout")
		localWG.Wait()
		close(chanNext)
		close(chanJob)
		close(chanErr)
		close(chanQuit)
	}()

	// signal ready to receive messages
	chanNext <- true

	for {
		select {
		case <-chanExit:
			chanQuit <- true
			fmt.Println("multiplier/consumer.go: exit")
			return

		case err := <-chanErr:
			fmt.Fprintf(os.Stderr, "[multiplier/consumer] ERROR %s\n", err.Error())
			chanNext <- true

		case job := <-chanJob:
			fmt.Printf("[multiplier/consumer] got job %s\n", job)
			out, err := c.Consume(job.Args...)
			if err != nil {
				fmt.Fprintf(os.Stderr, "[multiplier] ERROR %s\n", err.Error())
				chanNext <- true
				continue
			}
			fmt.Printf("[multiplier/consumer] INFO %f\n", out)
			chanOut <- out.(float64)
			chanNext <- true
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors 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 mungers

import (
	"math"
	"math/rand"

	"k8s.io/contrib/mungegithub/features"
	"k8s.io/contrib/mungegithub/github"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
)

// weightMap is a map of user to a weight for that user.
type weightMap map[string]int64

// A BlunderbussConfig maps a set of file prefixes to a set of owner names (github users)
type BlunderbussConfig struct {
	PrefixMap map[string][]string `json:"prefixMap,omitempty" yaml:"prefixMap,omitempty"`
}

// BlunderbussMunger will assign issues to users based on the config file
// provided by --blunderbuss-config.
type BlunderbussMunger struct {
	config              *BlunderbussConfig
	features            *features.Features
	BlunderbussReassign bool
}

func init() {
	blunderbuss := &BlunderbussMunger{}
	RegisterMungerOrDie(blunderbuss)
}

// Name is the name usable in --pr-mungers
func (b *BlunderbussMunger) Name() string { return "blunderbuss" }

// RequiredFeatures is a slice of 'features' that must be provided
func (b *BlunderbussMunger) RequiredFeatures() []string { return []string{features.RepoFeatureName} }

// Initialize will initialize the munger
func (b *BlunderbussMunger) Initialize(config *github.Config, features *features.Features) error {
	b.features = features
	return nil
}

// EachLoop is called at the start of every munge loop
func (b *BlunderbussMunger) EachLoop() error { return nil }

// AddFlags will add any request flags to the cobra `cmd`
func (b *BlunderbussMunger) AddFlags(cmd *cobra.Command, config *github.Config) {
	cmd.Flags().BoolVar(&b.BlunderbussReassign, "blunderbuss-reassign", false, "Assign PRs even if they're already assigned; use with -dry-run to judge changes to the assignment algorithm")
}

func chance(val, total int64) float64 {
	return 100.0 * float64(val) / float64(total)
}

func printChance(owners weightMap, total int64) {
	if !glog.V(4) {
		return
	}
	glog.Infof("Owner\tPercent")
	for name, weight := range owners {
		glog.Infof("%s\t%02.2f%%", name, chance(weight, total))
	}
}

// Munge is the workhorse the will actually make updates to the PR
func (b *BlunderbussMunger) Munge(obj *github.MungeObject) {
	if !obj.IsPR() {
		return
	}

	issue := obj.Issue
	if !b.BlunderbussReassign && issue.Assignee != nil {
		glog.V(6).Infof("skipping %v: reassign: %v assignee: %v", *issue.Number, b.BlunderbussReassign, github.DescribeUser(issue.Assignee))
		return
	}

	files, err := obj.ListFiles()
	if err != nil {
		return
	}

	potentialOwners := weightMap{}
	weightSum := int64(0)
	for _, file := range files {
		fileWeight := int64(1)
		if file.Changes != nil && *file.Changes != 0 {
			fileWeight = int64(*file.Changes)
		}
		// Judge file size on a log scale-- effectively this
		// makes three buckets, we shouldn't have many 10k+
		// line changes.
		fileWeight = int64(math.Log10(float64(fileWeight))) + 1
		fileOwners := b.features.Repos.LeafAssignees(*file.Filename)
		if fileOwners.Len() == 0 {
			glog.Warningf("Couldn't find an owner for: %s", *file.Filename)
		}

		if b.features.Aliases.IsEnabled {
			fileOwners = b.features.Aliases.Expand(fileOwners)
		}

		for _, owner := range fileOwners.List() {
			if owner == *issue.User.Login {
				continue
			}
			potentialOwners[owner] = potentialOwners[owner] + fileWeight
			weightSum += fileWeight
		}
	}
	if len(potentialOwners) == 0 {
		glog.Errorf("No owners found for PR %d", *issue.Number)
		return
	}
	printChance(potentialOwners, weightSum)
	if issue.Assignee != nil {
		cur := *issue.Assignee.Login
		c := chance(potentialOwners[cur], weightSum)
		glog.Infof("Current assignee %v has a %02.2f%% chance of having been chosen", cur, c)
	}
	selection := rand.Int63n(weightSum)
	owner := ""
	for o, w := range potentialOwners {
		owner = o
		selection -= w
		if selection <= 0 {
			break
		}
	}
	c := chance(potentialOwners[owner], weightSum)
	glog.Infof("Assigning %v to %v who had a %02.2f%% chance to be assigned (previously assigned to %v)", *issue.Number, owner, c, github.DescribeUser(issue.Assignee))
	obj.AssignPR(owner)
}
<commit_msg>That could be nil<commit_after>/*
Copyright 2015 The Kubernetes Authors 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 mungers

import (
	"math"
	"math/rand"

	"k8s.io/contrib/mungegithub/features"
	"k8s.io/contrib/mungegithub/github"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
)

// weightMap is a map of user to a weight for that user.
type weightMap map[string]int64

// A BlunderbussConfig maps a set of file prefixes to a set of owner names (github users)
type BlunderbussConfig struct {
	PrefixMap map[string][]string `json:"prefixMap,omitempty" yaml:"prefixMap,omitempty"`
}

// BlunderbussMunger will assign issues to users based on the config file
// provided by --blunderbuss-config.
type BlunderbussMunger struct {
	config              *BlunderbussConfig
	features            *features.Features
	BlunderbussReassign bool
}

func init() {
	blunderbuss := &BlunderbussMunger{}
	RegisterMungerOrDie(blunderbuss)
}

// Name is the name usable in --pr-mungers
func (b *BlunderbussMunger) Name() string { return "blunderbuss" }

// RequiredFeatures is a slice of 'features' that must be provided
func (b *BlunderbussMunger) RequiredFeatures() []string { return []string{features.RepoFeatureName} }

// Initialize will initialize the munger
func (b *BlunderbussMunger) Initialize(config *github.Config, features *features.Features) error {
	b.features = features
	return nil
}

// EachLoop is called at the start of every munge loop
func (b *BlunderbussMunger) EachLoop() error { return nil }

// AddFlags will add any request flags to the cobra `cmd`
func (b *BlunderbussMunger) AddFlags(cmd *cobra.Command, config *github.Config) {
	cmd.Flags().BoolVar(&b.BlunderbussReassign, "blunderbuss-reassign", false, "Assign PRs even if they're already assigned; use with -dry-run to judge changes to the assignment algorithm")
}

func chance(val, total int64) float64 {
	return 100.0 * float64(val) / float64(total)
}

func printChance(owners weightMap, total int64) {
	if !glog.V(4) {
		return
	}
	glog.Infof("Owner\tPercent")
	for name, weight := range owners {
		glog.Infof("%s\t%02.2f%%", name, chance(weight, total))
	}
}

// Munge is the workhorse the will actually make updates to the PR
func (b *BlunderbussMunger) Munge(obj *github.MungeObject) {
	if !obj.IsPR() {
		return
	}

	issue := obj.Issue
	if !b.BlunderbussReassign && issue.Assignee != nil {
		glog.V(6).Infof("skipping %v: reassign: %v assignee: %v", *issue.Number, b.BlunderbussReassign, github.DescribeUser(issue.Assignee))
		return
	}

	files, err := obj.ListFiles()
	if err != nil {
		return
	}

	potentialOwners := weightMap{}
	weightSum := int64(0)
	for _, file := range files {
		fileWeight := int64(1)
		if file.Changes != nil && *file.Changes != 0 {
			fileWeight = int64(*file.Changes)
		}
		// Judge file size on a log scale-- effectively this
		// makes three buckets, we shouldn't have many 10k+
		// line changes.
		fileWeight = int64(math.Log10(float64(fileWeight))) + 1
		fileOwners := b.features.Repos.LeafAssignees(*file.Filename)
		if fileOwners.Len() == 0 {
			glog.Warningf("Couldn't find an owner for: %s", *file.Filename)
		}

		if b.features.Aliases != nil && b.features.Aliases.IsEnabled {
			fileOwners = b.features.Aliases.Expand(fileOwners)
		}

		for _, owner := range fileOwners.List() {
			if owner == *issue.User.Login {
				continue
			}
			potentialOwners[owner] = potentialOwners[owner] + fileWeight
			weightSum += fileWeight
		}
	}
	if len(potentialOwners) == 0 {
		glog.Errorf("No owners found for PR %d", *issue.Number)
		return
	}
	printChance(potentialOwners, weightSum)
	if issue.Assignee != nil {
		cur := *issue.Assignee.Login
		c := chance(potentialOwners[cur], weightSum)
		glog.Infof("Current assignee %v has a %02.2f%% chance of having been chosen", cur, c)
	}
	selection := rand.Int63n(weightSum)
	owner := ""
	for o, w := range potentialOwners {
		owner = o
		selection -= w
		if selection <= 0 {
			break
		}
	}
	c := chance(potentialOwners[owner], weightSum)
	glog.Infof("Assigning %v to %v who had a %02.2f%% chance to be assigned (previously assigned to %v)", *issue.Number, owner, c, github.DescribeUser(issue.Assignee))
	obj.AssignPR(owner)
}
<|endoftext|>


<commit_before>package tgbotapi

import (
	"encoding/json"
)

// APIResponse is a response from the Telegram API with the result stored raw.
type APIResponse struct {
	Ok          bool            `json:"ok"`
	Result      json.RawMessage `json:"result"`
	ErrorCode   int             `json:"error_code"`
	Description string          `json:"description"`
}

// Update is an update response, from GetUpdates.
type Update struct {
	UpdateID int     `json:"update_id"`
	Message  Message `json:"message"`
}

// User is a user, contained in Message and returned by GetSelf.
type User struct {
	ID        int    `json:"id"`
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
	UserName  string `json:"username"`
}

// GroupChat is a group chat, and not currently in use.
type GroupChat struct {
	ID    int    `json:"id"`
	Title string `json:"title"`
}

// UserOrGroupChat is returned in Message, because it's not clear which it is.
type UserOrGroupChat struct {
	ID        int    `json:"id"`
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
	UserName  string `json:"username"`
	Title     string `json:"title"`
}

// Message is returned by almost every request, and contains data about almost anything.
type Message struct {
	MessageID           int             `json:"message_id"`
	From                User            `json:"from"`
	Date                int             `json:"date"`
	Chat                UserOrGroupChat `json:"chat"`
	ForwardFrom         User            `json:"forward_from"`
	ForwardDate         int             `json:"forward_date"`
	ReplyToMessage      *Message        `json:"reply_to_message"`
	Text                string          `json:"text"`
	Audio               Audio           `json:"audio"`
	Document            Document        `json:"document"`
	Photo               []PhotoSize     `json:"photo"`
	Sticker             Sticker         `json:"sticker"`
	Video               Video           `json:"video"`
	Contact             Contact         `json:"contact"`
	Location            Location        `json:"location"`
	NewChatParticipant  User            `json:"new_chat_participant"`
	LeftChatParticipant User            `json:"left_chat_participant"`
	NewChatTitle        string          `json:"new_chat_title"`
	NewChatPhoto        string          `json:"new_chat_photo"`
	DeleteChatPhoto     bool            `json:"delete_chat_photo"`
	GroupChatCreated    bool            `json:"group_chat_created"`
}

// PhotoSize contains information about photos, including ID and Width and Height.
type PhotoSize struct {
	FileID   string `json:"file_id"`
	Width    int    `json:"width"`
	Height   int    `json:"height"`
	FileSize int    `json:"file_size"`
}

// Audio contains information about audio, including ID and Duration.
type Audio struct {
	FileID   string `json:"file_id"`
	Duration int    `json:"duration"`
	MimeType string `json:"mime_type"`
	FileSize int    `json:"file_size"`
}

// Document contains information about a document, including ID and a Thumbnail.
type Document struct {
	FileID    string    `json:"file_id"`
	Thumbnail PhotoSize `json:"thumb"`
	FileName  string    `json:"file_name"`
	MimeType  string    `json:"mime_type"`
	FileSize  int       `json:"file_size"`
}

// Sticker contains information about a sticker, including ID and Thumbnail.
type Sticker struct {
	FileID    string    `json:"file_id"`
	Width     int       `json:"width"`
	Height    int       `json:"height"`
	Thumbnail PhotoSize `json:"thumb"`
	FileSize  int       `json:"file_size"`
}

// Video contains information about a video, including ID and duration and Thumbnail.
type Video struct {
	FileID    string    `json:"file_id"`
	Width     int       `json:"width"`
	Height    int       `json:"height"`
	Duration  int       `json:"duration"`
	Thumbnail PhotoSize `json:"thumb"`
	MimeType  string    `json:"mime_type"`
	FileSize  int       `json:"file_size"`
	Caption   string    `json:"caption"`
}

// Contact contains information about a contact, such as PhoneNumber and UserId.
type Contact struct {
	PhoneNumber string `json:"phone_number"`
	FirstName   string `json:"first_name"`
	LastName    string `json:"last_name"`
	UserID      string `json:"user_id"`
}

// Location contains information about a place, such as Longitude and Latitude.
type Location struct {
	Longitude float32 `json:"longitude"`
	Latitude  float32 `json:"latitude"`
}

// UserProfilePhotos contains information a set of user profile photos.
type UserProfilePhotos struct {
	TotalCount int         `json:"total_count"`
	Photos     []PhotoSize `json:"photos"`
}

// ReplyKeyboardMarkup allows the Bot to set a custom keyboard.
type ReplyKeyboardMarkup struct {
	Keyboard        [][]string `json:"keyboard"`
	ResizeKeyboard  bool       `json:"resize_keyboard"`
	OneTimeKeyboard bool       `json:"one_time_keyboard"`
	Selective       bool       `json:"selective"`
}

// ReplyKeyboardHide allows the Bot to hide a custom keyboard.
type ReplyKeyboardHide struct {
	HideKeyboard bool `json:"hide_keyboard"`
	Selective    bool `json:"selective"`
}

// ForceReply allows the Bot to have users directly reply to it without additional interaction.
type ForceReply struct {
	ForceReply bool `json:"force_reply"`
	Selective  bool `json:"force_reply"`
}
<commit_msg>Fix parameter name<commit_after>package tgbotapi

import (
	"encoding/json"
)

// APIResponse is a response from the Telegram API with the result stored raw.
type APIResponse struct {
	Ok          bool            `json:"ok"`
	Result      json.RawMessage `json:"result"`
	ErrorCode   int             `json:"error_code"`
	Description string          `json:"description"`
}

// Update is an update response, from GetUpdates.
type Update struct {
	UpdateID int     `json:"update_id"`
	Message  Message `json:"message"`
}

// User is a user, contained in Message and returned by GetSelf.
type User struct {
	ID        int    `json:"id"`
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
	UserName  string `json:"username"`
}

// GroupChat is a group chat, and not currently in use.
type GroupChat struct {
	ID    int    `json:"id"`
	Title string `json:"title"`
}

// UserOrGroupChat is returned in Message, because it's not clear which it is.
type UserOrGroupChat struct {
	ID        int    `json:"id"`
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
	UserName  string `json:"username"`
	Title     string `json:"title"`
}

// Message is returned by almost every request, and contains data about almost anything.
type Message struct {
	MessageID           int             `json:"message_id"`
	From                User            `json:"from"`
	Date                int             `json:"date"`
	Chat                UserOrGroupChat `json:"chat"`
	ForwardFrom         User            `json:"forward_from"`
	ForwardDate         int             `json:"forward_date"`
	ReplyToMessage      *Message        `json:"reply_to_message"`
	Text                string          `json:"text"`
	Audio               Audio           `json:"audio"`
	Document            Document        `json:"document"`
	Photo               []PhotoSize     `json:"photo"`
	Sticker             Sticker         `json:"sticker"`
	Video               Video           `json:"video"`
	Contact             Contact         `json:"contact"`
	Location            Location        `json:"location"`
	NewChatParticipant  User            `json:"new_chat_participant"`
	LeftChatParticipant User            `json:"left_chat_participant"`
	NewChatTitle        string          `json:"new_chat_title"`
	NewChatPhoto        string          `json:"new_chat_photo"`
	DeleteChatPhoto     bool            `json:"delete_chat_photo"`
	GroupChatCreated    bool            `json:"group_chat_created"`
}

// PhotoSize contains information about photos, including ID and Width and Height.
type PhotoSize struct {
	FileID   string `json:"file_id"`
	Width    int    `json:"width"`
	Height   int    `json:"height"`
	FileSize int    `json:"file_size"`
}

// Audio contains information about audio, including ID and Duration.
type Audio struct {
	FileID   string `json:"file_id"`
	Duration int    `json:"duration"`
	MimeType string `json:"mime_type"`
	FileSize int    `json:"file_size"`
}

// Document contains information about a document, including ID and a Thumbnail.
type Document struct {
	FileID    string    `json:"file_id"`
	Thumbnail PhotoSize `json:"thumb"`
	FileName  string    `json:"file_name"`
	MimeType  string    `json:"mime_type"`
	FileSize  int       `json:"file_size"`
}

// Sticker contains information about a sticker, including ID and Thumbnail.
type Sticker struct {
	FileID    string    `json:"file_id"`
	Width     int       `json:"width"`
	Height    int       `json:"height"`
	Thumbnail PhotoSize `json:"thumb"`
	FileSize  int       `json:"file_size"`
}

// Video contains information about a video, including ID and duration and Thumbnail.
type Video struct {
	FileID    string    `json:"file_id"`
	Width     int       `json:"width"`
	Height    int       `json:"height"`
	Duration  int       `json:"duration"`
	Thumbnail PhotoSize `json:"thumb"`
	MimeType  string    `json:"mime_type"`
	FileSize  int       `json:"file_size"`
	Caption   string    `json:"caption"`
}

// Contact contains information about a contact, such as PhoneNumber and UserId.
type Contact struct {
	PhoneNumber string `json:"phone_number"`
	FirstName   string `json:"first_name"`
	LastName    string `json:"last_name"`
	UserID      string `json:"user_id"`
}

// Location contains information about a place, such as Longitude and Latitude.
type Location struct {
	Longitude float32 `json:"longitude"`
	Latitude  float32 `json:"latitude"`
}

// UserProfilePhotos contains information a set of user profile photos.
type UserProfilePhotos struct {
	TotalCount int         `json:"total_count"`
	Photos     []PhotoSize `json:"photos"`
}

// ReplyKeyboardMarkup allows the Bot to set a custom keyboard.
type ReplyKeyboardMarkup struct {
	Keyboard        [][]string `json:"keyboard"`
	ResizeKeyboard  bool       `json:"resize_keyboard"`
	OneTimeKeyboard bool       `json:"one_time_keyboard"`
	Selective       bool       `json:"selective"`
}

// ReplyKeyboardHide allows the Bot to hide a custom keyboard.
type ReplyKeyboardHide struct {
	HideKeyboard bool `json:"hide_keyboard"`
	Selective    bool `json:"selective"`
}

// ForceReply allows the Bot to have users directly reply to it without additional interaction.
type ForceReply struct {
	ForceReply bool `json:"force_reply"`
	Selective  bool `json:"selective"`
}
<|endoftext|>


<commit_before>package lua

import (
	"fmt"
	"math"
	"reflect"
	"runtime"
	"strings"
)

type value interface{}
type float8 int

func debugValue(v value) string {
	switch v := v.(type) {
	case *table:
		entry := func(x value) string {
			if t, ok := x.(*table); ok {
				return fmt.Sprintf("table %#v", t)
			} else {
				return debugValue(x)
			}
		}
		s := fmt.Sprintf("table %#v {[", v)
		for _, x := range v.array {
			s += entry(x) + ", "
		}
		s += "], {"
		for k, x := range v.hash {
			s += entry(k) + ": " + entry(x) + ", "
		}
		return s + "}}"
	case string:
		return "'" + v + "'"
	case float64:
		return fmt.Sprintf("%f", v)
	case *luaClosure:
		return fmt.Sprintf("closure %s:%d %v", v.prototype.source, v.prototype.lineDefined, v)
	case *goClosure:
		return fmt.Sprintf("go closure %#v", v)
	case *goFunction:
		pc := reflect.ValueOf(v.Function).Pointer()
		f := runtime.FuncForPC(pc)
		file, line := f.FileLine(pc)
		return fmt.Sprintf("go function %s %s:%d", f.Name(), file, line)
	case *userData:
		return fmt.Sprintf("userdata %#v", v)
	case nil:
		return "nil"
	case bool:
		return fmt.Sprintf("%#v", v)
	}
	return fmt.Sprintf("unknown %#v %s", v, reflect.TypeOf(v).Name())
}

func stack(s []value) string {
	r := fmt.Sprintf("stack (len: %d, cap: %d):\n", len(s), cap(s))
	for i, v := range s {
		r = fmt.Sprintf("%s %d: %s\n", r, i, debugValue(v))
	}
	return r
}

func isFalse(s value) bool {
	if s == nil {
		return true
	}
	b, isBool := s.(bool)
	return isBool && !b
}

type localVariable struct {
	name           string
	startPC, endPC pc
}

type userData struct {
	metaTable, env *table
	data           interface{}
}

type upValueDesc struct {
	name    string
	isLocal bool
	index   int
}

type stackLocation struct {
	state *State
	index int
}

type prototype struct {
	constants                    []value
	code                         []instruction
	prototypes                   []prototype
	lineInfo                     []int32
	localVariables               []localVariable
	upValues                     []upValueDesc
	cache                        *luaClosure
	source                       string
	lineDefined, lastLineDefined int
	parameterCount, maxStackSize int
	isVarArg                     bool
}

func (p *prototype) upValueName(index int) string {
	if s := p.upValues[index].name; s != "" {
		return s
	}
	return "?"
}

func (p *prototype) lastLoad(reg int, lastPC pc) (loadPC pc, found bool) {
	var ip, jumpTarget pc
	for ; ip < lastPC; ip++ {
		i, maybe := p.code[ip], false
		switch i.opCode() {
		case opLoadNil:
			maybe = i.a() <= reg && reg <= i.a()+i.b()
		case opTForCall:
			maybe = reg >= i.a()+2
		case opCall, opTailCall:
			maybe = reg >= i.a()
		case opJump:
			if dest := ip + 1 + pc(i.sbx()); ip < dest && dest <= lastPC && dest > jumpTarget {
				jumpTarget = dest
			}
		case opTest:
			maybe = reg == i.a()
		default:
			maybe = testAMode(i.opCode()) && reg == i.a()
		}
		if maybe {
			if ip < jumpTarget { // Can't know loading instruction because code is conditional.
				found = false
			} else {
				loadPC, found = ip, true
			}
		}
	}
	return
}

func (p *prototype) objectName(reg int, lastPC pc) (name, kind string) {
	if name, isLocal := p.localName(reg+1, lastPC); isLocal {
		return name, "local"
	}
	if pc, found := p.lastLoad(reg, lastPC); found {
		i := p.code[pc]
		switch op := i.opCode(); op {
		case opMove:
			if b := i.b(); b < i.a() {
				return p.objectName(b, pc)
			}
		case opGetTableUp:
			name, kind = p.constantName(i.c(), pc), "local"
			if p.upValueName(i.b()) == "_ENV" {
				kind = "global"
			}
			return
		case opGetTable:
			name, kind = p.constantName(i.c(), pc), "local"
			if v, ok := p.localName(i.b()+1, pc); ok && v == "_ENV" {
				kind = "global"
			}
			return
		case opGetUpValue:
			return p.upValueName(i.b()), "upvalue"
		case opLoadConstant:
			if s, ok := p.constants[i.bx()].(string); ok {
				return s, "constant"
			}
		case opLoadConstantEx:
			if s, ok := p.constants[p.code[pc+1].ax()].(string); ok {
				return s, "constant"
			}
		case opSelf:
			return p.constantName(i.c(), pc), "method"
		}
	}
	return
}

func (p *prototype) constantName(k int, pc pc) string {
	if isConstant(k) {
		if s, ok := p.constants[constantIndex(k)].(string); ok {
			return s
		}
	} else if name, kind := p.objectName(k, pc); kind == "c" {
		return name
	}
	return "?"
}

func (p *prototype) localName(index int, pc pc) (string, bool) {
	for i := 0; i < len(p.localVariables) && p.localVariables[i].startPC <= pc; i++ {
		if pc < p.localVariables[i].endPC {
			if index--; index == 0 {
				return p.localVariables[i].name, true
			}
		}
	}
	return "", false
}

// Converts an integer to a "floating point byte", represented as
// (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if
// eeeee != 0 and (xxx) otherwise.
func float8FromInt(x int) float8 {
	if x < 8 {
		return float8(x)
	}
	e := 0
	for ; x >= 0x10; e++ {
		x = (x + 1) >> 1
	}
	return float8(((e + 1) << 3) | (x - 8))
}

func intFromFloat8(x float8) int {
	e := x >> 3 & 0x1f
	if e == 0 {
		return int(x)
	}
	return int(x&7+8) << uint(e-1)
}

func arith(op Operator, v1, v2 float64) float64 {
	switch op {
	case OpAdd:
		return v1 + v2
	case OpSub:
		return v1 - v2
	case OpMul:
		return v1 * v2
	case OpDiv:
		return v1 / v2
	case OpMod:
		return v1 - math.Floor(v1/v2)*v2
	case OpPow:
		// Golang bug: math.Pow(10.0, 33.0) is incorrect by 1 bit.
		if v1 == 10.0 && float64(int(v2)) == v2 {
			return math.Pow10(int(v2))
		}
		return math.Pow(v1, v2)
	case OpUnaryMinus:
		return -v1
	}
	panic(fmt.Sprintf("not an arithmetic op code (%d)", op))
}

func (l *State) parseNumber(s string) (v float64, ok bool) { // TODO this is f*cking ugly - scanner.readNumber should be refactored.
	if len(strings.Fields(s)) != 1 || strings.ContainsRune(s, 0) {
		return
	}
	scanner := scanner{l: l, r: strings.NewReader(s)}
	t := scanner.scan()
	if t.t == '-' {
		if t := scanner.scan(); t.t == tkNumber {
			v, ok = -t.n, true
		}
	} else if t.t == tkNumber {
		v, ok = t.n, true
	} else if t.t == '+' {
		if t := scanner.scan(); t.t == tkNumber {
			v, ok = t.n, true
		}
	}
	if ok && scanner.scan().t != tkEOS {
		ok = false
	} else if math.IsInf(v, 0) || math.IsNaN(v) {
		ok = false
	}
	return
}

func (l *State) toNumber(r value) (v float64, ok bool) {
	if v, ok = r.(float64); ok {
		return
	}
	var s string
	if s, ok = r.(string); ok {
		if err := l.protectedCall(func() { v, ok = l.parseNumber(strings.TrimSpace(s)) }, l.top, l.errorFunction); err != nil {
			l.pop() // Remove error message from the stack.
			ok = false
		}
	}
	return
}

func (l *State) toString(index int) (s string, ok bool) {
	if s, ok = toString(l.stack[index]); ok {
		l.stack[index] = s
	}
	return
}

func numberToString(f float64) string {
	return fmt.Sprintf("%.14g", f)
}

func toString(r value) (string, bool) {
	switch r := r.(type) {
	case string:
		return r, true
	case float64:
		return numberToString(r), true
	}
	return "", false
}

func pairAsNumbers(p1, p2 value) (f1, f2 float64, ok bool) {
	if f1, ok = p1.(float64); !ok {
		return
	}
	f2, ok = p2.(float64)
	return
}

func pairAsStrings(p1, p2 value) (s1, s2 string, ok bool) {
	if s1, ok = p1.(string); !ok {
		return
	}
	s2, ok = p2.(string)
	return
}
<commit_msg>Check if value in isFalse is none<commit_after>package lua

import (
	"fmt"
	"math"
	"reflect"
	"runtime"
	"strings"
)

type value interface{}
type float8 int

func debugValue(v value) string {
	switch v := v.(type) {
	case *table:
		entry := func(x value) string {
			if t, ok := x.(*table); ok {
				return fmt.Sprintf("table %#v", t)
			} else {
				return debugValue(x)
			}
		}
		s := fmt.Sprintf("table %#v {[", v)
		for _, x := range v.array {
			s += entry(x) + ", "
		}
		s += "], {"
		for k, x := range v.hash {
			s += entry(k) + ": " + entry(x) + ", "
		}
		return s + "}}"
	case string:
		return "'" + v + "'"
	case float64:
		return fmt.Sprintf("%f", v)
	case *luaClosure:
		return fmt.Sprintf("closure %s:%d %v", v.prototype.source, v.prototype.lineDefined, v)
	case *goClosure:
		return fmt.Sprintf("go closure %#v", v)
	case *goFunction:
		pc := reflect.ValueOf(v.Function).Pointer()
		f := runtime.FuncForPC(pc)
		file, line := f.FileLine(pc)
		return fmt.Sprintf("go function %s %s:%d", f.Name(), file, line)
	case *userData:
		return fmt.Sprintf("userdata %#v", v)
	case nil:
		return "nil"
	case bool:
		return fmt.Sprintf("%#v", v)
	}
	return fmt.Sprintf("unknown %#v %s", v, reflect.TypeOf(v).Name())
}

func stack(s []value) string {
	r := fmt.Sprintf("stack (len: %d, cap: %d):\n", len(s), cap(s))
	for i, v := range s {
		r = fmt.Sprintf("%s %d: %s\n", r, i, debugValue(v))
	}
	return r
}

func isFalse(s value) bool {
	if s == nil || s == none {
		return true
	}
	b, isBool := s.(bool)
	return isBool && !b
}

type localVariable struct {
	name           string
	startPC, endPC pc
}

type userData struct {
	metaTable, env *table
	data           interface{}
}

type upValueDesc struct {
	name    string
	isLocal bool
	index   int
}

type stackLocation struct {
	state *State
	index int
}

type prototype struct {
	constants                    []value
	code                         []instruction
	prototypes                   []prototype
	lineInfo                     []int32
	localVariables               []localVariable
	upValues                     []upValueDesc
	cache                        *luaClosure
	source                       string
	lineDefined, lastLineDefined int
	parameterCount, maxStackSize int
	isVarArg                     bool
}

func (p *prototype) upValueName(index int) string {
	if s := p.upValues[index].name; s != "" {
		return s
	}
	return "?"
}

func (p *prototype) lastLoad(reg int, lastPC pc) (loadPC pc, found bool) {
	var ip, jumpTarget pc
	for ; ip < lastPC; ip++ {
		i, maybe := p.code[ip], false
		switch i.opCode() {
		case opLoadNil:
			maybe = i.a() <= reg && reg <= i.a()+i.b()
		case opTForCall:
			maybe = reg >= i.a()+2
		case opCall, opTailCall:
			maybe = reg >= i.a()
		case opJump:
			if dest := ip + 1 + pc(i.sbx()); ip < dest && dest <= lastPC && dest > jumpTarget {
				jumpTarget = dest
			}
		case opTest:
			maybe = reg == i.a()
		default:
			maybe = testAMode(i.opCode()) && reg == i.a()
		}
		if maybe {
			if ip < jumpTarget { // Can't know loading instruction because code is conditional.
				found = false
			} else {
				loadPC, found = ip, true
			}
		}
	}
	return
}

func (p *prototype) objectName(reg int, lastPC pc) (name, kind string) {
	if name, isLocal := p.localName(reg+1, lastPC); isLocal {
		return name, "local"
	}
	if pc, found := p.lastLoad(reg, lastPC); found {
		i := p.code[pc]
		switch op := i.opCode(); op {
		case opMove:
			if b := i.b(); b < i.a() {
				return p.objectName(b, pc)
			}
		case opGetTableUp:
			name, kind = p.constantName(i.c(), pc), "local"
			if p.upValueName(i.b()) == "_ENV" {
				kind = "global"
			}
			return
		case opGetTable:
			name, kind = p.constantName(i.c(), pc), "local"
			if v, ok := p.localName(i.b()+1, pc); ok && v == "_ENV" {
				kind = "global"
			}
			return
		case opGetUpValue:
			return p.upValueName(i.b()), "upvalue"
		case opLoadConstant:
			if s, ok := p.constants[i.bx()].(string); ok {
				return s, "constant"
			}
		case opLoadConstantEx:
			if s, ok := p.constants[p.code[pc+1].ax()].(string); ok {
				return s, "constant"
			}
		case opSelf:
			return p.constantName(i.c(), pc), "method"
		}
	}
	return
}

func (p *prototype) constantName(k int, pc pc) string {
	if isConstant(k) {
		if s, ok := p.constants[constantIndex(k)].(string); ok {
			return s
		}
	} else if name, kind := p.objectName(k, pc); kind == "c" {
		return name
	}
	return "?"
}

func (p *prototype) localName(index int, pc pc) (string, bool) {
	for i := 0; i < len(p.localVariables) && p.localVariables[i].startPC <= pc; i++ {
		if pc < p.localVariables[i].endPC {
			if index--; index == 0 {
				return p.localVariables[i].name, true
			}
		}
	}
	return "", false
}

// Converts an integer to a "floating point byte", represented as
// (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if
// eeeee != 0 and (xxx) otherwise.
func float8FromInt(x int) float8 {
	if x < 8 {
		return float8(x)
	}
	e := 0
	for ; x >= 0x10; e++ {
		x = (x + 1) >> 1
	}
	return float8(((e + 1) << 3) | (x - 8))
}

func intFromFloat8(x float8) int {
	e := x >> 3 & 0x1f
	if e == 0 {
		return int(x)
	}
	return int(x&7+8) << uint(e-1)
}

func arith(op Operator, v1, v2 float64) float64 {
	switch op {
	case OpAdd:
		return v1 + v2
	case OpSub:
		return v1 - v2
	case OpMul:
		return v1 * v2
	case OpDiv:
		return v1 / v2
	case OpMod:
		return v1 - math.Floor(v1/v2)*v2
	case OpPow:
		// Golang bug: math.Pow(10.0, 33.0) is incorrect by 1 bit.
		if v1 == 10.0 && float64(int(v2)) == v2 {
			return math.Pow10(int(v2))
		}
		return math.Pow(v1, v2)
	case OpUnaryMinus:
		return -v1
	}
	panic(fmt.Sprintf("not an arithmetic op code (%d)", op))
}

func (l *State) parseNumber(s string) (v float64, ok bool) { // TODO this is f*cking ugly - scanner.readNumber should be refactored.
	if len(strings.Fields(s)) != 1 || strings.ContainsRune(s, 0) {
		return
	}
	scanner := scanner{l: l, r: strings.NewReader(s)}
	t := scanner.scan()
	if t.t == '-' {
		if t := scanner.scan(); t.t == tkNumber {
			v, ok = -t.n, true
		}
	} else if t.t == tkNumber {
		v, ok = t.n, true
	} else if t.t == '+' {
		if t := scanner.scan(); t.t == tkNumber {
			v, ok = t.n, true
		}
	}
	if ok && scanner.scan().t != tkEOS {
		ok = false
	} else if math.IsInf(v, 0) || math.IsNaN(v) {
		ok = false
	}
	return
}

func (l *State) toNumber(r value) (v float64, ok bool) {
	if v, ok = r.(float64); ok {
		return
	}
	var s string
	if s, ok = r.(string); ok {
		if err := l.protectedCall(func() { v, ok = l.parseNumber(strings.TrimSpace(s)) }, l.top, l.errorFunction); err != nil {
			l.pop() // Remove error message from the stack.
			ok = false
		}
	}
	return
}

func (l *State) toString(index int) (s string, ok bool) {
	if s, ok = toString(l.stack[index]); ok {
		l.stack[index] = s
	}
	return
}

func numberToString(f float64) string {
	return fmt.Sprintf("%.14g", f)
}

func toString(r value) (string, bool) {
	switch r := r.(type) {
	case string:
		return r, true
	case float64:
		return numberToString(r), true
	}
	return "", false
}

func pairAsNumbers(p1, p2 value) (f1, f2 float64, ok bool) {
	if f1, ok = p1.(float64); !ok {
		return
	}
	f2, ok = p2.(float64)
	return
}

func pairAsStrings(p1, p2 value) (s1, s2 string, ok bool) {
	if s1, ok = p1.(string); !ok {
		return
	}
	s2, ok = p2.(string)
	return
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package interfacer

import (
	"bytes"
	"fmt"
	"go/types"
	"io"
	"sort"
	"strings"

	"github.com/mvdan/interfacer/internal/util"
)

type methoder interface {
	NumMethods() int
	Method(int) *types.Func
}

func methoderFuncMap(m methoder, skip bool) map[string]string {
	ifuncs := make(map[string]string, m.NumMethods())
	for i := 0; i < m.NumMethods(); i++ {
		f := m.Method(i)
		fname := f.Name()
		if !util.Exported(fname) {
			if skip {
				continue
			}
			return nil
		}
		sign := f.Type().(*types.Signature)
		ifuncs[fname] = signString(sign)
	}
	return ifuncs
}

func typeFuncMap(t types.Type) map[string]string {
	switch x := t.(type) {
	case *types.Pointer:
		return typeFuncMap(x.Elem())
	case *types.Named:
		u := x.Underlying()
		if types.IsInterface(u) {
			return typeFuncMap(u)
		}
		return methoderFuncMap(x, true)
	case *types.Interface:
		return methoderFuncMap(x, false)
	default:
		return nil
	}
}

func funcMapString(iface map[string]string) string {
	fnames := make([]string, 0, len(iface))
	for fname := range iface {
		fnames = append(fnames, fname)
	}
	sort.Sort(util.ByAlph(fnames))
	var b bytes.Buffer
	for i, fname := range fnames {
		if i > 0 {
			io.WriteString(&b, "; ")
		}
		io.WriteString(&b, fname)
		io.WriteString(&b, iface[fname])
	}
	return b.String()
}

func tupleStrs(t *types.Tuple) []string {
	l := make([]string, 0, t.Len())
	for i := 0; i < t.Len(); i++ {
		v := t.At(i)
		l = append(l, v.Type().String())
	}
	return l
}

func signString(sign *types.Signature) string {
	ps := tupleStrs(sign.Params())
	rs := tupleStrs(sign.Results())
	if len(rs) == 0 {
		return fmt.Sprintf("(%s)", strings.Join(ps, ", "))
	}
	if len(rs) == 1 {
		return fmt.Sprintf("(%s) %s", strings.Join(ps, ", "), rs[0])
	}
	return fmt.Sprintf("(%s) (%s)", strings.Join(ps, ", "), strings.Join(rs, ", "))
}

func interesting(t types.Type) bool {
	switch x := t.(type) {
	case *types.Interface:
		return x.NumMethods() > 1
	case *types.Named:
		if u := x.Underlying(); types.IsInterface(u) {
			return interesting(u)
		}
		return x.NumMethods() >= 1
	case *types.Pointer:
		return interesting(x.Elem())
	default:
		return false
	}
}

func anyInteresting(params *types.Tuple) bool {
	for i := 0; i < params.Len(); i++ {
		t := params.At(i).Type()
		if interesting(t) {
			return true
		}
	}
	return false
}

func FromScope(scope *types.Scope) (ifaces, funcs map[string]string) {
	ifaces = make(map[string]string)
	funcs = make(map[string]string)
	ifaceFuncs := make(map[string]string)
	for _, name := range scope.Names() {
		tn, ok := scope.Lookup(name).(*types.TypeName)
		if !ok {
			continue
		}
		switch x := tn.Type().Underlying().(type) {
		case *types.Interface:
			iface := methoderFuncMap(x, false)
			if len(iface) == 0 {
				continue
			}
			for i := 0; i < x.NumMethods(); i++ {
				f := x.Method(i)
				sign := f.Type().(*types.Signature)
				if !anyInteresting(sign.Params()) {
					continue
				}
				s := signString(sign)
				if _, e := ifaceFuncs[s]; e {
					continue
				}
				ifaceFuncs[s] = tn.Name() + "." + f.Name()
			}
			s := funcMapString(iface)
			if _, e := ifaces[s]; !e {
				ifaces[s] = tn.Name()
			}
		case *types.Signature:
			if !anyInteresting(x.Params()) {
				continue
			}
			s := signString(x)
			if _, e := funcs[s]; !e {
				funcs[s] = tn.Name()
			}
		}
	}
	for s, name := range ifaceFuncs {
		if _, e := funcs[s]; !e {
			funcs[s] = name
		}
	}
	return ifaces, funcs
}

func mentionsName(fname, name string) bool {
	if fname == "" || name == "" {
		return false
	}
	if len(name) < 2 {
		return false
	}
	capit := strings.ToUpper(name[:1]) + name[1:]
	lower := strings.ToLower(name)
	return strings.Contains(fname, capit) || strings.HasPrefix(fname, lower)
}

func typeNamed(t types.Type) *types.Named {
	for {
		switch x := t.(type) {
		case *types.Named:
			return x
		case *types.Pointer:
			t = x.Elem()
		default:
			return nil
		}
	}
}
<commit_msg>Replace io.WriteString with fmt.Fprint<commit_after>// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package interfacer

import (
	"bytes"
	"fmt"
	"go/types"
	"sort"
	"strings"

	"github.com/mvdan/interfacer/internal/util"
)

type methoder interface {
	NumMethods() int
	Method(int) *types.Func
}

func methoderFuncMap(m methoder, skip bool) map[string]string {
	ifuncs := make(map[string]string, m.NumMethods())
	for i := 0; i < m.NumMethods(); i++ {
		f := m.Method(i)
		fname := f.Name()
		if !util.Exported(fname) {
			if skip {
				continue
			}
			return nil
		}
		sign := f.Type().(*types.Signature)
		ifuncs[fname] = signString(sign)
	}
	return ifuncs
}

func typeFuncMap(t types.Type) map[string]string {
	switch x := t.(type) {
	case *types.Pointer:
		return typeFuncMap(x.Elem())
	case *types.Named:
		u := x.Underlying()
		if types.IsInterface(u) {
			return typeFuncMap(u)
		}
		return methoderFuncMap(x, true)
	case *types.Interface:
		return methoderFuncMap(x, false)
	default:
		return nil
	}
}

func funcMapString(iface map[string]string) string {
	fnames := make([]string, 0, len(iface))
	for fname := range iface {
		fnames = append(fnames, fname)
	}
	sort.Sort(util.ByAlph(fnames))
	var b bytes.Buffer
	for i, fname := range fnames {
		if i > 0 {
			fmt.Fprint(&b, "; ")
		}
		fmt.Fprint(&b, fname, iface[fname])
	}
	return b.String()
}

func tupleStrs(t *types.Tuple) []string {
	l := make([]string, 0, t.Len())
	for i := 0; i < t.Len(); i++ {
		v := t.At(i)
		l = append(l, v.Type().String())
	}
	return l
}

func signString(sign *types.Signature) string {
	ps := tupleStrs(sign.Params())
	rs := tupleStrs(sign.Results())
	if len(rs) == 0 {
		return fmt.Sprintf("(%s)", strings.Join(ps, ", "))
	}
	if len(rs) == 1 {
		return fmt.Sprintf("(%s) %s", strings.Join(ps, ", "), rs[0])
	}
	return fmt.Sprintf("(%s) (%s)", strings.Join(ps, ", "), strings.Join(rs, ", "))
}

func interesting(t types.Type) bool {
	switch x := t.(type) {
	case *types.Interface:
		return x.NumMethods() > 1
	case *types.Named:
		if u := x.Underlying(); types.IsInterface(u) {
			return interesting(u)
		}
		return x.NumMethods() >= 1
	case *types.Pointer:
		return interesting(x.Elem())
	default:
		return false
	}
}

func anyInteresting(params *types.Tuple) bool {
	for i := 0; i < params.Len(); i++ {
		t := params.At(i).Type()
		if interesting(t) {
			return true
		}
	}
	return false
}

func FromScope(scope *types.Scope) (ifaces, funcs map[string]string) {
	ifaces = make(map[string]string)
	funcs = make(map[string]string)
	ifaceFuncs := make(map[string]string)
	for _, name := range scope.Names() {
		tn, ok := scope.Lookup(name).(*types.TypeName)
		if !ok {
			continue
		}
		switch x := tn.Type().Underlying().(type) {
		case *types.Interface:
			iface := methoderFuncMap(x, false)
			if len(iface) == 0 {
				continue
			}
			for i := 0; i < x.NumMethods(); i++ {
				f := x.Method(i)
				sign := f.Type().(*types.Signature)
				if !anyInteresting(sign.Params()) {
					continue
				}
				s := signString(sign)
				if _, e := ifaceFuncs[s]; e {
					continue
				}
				ifaceFuncs[s] = tn.Name() + "." + f.Name()
			}
			s := funcMapString(iface)
			if _, e := ifaces[s]; !e {
				ifaces[s] = tn.Name()
			}
		case *types.Signature:
			if !anyInteresting(x.Params()) {
				continue
			}
			s := signString(x)
			if _, e := funcs[s]; !e {
				funcs[s] = tn.Name()
			}
		}
	}
	for s, name := range ifaceFuncs {
		if _, e := funcs[s]; !e {
			funcs[s] = name
		}
	}
	return ifaces, funcs
}

func mentionsName(fname, name string) bool {
	if fname == "" || name == "" {
		return false
	}
	if len(name) < 2 {
		return false
	}
	capit := strings.ToUpper(name[:1]) + name[1:]
	lower := strings.ToLower(name)
	return strings.Contains(fname, capit) || strings.HasPrefix(fname, lower)
}

func typeNamed(t types.Type) *types.Named {
	for {
		switch x := t.(type) {
		case *types.Named:
			return x
		case *types.Pointer:
			t = x.Elem()
		default:
			return nil
		}
	}
}
<|endoftext|>


<commit_before>package sdl

/*
#include "sdl_wrapper.h"

#if defined(__WIN32)
#include <SDL2/SDL_syswm.h>
#else
#include <SDL_syswm.h>
#endif

#if !(SDL_VERSION_ATLEAST(2,0,2))
#define SDL_SYSWM_WAYLAND SDL_SYSWM_UNKNOWN
#define SDL_SYSWM_MIR SDL_SYSWM_UNKNOWN
#endif
*/
import "C"
import "unsafe"

// Various supported windowing subsystems.
const (
	SYSWM_UNKNOWN  = C.SDL_SYSWM_UNKNOWN
	SYSWM_WINDOWS  = C.SDL_SYSWM_WINDOWS  // Microsoft Windows
	SYSWM_X11      = C.SDL_SYSWM_X11      // X Window System
	SYSWM_DIRECTFB = C.SDL_SYSWM_DIRECTFB // DirectFB
	SYSWM_COCOA    = C.SDL_SYSWM_COCOA    // Apple Mac OS X
	SYSWM_UIKIT    = C.SDL_SYSWM_UIKIT    // Apple iOS
	SYSWM_WAYLAND  = C.SDL_SYSWM_WAYLAND  // Wayland (>= SDL 2.0.2)
	SYSWM_MIR      = C.SDL_SYSWM_MIR      // Mir (>= SDL 2.0.2)
)

// SysWMInfo contains system-dependent information about a window.
// (https://wiki.libsdl.org/SDL_SysWMinfo)
type SysWMInfo struct {
	Version   Version  // a Version structure that contains the current SDL version
	Subsystem uint32   // the windowing system type
	dummy     [24]byte // unused (to help compilers when no specific system is available)
}

// WindowsInfo contains Microsoft Windows window information.
type WindowsInfo struct {
	Window unsafe.Pointer // the window handle
}

// X11Info contains X Window System window information.
type X11Info struct {
	Display unsafe.Pointer // the X11 display
	Window  uint           // the X11 window
}

// DFBInfo contains DirectFB window information.
type DFBInfo struct {
	Dfb     unsafe.Pointer // the DirectFB main interface
	Window  unsafe.Pointer // the DirectFB window handle
	Surface unsafe.Pointer // the DirectFB client surface
}

// CocoaInfo contains Apple Mac OS X window information.
type CocoaInfo struct {
	Window unsafe.Pointer // the Cocoa window
}

// UIKitInfo contains Apple iOS window information.
type UIKitInfo struct {
	Window unsafe.Pointer // the UIKit window
}

func (info *SysWMInfo) cptr() *C.SDL_SysWMinfo {
	return (*C.SDL_SysWMinfo)(unsafe.Pointer(info))
}

// GetWMInfo returns driver specific information about a window.
// (https://wiki.libsdl.org/SDL_GetWindowWMInfo)
func (window *Window) GetWMInfo() (*SysWMInfo, error) {
	var info SysWMInfo
	VERSION(&info.Version)
	if C.SDL_GetWindowWMInfo(window.cptr(), info.cptr()) == 0 {
		return nil, GetError()
	}
	return &info, nil
}

// GetWindowsInfo returns Microsoft Windows window information.
func (info *SysWMInfo) GetWindowsInfo() *WindowsInfo {
	return (*WindowsInfo)(unsafe.Pointer(&info.dummy[0]))
}

// GetX11Info returns X Window System window information.
func (info *SysWMInfo) GetX11Info() *X11Info {
	return (*X11Info)(unsafe.Pointer(&info.dummy[0]))
}

// GetDFBInfo returns DirectFB window information.
func (info *SysWMInfo) GetDFBInfo() *DFBInfo {
	return (*DFBInfo)(unsafe.Pointer(&info.dummy[0]))
}

// GetCocoaInfo returns Apple Mac OS X window information.
func (info *SysWMInfo) GetCocoaInfo() *CocoaInfo {
	return (*CocoaInfo)(unsafe.Pointer(&info.dummy[0]))
}

// GetUIKitInfo returns Apple iOS window information.
func (info *SysWMInfo) GetUIKitInfo() *UIKitInfo {
	return (*UIKitInfo)(unsafe.Pointer(&info.dummy[0]))
}
<commit_msg>sdl: syswm.go: Add missing SYSWM_WINRT, SYSWM_ANDROID, and SYSWM_VIVANTE subsystem flags<commit_after>package sdl

/*
#include "sdl_wrapper.h"

#if defined(__WIN32)
#include <SDL2/SDL_syswm.h>
#else
#include <SDL_syswm.h>
#endif

#if !(SDL_VERSION_ATLEAST(2,0,2))
#define SDL_SYSWM_WAYLAND SDL_SYSWM_UNKNOWN
#define SDL_SYSWM_MIR SDL_SYSWM_UNKNOWN
#endif

#if !(SDL_VERSION_ATLEAST(2,0,3))
#pragma message("SDL_SYSWM_WINRT is not supported before SDL 2.0.3")
#define SDL_SYSWM_WINRT (0)
#endif

#if !(SDL_VERSION_ATLEAST(2,0,4))
#pragma message("SDL_SYSWM_ANDROID is not supported before SDL 2.0.4")
#define SDL_SYSWM_ANDROID (0)
#endif

#if !(SDL_VERSION_ATLEAST(2,0,5))
#pragma message("SDL_SYSWM_VIVANTE is not supported before SDL 2.0.5")
#define SDL_SYSWM_VIVANTE (0)
#endif
*/
import "C"
import "unsafe"

// Various supported windowing subsystems.
const (
	SYSWM_UNKNOWN  = C.SDL_SYSWM_UNKNOWN
	SYSWM_WINDOWS  = C.SDL_SYSWM_WINDOWS  // Microsoft Windows
	SYSWM_X11      = C.SDL_SYSWM_X11      // X Window System
	SYSWM_DIRECTFB = C.SDL_SYSWM_DIRECTFB // DirectFB
	SYSWM_COCOA    = C.SDL_SYSWM_COCOA    // Apple Mac OS X
	SYSWM_UIKIT    = C.SDL_SYSWM_UIKIT    // Apple iOS
	SYSWM_WAYLAND  = C.SDL_SYSWM_WAYLAND  // Wayland (>= SDL 2.0.2)
	SYSWM_MIR      = C.SDL_SYSWM_MIR      // Mir (>= SDL 2.0.2)
	SYSWM_WINRT    = C.SDL_SYSWM_WINRT    // WinRT (>= SDL 2.0.3)
	SYSWM_ANDROID  = C.SDL_SYSWM_ANDROID  // Android (>= SDL 2.0.4)
	SYSWM_VIVANTE  = C.SDL_SYSWM_VIVANTE  // Vivante (>= SDL 2.0.5)
)

// SysWMInfo contains system-dependent information about a window.
// (https://wiki.libsdl.org/SDL_SysWMinfo)
type SysWMInfo struct {
	Version   Version  // a Version structure that contains the current SDL version
	Subsystem uint32   // the windowing system type
	dummy     [24]byte // unused (to help compilers when no specific system is available)
}

// WindowsInfo contains Microsoft Windows window information.
type WindowsInfo struct {
	Window unsafe.Pointer // the window handle
}

// X11Info contains X Window System window information.
type X11Info struct {
	Display unsafe.Pointer // the X11 display
	Window  uint           // the X11 window
}

// DFBInfo contains DirectFB window information.
type DFBInfo struct {
	Dfb     unsafe.Pointer // the DirectFB main interface
	Window  unsafe.Pointer // the DirectFB window handle
	Surface unsafe.Pointer // the DirectFB client surface
}

// CocoaInfo contains Apple Mac OS X window information.
type CocoaInfo struct {
	Window unsafe.Pointer // the Cocoa window
}

// UIKitInfo contains Apple iOS window information.
type UIKitInfo struct {
	Window unsafe.Pointer // the UIKit window
}

func (info *SysWMInfo) cptr() *C.SDL_SysWMinfo {
	return (*C.SDL_SysWMinfo)(unsafe.Pointer(info))
}

// GetWMInfo returns driver specific information about a window.
// (https://wiki.libsdl.org/SDL_GetWindowWMInfo)
func (window *Window) GetWMInfo() (*SysWMInfo, error) {
	var info SysWMInfo
	VERSION(&info.Version)
	if C.SDL_GetWindowWMInfo(window.cptr(), info.cptr()) == 0 {
		return nil, GetError()
	}
	return &info, nil
}

// GetWindowsInfo returns Microsoft Windows window information.
func (info *SysWMInfo) GetWindowsInfo() *WindowsInfo {
	return (*WindowsInfo)(unsafe.Pointer(&info.dummy[0]))
}

// GetX11Info returns X Window System window information.
func (info *SysWMInfo) GetX11Info() *X11Info {
	return (*X11Info)(unsafe.Pointer(&info.dummy[0]))
}

// GetDFBInfo returns DirectFB window information.
func (info *SysWMInfo) GetDFBInfo() *DFBInfo {
	return (*DFBInfo)(unsafe.Pointer(&info.dummy[0]))
}

// GetCocoaInfo returns Apple Mac OS X window information.
func (info *SysWMInfo) GetCocoaInfo() *CocoaInfo {
	return (*CocoaInfo)(unsafe.Pointer(&info.dummy[0]))
}

// GetUIKitInfo returns Apple iOS window information.
func (info *SysWMInfo) GetUIKitInfo() *UIKitInfo {
	return (*UIKitInfo)(unsafe.Pointer(&info.dummy[0]))
}
<|endoftext|>


<commit_before>package sema

import (
	"sync"
	"testing"
	"time"
)

func TestNew(t *testing.T) {
	if _, err := New(0); err == nil {
		t.Errorf("New() error = %v, wantErr %v", err, errCap)
	}

	if _, err := New(1); err != nil {
		t.Errorf("New() error = %v, wantErr %v", err, nil)
	}
}

func TestAcquireRelease(t *testing.T) {
	sema, _ := New(10)

	for x := 1; x <= sema.Cap(); x++ {
		sema.Acquire()
	}

	if sema.Holders() != sema.Cap() {
		t.Errorf("Holders() %d != %d Sema.Cap() ", sema.Holders(), sema.Cap())
	}

	for x := 1; x <= sema.Cap(); x++ {
		sema.Release()
	}

	if sema.Holders() != 0 {
		t.Errorf("Holders() == %d  after releasing Sema.Cap() == %d", sema.Holders(), sema.Cap())
	}

}

func TestTryAcquire(t *testing.T) {
	sema, _ := New(1)

	if !sema.TryAcquire() {
		t.Errorf("Failed to TryAcquire() with Cap() == %d and Holders() == %d", sema.Cap(), sema.Holders())
	}

	if sema.TryAcquire() {
		t.Errorf("Success to TryAcquire() with Cap() == %d and Holders() == %d", sema.Cap(), sema.Holders())
	}
}

func TestAcquireWithin(t *testing.T) {
	sema, _ := New(1)

	if !sema.AcquireWithin(1 * time.Millisecond) {
		t.Errorf("Failed to AcquireWithin() with Cap() == %d and Holders() == %d", sema.Cap(), sema.Holders())
	}
	if sema.AcquireWithin(1 * time.Millisecond) {
		t.Errorf("Success to AcquireWithin() with Cap() == %d and Holders() == %d", sema.Cap(), sema.Holders())
	}
}

func TestConcurrency(t *testing.T) {
	cap := 10
	sema, _ := New(cap)
	wg := &sync.WaitGroup{}

	for x := 1; x <= cap; x++ {
		sema.Acquire()
	}

	wg.Add(1)
	go func() {
		defer wg.Done()
		for x := 1; x <= cap; x++ {
			sema.Acquire()
			time.Sleep(time.Duration(cap) * time.Nanosecond)
			sema.Release()
		}
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		for x := 1; x <= cap; x++ {
			sema.Acquire()
			time.Sleep(time.Duration(cap) * time.Nanosecond)
			sema.Release()
		}
	}()

	for x := 1; x <= cap; x++ {
		time.Sleep(time.Duration(cap) * time.Nanosecond)
		sema.Release()
	}

	wg.Wait()
	if sema.Holders() != 0 {
		t.Errorf("Expected Holders() == %d, got %d", 0, sema.Holders())
	}
}
<commit_msg>benchmarks<commit_after>package sema

import (
	"sync"
	"testing"
	"time"
)

func TestNew(t *testing.T) {
	if _, err := New(0); err == nil {
		t.Errorf("New() error = %v, wantErr %v", err, errCap)
	}

	if _, err := New(1); err != nil {
		t.Errorf("New() error = %v, wantErr %v", err, nil)
	}
}

func TestAcquireRelease(t *testing.T) {
	sema, _ := New(10)

	for x := 1; x <= sema.Cap(); x++ {
		sema.Acquire()
	}

	if sema.Holders() != sema.Cap() {
		t.Errorf("Holders() %d != %d Sema.Cap() ", sema.Holders(), sema.Cap())
	}

	for x := 1; x <= sema.Cap(); x++ {
		sema.Release()
	}

	if sema.Holders() != 0 {
		t.Errorf("Holders() == %d  after releasing Sema.Cap() == %d", sema.Holders(), sema.Cap())
	}

}

func TestTryAcquire(t *testing.T) {
	sema, _ := New(1)

	if !sema.TryAcquire() {
		t.Errorf("Failed to TryAcquire() with Cap() == %d and Holders() == %d", sema.Cap(), sema.Holders())
	}

	if sema.TryAcquire() {
		t.Errorf("Success to TryAcquire() with Cap() == %d and Holders() == %d", sema.Cap(), sema.Holders())
	}
}

func TestAcquireWithin(t *testing.T) {
	sema, _ := New(1)

	if !sema.AcquireWithin(1 * time.Millisecond) {
		t.Errorf("Failed to AcquireWithin() with Cap() == %d and Holders() == %d", sema.Cap(), sema.Holders())
	}
	if sema.AcquireWithin(1 * time.Millisecond) {
		t.Errorf("Success to AcquireWithin() with Cap() == %d and Holders() == %d", sema.Cap(), sema.Holders())
	}
}

func TestConcurrency(t *testing.T) {
	cap := 10
	sema, _ := New(cap)
	wg := &sync.WaitGroup{}

	for x := 1; x <= cap; x++ {
		sema.Acquire()
	}

	wg.Add(1)
	go func() {
		defer wg.Done()
		for x := 1; x <= cap; x++ {
			sema.Acquire()
			time.Sleep(time.Duration(cap) * time.Nanosecond)
			sema.Release()
		}
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		for x := 1; x <= cap; x++ {
			sema.Acquire()
			time.Sleep(time.Duration(cap) * time.Nanosecond)
			sema.Release()
		}
	}()

	for x := 1; x <= cap; x++ {
		time.Sleep(time.Duration(cap) * time.Nanosecond)
		sema.Release()
	}

	wg.Wait()
	if sema.Holders() != 0 {
		t.Errorf("Expected Holders() == %d, got %d", 0, sema.Holders())
	}
}

func BenchmarkAcquireRelease(b *testing.B) {
	sema, _ := New(10)
	for i := 0; i < b.N; i++ {
		for x := 0; x < 10; x++ {
			sema.Acquire()
		}

		for x := 0; x < 10; x++ {
			sema.Release()
		}
	}
}

func BenchmarkConcurrency(b *testing.B) {
	cap := 10
	sema, _ := New(cap)
	wg := &sync.WaitGroup{}

	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		for x := 1; x <= cap; x++ {
			sema.Acquire()
		}

		wg.Add(1)
		go func() {
			defer wg.Done()
			for x := 1; x <= cap; x++ {
				sema.Acquire()
				sema.Release()
			}
		}()

		wg.Add(1)
		go func() {
			defer wg.Done()
			for x := 1; x <= cap; x++ {
				sema.Acquire()
				sema.Release()
			}
		}()

		for x := 1; x <= cap; x++ {
			sema.Release()
		}

		wg.Wait()
	}
}
<|endoftext|>


<commit_before>// Copyright 2012 The Plotinum Authors. All rights reserved.
// Use of this source code is governed by an MIT-style license
// that can be found in the LICENSE file.

// Some of this code (namely the code for computing the
// width of a string in a given font) was copied from
// code.google.com/p/freetype-go/freetype/ which includes
// the following copyright notice:
// Copyright 2010 The Freetype-Go Authors. All rights reserved.

package vg

import (
	"errors"
	"go/build"
	"io/ioutil"
	"os"
	"path/filepath"

	"code.google.com/p/freetype-go/freetype"
	"code.google.com/p/freetype-go/freetype/truetype"
)

const (
	// importString is the import string expected for
	// this package.  It is used to find the font
	// directory included with the package source.
	importString = "code.google.com/p/plotinum/vg"
)

var (
	// FontMap maps Postscript/PDF font names to compatible
	// free fonts (TrueType converted ghostscript fonts).
	// Fonts that are not keys of this map are not supported.
	FontMap = map[string]string{

		// At the moment, we use fonts from GNU's freefont
		// project.

		"Courier":             "NimbusMonL-Regu",
		"Courier-Bold":        "NimbusMonL-Bold",
		"Courier-Oblique":     "NimbusMonL-ReguObli",
		"Courier-BoldOblique": "NimbusMonL-BoldObli",

		"Helvetica":             "NimbusSanL-Regu",
		"Helvetica-Bold":        "NimbusSanL-Bold",
		"Helvetica-Oblique":     "NimbusSanL-ReguItal",
		"Helvetica-BoldOblique": "NimbusSanL-BoldItal",

		"Times-Roman":      "NimbusRomNo9L-Regu",
		"Times-Bold":       "NimbusRomNo9L-Medi",
		"Times-Italic":     "NimbusRomNo9L-ReguItal",
		"Times-BoldItalic": "NimbusRomNo9L-MediItal",
	}

	// loadedFonts is indexed by a font name and it
	// caches the associated *truetype.Font.
	loadedFonts = make(map[string]*truetype.Font)
)

// A Font represents one of the supported font
// faces.
type Font struct {
	// Size is the size of the font.  The font size can
	// be used as a reasonable value for the vertical
	// distance between two successive lines of text.
	Size Length

	// name is the name of this font.
	name string

	// This is a little bit of a hack, but the truetype
	// font is currently only needed to determine the
	// dimensions of strings drawn in this font.
	// The actual drawing of the strings is handled
	// separately by different back-ends:
	// Both Postscript and PDF are capable of drawing
	// their own fonts and draw2d loads its own copy of
	// the truetype fonts for its own output.
	//
	// This isn't a necessity--some future backend is
	// free to use this field--however it is a consequence
	// of the fact that the current backends were
	// developed independently of this package.

	// font is the truetype font pointer for this
	// font.
	font *truetype.Font
}

// MakeFont returns a font object.  The name of the font must
// be a key of the FontMap.  The font file is located by searching
// the FontDirs slice for a directory containing the relevant font
// file.  The font file name is name mapped by FontMap with the
// .ttf extension.  For example, the font file for the font name
// Courier is NimbusMonL-Regu.ttf.
func MakeFont(name string, size Length) (font Font, err error) {
	font.Size = size
	font.name = name
	font.font, err = getFont(name)
	return
}

// Name returns the name of the font.
func (f *Font) Name() string {
	return f.name
}

// Font returns the corresponding truetype.Font.
func (f *Font) Font() *truetype.Font {
	return f.font
}

// SetName sets the name of the font, effectively
// changing the font.  If an error is returned then
// the font is left unchanged.
func (f *Font) SetName(name string) error {
	font, err := getFont(name)
	if err != nil {
		return err
	}
	f.name = name
	f.font = font
	return nil
}

// FontExtents contains font metric information.
type FontExtents struct {
	// Ascent is the distance that the text
	// extends above the baseline.
	Ascent Length

	// Descent is the distance that the text
	// extends below the baseline.  The descent
	// is given as a negative value.
	Descent Length

	// Height is the distance from the lowest
	// descending point to the highest ascending
	// point.
	Height Length
}

// Extents returns the FontExtents for a font.
func (f *Font) Extents() FontExtents {
	bounds := f.font.Bounds(f.Font().FUnitsPerEm())
	scale := f.Size / Points(float64(f.Font().FUnitsPerEm()))
	return FontExtents{
		Ascent:  Points(float64(bounds.YMax)) * scale,
		Descent: Points(float64(bounds.YMin)) * scale,
		Height:  Points(float64(bounds.YMax-bounds.YMin)) * scale,
	}
}

// Width returns width of a string when drawn using the font.
func (f *Font) Width(s string) Length {
	// scale converts truetype.FUnit to float64
	scale := f.Size / Points(float64(f.font.FUnitsPerEm()))

	width := 0
	prev, hasPrev := truetype.Index(0), false
	for _, rune := range s {
		index := f.font.Index(rune)
		if hasPrev {
			width += int(f.font.Kerning(f.font.FUnitsPerEm(), prev, index))
		}
		width += int(f.font.HMetric(f.font.FUnitsPerEm(), index).AdvanceWidth)
		prev, hasPrev = index, true
	}
	return Points(float64(width)) * scale
}

// getFont returns the truetype.Font for the given font name or an error.
func getFont(name string) (*truetype.Font, error) {
	if f, ok := loadedFonts[name]; ok {
		return f, nil
	}

	path, err := fontPath(name)
	if err != nil {
		return nil, err
	}

	file, err := os.Open(path)
	if err != nil {
		return nil, errors.New("Failed to open font file: " + err.Error())
	}
	defer file.Close()

	bytes, err := ioutil.ReadAll(file)
	if err != nil {
		return nil, errors.New("Failed to read font file: " + err.Error())
	}

	font, err := freetype.ParseFont(bytes)
	if err == nil {
		loadedFonts[name] = font
	} else {
		err = errors.New("Failed to parse font file: " + err.Error())
	}

	return font, err
}

// FontPath returns the path for a font name or an error if it is not found.
func fontPath(name string) (string, error) {
	fname, err := fontFile(name)
	if err != nil {
		return "", err
	}

	for _, d := range FontDirs {
		p := filepath.Join(d, fname)
		if _, err := os.Stat(p); err != nil {
			continue
		}
		return p, nil
	}

	return "", errors.New("Failed to locate a font file " + fname + " for font name " + name)
}

// FontDirs is a slice of directories searched for font data files.
// If the first font file found is unreadable or cannot be parsed, then
// subsequent directories are not tried, and the font will fail to load.
//
// The default slice contains, in the following order, the values of the
// environment variable VGFONTPATH if it is defined, then the vg
// source fonts directory if it is found (i.e., if vg was installed by
// go get).  If the resulting FontDirs slice is empty then the current
// directory is added to it.  This slice may be changed to load fonts
// from different locations.
var FontDirs = initFontDirs()

// InitFontDirs returns the initial value for the FontDirectories variable.
func initFontDirs() []string {
	dirs := filepath.SplitList(os.Getenv("VGFONTPATH"))

	if pkg, err := build.Import(importString, "", build.FindOnly); err == nil {
		p := filepath.Join(pkg.Dir, "fonts")
		if _, err := os.Stat(p); err == nil {
			dirs = append(dirs, p)
		}
	}

	if len(dirs) == 0 {
		dirs = []string{"./fonts"}
	}

	return dirs
}

// FontFile returns the font file name for a font name or an error
// if it is an unknown font (i.e., not in the FontMap).
func fontFile(name string) (string, error) {
	var err error
	n, ok := FontMap[name]
	if !ok {
		errStr := "Unknown font: " + name + ".  Available fonts are:"
		for n := range FontMap {
			errStr += " " + n
		}
		err = errors.New(errStr)
	}
	return n + ".ttf", err
}
<commit_msg>vg: protect access to loadedFonts with an rwmutex.<commit_after>// Copyright 2012 The Plotinum Authors. All rights reserved.
// Use of this source code is governed by an MIT-style license
// that can be found in the LICENSE file.

// Some of this code (namely the code for computing the
// width of a string in a given font) was copied from
// code.google.com/p/freetype-go/freetype/ which includes
// the following copyright notice:
// Copyright 2010 The Freetype-Go Authors. All rights reserved.

package vg

import (
	"errors"
	"go/build"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"

	"code.google.com/p/freetype-go/freetype"
	"code.google.com/p/freetype-go/freetype/truetype"
)

const (
	// importString is the import string expected for
	// this package.  It is used to find the font
	// directory included with the package source.
	importString = "code.google.com/p/plotinum/vg"
)

var (
	// FontMap maps Postscript/PDF font names to compatible
	// free fonts (TrueType converted ghostscript fonts).
	// Fonts that are not keys of this map are not supported.
	FontMap = map[string]string{

		// At the moment, we use fonts from GNU's freefont
		// project.

		"Courier":             "NimbusMonL-Regu",
		"Courier-Bold":        "NimbusMonL-Bold",
		"Courier-Oblique":     "NimbusMonL-ReguObli",
		"Courier-BoldOblique": "NimbusMonL-BoldObli",

		"Helvetica":             "NimbusSanL-Regu",
		"Helvetica-Bold":        "NimbusSanL-Bold",
		"Helvetica-Oblique":     "NimbusSanL-ReguItal",
		"Helvetica-BoldOblique": "NimbusSanL-BoldItal",

		"Times-Roman":      "NimbusRomNo9L-Regu",
		"Times-Bold":       "NimbusRomNo9L-Medi",
		"Times-Italic":     "NimbusRomNo9L-ReguItal",
		"Times-BoldItalic": "NimbusRomNo9L-MediItal",
	}

	// loadedFonts is indexed by a font name and it
	// caches the associated *truetype.Font.
	loadedFonts = make(map[string]*truetype.Font)

	// FontLock protects access to the loadedFonts map.
	fontLock sync.RWMutex
)

// A Font represents one of the supported font
// faces.
type Font struct {
	// Size is the size of the font.  The font size can
	// be used as a reasonable value for the vertical
	// distance between two successive lines of text.
	Size Length

	// name is the name of this font.
	name string

	// This is a little bit of a hack, but the truetype
	// font is currently only needed to determine the
	// dimensions of strings drawn in this font.
	// The actual drawing of the strings is handled
	// separately by different back-ends:
	// Both Postscript and PDF are capable of drawing
	// their own fonts and draw2d loads its own copy of
	// the truetype fonts for its own output.
	//
	// This isn't a necessity--some future backend is
	// free to use this field--however it is a consequence
	// of the fact that the current backends were
	// developed independently of this package.

	// font is the truetype font pointer for this
	// font.
	font *truetype.Font
}

// MakeFont returns a font object.  The name of the font must
// be a key of the FontMap.  The font file is located by searching
// the FontDirs slice for a directory containing the relevant font
// file.  The font file name is name mapped by FontMap with the
// .ttf extension.  For example, the font file for the font name
// Courier is NimbusMonL-Regu.ttf.
func MakeFont(name string, size Length) (font Font, err error) {
	font.Size = size
	font.name = name
	font.font, err = getFont(name)
	return
}

// Name returns the name of the font.
func (f *Font) Name() string {
	return f.name
}

// Font returns the corresponding truetype.Font.
func (f *Font) Font() *truetype.Font {
	return f.font
}

// SetName sets the name of the font, effectively
// changing the font.  If an error is returned then
// the font is left unchanged.
func (f *Font) SetName(name string) error {
	font, err := getFont(name)
	if err != nil {
		return err
	}
	f.name = name
	f.font = font
	return nil
}

// FontExtents contains font metric information.
type FontExtents struct {
	// Ascent is the distance that the text
	// extends above the baseline.
	Ascent Length

	// Descent is the distance that the text
	// extends below the baseline.  The descent
	// is given as a negative value.
	Descent Length

	// Height is the distance from the lowest
	// descending point to the highest ascending
	// point.
	Height Length
}

// Extents returns the FontExtents for a font.
func (f *Font) Extents() FontExtents {
	bounds := f.font.Bounds(f.Font().FUnitsPerEm())
	scale := f.Size / Points(float64(f.Font().FUnitsPerEm()))
	return FontExtents{
		Ascent:  Points(float64(bounds.YMax)) * scale,
		Descent: Points(float64(bounds.YMin)) * scale,
		Height:  Points(float64(bounds.YMax-bounds.YMin)) * scale,
	}
}

// Width returns width of a string when drawn using the font.
func (f *Font) Width(s string) Length {
	// scale converts truetype.FUnit to float64
	scale := f.Size / Points(float64(f.font.FUnitsPerEm()))

	width := 0
	prev, hasPrev := truetype.Index(0), false
	for _, rune := range s {
		index := f.font.Index(rune)
		if hasPrev {
			width += int(f.font.Kerning(f.font.FUnitsPerEm(), prev, index))
		}
		width += int(f.font.HMetric(f.font.FUnitsPerEm(), index).AdvanceWidth)
		prev, hasPrev = index, true
	}
	return Points(float64(width)) * scale
}

// getFont returns the truetype.Font for the given font name or an error.
func getFont(name string) (*truetype.Font, error) {
	fontLock.RLock()
	f, ok := loadedFonts[name]
	fontLock.RUnlock()
	if ok {
		return f, nil
	}

	path, err := fontPath(name)
	if err != nil {
		return nil, err
	}

	file, err := os.Open(path)
	if err != nil {
		return nil, errors.New("Failed to open font file: " + err.Error())
	}
	defer file.Close()

	bytes, err := ioutil.ReadAll(file)
	if err != nil {
		return nil, errors.New("Failed to read font file: " + err.Error())
	}

	font, err := freetype.ParseFont(bytes)
	if err == nil {
		fontLock.Lock()
		loadedFonts[name] = font
		fontLock.Unlock()
	} else {
		err = errors.New("Failed to parse font file: " + err.Error())
	}

	return font, err
}

// FontPath returns the path for a font name or an error if it is not found.
func fontPath(name string) (string, error) {
	fname, err := fontFile(name)
	if err != nil {
		return "", err
	}

	for _, d := range FontDirs {
		p := filepath.Join(d, fname)
		if _, err := os.Stat(p); err != nil {
			continue
		}
		return p, nil
	}

	return "", errors.New("Failed to locate a font file " + fname + " for font name " + name)
}

// FontDirs is a slice of directories searched for font data files.
// If the first font file found is unreadable or cannot be parsed, then
// subsequent directories are not tried, and the font will fail to load.
//
// The default slice contains, in the following order, the values of the
// environment variable VGFONTPATH if it is defined, then the vg
// source fonts directory if it is found (i.e., if vg was installed by
// go get).  If the resulting FontDirs slice is empty then the current
// directory is added to it.  This slice may be changed to load fonts
// from different locations.
var FontDirs = initFontDirs()

// InitFontDirs returns the initial value for the FontDirectories variable.
func initFontDirs() []string {
	dirs := filepath.SplitList(os.Getenv("VGFONTPATH"))

	if pkg, err := build.Import(importString, "", build.FindOnly); err == nil {
		p := filepath.Join(pkg.Dir, "fonts")
		if _, err := os.Stat(p); err == nil {
			dirs = append(dirs, p)
		}
	}

	if len(dirs) == 0 {
		dirs = []string{"./fonts"}
	}

	return dirs
}

// FontFile returns the font file name for a font name or an error
// if it is an unknown font (i.e., not in the FontMap).
func fontFile(name string) (string, error) {
	var err error
	n, ok := FontMap[name]
	if !ok {
		errStr := "Unknown font: " + name + ".  Available fonts are:"
		for n := range FontMap {
			errStr += " " + n
		}
		err = errors.New(errStr)
	}
	return n + ".ttf", err
}
<|endoftext|>