<commit_before>// Functions related to traversal of the first Brillouin zone.
package scExplorer

import "math"

type BzFunc func(k []float64) float64
type consumer func(next float64, total *float64)

// Sum values of fn over all Brillouin zone points.
// Uses Kahan summation algorithm for increased accuracy.
func BzSum(pointsPerSide int64, dimension int64, fn BzFunc) float64 {
	c := 0.0
	var y, t float64
	add := func(next float64, total *float64) {
		y = next - c
		t = *total + y
		c = (t - *total) - y
		*total = t
	}
	return reduce(add, 0.0, pointsPerSide, dimension, fn)
}

// Find the minimum of fn over all Brillouin zone points.
func BzMinimum(pointsPerSide int64, dimension int64, fn BzFunc) float64 {
	minimum := func(next float64, min *float64) {
		if next < *min {
			*min = next
		}
	}
	return reduce(minimum, math.MaxFloat64, pointsPerSide, dimension, fn)
}

func reduce(cs consumer, start float64, pointsPerSide int64, dimension int64, fn BzFunc) float64 {
	return 0.0
}

func bzPoints(pointsPerSide int64, dimension int64) (chan []float64, chan bool) {
	return nil, nil
}
<commit_msg>Implement bzReduce (missing way of configuring max workers).<commit_after>// Functions related to traversal of the first Brillouin zone.
package scExplorer

import "math"

type BzPoint []float64
type BzFunc func(k BzPoint) float64
type bzConsumer func(next float64, total *float64)

// Sum values of fn over all Brillouin zone points.
// Uses Kahan summation algorithm for increased accuracy.
func BzSum(pointsPerSide int64, dimension int64, fn BzFunc) float64 {
	c := 0.0
	var y, t float64
	add := func(next float64, total *float64) {
		y = next - c
		t = *total + y
		c = (t - *total) - y
		*total = t
	}
	return bzReduce(add, 0.0, pointsPerSide, dimension, fn)
}

// Find the minimum of fn over all Brillouin zone points.
func BzMinimum(pointsPerSide int64, dimension int64, fn BzFunc) float64 {
	minimum := func(next float64, min *float64) {
		if next < *min {
			*min = next
		}
	}
	return bzReduce(minimum, math.MaxFloat64, pointsPerSide, dimension, fn)
}

// Iterate over the Brillouin zone, accumulating the values of fn with cs.
func bzReduce(cs bzConsumer, start float64, pointsPerSide int64, dimension int64, fn BzFunc) float64 {
	maxWorkers := 2 // TODO: set this via config file
	points, done := bzPoints(pointsPerSide, dimension)
	results := make([]chan float64, maxWorkers)
	work := func(result chan float64) {
		var k BzPoint
		total := start
		for {
			select {
			case k = <-points:
				cs(fn(k), &total)
			case <-done:
				done <- true
				break
			}
		}
		result <- total
	}

	for i := 0; i < maxWorkers; i++ {
		results[i] = make(chan float64)
		go work(results[i])
	}
	fullTotal := start
	for i := 0; i < maxWorkers; i++ {
		cs(<-results[i], &fullTotal)
	}
	return fullTotal
}

// Produce (points, done) where points is a channel whose values cover each
// Brillouin zone point once, and done is is a channel which contains true
// after all points have been traversed.
func bzPoints(pointsPerSide int64, dimension int64) (chan BzPoint, chan bool) {
	return nil, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"strings"
	"sync"
)

// ScalewayCache is used not to query the API to resolve full identifiers
type ScalewayCache struct {
	// Images contains names of Scaleway images indexed by identifier
	Images map[string]string `json:"images"`

	// Snapshots contains names of Scaleway snapshots indexed by identifier
	Snapshots map[string]string `json:"snapshots"`

	// Bootscripts contains names of Scaleway bootscripts indexed by identifier
	Bootscripts map[string]string `json:"bootscripts"`

	// Servers contains names of Scaleway C1 servers indexed by identifier
	Servers map[string]string `json:"servers"`

	// Path is the path to the cache file
	Path string `json:"-"`

	// Modified tells if the cache needs to be overwritten or not
	Modified bool `json:"-"`

	// Lock allows ScalewayCache to be used concurrently
	Lock sync.Mutex `json:"-"`
}

const (
	IDENTIFIER_SERVER = iota
	IDENTIFIER_IMAGE
	IDENTIFIER_BOOTSCRIPT
)

// ScalewayIdentifier is a unique identifier on Scaleway
type ScalewayIdentifier struct {
	// Identifier is a unique identifier on
	Identifier string

	// Type of the identifier
	Type int
}

// NewScalewayCache loads a per-user cache
func NewScalewayCache() (*ScalewayCache, error) {
	u, err := user.Current()
	if err != nil {
		return nil, err
	}
	cache_path := fmt.Sprintf("%s/.scw-cache.db", u.HomeDir)
	_, err = os.Stat(cache_path)
	if os.IsNotExist(err) {
		return &ScalewayCache{
			Images:      make(map[string]string),
			Snapshots:   make(map[string]string),
			Bootscripts: make(map[string]string),
			Servers:     make(map[string]string),
			Path:        cache_path,
		}, nil
	} else if err != nil {
		return nil, err
	}
	file, err := ioutil.ReadFile(cache_path)
	if err != nil {
		return nil, err
	}
	var cache ScalewayCache
	cache.Path = cache_path
	err = json.Unmarshal(file, &cache)
	if err != nil {
		return nil, err
	}
	return &cache, nil
}

// Save atomically overwrites the current cache database
func (c *ScalewayCache) Save() error {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	if c.Modified {
		file, err := ioutil.TempFile("", "")
		if err != nil {
			return err
		}
		encoder := json.NewEncoder(file)
		err = encoder.Encode(*c)
		if err != nil {
			return err
		}
		return os.Rename(file.Name(), c.Path)
	}
	return nil
}

// LookupImages attempts to return identifiers matching a pattern
func (c *ScalewayCache) LookUpImages(needle string) []string {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	var res []string
	for identifier, name := range c.Images {
		if strings.HasPrefix(identifier, needle) || strings.HasPrefix(name, needle) {
			res = append(res, identifier)
		}
	}
	return res
}

// LookupSnapshots attempts to return identifiers matching a pattern
func (c *ScalewayCache) LookUpSnapshots(needle string) []string {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	var res []string
	for identifier, name := range c.Snapshots {
		if strings.HasPrefix(identifier, needle) || strings.HasPrefix(name, needle) {
			res = append(res, identifier)
		}
	}
	return res
}

// LookupBootscripts attempts to return identifiers matching a pattern
func (c *ScalewayCache) LookUpBootscripts(needle string) []string {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	var res []string
	for identifier, name := range c.Bootscripts {
		if strings.HasPrefix(identifier, needle) || strings.HasPrefix(name, needle) {
			res = append(res, identifier)
		}
	}
	return res
}

// LookupServers attempts to return identifiers matching a pattern
func (c *ScalewayCache) LookUpServers(needle string) []string {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	var res []string
	for identifier, name := range c.Servers {
		if strings.HasPrefix(identifier, needle) || strings.HasPrefix(name, needle) {
			res = append(res, identifier)
		}
	}
	return res
}

// LookupIdentifier attempts to return identifiers matching a pattern
func (c *ScalewayCache) LookUpIdentifiers(needle string) []ScalewayIdentifier {
	result := []ScalewayIdentifier{}

	for _, identifier := range c.LookUpServers(needle) {
		result = append(result, ScalewayIdentifier{
			Identifier: identifier,
			Type:       IDENTIFIER_SERVER,
		})
	}

	for _, identifier := range c.LookUpImages(needle) {
		result = append(result, ScalewayIdentifier{
			Identifier: identifier,
			Type:       IDENTIFIER_IMAGE,
		})
	}

	// FIXME: add bootscripts

	return result
}

// InsertServer registers a server in the cache
func (c *ScalewayCache) InsertServer(identifier, name string) {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	current_name, exists := c.Servers[identifier]
	if !exists || current_name != name {
		c.Servers[identifier] = name
		c.Modified = true
	}
}

// InsertImage registers an image in the cache
func (c *ScalewayCache) InsertImage(identifier, name string) {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	current_name, exists := c.Images[identifier]
	if !exists || current_name != name {
		c.Images[identifier] = name
		c.Modified = true
	}
}

// InsertSnapshot registers an snapshot in the cache
func (c *ScalewayCache) InsertSnapshot(identifier, name string) {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	current_name, exists := c.Snapshots[identifier]
	if !exists || current_name != name {
		c.Snapshots[identifier] = name
		c.Modified = true
	}
}

// InsertBootscript registers an bootscript in the cache
func (c *ScalewayCache) InsertBootscript(identifier, name string) {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	current_name, exists := c.Bootscripts[identifier]
	if !exists || current_name != name {
		c.Bootscripts[identifier] = name
		c.Modified = true
	}
}

// GetNbServers returns the number of servers in the cache
func (c *ScalewayCache) GetNbServers() int {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	return len(c.Servers)
}

// GetNbImages returns the number of images in the cache
func (c *ScalewayCache) GetNbImages() int {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	return len(c.Images)
}

// GetNbSnapshots returns the number of snapshots in the cache
func (c *ScalewayCache) GetNbSnapshots() int {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	return len(c.Snapshots)
}

// GetNbBootscripts returns the number of bootscripts in the cache
func (c *ScalewayCache) GetNbBootscripts() int {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	return len(c.Bootscripts)
}
<commit_msg>Added regexp matching for lookups<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"regexp"
	"strings"
	"sync"
)

// ScalewayCache is used not to query the API to resolve full identifiers
type ScalewayCache struct {
	// Images contains names of Scaleway images indexed by identifier
	Images map[string]string `json:"images"`

	// Snapshots contains names of Scaleway snapshots indexed by identifier
	Snapshots map[string]string `json:"snapshots"`

	// Bootscripts contains names of Scaleway bootscripts indexed by identifier
	Bootscripts map[string]string `json:"bootscripts"`

	// Servers contains names of Scaleway C1 servers indexed by identifier
	Servers map[string]string `json:"servers"`

	// Path is the path to the cache file
	Path string `json:"-"`

	// Modified tells if the cache needs to be overwritten or not
	Modified bool `json:"-"`

	// Lock allows ScalewayCache to be used concurrently
	Lock sync.Mutex `json:"-"`
}

const (
	IDENTIFIER_SERVER = iota
	IDENTIFIER_IMAGE
	IDENTIFIER_BOOTSCRIPT
)

// ScalewayIdentifier is a unique identifier on Scaleway
type ScalewayIdentifier struct {
	// Identifier is a unique identifier on
	Identifier string

	// Type of the identifier
	Type int
}

// NewScalewayCache loads a per-user cache
func NewScalewayCache() (*ScalewayCache, error) {
	u, err := user.Current()
	if err != nil {
		return nil, err
	}
	cache_path := fmt.Sprintf("%s/.scw-cache.db", u.HomeDir)
	_, err = os.Stat(cache_path)
	if os.IsNotExist(err) {
		return &ScalewayCache{
			Images:      make(map[string]string),
			Snapshots:   make(map[string]string),
			Bootscripts: make(map[string]string),
			Servers:     make(map[string]string),
			Path:        cache_path,
		}, nil
	} else if err != nil {
		return nil, err
	}
	file, err := ioutil.ReadFile(cache_path)
	if err != nil {
		return nil, err
	}
	var cache ScalewayCache
	cache.Path = cache_path
	err = json.Unmarshal(file, &cache)
	if err != nil {
		return nil, err
	}
	return &cache, nil
}

// Save atomically overwrites the current cache database
func (c *ScalewayCache) Save() error {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	if c.Modified {
		file, err := ioutil.TempFile("", "")
		if err != nil {
			return err
		}
		encoder := json.NewEncoder(file)
		err = encoder.Encode(*c)
		if err != nil {
			return err
		}
		return os.Rename(file.Name(), c.Path)
	}
	return nil
}

// LookupImages attempts to return identifiers matching a pattern
func (c *ScalewayCache) LookUpImages(needle string) []string {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	var res []string
	nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*"))
	for identifier, name := range c.Images {
		if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(name) {
			res = append(res, identifier)
		}
	}
	return res
}

// LookupSnapshots attempts to return identifiers matching a pattern
func (c *ScalewayCache) LookUpSnapshots(needle string) []string {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	var res []string
	nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*"))
	for identifier, name := range c.Snapshots {
		if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(name) {
			res = append(res, identifier)
		}
	}
	return res
}

// LookupBootscripts attempts to return identifiers matching a pattern
func (c *ScalewayCache) LookUpBootscripts(needle string) []string {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	var res []string
	nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*"))
	for identifier, name := range c.Bootscripts {
		if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(name) {
			res = append(res, identifier)
		}
	}
	return res
}

// LookupServers attempts to return identifiers matching a pattern
func (c *ScalewayCache) LookUpServers(needle string) []string {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	var res []string
	nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*"))
	for identifier, name := range c.Servers {
		if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(name) {
			res = append(res, identifier)
		}
	}
	return res
}

// LookupIdentifier attempts to return identifiers matching a pattern
func (c *ScalewayCache) LookUpIdentifiers(needle string) []ScalewayIdentifier {
	result := []ScalewayIdentifier{}

	for _, identifier := range c.LookUpServers(needle) {
		result = append(result, ScalewayIdentifier{
			Identifier: identifier,
			Type:       IDENTIFIER_SERVER,
		})
	}

	for _, identifier := range c.LookUpImages(needle) {
		result = append(result, ScalewayIdentifier{
			Identifier: identifier,
			Type:       IDENTIFIER_IMAGE,
		})
	}

	// FIXME: add bootscripts

	return result
}

// InsertServer registers a server in the cache
func (c *ScalewayCache) InsertServer(identifier, name string) {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	current_name, exists := c.Servers[identifier]
	if !exists || current_name != name {
		c.Servers[identifier] = name
		c.Modified = true
	}
}

// InsertImage registers an image in the cache
func (c *ScalewayCache) InsertImage(identifier, name string) {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	current_name, exists := c.Images[identifier]
	if !exists || current_name != name {
		c.Images[identifier] = name
		c.Modified = true
	}
}

// InsertSnapshot registers an snapshot in the cache
func (c *ScalewayCache) InsertSnapshot(identifier, name string) {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	current_name, exists := c.Snapshots[identifier]
	if !exists || current_name != name {
		c.Snapshots[identifier] = name
		c.Modified = true
	}
}

// InsertBootscript registers an bootscript in the cache
func (c *ScalewayCache) InsertBootscript(identifier, name string) {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	current_name, exists := c.Bootscripts[identifier]
	if !exists || current_name != name {
		c.Bootscripts[identifier] = name
		c.Modified = true
	}
}

// GetNbServers returns the number of servers in the cache
func (c *ScalewayCache) GetNbServers() int {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	return len(c.Servers)
}

// GetNbImages returns the number of images in the cache
func (c *ScalewayCache) GetNbImages() int {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	return len(c.Images)
}

// GetNbSnapshots returns the number of snapshots in the cache
func (c *ScalewayCache) GetNbSnapshots() int {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	return len(c.Snapshots)
}

// GetNbBootscripts returns the number of bootscripts in the cache
func (c *ScalewayCache) GetNbBootscripts() int {
	c.Lock.Lock()
	defer c.Lock.Unlock()

	return len(c.Bootscripts)
}
<|endoftext|>


<commit_before>package luar

import (
	"container/list"
	"reflect"
	"sync"

	"github.com/yuin/gopher-lua"
)

const (
	cacheKey = "github.com/layeh/gopher-luar"
	tagName  = "luar"
)

var mu sync.Mutex

type mtCache struct {
	regular, types map[reflect.Type]lua.LValue
}

func newMTCache() *mtCache {
	return &mtCache{
		regular: make(map[reflect.Type]lua.LValue),
		types:   make(map[reflect.Type]lua.LValue),
	}
}

func getMTCache(L *lua.LState) *mtCache {
	registry, ok := L.Get(lua.RegistryIndex).(*lua.LTable)
	if !ok {
		panic("gopher-luar: corrupt lua registry")
	}
	lCache, ok := registry.RawGetString(cacheKey).(*lua.LUserData)
	if !ok {
		lCache = L.NewUserData()
		lCache.Value = newMTCache()
		registry.RawSetString(cacheKey, lCache)
	}
	cache, ok := lCache.Value.(*mtCache)
	if !ok {
		panic("gopher-luar: corrupt luar metatable cache")
	}
	return cache
}

func addMethods(L *lua.LState, value reflect.Value, tbl *lua.LTable) {
	vtype := value.Type()
	for i := 0; i < vtype.NumMethod(); i++ {
		method := vtype.Method(i)
		if method.PkgPath != "" {
			continue
		}
		fn := New(L, method.Func.Interface())
		tbl.RawSetString(method.Name, fn)
		tbl.RawSetString(getUnexportedName(method.Name), fn)
	}
}

func addFields(L *lua.LState, value reflect.Value, tbl *lua.LTable) {
	type element struct {
		Type  reflect.Type
		Index []int
	}

	queue := list.New()
	vtype := value.Type()
	queue.PushFront(element{
		Type: vtype,
	})

	for queue.Len() > 0 {
		e := queue.Back()
		elem := e.Value.(element)
		vtype := elem.Type
		if vtype.Kind() == reflect.Ptr {
			vtype = vtype.Elem()
		}
	fields:
		for i := 0; i < vtype.NumField(); i++ {
			field := vtype.Field(i)
			if field.PkgPath != "" {
				continue
			}
			var names []string
			tag := field.Tag.Get(tagName)
			if tag == "-" {
				continue
			}
			if tag != "" {
				names = []string{
					tag,
				}
			} else {
				names = []string{
					field.Name,
					getUnexportedName(field.Name),
				}
			}
			for _, key := range names {
				if tbl.RawGetString(key) != lua.LNil {
					continue fields
				}
			}
			index := make([]int, len(elem.Index)+1)
			copy(index, elem.Index)
			index[len(elem.Index)] = i

			ud := L.NewUserData()
			ud.Value = index
			for _, key := range names {
				tbl.RawSetString(key, ud)
			}
			if field.Anonymous {
				index := make([]int, len(elem.Index)+1)
				copy(index, elem.Index)
				index[len(elem.Index)] = i
				queue.PushFront(element{
					Type:  field.Type,
					Index: index,
				})
			}
		}

		queue.Remove(e)
	}
}

func getMetatable(L *lua.LState, value reflect.Value) lua.LValue {
	mu.Lock()
	defer mu.Unlock()

	cache := getMTCache(L)

	vtype := value.Type()
	if v := cache.regular[vtype]; v != nil {
		return v
	}

	mt := L.NewTable()
	mt.RawSetString("__metatable", L.NewTable())

	switch vtype.Kind() {
	case reflect.Array:
		methods := L.NewTable()
		addMethods(L, value, methods)
		mt.RawSetString("methods", methods)

		mt.RawSetString("__index", L.NewFunction(arrayIndex))
		mt.RawSetString("__len", L.NewFunction(arrayLen))
		mt.RawSetString("__eq", L.NewFunction(arrayEq))
	case reflect.Chan:
		methods := L.NewTable()
		methods.RawSetString("send", L.NewFunction(chanSend))
		methods.RawSetString("receive", L.NewFunction(chanReceive))
		methods.RawSetString("close", L.NewFunction(chanClose))
		addMethods(L, value, methods)

		mt.RawSetString("__index", methods)
		mt.RawSetString("__len", L.NewFunction(chanLen))
		mt.RawSetString("__tostring", L.NewFunction(allTostring))
		mt.RawSetString("__eq", L.NewFunction(chanEq))
	case reflect.Map:
		methods := L.NewTable()
		addMethods(L, value, methods)
		mt.RawSetString("methods", methods)

		mt.RawSetString("__index", L.NewFunction(mapIndex))
		mt.RawSetString("__newindex", L.NewFunction(mapNewIndex))
		mt.RawSetString("__len", L.NewFunction(mapLen))
		mt.RawSetString("__call", L.NewFunction(mapCall))
		mt.RawSetString("__tostring", L.NewFunction(allTostring))
		mt.RawSetString("__eq", L.NewFunction(mapEq))
	case reflect.Ptr:
		ptrMethods := L.NewTable()
		addMethods(L, value, ptrMethods)
		mt.RawSetString("ptr_methods", ptrMethods)
		methods := L.NewTable()
		addMethods(L, value.Elem(), methods)
		mt.RawSetString("methods", methods)
		if value.Elem().Kind() == reflect.Struct {
			fields := L.NewTable()
			addFields(L, value.Elem(), fields)
			mt.RawSetString("fields", fields)
		}

		if value.Elem().Kind() == reflect.Array {
			mt.RawSetString("__index", L.NewFunction(arrayIndex))
		} else {
			mt.RawSetString("__index", L.NewFunction(ptrIndex))
		}
		switch value.Elem().Kind() {
		case reflect.Array:
			mt.RawSetString("__newindex", L.NewFunction(arrayNewIndex))
		case reflect.Struct:
			mt.RawSetString("__newindex", L.NewFunction(structNewIndex))
		}
		mt.RawSetString("__pow", L.NewFunction(ptrPow))
		mt.RawSetString("__tostring", L.NewFunction(allTostring))
		mt.RawSetString("__unm", L.NewFunction(ptrUnm))
		if value.Elem().Kind() == reflect.Array {
			mt.RawSetString("__len", L.NewFunction(arrayLen))
		}
		mt.RawSetString("__eq", L.NewFunction(ptrEq))
	case reflect.Slice:
		methods := L.NewTable()
		methods.RawSetString("capacity", L.NewFunction(sliceCapacity))
		methods.RawSetString("append", L.NewFunction(sliceAppend))
		addMethods(L, value, methods)
		mt.RawSetString("methods", methods)

		mt.RawSetString("__index", L.NewFunction(sliceIndex))
		mt.RawSetString("__newindex", L.NewFunction(sliceNewIndex))
		mt.RawSetString("__len", L.NewFunction(sliceLen))
		mt.RawSetString("__tostring", L.NewFunction(allTostring))
		mt.RawSetString("__eq", L.NewFunction(sliceEq))
	case reflect.Struct:
		methods := L.NewTable()
		addMethods(L, value, methods)
		mt.RawSetString("methods", methods)
		fields := L.NewTable()
		addFields(L, value, fields)
		mt.RawSetString("fields", fields)

		mt.RawSetString("__index", L.NewFunction(structIndex))
		mt.RawSetString("__newindex", L.NewFunction(structNewIndex))
		mt.RawSetString("__tostring", L.NewFunction(allTostring))
	}

	cache.regular[vtype] = mt
	return mt
}

func getTypeMetatable(L *lua.LState, t reflect.Type) lua.LValue {
	mu.Lock()
	defer mu.Unlock()

	cache := getMTCache(L)

	if v := cache.types[t]; v != nil {
		return v
	}

	mt := L.NewTable()
	mt.RawSetString("__call", L.NewFunction(typeCall))
	mt.RawSetString("__tostring", L.NewFunction(allTostring))
	mt.RawSetString("__eq", L.NewFunction(typeEq))

	cache.types[t] = mt
	return mt
}
<commit_msg>return concrete types from getMetatable, getTypeMetatable<commit_after>package luar

import (
	"container/list"
	"reflect"
	"sync"

	"github.com/yuin/gopher-lua"
)

const (
	cacheKey = "github.com/layeh/gopher-luar"
	tagName  = "luar"
)

var mu sync.Mutex

type mtCache struct {
	regular, types map[reflect.Type]*lua.LTable
}

func newMTCache() *mtCache {
	return &mtCache{
		regular: make(map[reflect.Type]*lua.LTable),
		types:   make(map[reflect.Type]*lua.LTable),
	}
}

func getMTCache(L *lua.LState) *mtCache {
	registry, ok := L.Get(lua.RegistryIndex).(*lua.LTable)
	if !ok {
		panic("gopher-luar: corrupt lua registry")
	}
	lCache, ok := registry.RawGetString(cacheKey).(*lua.LUserData)
	if !ok {
		lCache = L.NewUserData()
		lCache.Value = newMTCache()
		registry.RawSetString(cacheKey, lCache)
	}
	cache, ok := lCache.Value.(*mtCache)
	if !ok {
		panic("gopher-luar: corrupt luar metatable cache")
	}
	return cache
}

func addMethods(L *lua.LState, value reflect.Value, tbl *lua.LTable) {
	vtype := value.Type()
	for i := 0; i < vtype.NumMethod(); i++ {
		method := vtype.Method(i)
		if method.PkgPath != "" {
			continue
		}
		fn := New(L, method.Func.Interface())
		tbl.RawSetString(method.Name, fn)
		tbl.RawSetString(getUnexportedName(method.Name), fn)
	}
}

func addFields(L *lua.LState, value reflect.Value, tbl *lua.LTable) {
	type element struct {
		Type  reflect.Type
		Index []int
	}

	queue := list.New()
	vtype := value.Type()
	queue.PushFront(element{
		Type: vtype,
	})

	for queue.Len() > 0 {
		e := queue.Back()
		elem := e.Value.(element)
		vtype := elem.Type
		if vtype.Kind() == reflect.Ptr {
			vtype = vtype.Elem()
		}
	fields:
		for i := 0; i < vtype.NumField(); i++ {
			field := vtype.Field(i)
			if field.PkgPath != "" {
				continue
			}
			var names []string
			tag := field.Tag.Get(tagName)
			if tag == "-" {
				continue
			}
			if tag != "" {
				names = []string{
					tag,
				}
			} else {
				names = []string{
					field.Name,
					getUnexportedName(field.Name),
				}
			}
			for _, key := range names {
				if tbl.RawGetString(key) != lua.LNil {
					continue fields
				}
			}
			index := make([]int, len(elem.Index)+1)
			copy(index, elem.Index)
			index[len(elem.Index)] = i

			ud := L.NewUserData()
			ud.Value = index
			for _, key := range names {
				tbl.RawSetString(key, ud)
			}
			if field.Anonymous {
				index := make([]int, len(elem.Index)+1)
				copy(index, elem.Index)
				index[len(elem.Index)] = i
				queue.PushFront(element{
					Type:  field.Type,
					Index: index,
				})
			}
		}

		queue.Remove(e)
	}
}

func getMetatable(L *lua.LState, value reflect.Value) *lua.LTable {
	mu.Lock()
	defer mu.Unlock()

	cache := getMTCache(L)

	vtype := value.Type()
	if v := cache.regular[vtype]; v != nil {
		return v
	}

	mt := L.NewTable()
	mt.RawSetString("__metatable", L.NewTable())

	switch vtype.Kind() {
	case reflect.Array:
		methods := L.NewTable()
		addMethods(L, value, methods)
		mt.RawSetString("methods", methods)

		mt.RawSetString("__index", L.NewFunction(arrayIndex))
		mt.RawSetString("__len", L.NewFunction(arrayLen))
		mt.RawSetString("__eq", L.NewFunction(arrayEq))
	case reflect.Chan:
		methods := L.NewTable()
		methods.RawSetString("send", L.NewFunction(chanSend))
		methods.RawSetString("receive", L.NewFunction(chanReceive))
		methods.RawSetString("close", L.NewFunction(chanClose))
		addMethods(L, value, methods)

		mt.RawSetString("__index", methods)
		mt.RawSetString("__len", L.NewFunction(chanLen))
		mt.RawSetString("__tostring", L.NewFunction(allTostring))
		mt.RawSetString("__eq", L.NewFunction(chanEq))
	case reflect.Map:
		methods := L.NewTable()
		addMethods(L, value, methods)
		mt.RawSetString("methods", methods)

		mt.RawSetString("__index", L.NewFunction(mapIndex))
		mt.RawSetString("__newindex", L.NewFunction(mapNewIndex))
		mt.RawSetString("__len", L.NewFunction(mapLen))
		mt.RawSetString("__call", L.NewFunction(mapCall))
		mt.RawSetString("__tostring", L.NewFunction(allTostring))
		mt.RawSetString("__eq", L.NewFunction(mapEq))
	case reflect.Ptr:
		ptrMethods := L.NewTable()
		addMethods(L, value, ptrMethods)
		mt.RawSetString("ptr_methods", ptrMethods)
		methods := L.NewTable()
		addMethods(L, value.Elem(), methods)
		mt.RawSetString("methods", methods)
		if value.Elem().Kind() == reflect.Struct {
			fields := L.NewTable()
			addFields(L, value.Elem(), fields)
			mt.RawSetString("fields", fields)
		}

		if value.Elem().Kind() == reflect.Array {
			mt.RawSetString("__index", L.NewFunction(arrayIndex))
		} else {
			mt.RawSetString("__index", L.NewFunction(ptrIndex))
		}
		switch value.Elem().Kind() {
		case reflect.Array:
			mt.RawSetString("__newindex", L.NewFunction(arrayNewIndex))
		case reflect.Struct:
			mt.RawSetString("__newindex", L.NewFunction(structNewIndex))
		}
		mt.RawSetString("__pow", L.NewFunction(ptrPow))
		mt.RawSetString("__tostring", L.NewFunction(allTostring))
		mt.RawSetString("__unm", L.NewFunction(ptrUnm))
		if value.Elem().Kind() == reflect.Array {
			mt.RawSetString("__len", L.NewFunction(arrayLen))
		}
		mt.RawSetString("__eq", L.NewFunction(ptrEq))
	case reflect.Slice:
		methods := L.NewTable()
		methods.RawSetString("capacity", L.NewFunction(sliceCapacity))
		methods.RawSetString("append", L.NewFunction(sliceAppend))
		addMethods(L, value, methods)
		mt.RawSetString("methods", methods)

		mt.RawSetString("__index", L.NewFunction(sliceIndex))
		mt.RawSetString("__newindex", L.NewFunction(sliceNewIndex))
		mt.RawSetString("__len", L.NewFunction(sliceLen))
		mt.RawSetString("__tostring", L.NewFunction(allTostring))
		mt.RawSetString("__eq", L.NewFunction(sliceEq))
	case reflect.Struct:
		methods := L.NewTable()
		addMethods(L, value, methods)
		mt.RawSetString("methods", methods)
		fields := L.NewTable()
		addFields(L, value, fields)
		mt.RawSetString("fields", fields)

		mt.RawSetString("__index", L.NewFunction(structIndex))
		mt.RawSetString("__newindex", L.NewFunction(structNewIndex))
		mt.RawSetString("__tostring", L.NewFunction(allTostring))
	}

	cache.regular[vtype] = mt
	return mt
}

func getTypeMetatable(L *lua.LState, t reflect.Type) *lua.LTable {
	mu.Lock()
	defer mu.Unlock()

	cache := getMTCache(L)

	if v := cache.types[t]; v != nil {
		return v
	}

	mt := L.NewTable()
	mt.RawSetString("__call", L.NewFunction(typeCall))
	mt.RawSetString("__tostring", L.NewFunction(allTostring))
	mt.RawSetString("__eq", L.NewFunction(typeEq))

	cache.types[t] = mt
	return mt
}
<|endoftext|>


<commit_before>// Simple caching library with expiration capabilities
package cache2go

import (
	"errors"
	"log"
	"sync"
	"time"
)

// Structure of an item in the cache
// data contains the user-set value in the cache
type CacheItem struct {
	sync.RWMutex
	key      interface{}
	data     interface{}
	lifeSpan time.Duration

	createdOn   time.Time
	accessedOn  time.Time
	accessCount int64

	// Callback method triggered right before removing the item from the cache
	aboutToExpire func(interface{})
}

// Structure of a table with items in the cache
type CacheTable struct {
	sync.RWMutex
	name            string
	items           map[interface{}]*CacheItem
	cleanupTimer    *time.Timer
	cleanupInterval time.Duration

	logger *log.Logger

	// Callback method triggered when trying to load a non-existing key
	loadData func(interface{}) *CacheItem

	// Callback method triggered when adding a new item to the cache
	addedItem func(*CacheItem)
	// Callback method triggered before deleting an item from the cache
	aboutToDeleteItem func(*CacheItem)
}

var (
	cache = make(map[string]*CacheTable)
	mutex sync.RWMutex
)

// Returns a newly created CacheItem
func CreateCacheItem(key interface{}, lifeSpan time.Duration, data interface{}) CacheItem {
	t := time.Now()
	return CacheItem{
		key:           key,
		lifeSpan:      lifeSpan,
		createdOn:     t,
		accessedOn:    t,
		accessCount:   0,
		aboutToExpire: nil,
		data:          data,
	}
}

// Mark item to be kept for another expireDuration period
func (item *CacheItem) KeepAlive() {
	item.Lock()
	defer item.Unlock()
	item.accessedOn = time.Now()
	item.accessCount++
}

// Returns this item's expiration duration
func (item *CacheItem) LifeSpan() time.Duration {
	// immutable
	return item.lifeSpan
}

// Returns when this item was last accessed
func (item *CacheItem) AccessedOn() time.Time {
	item.RLock()
	defer item.RUnlock()
	return item.accessedOn
}

// Returns when this item was added to the cache
func (item *CacheItem) CreatedOn() time.Time {
	// immutable
	return item.createdOn
}

// Returns how often this item has been accessed
func (item *CacheItem) AccessCount() int64 {
	item.RLock()
	defer item.RUnlock()
	return item.accessCount
}

// Returns the key of this cached item
func (item *CacheItem) Key() interface{} {
	// immutable
	return item.key
}

// Returns the value of this cached item
func (item *CacheItem) Data() interface{} {
	// immutable
	return item.data
}

// Configures a callback, which will be called right before the item
// is about to be removed from the cache
func (item *CacheItem) SetAboutToExpireCallback(f func(interface{})) {
	item.Lock()
	defer item.Unlock()
	item.aboutToExpire = f
}

// Returns the existing cache table with given name or creates a new one
// if the table does not exist yet
func Cache(table string) *CacheTable {
	mutex.RLock()
	t, ok := cache[table]
	mutex.RUnlock()

	if !ok {
		t = &CacheTable{
			name:  table,
			items: make(map[interface{}]*CacheItem),
		}

		mutex.Lock()
		cache[table] = t
		mutex.Unlock()
	}

	return t
}

// Returns how many items are currently stored in the cache
func (table *CacheTable) Count() int {
	table.RLock()
	defer table.RUnlock()

	return len(table.items)
}

// Configures a data-loader callback, which will be called when trying
// to use access a non-existing key
func (table *CacheTable) SetDataLoader(f func(interface{}) *CacheItem) {
	table.Lock()
	defer table.Unlock()
	table.loadData = f
}

// Configures a callback, which will be called every time a new item
// is added to the cache
func (table *CacheTable) SetAddedItemCallback(f func(*CacheItem)) {
	table.Lock()
	defer table.Unlock()
	table.addedItem = f
}

// Configures a callback, which will be called every time an item
// is about to be removed from the cache
func (table *CacheTable) SetAboutToDeleteItemCallback(f func(*CacheItem)) {
	table.Lock()
	defer table.Unlock()
	table.aboutToDeleteItem = f
}

// Sets the logger to be used by this cache table
func (table *CacheTable) SetLogger(logger *log.Logger) {
	table.Lock()
	defer table.Unlock()
	table.logger = logger
}

// Expiration check loop, triggered by a self-adjusting timer
func (table *CacheTable) expirationCheck() {
	table.Lock()
	if table.cleanupInterval > 0 {
		table.log("Expiration check triggered after", table.cleanupInterval, "for table", table.name)
	} else {
		table.log("Expiration check installed for table", table.name)
	}
	if table.cleanupTimer != nil {
		table.cleanupTimer.Stop()
	}

	// Take a copy of cache so we can iterate over it without blocking the mutex
	cc := table.items
	table.Unlock()

	// To be more accurate with timers, we would need to update 'now' on every
	// loop iteration. Not sure it's really efficient though.
	now := time.Now()
	smallestDuration := 0 * time.Second
	for key, c := range cc {
		c.RLock()
		defer c.RUnlock()

		if c.lifeSpan == 0 {
			continue
		}
		if now.Sub(c.accessedOn) >= c.lifeSpan {
			table.Delete(key)
		} else {
			if smallestDuration == 0 || c.lifeSpan < smallestDuration {
				smallestDuration = c.lifeSpan - now.Sub(c.accessedOn)
			}
		}
	}

	table.Lock()
	table.cleanupInterval = smallestDuration
	if smallestDuration > 0 {
		table.cleanupTimer = time.AfterFunc(smallestDuration, func() {
			go table.expirationCheck()
		})
	}
	table.Unlock()
}

/* Adds a key/value pair to the cache
/ key is a unique cache-item key in the cache
/ lifeSpan indicates how long this item will remain in the cache after its
/ last access
/ data is the cache-item value
*/
func (table *CacheTable) Cache(key interface{}, lifeSpan time.Duration, data interface{}) *CacheItem {
	item := CreateCacheItem(key, lifeSpan, data)

	table.Lock()
	table.log("Adding item with key", key, "and lifespan of", lifeSpan, "to table", table.name)
	table.items[key] = &item
	expDur := table.cleanupInterval
	table.Unlock()

	// Trigger callback after adding an item to cache
	if table.addedItem != nil {
		table.addedItem(&item)
	}

	// If we haven't set up any expiration check timer or found a more imminent item
	if lifeSpan > 0 && (expDur == 0 || lifeSpan < expDur) {
		table.expirationCheck()
	}

	return &item
}

// Delete an item from the cache
func (table *CacheTable) Delete(key interface{}) (*CacheItem, error) {
	table.RLock()
	r, ok := table.items[key]

	if !ok {
		table.RUnlock()
		return nil, errors.New("Key not found in cache")
	}

	// Trigger callbacks before deleting an item from cache
	if table.aboutToDeleteItem != nil {
		table.aboutToDeleteItem(r)
	}
	table.RUnlock()
	r.RLock()
	defer r.RUnlock()
	if r.aboutToExpire != nil {
		r.aboutToExpire(key)
	}

	table.Lock()
	defer table.Unlock()

	table.log("Deleting item with key", key, "created on", r.createdOn, "and hit", r.accessCount, "times from table", table.name)
	delete(table.items, key)
	return r, nil
}

// Test whether an item exists in the cache. Unlike the Value method
// Exists never tries to fetch data via the loadData callback
func (table *CacheTable) Exists(key interface{}) bool {
	table.RLock()
	defer table.RUnlock()
	_, ok := table.items[key]

	return ok
}

// Get an item from the cache and mark it to be kept alive
func (table *CacheTable) Value(key interface{}) (*CacheItem, error) {
	table.RLock()
	r, ok := table.items[key]
	table.RUnlock()

	if ok {
		r.KeepAlive()
		return r, nil
	}

	if table.loadData != nil {
		item := table.loadData(key)
		table.Cache(key, item.lifeSpan, item.data)
		if item != nil {
			return item, nil
		}

		return nil, errors.New("Key not found and could not be loaded into cache")
	}

	return nil, errors.New("Key not found in cache")
}

// Delete all items from cache
func (table *CacheTable) Flush() {
	table.Lock()
	defer table.Unlock()

	table.log("Flushing table", table.name)

	table.items = make(map[interface{}]*CacheItem)
	table.cleanupInterval = 0
	if table.cleanupTimer != nil {
		table.cleanupTimer.Stop()
	}
}

// Get an item from the cache and mark it to be kept alive
func (table *CacheTable) log(v ...interface{}) {
	if table.logger == nil {
		return
	}

	table.logger.Println(v)
}
<commit_msg>* Added copyright header to source.<commit_after>/* Simple caching library with expiration capabilities
       Copyright (c) 2012, Radu Ioan Fericean
                     2013, Christian Muehlhaeuser <muesli@gmail.com>

    For license see LICENSE.txt
 */

package cache2go

import (
	"errors"
	"log"
	"sync"
	"time"
)

// Structure of an item in the cache
// data contains the user-set value in the cache
type CacheItem struct {
	sync.RWMutex
	key      interface{}
	data     interface{}
	lifeSpan time.Duration

	createdOn   time.Time
	accessedOn  time.Time
	accessCount int64

	// Callback method triggered right before removing the item from the cache
	aboutToExpire func(interface{})
}

// Structure of a table with items in the cache
type CacheTable struct {
	sync.RWMutex
	name            string
	items           map[interface{}]*CacheItem
	cleanupTimer    *time.Timer
	cleanupInterval time.Duration

	logger *log.Logger

	// Callback method triggered when trying to load a non-existing key
	loadData func(interface{}) *CacheItem

	// Callback method triggered when adding a new item to the cache
	addedItem func(*CacheItem)
	// Callback method triggered before deleting an item from the cache
	aboutToDeleteItem func(*CacheItem)
}

var (
	cache = make(map[string]*CacheTable)
	mutex sync.RWMutex
)

// Returns a newly created CacheItem
func CreateCacheItem(key interface{}, lifeSpan time.Duration, data interface{}) CacheItem {
	t := time.Now()
	return CacheItem{
		key:           key,
		lifeSpan:      lifeSpan,
		createdOn:     t,
		accessedOn:    t,
		accessCount:   0,
		aboutToExpire: nil,
		data:          data,
	}
}

// Mark item to be kept for another expireDuration period
func (item *CacheItem) KeepAlive() {
	item.Lock()
	defer item.Unlock()
	item.accessedOn = time.Now()
	item.accessCount++
}

// Returns this item's expiration duration
func (item *CacheItem) LifeSpan() time.Duration {
	// immutable
	return item.lifeSpan
}

// Returns when this item was last accessed
func (item *CacheItem) AccessedOn() time.Time {
	item.RLock()
	defer item.RUnlock()
	return item.accessedOn
}

// Returns when this item was added to the cache
func (item *CacheItem) CreatedOn() time.Time {
	// immutable
	return item.createdOn
}

// Returns how often this item has been accessed
func (item *CacheItem) AccessCount() int64 {
	item.RLock()
	defer item.RUnlock()
	return item.accessCount
}

// Returns the key of this cached item
func (item *CacheItem) Key() interface{} {
	// immutable
	return item.key
}

// Returns the value of this cached item
func (item *CacheItem) Data() interface{} {
	// immutable
	return item.data
}

// Configures a callback, which will be called right before the item
// is about to be removed from the cache
func (item *CacheItem) SetAboutToExpireCallback(f func(interface{})) {
	item.Lock()
	defer item.Unlock()
	item.aboutToExpire = f
}

// Returns the existing cache table with given name or creates a new one
// if the table does not exist yet
func Cache(table string) *CacheTable {
	mutex.RLock()
	t, ok := cache[table]
	mutex.RUnlock()

	if !ok {
		t = &CacheTable{
			name:  table,
			items: make(map[interface{}]*CacheItem),
		}

		mutex.Lock()
		cache[table] = t
		mutex.Unlock()
	}

	return t
}

// Returns how many items are currently stored in the cache
func (table *CacheTable) Count() int {
	table.RLock()
	defer table.RUnlock()

	return len(table.items)
}

// Configures a data-loader callback, which will be called when trying
// to use access a non-existing key
func (table *CacheTable) SetDataLoader(f func(interface{}) *CacheItem) {
	table.Lock()
	defer table.Unlock()
	table.loadData = f
}

// Configures a callback, which will be called every time a new item
// is added to the cache
func (table *CacheTable) SetAddedItemCallback(f func(*CacheItem)) {
	table.Lock()
	defer table.Unlock()
	table.addedItem = f
}

// Configures a callback, which will be called every time an item
// is about to be removed from the cache
func (table *CacheTable) SetAboutToDeleteItemCallback(f func(*CacheItem)) {
	table.Lock()
	defer table.Unlock()
	table.aboutToDeleteItem = f
}

// Sets the logger to be used by this cache table
func (table *CacheTable) SetLogger(logger *log.Logger) {
	table.Lock()
	defer table.Unlock()
	table.logger = logger
}

// Expiration check loop, triggered by a self-adjusting timer
func (table *CacheTable) expirationCheck() {
	table.Lock()
	if table.cleanupInterval > 0 {
		table.log("Expiration check triggered after", table.cleanupInterval, "for table", table.name)
	} else {
		table.log("Expiration check installed for table", table.name)
	}
	if table.cleanupTimer != nil {
		table.cleanupTimer.Stop()
	}

	// Take a copy of cache so we can iterate over it without blocking the mutex
	cc := table.items
	table.Unlock()

	// To be more accurate with timers, we would need to update 'now' on every
	// loop iteration. Not sure it's really efficient though.
	now := time.Now()
	smallestDuration := 0 * time.Second
	for key, c := range cc {
		c.RLock()
		defer c.RUnlock()

		if c.lifeSpan == 0 {
			continue
		}
		if now.Sub(c.accessedOn) >= c.lifeSpan {
			table.Delete(key)
		} else {
			if smallestDuration == 0 || c.lifeSpan < smallestDuration {
				smallestDuration = c.lifeSpan - now.Sub(c.accessedOn)
			}
		}
	}

	table.Lock()
	table.cleanupInterval = smallestDuration
	if smallestDuration > 0 {
		table.cleanupTimer = time.AfterFunc(smallestDuration, func() {
			go table.expirationCheck()
		})
	}
	table.Unlock()
}

/* Adds a key/value pair to the cache
   key is a unique item-key in the cache
   lifeSpan indicates how long this item will remain in cache after its last access
   data is the item's value
 */
func (table *CacheTable) Cache(key interface{}, lifeSpan time.Duration, data interface{}) *CacheItem {
	item := CreateCacheItem(key, lifeSpan, data)

	table.Lock()
	table.log("Adding item with key", key, "and lifespan of", lifeSpan, "to table", table.name)
	table.items[key] = &item
	expDur := table.cleanupInterval
	table.Unlock()

	// Trigger callback after adding an item to cache
	if table.addedItem != nil {
		table.addedItem(&item)
	}

	// If we haven't set up any expiration check timer or found a more imminent item
	if lifeSpan > 0 && (expDur == 0 || lifeSpan < expDur) {
		table.expirationCheck()
	}

	return &item
}

// Delete an item from the cache
func (table *CacheTable) Delete(key interface{}) (*CacheItem, error) {
	table.RLock()
	r, ok := table.items[key]

	if !ok {
		table.RUnlock()
		return nil, errors.New("Key not found in cache")
	}

	// Trigger callbacks before deleting an item from cache
	if table.aboutToDeleteItem != nil {
		table.aboutToDeleteItem(r)
	}
	table.RUnlock()
	r.RLock()
	defer r.RUnlock()
	if r.aboutToExpire != nil {
		r.aboutToExpire(key)
	}

	table.Lock()
	defer table.Unlock()

	table.log("Deleting item with key", key, "created on", r.createdOn, "and hit", r.accessCount, "times from table", table.name)
	delete(table.items, key)
	return r, nil
}

// Test whether an item exists in the cache. Unlike the Value method
// Exists never tries to fetch data via the loadData callback
func (table *CacheTable) Exists(key interface{}) bool {
	table.RLock()
	defer table.RUnlock()
	_, ok := table.items[key]

	return ok
}

// Get an item from the cache and mark it to be kept alive
func (table *CacheTable) Value(key interface{}) (*CacheItem, error) {
	table.RLock()
	r, ok := table.items[key]
	table.RUnlock()

	if ok {
		r.KeepAlive()
		return r, nil
	}

	if table.loadData != nil {
		item := table.loadData(key)
		table.Cache(key, item.lifeSpan, item.data)
		if item != nil {
			return item, nil
		}

		return nil, errors.New("Key not found and could not be loaded into cache")
	}

	return nil, errors.New("Key not found in cache")
}

// Delete all items from cache
func (table *CacheTable) Flush() {
	table.Lock()
	defer table.Unlock()

	table.log("Flushing table", table.name)

	table.items = make(map[interface{}]*CacheItem)
	table.cleanupInterval = 0
	if table.cleanupTimer != nil {
		table.cleanupTimer.Stop()
	}
}

// Get an item from the cache and mark it to be kept alive
func (table *CacheTable) log(v ...interface{}) {
	if table.logger == nil {
		return
	}

	table.logger.Println(v)
}
<|endoftext|>


<commit_before>package cache

import (
	"net/http"
	"net/http/httptest"
	"reflect"
	"strings"
	"time"

	"github.com/mholt/caddy/caddyhttp/httpserver"
	"github.com/nicolasazrak/caddy-cache/storage"
	"github.com/pquerna/cachecontrol"
)

type CachedRequest struct {
	HeaderMap http.Header // Headers are the only useful information
}

type CachedResponse struct {
	Code      int // the HTTP response code from WriteHeader
	Body      []byte
	HeaderMap http.Header // the HTTP response headers
}

type CacheEntry struct {
	Request  *CachedRequest
	Response *CachedResponse
}

type CacheHandler struct {
	Config *Config
	Client storage.Storage
	Next   httpserver.Handler
}

func respond(response *CachedResponse, w http.ResponseWriter) {
	for k, values := range response.HeaderMap {
		for _, v := range values {
			w.Header().Add(k, v)
		}
	}
	w.WriteHeader(response.Code)
	w.Write(response.Body)
}

func shouldUseCache(req *http.Request) bool {
	// TODO Add more logic like get params, ?nocache=true

	if req.Method != "GET" && req.Method != "HEAD" {
		// Only cache Get and head request
		return false
	}

	// Range responses still not supported
	if req.Header.Get("accept-ranges") != "" {
		return false
	}

	return true
}

func getCacheableStatus(req *http.Request, res *httptest.ResponseRecorder, config *Config) (bool, time.Time, error) {
	reasonsNotToCache, expiration, err := cachecontrol.CachableResponse(req, res.Result(), cachecontrol.Options{})

	if err != nil {
		return false, time.Now(), err
	}

	canBeStored := len(reasonsNotToCache) == 0

	if !canBeStored {
		return false, time.Now(), nil
	}

	varyHeaders, ok := res.HeaderMap["Vary"]
	if ok && varyHeaders[0] == "*" {
		return false, time.Now(), nil
	}

	hasExplicitExpiration := expiration.After(time.Now().UTC())

	if expiration.Before(time.Now().UTC().Add(time.Duration(1) * time.Second)) {
		// If expiration is not specified or is before now use default MaxAge
		expiration = time.Now().UTC().Add(config.DefaultMaxAge)
	}

	anyCacheRulesMatches := false
	for _, rule := range config.CacheRules {
		if rule.matches(req, res) {
			anyCacheRulesMatches = true
			break
		}
	}

	if err != nil {
		return false, time.Now(), err
	}

	return anyCacheRulesMatches || hasExplicitExpiration, expiration, nil
}

func getKey(r *http.Request) string {
	key := r.Method + " " + r.Host + r.URL.Path

	q := r.URL.Query().Encode()
	if len(q) > 0 {
		key += "?" + q
	}

	return key
}

func (h CacheHandler) chooseIfVary(r *http.Request) func(storage.Value) bool {
	return func(value storage.Value) bool {
		entry := value.(*CacheEntry)
		vary, hasVary := entry.Response.HeaderMap["Vary"]
		if !hasVary {
			return true
		}

		for _, searchedHeader := range strings.Split(vary[0], ",") {
			searchedHeader = strings.TrimSpace(searchedHeader)
			if !reflect.DeepEqual(entry.Request.HeaderMap[searchedHeader], r.Header[searchedHeader]) {
				return false
			}
		}
		return true
	}
}

func (h CacheHandler) AddStatusHeader(w http.ResponseWriter, status string) {
	if h.Config.StatusHeader != "" {
		w.Header().Add(h.Config.StatusHeader, status)
	}
}

func (h CacheHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	if !shouldUseCache(r) {
		code, err := h.Next.ServeHTTP(w, r)
		h.AddStatusHeader(w, "skip")
		return code, err
	}

	value, err := h.Client.Get(getKey(r), h.chooseIfVary(r))
	if err != nil {
		return http.StatusInternalServerError, err
	}

	if value == nil {
		rec := httptest.NewRecorder()
		_, err := h.Next.ServeHTTP(rec, r)

		response := &CacheEntry{
			Request: &CachedRequest{
				HeaderMap: r.Header,
			},
			Response: &CachedResponse{
				Body:      rec.Body.Bytes(),
				HeaderMap: rec.HeaderMap,
				Code:      rec.Code,
			},
		}

		isCacheable, expirationTime, err := getCacheableStatus(r, rec, h.Config)

		if err != nil {
			return 500, err
		}

		if isCacheable {
			err = h.Client.Push(getKey(r), response, expirationTime)
			if err != nil {
				return http.StatusInternalServerError, err
			}
		}

		h.AddStatusHeader(w, "miss")
		respond(response.Response, w)
		return response.Response.Code, err
	} else {
		cached := value.(*CacheEntry)
		respond(cached.Response, w)
		h.AddStatusHeader(w, "hit")
		return cached.Response.Code, nil
	}
}
<commit_msg>Fix cache status header<commit_after>package cache

import (
	"net/http"
	"net/http/httptest"
	"reflect"
	"strings"
	"time"

	"github.com/mholt/caddy/caddyhttp/httpserver"
	"github.com/nicolasazrak/caddy-cache/storage"
	"github.com/pquerna/cachecontrol"
)

type CachedRequest struct {
	HeaderMap http.Header // Headers are the only useful information
}

type CachedResponse struct {
	Code      int // the HTTP response code from WriteHeader
	Body      []byte
	HeaderMap http.Header // the HTTP response headers
}

type CacheEntry struct {
	Request  *CachedRequest
	Response *CachedResponse
}

type CacheHandler struct {
	Config *Config
	Client storage.Storage
	Next   httpserver.Handler
}

func respond(response *CachedResponse, w http.ResponseWriter) {
	for k, values := range response.HeaderMap {
		for _, v := range values {
			w.Header().Add(k, v)
		}
	}
	w.WriteHeader(response.Code)
	w.Write(response.Body)
}

func shouldUseCache(req *http.Request) bool {
	// TODO Add more logic like get params, ?nocache=true

	if req.Method != "GET" && req.Method != "HEAD" {
		// Only cache Get and head request
		return false
	}

	// Range responses still not supported
	if req.Header.Get("accept-ranges") != "" {
		return false
	}

	return true
}

func getCacheableStatus(req *http.Request, res *httptest.ResponseRecorder, config *Config) (bool, time.Time, error) {
	reasonsNotToCache, expiration, err := cachecontrol.CachableResponse(req, res.Result(), cachecontrol.Options{})

	if err != nil {
		return false, time.Now(), err
	}

	canBeStored := len(reasonsNotToCache) == 0

	if !canBeStored {
		return false, time.Now(), nil
	}

	varyHeaders, ok := res.HeaderMap["Vary"]
	if ok && varyHeaders[0] == "*" {
		return false, time.Now(), nil
	}

	hasExplicitExpiration := expiration.After(time.Now().UTC())

	if expiration.Before(time.Now().UTC().Add(time.Duration(1) * time.Second)) {
		// If expiration is not specified or is before now use default MaxAge
		expiration = time.Now().UTC().Add(config.DefaultMaxAge)
	}

	anyCacheRulesMatches := false
	for _, rule := range config.CacheRules {
		if rule.matches(req, res) {
			anyCacheRulesMatches = true
			break
		}
	}

	if err != nil {
		return false, time.Now(), err
	}

	return anyCacheRulesMatches || hasExplicitExpiration, expiration, nil
}

func getKey(r *http.Request) string {
	key := r.Method + " " + r.Host + r.URL.Path

	q := r.URL.Query().Encode()
	if len(q) > 0 {
		key += "?" + q
	}

	return key
}

func (h CacheHandler) chooseIfVary(r *http.Request) func(storage.Value) bool {
	return func(value storage.Value) bool {
		entry := value.(*CacheEntry)
		vary, hasVary := entry.Response.HeaderMap["Vary"]
		if !hasVary {
			return true
		}

		for _, searchedHeader := range strings.Split(vary[0], ",") {
			searchedHeader = strings.TrimSpace(searchedHeader)
			if !reflect.DeepEqual(entry.Request.HeaderMap[searchedHeader], r.Header[searchedHeader]) {
				return false
			}
		}
		return true
	}
}

func (h CacheHandler) AddStatusHeader(w http.ResponseWriter, status string) {
	if h.Config.StatusHeader != "" {
		w.Header().Add(h.Config.StatusHeader, status)
	}
}

func (h CacheHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	if !shouldUseCache(r) {
		h.AddStatusHeader(w, "skip")
		code, err := h.Next.ServeHTTP(w, r)
		return code, err
	}

	value, err := h.Client.Get(getKey(r), h.chooseIfVary(r))
	if err != nil {
		return http.StatusInternalServerError, err
	}

	if value == nil {
		rec := httptest.NewRecorder()
		_, err := h.Next.ServeHTTP(rec, r)

		response := &CacheEntry{
			Request: &CachedRequest{
				HeaderMap: r.Header,
			},
			Response: &CachedResponse{
				Body:      rec.Body.Bytes(),
				HeaderMap: rec.HeaderMap,
				Code:      rec.Code,
			},
		}

		isCacheable, expirationTime, err := getCacheableStatus(r, rec, h.Config)

		if err != nil {
			return 500, err
		}

		if isCacheable {
			err = h.Client.Push(getKey(r), response, expirationTime)
			if err != nil {
				return http.StatusInternalServerError, err
			}
		}

		h.AddStatusHeader(w, "miss")
		respond(response.Response, w)
		return response.Response.Code, err
	} else {
		cached := value.(*CacheEntry)
		h.AddStatusHeader(w, "hit")
		respond(cached.Response, w)
		return cached.Response.Code, nil
	}
}
<|endoftext|>


<commit_before>package dex

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	. "polydawn.net/pogo/gosh"
	. "polydawn.net/docket/crocker"
	"polydawn.net/docket/util"
	"strings"
)

type Graph struct {
	/*
		Absolute path to the base/working dir for of the graph git repository.
	*/
	dir string

	/*
		Cached command template for exec'ing git with this graph's cwd.
	*/
	cmd Command
}

/*
	Loads a Graph if there is a git repo initialized at the given dir; returns nil if a graph repo not found.
	The dir must be the root of the working tree of the git dir.

	A graph git repo is distingushed by containing branches that start with "docket/" -- this is how docket outputs branches that contain its data.
*/
func LoadGraph(dir string) *Graph {
	// optimistically, set up the struct we're checking out
	g := newGraph(dir)

	// ask git what it thinks of all this.
	if g.isDocketGraphRepo() {
		return g
	} else {
		return nil
	}
}

/*
	Attempts to load a Graph at the given dir, or creates a new one if no graph repo is found.
	If a new graph is fabricated, it will be initialized by:
	 - creating a new git repo,
	 - making a blank root commit,
	 - and tagging it with a branch name that declares it to be a graph repo.

	Note if your cwd is already in a git repo, the new graph will not be commited, nor will it be made a submodule.
	You're free to make it a submodule yourself, but git quite wants you to have a remote url before it accepts your submodule.
*/
func NewGraph(dir string) *Graph {
	g := newGraph(dir)
	if g.isDocketGraphRepo() {
		// if we can just be a load, do it
		return g
	} else if g.isRepoRoot() {
		// if this is a repo root, but didn't look like a real graph...
		util.ExitGently("Attempted to make a docket graph at ", g.dir, ", but there is already a git repo there and it does not appear to belong to docket.")
	} // else carry on, make it!

	// we'll make exactly one new dir if the path doesn't exist yet.  more is probably argument error and we abort.
	// this is actually implemented via MkdirAll here (because Mkdir errors on existing, and I can't be arsed) and letting the SaneDir check earlier blow up if we're way out.
	err := os.MkdirAll(g.dir, 0755)
	if err != nil { panic(err); }

	// git init
	g.cmd("init")("--bare")()

	g.withTempTree(func (cmd Command) {
		// set up basic repo to identify as graph repo
		cmd("commit", "--allow-empty", "-mdocket")()
		cmd("checkout", "-b", "docket/init")()

		// discard master branch.  a docket graph has no real use for it.
		cmd("branch", "-D", "master")()
	})

	// should be good to go
	return g
}

func newGraph(dir string) *Graph {
	dir = util.SanePath(dir)

	// optimistically, set up the struct.
	// we still need to either verify or initalize git here.
	return &Graph{
		dir: dir,
		cmd: Sh("git")(DefaultIO)(Opts{Cwd: dir}),
	}
}

func (g *Graph) isRepoRoot() (v bool) {
	defer func() {
		// if the path doesn't even exist, launching the command will panic, and that's fine.
		// if the path isn't within a git repo at all, it will exit with 128, gosh will panic, and that's fine.
		if recover() != nil {
			v = false
		}
	}()
	revp := g.cmd(NullIO)("rev-parse", "--is-bare-repository").Output()
	v = (revp == "true\n")
	return
}

/*
	Creates a temporary working tree in a new directory.  Changes the cwd to that location.
	The directory will be empty.  The directory will be removed when your function returns.
*/
func (g *Graph) withTempTree(fn func(cmd Command)) {
	// ensure zone for temp trees is established
	tmpTreeBase := filepath.Join(g.dir, "worktrees")
	err := os.MkdirAll(tmpTreeBase, 0755)
	if err != nil { panic(err); }

	// make temp dir for tree
	tmpdir, err := ioutil.TempDir(tmpTreeBase, "tree.")
	if err != nil { panic(err); }
	defer os.RemoveAll(tmpdir)

	// set cwd
	retreat, err := os.Getwd()
	if err != nil { panic(err); }
	defer os.Chdir(retreat)
	err = os.Chdir(tmpdir)
	if err != nil { panic(err); }

	// construct git command template that knows what's up
	gt := g.cmd(
		Opts{
			Cwd:tmpdir,
		},
		Env{
			"GIT_WORK_TREE": tmpdir,
			"GIT_DIR": g.dir,
		},
	)

	// go time
	fn(gt)
}

func (g *Graph) Publish(lineage string, ancestor string, gr GraphStoreRequest) (hash string) {
	// Handle tags - currently, we discard them when dealing with a graph repo.
	lineage, _  = SplitImageName(lineage)
	ancestor, _ = SplitImageName(ancestor)

	g.withTempTree(func(cmd Command) {
		fmt.Println("starting publish", lineage, "<--", ancestor)

		// check if appropriate branches already exist, and make them if necesary
		if strings.Count(g.cmd("branch", "--list", lineage).Output(), "\n") >= 1 {
			fmt.Println("linage already existed")
			// this is an existing lineage
			g.cmd("checkout", "-f", lineage)()
			g.cmd("rm", "-r", ".")()
		} else {
			// this is a new lineage
			if ancestor == "" {
				fmt.Println("new linage!  making orphan branch for it")
				g.cmd("checkout", "--orphan", lineage)()	//TODO: docket/image/
			} else {
				fmt.Println("new linage!  forking it from ancestor branch.")
				g.cmd("branch", lineage, ancestor)()
				g.cmd("checkout", "-f", lineage)()
				g.cmd("rm", "-r", ".")()
			}
		}

		// apply the GraphStoreRequest to unpack the fs (read from fs.tarReader, essentially)
		gr.place(".")	//TODO: verify that a relative path here is safe, or just replace is os.Getwd again.

		// exec git add, tree write, merge, commit.
		g.cmd("add", "--all")()
		g.forceMerge(ancestor, lineage)

		hash = ""	//FIXME
	})
	return
}

func (g *Graph) Load(lineage string, gr GraphLoadRequest) (hash string) {
	lineage, _ = SplitImageName(lineage) //Handle tags

	g.withTempTree(func(cmd Command) {
		// checkout lineage.
		// "-f" because otherwise if git thinks we already had this branch checked out, this working tree is just chock full of deletes.
		g.cmd("checkout", "-f", lineage)()

		// the gr consumes this filesystem and shoves it at whoever it deals with; we're actually hands free after handing over a dir.
		gr.receive(".")	//TODO: verify that a relative path here is safe, or just replace is os.Getwd again.

		hash = ""	//FIXME
	})
	return
}

// having a load-by-hash:
//   - you can't combine it with lineage, because git doesn't really know what branches are, historically speaking.
//       - unless we decide we're committing lineage in some structured form of the commit messages, which is possible, but not sure if want.
//       - we could traverse up from the lineage branch ref and make sure the hash is reachable from it, but more than one ref is going to be able to reach most hashes (i.e. hashes that are pd-base will be reachable from pd-nginx).

func (g *Graph) forceMerge(source string, target string) {
	writeTree := g.cmd("write-tree").Output()
	writeTree = strings.Trim(writeTree, "\n")
	commitMsg := fmt.Sprintf("updated %s<<%s", target, source)
	commitTreeCmd := g.cmd("commit-tree", writeTree, Opts{In: commitMsg})
	if source != "" {
		commitTreeCmd = commitTreeCmd("-p", source, "-p", target)
	}
	mergeTree := strings.Trim(commitTreeCmd.Output(), "\n")
	g.cmd("merge", "-q", mergeTree)()
}

//Checks if the graph has a branch.
func (g *Graph) HasBranch(branch string) bool {
	//Git magic is involved. Response will be of non-zero length if branch exists.
	result := g.cmd("ls-remote", ".", "refs/heads/" + branch).Output()
	return len(result) > 0
}

/*
	Check if a git repo exists and if it has the branches that declare it a docket graph.
*/
func (g *Graph) isDocketGraphRepo() bool {
	if !g.isRepoRoot() { return false; }
	if !g.HasBranch("docket/init") { return false; }
	// We could say a docket graph shouldn't have a master branch, but we won't.
	// We don't create one by default, but you're perfectly welcome to do so and put a readme for your coworkers in it or whatever.
	return true
}
<commit_msg>change the default commit message format.<commit_after>package dex

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	. "polydawn.net/pogo/gosh"
	. "polydawn.net/docket/crocker"
	"polydawn.net/docket/util"
	"strings"
)

type Graph struct {
	/*
		Absolute path to the base/working dir for of the graph git repository.
	*/
	dir string

	/*
		Cached command template for exec'ing git with this graph's cwd.
	*/
	cmd Command
}

/*
	Loads a Graph if there is a git repo initialized at the given dir; returns nil if a graph repo not found.
	The dir must be the root of the working tree of the git dir.

	A graph git repo is distingushed by containing branches that start with "docket/" -- this is how docket outputs branches that contain its data.
*/
func LoadGraph(dir string) *Graph {
	// optimistically, set up the struct we're checking out
	g := newGraph(dir)

	// ask git what it thinks of all this.
	if g.isDocketGraphRepo() {
		return g
	} else {
		return nil
	}
}

/*
	Attempts to load a Graph at the given dir, or creates a new one if no graph repo is found.
	If a new graph is fabricated, it will be initialized by:
	 - creating a new git repo,
	 - making a blank root commit,
	 - and tagging it with a branch name that declares it to be a graph repo.

	Note if your cwd is already in a git repo, the new graph will not be commited, nor will it be made a submodule.
	You're free to make it a submodule yourself, but git quite wants you to have a remote url before it accepts your submodule.
*/
func NewGraph(dir string) *Graph {
	g := newGraph(dir)
	if g.isDocketGraphRepo() {
		// if we can just be a load, do it
		return g
	} else if g.isRepoRoot() {
		// if this is a repo root, but didn't look like a real graph...
		util.ExitGently("Attempted to make a docket graph at ", g.dir, ", but there is already a git repo there and it does not appear to belong to docket.")
	} // else carry on, make it!

	// we'll make exactly one new dir if the path doesn't exist yet.  more is probably argument error and we abort.
	// this is actually implemented via MkdirAll here (because Mkdir errors on existing, and I can't be arsed) and letting the SaneDir check earlier blow up if we're way out.
	err := os.MkdirAll(g.dir, 0755)
	if err != nil { panic(err); }

	// git init
	g.cmd("init")("--bare")()

	g.withTempTree(func (cmd Command) {
		// set up basic repo to identify as graph repo
		cmd("commit", "--allow-empty", "-mdocket")()
		cmd("checkout", "-b", "docket/init")()

		// discard master branch.  a docket graph has no real use for it.
		cmd("branch", "-D", "master")()
	})

	// should be good to go
	return g
}

func newGraph(dir string) *Graph {
	dir = util.SanePath(dir)

	// optimistically, set up the struct.
	// we still need to either verify or initalize git here.
	return &Graph{
		dir: dir,
		cmd: Sh("git")(DefaultIO)(Opts{Cwd: dir}),
	}
}

func (g *Graph) isRepoRoot() (v bool) {
	defer func() {
		// if the path doesn't even exist, launching the command will panic, and that's fine.
		// if the path isn't within a git repo at all, it will exit with 128, gosh will panic, and that's fine.
		if recover() != nil {
			v = false
		}
	}()
	revp := g.cmd(NullIO)("rev-parse", "--is-bare-repository").Output()
	v = (revp == "true\n")
	return
}

/*
	Creates a temporary working tree in a new directory.  Changes the cwd to that location.
	The directory will be empty.  The directory will be removed when your function returns.
*/
func (g *Graph) withTempTree(fn func(cmd Command)) {
	// ensure zone for temp trees is established
	tmpTreeBase := filepath.Join(g.dir, "worktrees")
	err := os.MkdirAll(tmpTreeBase, 0755)
	if err != nil { panic(err); }

	// make temp dir for tree
	tmpdir, err := ioutil.TempDir(tmpTreeBase, "tree.")
	if err != nil { panic(err); }
	defer os.RemoveAll(tmpdir)

	// set cwd
	retreat, err := os.Getwd()
	if err != nil { panic(err); }
	defer os.Chdir(retreat)
	err = os.Chdir(tmpdir)
	if err != nil { panic(err); }

	// construct git command template that knows what's up
	gt := g.cmd(
		Opts{
			Cwd:tmpdir,
		},
		Env{
			"GIT_WORK_TREE": tmpdir,
			"GIT_DIR": g.dir,
		},
	)

	// go time
	fn(gt)
}

func (g *Graph) Publish(lineage string, ancestor string, gr GraphStoreRequest) (hash string) {
	// Handle tags - currently, we discard them when dealing with a graph repo.
	lineage, _  = SplitImageName(lineage)
	ancestor, _ = SplitImageName(ancestor)

	g.withTempTree(func(cmd Command) {
		fmt.Println("starting publish", lineage, "<--", ancestor)

		// check if appropriate branches already exist, and make them if necesary
		if strings.Count(g.cmd("branch", "--list", lineage).Output(), "\n") >= 1 {
			fmt.Println("linage already existed")
			// this is an existing lineage
			g.cmd("checkout", "-f", lineage)()
			g.cmd("rm", "-r", ".")()
		} else {
			// this is a new lineage
			if ancestor == "" {
				fmt.Println("new linage!  making orphan branch for it")
				g.cmd("checkout", "--orphan", lineage)()	//TODO: docket/image/
			} else {
				fmt.Println("new linage!  forking it from ancestor branch.")
				g.cmd("branch", lineage, ancestor)()
				g.cmd("checkout", "-f", lineage)()
				g.cmd("rm", "-r", ".")()
			}
		}

		// apply the GraphStoreRequest to unpack the fs (read from fs.tarReader, essentially)
		gr.place(".")	//TODO: verify that a relative path here is safe, or just replace is os.Getwd again.

		// exec git add, tree write, merge, commit.
		g.cmd("add", "--all")()
		g.forceMerge(ancestor, lineage)

		hash = ""	//FIXME
	})
	return
}

func (g *Graph) Load(lineage string, gr GraphLoadRequest) (hash string) {
	lineage, _ = SplitImageName(lineage) //Handle tags

	g.withTempTree(func(cmd Command) {
		// checkout lineage.
		// "-f" because otherwise if git thinks we already had this branch checked out, this working tree is just chock full of deletes.
		g.cmd("checkout", "-f", lineage)()

		// the gr consumes this filesystem and shoves it at whoever it deals with; we're actually hands free after handing over a dir.
		gr.receive(".")	//TODO: verify that a relative path here is safe, or just replace is os.Getwd again.

		hash = ""	//FIXME
	})
	return
}

// having a load-by-hash:
//   - you can't combine it with lineage, because git doesn't really know what branches are, historically speaking.
//       - we could traverse up from the lineage branch ref and make sure the hash is reachable from it, but more than one ref is going to be able to reach most hashes (i.e. hashes that are pd-base will be reachable from pd-nginx).
//       - unless we decide we're committing lineage in some structured form of the commit messages.
//            - which... yes, yes we are.  the first word of any commit is going to be the image lineage name.
//            - after the space can be anything, but we're going to default to a short description of where it came from.
//            - additional lines can be anything you want.
//            - if we need more attributes in the future, we'll start doing them with the git psuedo-standard of trailing "Signed-Off-By: %{name}\n" key-value pairs.
//            - we won't validate any of this if you're not using load-by-hash.

func (g *Graph) forceMerge(source string, target string) {
	writeTree := g.cmd("write-tree").Output()
	writeTree = strings.Trim(writeTree, "\n")
	commitMsg := ""
	if source == "" {
		commitMsg = fmt.Sprintf("%s imported from an external source", target)
	} else {
		commitMsg = fmt.Sprintf("%s updated from %s", target, source)
	}
	commitTreeCmd := g.cmd("commit-tree", writeTree, Opts{In: commitMsg})
	if source != "" {
		commitTreeCmd = commitTreeCmd("-p", source, "-p", target)
	}
	mergeTree := strings.Trim(commitTreeCmd.Output(), "\n")
	g.cmd("merge", "-q", mergeTree)()
}

//Checks if the graph has a branch.
func (g *Graph) HasBranch(branch string) bool {
	//Git magic is involved. Response will be of non-zero length if branch exists.
	result := g.cmd("ls-remote", ".", "refs/heads/" + branch).Output()
	return len(result) > 0
}

/*
	Check if a git repo exists and if it has the branches that declare it a docket graph.
*/
func (g *Graph) isDocketGraphRepo() bool {
	if !g.isRepoRoot() { return false; }
	if !g.HasBranch("docket/init") { return false; }
	// We could say a docket graph shouldn't have a master branch, but we won't.
	// We don't create one by default, but you're perfectly welcome to do so and put a readme for your coworkers in it or whatever.
	return true
}
<|endoftext|>


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

package arm64

import (
	"bytes"
	"fmt"
	"internal/testenv"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"testing"
)

// TestLarge generates a very large file to verify that large
// program builds successfully, in particular, too-far
// conditional branches are fixed.
func TestLarge(t *testing.T) {
	if testing.Short() {
		t.Skip("Skip in short mode")
	}
	testenv.MustHaveGoBuild(t)

	dir, err := ioutil.TempDir("", "testlarge")
	if err != nil {
		t.Fatalf("could not create directory: %v", err)
	}
	defer os.RemoveAll(dir)

	// generate a very large function
	buf := bytes.NewBuffer(make([]byte, 0, 7000000))
	gen(buf)

	tmpfile := filepath.Join(dir, "x.s")
	err = ioutil.WriteFile(tmpfile, buf.Bytes(), 0644)
	if err != nil {
		t.Fatalf("can't write output: %v\n", err)
	}

	// build generated file
	cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
	cmd.Env = append(os.Environ(), "GOARCH=arm64", "GOOS=linux")
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Errorf("Build failed: %v, output: %s", err, out)
	}
}

// gen generates a very large program, with a very far conditional branch.
func gen(buf *bytes.Buffer) {
	fmt.Fprintln(buf, "TEXT f(SB),0,$0-0")
	fmt.Fprintln(buf, "TBZ $5, R0, label")
	fmt.Fprintln(buf, "CBZ R0, label")
	fmt.Fprintln(buf, "BEQ label")
	for i := 0; i < 1<<19; i++ {
		fmt.Fprintln(buf, "MOVD R0, R1")
	}
	fmt.Fprintln(buf, "label:")
	fmt.Fprintln(buf, "RET")
}

// Issue 20348.
func TestNoRet(t *testing.T) {
	dir, err := ioutil.TempDir("", "testnoret")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir)
	tmpfile := filepath.Join(dir, "x.s")
	if err := ioutil.WriteFile(tmpfile, []byte("TEXT ·stub(SB),$0-0\nNOP\n"), 0644); err != nil {
		t.Fatal(err)
	}
	cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
	cmd.Env = append(os.Environ(), "GOARCH=arm64", "GOOS=linux")
	if out, err := cmd.CombinedOutput(); err != nil {
		t.Errorf("%v\n%s", err, out)
	}
}

// TestPCALIGN verifies the correctness of the PCALIGN by checking if the
// code can be aligned to the alignment value.
func TestPCALIGN(t *testing.T) {
	testenv.MustHaveGoBuild(t)
	dir, err := ioutil.TempDir("", "testpcalign")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir)
	tmpfile := filepath.Join(dir, "test.s")

	code1 := []byte("TEXT ·foo(SB),$0-0\nMOVD $0, R0\nPCALIGN $8\nMOVD $1, R1\nRET\n")
	code2 := []byte("TEXT ·foo(SB),$0-0\nMOVD $0, R0\nPCALIGN $16\nMOVD $2, R2\nRET\n")
	// If the output contains this pattern, the pc-offsite of "MOVD $1, R1" is 8 bytes aligned.
	out1 := `0x0008\s00008\s\(.*\)\tMOVD\t\$1,\sR1`
	// If the output contains this pattern, the pc-offsite of "MOVD $2, R2" is 16 bytes aligned.
	out2 := `0x0010\s00016\s\(.*\)\tMOVD\t\$2,\sR2`
	var testCases = []struct {
		name string
		code []byte
		out  string
	}{
		{"8-byte alignment", code1, out1},
		{"16-byte alignment", code2, out2},
	}

	for _, test := range testCases {
		if err := ioutil.WriteFile(tmpfile, test.code, 0644); err != nil {
			t.Fatal(err)
		}
		cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-S", tmpfile)
		cmd.Env = append(os.Environ(), "GOARCH=arm64", "GOOS=linux")
		out, err := cmd.CombinedOutput()
		if err != nil {
			t.Errorf("The %s build failed: %v, output: %s", test.name, err, out)
			continue
		}

		matched, err := regexp.MatchString(test.out, string(out))
		if err != nil {
			t.Fatal(err)
		}
		if !matched {
			t.Errorf("The %s testing failed!\ninput: %s\noutput: %s\n", test.name, test.code, out)
		}
	}
}
<commit_msg>cmd/internal/obj/arm64: write test output to temp dir<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package arm64

import (
	"bytes"
	"fmt"
	"internal/testenv"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"testing"
)

// TestLarge generates a very large file to verify that large
// program builds successfully, in particular, too-far
// conditional branches are fixed.
func TestLarge(t *testing.T) {
	if testing.Short() {
		t.Skip("Skip in short mode")
	}
	testenv.MustHaveGoBuild(t)

	dir, err := ioutil.TempDir("", "testlarge")
	if err != nil {
		t.Fatalf("could not create directory: %v", err)
	}
	defer os.RemoveAll(dir)

	// generate a very large function
	buf := bytes.NewBuffer(make([]byte, 0, 7000000))
	gen(buf)

	tmpfile := filepath.Join(dir, "x.s")
	err = ioutil.WriteFile(tmpfile, buf.Bytes(), 0644)
	if err != nil {
		t.Fatalf("can't write output: %v\n", err)
	}

	// build generated file
	cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
	cmd.Env = append(os.Environ(), "GOARCH=arm64", "GOOS=linux")
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Errorf("Build failed: %v, output: %s", err, out)
	}
}

// gen generates a very large program, with a very far conditional branch.
func gen(buf *bytes.Buffer) {
	fmt.Fprintln(buf, "TEXT f(SB),0,$0-0")
	fmt.Fprintln(buf, "TBZ $5, R0, label")
	fmt.Fprintln(buf, "CBZ R0, label")
	fmt.Fprintln(buf, "BEQ label")
	for i := 0; i < 1<<19; i++ {
		fmt.Fprintln(buf, "MOVD R0, R1")
	}
	fmt.Fprintln(buf, "label:")
	fmt.Fprintln(buf, "RET")
}

// Issue 20348.
func TestNoRet(t *testing.T) {
	dir, err := ioutil.TempDir("", "testnoret")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir)
	tmpfile := filepath.Join(dir, "x.s")
	if err := ioutil.WriteFile(tmpfile, []byte("TEXT ·stub(SB),$0-0\nNOP\n"), 0644); err != nil {
		t.Fatal(err)
	}
	cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
	cmd.Env = append(os.Environ(), "GOARCH=arm64", "GOOS=linux")
	if out, err := cmd.CombinedOutput(); err != nil {
		t.Errorf("%v\n%s", err, out)
	}
}

// TestPCALIGN verifies the correctness of the PCALIGN by checking if the
// code can be aligned to the alignment value.
func TestPCALIGN(t *testing.T) {
	testenv.MustHaveGoBuild(t)
	dir, err := ioutil.TempDir("", "testpcalign")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir)
	tmpfile := filepath.Join(dir, "test.s")
	tmpout := filepath.Join(dir, "test.o")

	code1 := []byte("TEXT ·foo(SB),$0-0\nMOVD $0, R0\nPCALIGN $8\nMOVD $1, R1\nRET\n")
	code2 := []byte("TEXT ·foo(SB),$0-0\nMOVD $0, R0\nPCALIGN $16\nMOVD $2, R2\nRET\n")
	// If the output contains this pattern, the pc-offsite of "MOVD $1, R1" is 8 bytes aligned.
	out1 := `0x0008\s00008\s\(.*\)\tMOVD\t\$1,\sR1`
	// If the output contains this pattern, the pc-offsite of "MOVD $2, R2" is 16 bytes aligned.
	out2 := `0x0010\s00016\s\(.*\)\tMOVD\t\$2,\sR2`
	var testCases = []struct {
		name string
		code []byte
		out  string
	}{
		{"8-byte alignment", code1, out1},
		{"16-byte alignment", code2, out2},
	}

	for _, test := range testCases {
		if err := ioutil.WriteFile(tmpfile, test.code, 0644); err != nil {
			t.Fatal(err)
		}
		cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-S", "-o", tmpout, tmpfile)
		cmd.Env = append(os.Environ(), "GOARCH=arm64", "GOOS=linux")
		out, err := cmd.CombinedOutput()
		if err != nil {
			t.Errorf("The %s build failed: %v, output: %s", test.name, err, out)
			continue
		}

		matched, err := regexp.MatchString(test.out, string(out))
		if err != nil {
			t.Fatal(err)
		}
		if !matched {
			t.Errorf("The %s testing failed!\ninput: %s\noutput: %s\n", test.name, test.code, out)
		}
	}
}
<|endoftext|>


<commit_before>package tachymeter

import (
	"math"
	"sort"
	"time"
)

// Satisfy sort for timeSlice.
// Sorts in increasing order of duration.

func (p timeSlice) Len() int {
	return len(p)
}

func (p timeSlice) Less(i, j int) bool {
	return int64(p[i]) < int64(p[j])
}

func (p timeSlice) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}

// Calc calcs data held in a *Tachymeter
// and returns a *Metrics.
func (m *Tachymeter) Calc() *Metrics {
	m.Lock()
	defer m.Unlock()

	m.Times = m.Times[:m.TimesUsed]
	sort.Sort(m.Times)

	metrics := &Metrics{}
	metrics.Samples = m.TimesUsed
	metrics.Count = m.Count
	metrics.Time.Total = calcTotal(m.Times)
	metrics.Time.Avg = calcAvg(metrics.Time.Total, metrics.Samples)
	metrics.Time.Median = m.Times[len(m.Times)/2]
	metrics.Time.p95 = calcp95(m.Times)
	metrics.Time.Long5p = calcLong5p(m.Times)
	metrics.Time.Short5p = calcShort5p(m.Times)
	metrics.Time.Max = m.Times[metrics.Samples-1]
	metrics.Time.Min = m.Times[0]
	rateTime := float64(metrics.Samples) / float64(metrics.Time.Total)
	metrics.Rate.Second = rateTime * 1e9

	return metrics
}

// These should be self-explanatory:

func calcTotal(d []time.Duration) time.Duration {
	var t time.Duration
	for _, d := range d {
		t += d
	}

	return t
}

func calcAvg(d time.Duration, c int) time.Duration {
	return time.Duration(int(d) / c)
}

func calcp95(d []time.Duration) time.Duration {
	return d[int(float64(len(d))*0.95)]
}

func calcLong5p(d []time.Duration) time.Duration {
	set := d[int(float64(len(d))*0.95):]

	var t time.Duration
	var i int
	for _, n := range set {
		t += n
		i++
	}

	return time.Duration(int(t) / i)
}

func calcShort5p(d []time.Duration) time.Duration {
	set := d[:int(math.Ceil(float64(len(d))*0.05))]

	var t time.Duration
	var i int
	for _, n := range set {
		t += n
		i++
	}

	return time.Duration(int(t) / i)
}
<commit_msg>fixes divide by zero scenarios<commit_after>package tachymeter

import (
	"math"
	"sort"
	"time"
)

// Satisfy sort for timeSlice.
// Sorts in increasing order of duration.

func (p timeSlice) Len() int {
	return len(p)
}

func (p timeSlice) Less(i, j int) bool {
	return int64(p[i]) < int64(p[j])
}

func (p timeSlice) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}

// Calc calcs data held in a *Tachymeter
// and returns a *Metrics.
func (m *Tachymeter) Calc() *Metrics {
	m.Lock()
	defer m.Unlock()

	metrics := &Metrics{}
	if m.Count == 0 {
		return metrics
	}

	m.Times = m.Times[:m.TimesUsed]
	sort.Sort(m.Times)

	metrics.Samples = m.TimesUsed
	metrics.Count = m.Count
	metrics.Time.Total = calcTotal(m.Times)
	metrics.Time.Avg = calcAvg(metrics.Time.Total, metrics.Samples)
	metrics.Time.Median = m.Times[len(m.Times)/2]
	metrics.Time.p95 = calcp95(m.Times)
	metrics.Time.Long5p = calcLong5p(m.Times)
	metrics.Time.Short5p = calcShort5p(m.Times)
	metrics.Time.Max = m.Times[metrics.Samples-1]
	metrics.Time.Min = m.Times[0]
	rateTime := float64(metrics.Samples) / float64(metrics.Time.Total)
	metrics.Rate.Second = rateTime * 1e9

	return metrics
}

// These should be self-explanatory:

func calcTotal(d []time.Duration) time.Duration {
	var t time.Duration
	for _, d := range d {
		t += d
	}

	return t
}

func calcAvg(d time.Duration, c int) time.Duration {
	return time.Duration(int(d) / c)
}

func calcp95(d []time.Duration) time.Duration {
	return d[int(float64(len(d))*0.95)]
}

func calcLong5p(d []time.Duration) time.Duration {
	set := d[int(float64(len(d))*0.95):]

	var t time.Duration
	var i int
	for _, n := range set {
		t += n
		i++
	}

	return time.Duration(int(t) / i)
}

func calcShort5p(d []time.Duration) time.Duration {
	set := d[:int(math.Ceil(float64(len(d))*0.05))]

	var t time.Duration
	var i int
	for _, n := range set {
		t += n
		i++
	}

	return time.Duration(int(t) / i)
}
<|endoftext|>


<commit_before>// +build medium
// +build !without_external

package blockstorage

import (
	"context"
	"net/url"
	"path/filepath"
	"testing"

	"code.cloudfoundry.org/bytefmt"
	"github.com/google/go-cmp/cmp"
)

func TestAgentCreateEmptyBlockStorage(t *testing.T) {
	bsaa, err := CreateBlockStorageAgentAPI(".")
	if err != nil {
		t.Fatalf("CreateBlockStorageAgentAPI is failed: err=%s", err.Error())
	}

	name := "test-empty"
	size := uint64(10 * bytefmt.MEGABYTE)
	pwd, _ := filepath.Abs(".")
	path := filepath.Join(pwd, name)

	createRes, err := bsaa.CreateEmptyBlockStorage(context.Background(), &CreateEmptyBlockStorageRequest{
		Name:  name,
		Bytes: size,
	})
	if err != nil {
		t.Fatalf("CreateEmptyBlockStorage got error: err=%s", err.Error())
	}
	if diff := cmp.Diff(&CreateEmptyBlockStorageResponse{
		Path: path,
	}, createRes); diff != "" {
		t.Errorf("CreateEmptyBlockStorage response is wrong: diff=(-want +got)\n%s", diff)
	}

	_, err = bsaa.DeleteBlockStorage(context.Background(), &DeleteBlockStorageRequest{Path: createRes.Path})
	if err != nil {
		t.Errorf("DeleteBlockStorage got error: err=%s", err.Error())
	}
}

func TestAgentFetchBlockStorage(t *testing.T) {
	bsaa, err := CreateBlockStorageAgentAPI(".")
	if err != nil {
		t.Fatalf("CreateBlockStorageAgentAPI is failed: err=%s", err.Error())
	}

	name := "test-empty"
	size := uint64(10 * bytefmt.MEGABYTE)
	pwd, _ := filepath.Abs(".")
	path := filepath.Join(pwd, name)

	fetchRes, err := bsaa.FetchBlockStorage(context.Background(), &FetchBlockStorageRequest{
		Name:      name,
		Bytes:     size,
		SourceUrl: "http://archive.ubuntu.com/ubuntu/dists/bionic-updates/main/installer-amd64/current/images/netboot/mini.iso",
	})
	if err != nil {
		t.Fatalf("[http] FetchBlockStorage got error: err=%s", err.Error())
	}
	if diff := cmp.Diff(&FetchBlockStorageResponse{
		Path: path,
	}, fetchRes); diff != "" {
		t.Errorf("[http] FetchBlockStorage response is wrong: diff=(-want +got)\n%s", diff)
	}

	fname := name + "-copied"
	fpath := filepath.Join(pwd, fname)
	ffetchRes, err := bsaa.FetchBlockStorage(context.Background(), &FetchBlockStorageRequest{
		Name:  fname,
		Bytes: size,
		SourceUrl: (&url.URL{
			Scheme: "file",
			Path:   fetchRes.Path,
		}).String(),
	})
	if err != nil {
		t.Fatalf("[file] FetchBlockStorage got error: err=%s", err.Error())
	}
	if diff := cmp.Diff(&FetchBlockStorageResponse{
		Path: fpath,
	}, ffetchRes); diff != "" {
		t.Errorf("[file] FetchBlockStorage response is wrong: diff=(-want +got)\n%s", diff)
	}

	_, err = bsaa.DeleteBlockStorage(context.Background(), &DeleteBlockStorageRequest{Path: fetchRes.Path})
	if err != nil {
		t.Errorf("DeleteBlockStorage got error: err=%s", err.Error())
	}
	_, err = bsaa.DeleteBlockStorage(context.Background(), &DeleteBlockStorageRequest{Path: ffetchRes.Path})
	if err != nil {
		t.Errorf("DeleteBlockStorage got error: err=%s", err.Error())
	}
}

// // errors
// // からの場合を
// func TestAgentFetchBlockStorageAboutErrors(t *testing.T) {
// 	bsaa, err := CreateBlockStorageAgentAPI(".")
// 	if err != nil {
// 		t.Fatalf("CreateBlockStorageAgentAPI is failed: err=%s", err.Error())
// 	}

// 	cases := []struct {
// 		name string
// 		req  *FetchBlockStorageRequest
// 		res  *FetchBlockStorageResponse
// 		code codes.Code
// 	}{
// 		{
// 			"no url",
// 			&FetchBlockStorageRequest{},
// 			nil,
// 			codes.Internal,
// 		},
// 	}

// 	for _, c := range cases {
// 		res, err := bsaa.FetchBlockStorage(context.Background(), c.req)
// 		if diff := cmp.Diff(c.res, res); diff != "" {
// 			t.Errorf("")
// 		}

// 		if c.code == 0 && err != nil {
// 			t.Errorf("")
// 		}

// 		if grpc.Code(err) != c.code {
// 			t.Errorf("")
// 		}
// 	}
// }
<commit_msg>add agent TestResizeBlogkStorage<commit_after>// +build medium
// +build !without_external

package blockstorage

import (
	"context"
	"net/url"
	"path/filepath"
	"testing"

	"code.cloudfoundry.org/bytefmt"
	"github.com/google/go-cmp/cmp"
)

func TestAgentCreateEmptyBlockStorage(t *testing.T) {
	bsaa, err := CreateBlockStorageAgentAPI(".")
	if err != nil {
		t.Fatalf("CreateBlockStorageAgentAPI is failed: err=%s", err.Error())
	}

	name := "test-empty"
	size := uint64(10 * bytefmt.MEGABYTE)
	pwd, _ := filepath.Abs(".")
	path := filepath.Join(pwd, name)

	createRes, err := bsaa.CreateEmptyBlockStorage(context.Background(), &CreateEmptyBlockStorageRequest{
		Name:  name,
		Bytes: size,
	})
	if err != nil {
		t.Fatalf("CreateEmptyBlockStorage got error: err=%s", err.Error())
	}
	if diff := cmp.Diff(&CreateEmptyBlockStorageResponse{
		Path: path,
	}, createRes); diff != "" {
		t.Errorf("CreateEmptyBlockStorage response is wrong: diff=(-want +got)\n%s", diff)
	}

	_, err = bsaa.DeleteBlockStorage(context.Background(), &DeleteBlockStorageRequest{Path: createRes.Path})
	if err != nil {
		t.Errorf("DeleteBlockStorage got error: err=%s", err.Error())
	}
}

func TestAgentFetchBlockStorage(t *testing.T) {
	bsaa, err := CreateBlockStorageAgentAPI(".")
	if err != nil {
		t.Fatalf("CreateBlockStorageAgentAPI is failed: err=%s", err.Error())
	}

	name := "test-empty"
	size := uint64(10 * bytefmt.MEGABYTE)
	pwd, _ := filepath.Abs(".")
	path := filepath.Join(pwd, name)

	fetchRes, err := bsaa.FetchBlockStorage(context.Background(), &FetchBlockStorageRequest{
		Name:      name,
		Bytes:     size,
		SourceUrl: "http://archive.ubuntu.com/ubuntu/dists/bionic-updates/main/installer-amd64/current/images/netboot/mini.iso",
	})
	if err != nil {
		t.Fatalf("[http] FetchBlockStorage got error: err=%s", err.Error())
	}
	if diff := cmp.Diff(&FetchBlockStorageResponse{
		Path: path,
	}, fetchRes); diff != "" {
		t.Errorf("[http] FetchBlockStorage response is wrong: diff=(-want +got)\n%s", diff)
	}

	fname := name + "-copied"
	fpath := filepath.Join(pwd, fname)
	ffetchRes, err := bsaa.FetchBlockStorage(context.Background(), &FetchBlockStorageRequest{
		Name:  fname,
		Bytes: size,
		SourceUrl: (&url.URL{
			Scheme: "file",
			Path:   fetchRes.Path,
		}).String(),
	})
	if err != nil {
		t.Fatalf("[file] FetchBlockStorage got error: err=%s", err.Error())
	}
	if diff := cmp.Diff(&FetchBlockStorageResponse{
		Path: fpath,
	}, ffetchRes); diff != "" {
		t.Errorf("[file] FetchBlockStorage response is wrong: diff=(-want +got)\n%s", diff)
	}

	_, err = bsaa.DeleteBlockStorage(context.Background(), &DeleteBlockStorageRequest{Path: fetchRes.Path})
	if err != nil {
		t.Errorf("DeleteBlockStorage got error: err=%s", err.Error())
	}
	_, err = bsaa.DeleteBlockStorage(context.Background(), &DeleteBlockStorageRequest{Path: ffetchRes.Path})
	if err != nil {
		t.Errorf("DeleteBlockStorage got error: err=%s", err.Error())
	}
}

// // errors
// // からの場合を
// func TestAgentFetchBlockStorageAboutErrors(t *testing.T) {
// 	bsaa, err := CreateBlockStorageAgentAPI(".")
// 	if err != nil {
// 		t.Fatalf("CreateBlockStorageAgentAPI is failed: err=%s", err.Error())
// 	}

// 	cases := []struct {
// 		name string
// 		req  *FetchBlockStorageRequest
// 		res  *FetchBlockStorageResponse
// 		code codes.Code
// 	}{
// 		{
// 			"no url",
// 			&FetchBlockStorageRequest{},
// 			nil,
// 			codes.Internal,
// 		},
// 	}

// 	for _, c := range cases {
// 		res, err := bsaa.FetchBlockStorage(context.Background(), c.req)
// 		if diff := cmp.Diff(c.res, res); diff != "" {
// 			t.Errorf("")
// 		}

// 		if c.code == 0 && err != nil {
// 			t.Errorf("")
// 		}

// 		if grpc.Code(err) != c.code {
// 			t.Errorf("")
// 		}
// 	}
// }

func TestAgentResizeBlockStorage(t *testing.T) {
	bsaa, err := CreateBlockStorageAgentAPI(".")
	if err != nil {
		t.Fatalf("CreateBlockStorageAgentAPI is failed: err=%s", err.Error())
	}

	name := "test-empty"
	size := uint64(20 * bytefmt.MEGABYTE)

	createRes, err := bsaa.CreateEmptyBlockStorage(context.Background(), &CreateEmptyBlockStorageRequest{
		Name:  name,
		Bytes: uint64(10 * bytefmt.MEGABYTE),
	})
	if err != nil {
		t.Fatalf("CreateEmptyBlockStorage got error: err=%s", err.Error())
	}

	if _, err := bsaa.ResizeBlockStorage(context.Background(), &ResizeBlockStorageRequest{
		Bytes: size,
		Path:  createRes.Path,
	}); err != nil {
		t.Errorf("ResizeBlockStorage got error: err=%s", err.Error())
	}

	_, err = bsaa.DeleteBlockStorage(context.Background(), &DeleteBlockStorageRequest{Path: createRes.Path})
	if err != nil {
		t.Errorf("DeleteBlockStorage got error: err=%s", err.Error())
	}
}
<|endoftext|>


<commit_before>package aci

import (
	"bytes"
	"fmt"
)

func domPhysRN(name string) string {
	return "phys-" + name
}

func apiDomain(rn string) string {
	return "/api/node/mo/uni/" + rn + ".json"
}

// PhysicalDomainAdd creates a new physical domain.
func (c *Client) PhysicalDomainAdd(name, vlanpoolName, vlanpoolMode string) error {

	pool := nameVP(vlanpoolName, vlanpoolMode)

	rn := domPhysRN(name)

	api := apiDomain(rn)

	j := fmt.Sprintf(`{"physDomP":{"attributes":{"dn":"uni/%s","name":"%s","rn":"%s","status":"created"},"children":[{"infraRsVlanNs":{"attributes":{"tDn":"uni/infra/%s","status":"created"}}}]}}`,
		rn, name, rn, pool)

	url := c.getURL(api)

	c.debugf("PhysicalDomainAdd: url=%s json=%s", url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return errPost
	}

	c.debugf("PhysicalDomainAdd: reply: %s", string(body))

	return parseJSONError(body)
}

// PhysicalDomainDel deletes an existing physical domain.
func (c *Client) PhysicalDomainDel(name string) error {

	rn := domPhysRN(name)

	api := apiDomain(rn)

	url := c.getURL(api)

	c.debugf("PhysicalDomainDel: url=%s", url)

	body, errDel := c.delete(url)
	if errDel != nil {
		return errDel
	}

	c.debugf("PhysicalDomainDel: reply: %s", string(body))

	return parseJSONError(body)
}

// PhysicalDomainList retrieves the list of physical domains.
func (c *Client) PhysicalDomainList() ([]map[string]interface{}, error) {

	key := "physDomP"

	api := "/api/node/mo/uni.json?query-target=subtree&target-subtree-class=" + key

	url := c.getURL(api)

	c.debugf("PhysicalDomainList: url=%s", url)

	body, errGet := c.get(url)
	if errGet != nil {
		return nil, errGet
	}

	c.debugf("PhysicalDomainList: reply: %s", string(body))

	return jsonImdataAttributes(c, body, key, "PhysicalDomainList")
}

// PhysicalDomainVlanPoolGet retrieves the VLAN pool for the physical domain.
func (c *Client) PhysicalDomainVlanPoolGet(name string) (string, error) {

	key := "infraRsVlanNs"

	rn := domPhysRN(name)

	api := "/api/node/mo/uni/" + rn + ".json?query-target=children&target-subtree-class=" + key

	url := c.getURL(api)

	c.debugf("PhysicalDomainVlanPoolGet: url=%s", url)

	body, errGet := c.get(url)
	if errGet != nil {
		return "", errGet
	}

	c.debugf("PhysicalDomainVlanPoolGet: reply: %s", string(body))

	attrs, errAttr := jsonImdataAttributes(c, body, key, "PhysicalDomainVlanPoolGet")
	if errAttr != nil {
		return "", errAttr
	}

	if len(attrs) < 1 {
		return "", fmt.Errorf("empty list of vlanpool")
	}

	attr := attrs[0]
	pool := attr["tDn"]

	poolName, isStr := pool.(string)
	if !isStr {
		return "", fmt.Errorf("vlanpool is not a string")
	}

	return poolName, nil
}
<commit_msg>Check vlanpool attribute in physical domain.<commit_after>package aci

import (
	"bytes"
	"fmt"
)

func domPhysRN(name string) string {
	return "phys-" + name
}

func apiDomain(rn string) string {
	return "/api/node/mo/uni/" + rn + ".json"
}

// PhysicalDomainAdd creates a new physical domain.
func (c *Client) PhysicalDomainAdd(name, vlanpoolName, vlanpoolMode string) error {

	pool := nameVP(vlanpoolName, vlanpoolMode)

	rn := domPhysRN(name)

	api := apiDomain(rn)

	j := fmt.Sprintf(`{"physDomP":{"attributes":{"dn":"uni/%s","name":"%s","rn":"%s","status":"created"},"children":[{"infraRsVlanNs":{"attributes":{"tDn":"uni/infra/%s","status":"created"}}}]}}`,
		rn, name, rn, pool)

	url := c.getURL(api)

	c.debugf("PhysicalDomainAdd: url=%s json=%s", url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return errPost
	}

	c.debugf("PhysicalDomainAdd: reply: %s", string(body))

	return parseJSONError(body)
}

// PhysicalDomainDel deletes an existing physical domain.
func (c *Client) PhysicalDomainDel(name string) error {

	rn := domPhysRN(name)

	api := apiDomain(rn)

	url := c.getURL(api)

	c.debugf("PhysicalDomainDel: url=%s", url)

	body, errDel := c.delete(url)
	if errDel != nil {
		return errDel
	}

	c.debugf("PhysicalDomainDel: reply: %s", string(body))

	return parseJSONError(body)
}

// PhysicalDomainList retrieves the list of physical domains.
func (c *Client) PhysicalDomainList() ([]map[string]interface{}, error) {

	key := "physDomP"

	api := "/api/node/mo/uni.json?query-target=subtree&target-subtree-class=" + key

	url := c.getURL(api)

	c.debugf("PhysicalDomainList: url=%s", url)

	body, errGet := c.get(url)
	if errGet != nil {
		return nil, errGet
	}

	c.debugf("PhysicalDomainList: reply: %s", string(body))

	return jsonImdataAttributes(c, body, key, "PhysicalDomainList")
}

// PhysicalDomainVlanPoolGet retrieves the VLAN pool for the physical domain.
func (c *Client) PhysicalDomainVlanPoolGet(name string) (string, error) {

	key := "infraRsVlanNs"

	rn := domPhysRN(name)

	api := "/api/node/mo/uni/" + rn + ".json?query-target=children&target-subtree-class=" + key

	url := c.getURL(api)

	c.debugf("PhysicalDomainVlanPoolGet: url=%s", url)

	body, errGet := c.get(url)
	if errGet != nil {
		return "", errGet
	}

	c.debugf("PhysicalDomainVlanPoolGet: reply: %s", string(body))

	attrs, errAttr := jsonImdataAttributes(c, body, key, "PhysicalDomainVlanPoolGet")
	if errAttr != nil {
		return "", errAttr
	}

	if len(attrs) < 1 {
		return "", fmt.Errorf("empty list of vlanpool")
	}

	attr := attrs[0]

	pool, found := attr["tDn"]
	if !found {
		return "", fmt.Errorf("vlanpool not found")
	}

	poolName, isStr := pool.(string)
	if !isStr {
		return "", fmt.Errorf("vlanpool is not a string")
	}

	return poolName, nil
}
<|endoftext|>


<commit_before>package turn

import (
	"errors"
	"strconv"

	"github.com/pion/stun"
)

// ChannelNumber represents CHANNEL-NUMBER attribute.
//
// The CHANNEL-NUMBER attribute contains the number of the channel.
//
// RFC 5766 Section 14.1
type ChannelNumber int // encoded as uint16

func (n ChannelNumber) String() string { return strconv.Itoa(int(n)) }

// 16 bits of uint + 16 bits of RFFU = 0.
const channelNumberSize = 4

// AddTo adds CHANNEL-NUMBER to message.
func (n ChannelNumber) AddTo(m *stun.Message) error {
	v := make([]byte, channelNumberSize)
	bin.PutUint16(v[:2], uint16(n))
	// v[2:4] are zeroes (RFFU = 0)
	m.Add(stun.AttrChannelNumber, v)
	return nil
}

// GetFrom decodes CHANNEL-NUMBER from message.
func (n *ChannelNumber) GetFrom(m *stun.Message) error {
	v, err := m.Get(stun.AttrChannelNumber)
	if err != nil {
		return err
	}
	if err = stun.CheckSize(stun.AttrChannelNumber, len(v), channelNumberSize); err != nil {
		return err
	}
	_ = v[channelNumberSize-1] // asserting length
	*n = ChannelNumber(bin.Uint16(v[:2]))
	// v[2:4] is RFFU and equals to 0.
	return nil
}

// See https://tools.ietf.org/html/rfc5766#section-11:
//
// 0x4000 through 0x7FFF: These values are the allowed channel
// numbers (16,383 possible values).
const (
	MinChannelNumber = 0x4000
	MaxChannelNumber = 0x7FFF
)

// ErrInvalidChannelNumber means that channel number is not valid as by RFC 5766 Section 11.
var ErrInvalidChannelNumber = errors.New("channel number not in [0x4000, 0x7FFF]")

// isChannelNumberValid returns true if c in [0x4000, 0x7FFF].
func isChannelNumberValid(c uint16) bool {
	return c >= MinChannelNumber && c <= MaxChannelNumber
}

// Valid returns true if channel number has correct value that complies RFC 5766 Section 11 range.
func (n ChannelNumber) Valid() bool {
	return isChannelNumberValid(uint16(n))
}
<commit_msg>Change ChnnelNumber type<commit_after>package turn

import (
	"errors"
	"strconv"

	"github.com/pion/stun"
)

// ChannelNumber represents CHANNEL-NUMBER attribute.
//
// The CHANNEL-NUMBER attribute contains the number of the channel.
//
// RFC 5766 Section 14.1
type ChannelNumber uint16 // encoded as uint16

func (n ChannelNumber) String() string { return strconv.Itoa(int(n)) }

// 16 bits of uint + 16 bits of RFFU = 0.
const channelNumberSize = 4

// AddTo adds CHANNEL-NUMBER to message.
func (n ChannelNumber) AddTo(m *stun.Message) error {
	v := make([]byte, channelNumberSize)
	bin.PutUint16(v[:2], uint16(n))
	// v[2:4] are zeroes (RFFU = 0)
	m.Add(stun.AttrChannelNumber, v)
	return nil
}

// GetFrom decodes CHANNEL-NUMBER from message.
func (n *ChannelNumber) GetFrom(m *stun.Message) error {
	v, err := m.Get(stun.AttrChannelNumber)
	if err != nil {
		return err
	}
	if err = stun.CheckSize(stun.AttrChannelNumber, len(v), channelNumberSize); err != nil {
		return err
	}
	_ = v[channelNumberSize-1] // asserting length
	*n = ChannelNumber(bin.Uint16(v[:2]))
	// v[2:4] is RFFU and equals to 0.
	return nil
}

// See https://tools.ietf.org/html/rfc5766#section-11:
//
// 0x4000 through 0x7FFF: These values are the allowed channel
// numbers (16,383 possible values).
const (
	MinChannelNumber = 0x4000
	MaxChannelNumber = 0x7FFF
)

// ErrInvalidChannelNumber means that channel number is not valid as by RFC 5766 Section 11.
var ErrInvalidChannelNumber = errors.New("channel number not in [0x4000, 0x7FFF]")

// isChannelNumberValid returns true if c in [0x4000, 0x7FFF].
func isChannelNumberValid(c uint16) bool {
	return c >= MinChannelNumber && c <= MaxChannelNumber
}

// Valid returns true if channel number has correct value that complies RFC 5766 Section 11 range.
func (n ChannelNumber) Valid() bool {
	return isChannelNumberValid(uint16(n))
}
<|endoftext|>