<commit_before>package cidre

import (
	"crypto/sha1"
	"fmt"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"
)

// SessionConfig is a configuration object for the SessionMiddleware
type SessionConfig struct {
	// default: gossessionid
	CookieName   string
	CookieDomain string
	// default: false
	CookieSecure  bool
	CookiePath    string
	CookieExpires time.Duration
	// A term used to authenticate the cookie value using HMAC
	Secret string
	// default: "cidre.MemorySessionStore"
	SessionStore string
	// default: 30m
	GcInterval time.Duration
	// default: 30m
	LifeTime time.Duration
}

// Returns a SessionConfig object that has default values set.
// If an 'init' function object argument is not nil, this function
// will call the function with the SessionConfig object.
func DefaultSessionConfig(init ...func(*SessionConfig)) *SessionConfig {
	self := &SessionConfig{
		CookieName:    "gosessionid",
		CookieDomain:  "",
		CookieSecure:  false,
		CookiePath:    "",
		CookieExpires: 0,
		Secret:        "",
		SessionStore:  "cidre.MemorySessionStore",
		GcInterval:    time.Minute * 30,
		LifeTime:      time.Minute * 30,
	}
	if len(init) > 0 {
		init[0](self)
	}
	return self
}

// Middleware for session management.
type SessionMiddleware struct {
	app    *App
	Config *SessionConfig
	Store  SessionStore
}

// Returns a new SessionMiddleware object.
func NewSessionMiddleware(app *App, config *SessionConfig, storeConfig interface{}) *SessionMiddleware {
	self := &SessionMiddleware{app: app, Config: config}
	if len(self.Config.Secret) == 0 {
		panic("Session secret must not be empty.")
	}
	DynamicObjectFactory.Register(MemorySessionStore{})
	store, _ := DynamicObjectFactory.New(self.Config.SessionStore).(SessionStore)
	self.Store = store
	self.Store.Init(self, storeConfig)

	app.Hooks.Add("start_server", func(w http.ResponseWriter, r *http.Request, data interface{}) {
		time.AfterFunc(self.Config.GcInterval, self.Gc)
	})

	return self
}

func (self *SessionMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ctx := RequestContext(r)
	if !ctx.IsDynamicRoute() {
		ctx.MiddlewareChain.DoNext(w, r)
	} else {
		if !strings.HasPrefix(r.URL.Path, self.Config.CookiePath) {
			return
		}
		func() {
			self.Store.Lock()
			defer self.Store.Unlock()
			signedString, _ := r.Cookie(self.Config.CookieName)
			var session *Session
			if signedString != nil {
				sessionId, err := ValidateSignedString(signedString.Value, self.Config.Secret)
				if err != nil {
					panic(err)
				}
				session = self.Store.Load(sessionId)
			} else {
				session = self.Store.NewSession()
			}
			if session != nil {
				ctx.Session = session
				session.UpdateLastAccessTime()
			}
		}()

		w.(ResponseWriter).Hooks().Add("before_write_header", func(w http.ResponseWriter, rnil *http.Request, statusCode interface{}) {
			if strings.Index(r.URL.Path, self.Config.CookiePath) != 0 {
				return
			}
			self.Store.Lock()
			defer self.Store.Unlock()
            domain := self.Config.CookieDomain
            if len(domain) == 0 {
              domain = strings.Split(r.Host,":")[0]
            }
			cookie := &http.Cookie{
				Domain: domain,
				Secure: self.Config.CookieSecure,
				Path:   self.Config.CookiePath,
			}
			if self.Config.CookieExpires != 0 {
				cookie.Expires = time.Now().Add(self.Config.CookieExpires)
			}
			session := ctx.Session
			if session == nil {
				return
			}
			if session.Killed {
				cookie.MaxAge = -1
				self.Store.Delete(session.Id)
			} else {
				self.Store.Save(session)
			}
			cookie.Name = self.Config.CookieName
			cookie.Value = SignString(session.Id, self.Config.Secret)
			http.SetCookie(w, cookie)
		})

		ctx.MiddlewareChain.DoNext(w, r)
	}

}

func (self *SessionMiddleware) Gc() {
	self.Store.Lock()
	defer self.Store.Unlock()
	self.app.Logger(LogLevelDebug, "Session Gc")
	self.Store.Gc()
	time.AfterFunc(self.Config.GcInterval, self.Gc)
}

// Session value container.
type Session struct {
	Dict
	Killed         bool
	Id             string
	LastAccessTime time.Time
}

const FlashKey = "_flash"

func NewSession(id string) *Session {
	self := &Session{
		Dict:   NewDict(),
		Killed: false, Id: id,
		LastAccessTime: time.Now()}
	self.Set(FlashKey, make(map[string][]string))
	return self
}

func (self *Session) UpdateLastAccessTime() {
	self.LastAccessTime = time.Now()
}

func (self *Session) Kill() {
	self.Killed = true
}

// Adds a flash message to the session
func (self *Session) AddFlash(category string, message string) {
	flash := self.Get(FlashKey).(map[string][]string)
	if _, ok := flash[category]; !ok {
		flash[category] = make([]string, 0, 10)
	}
	flash[category] = append(flash[category], message)
}

// Returns a flash message associated with the given category.
func (self *Session) Flash(category string) []string {
	flash := self.Get(FlashKey).(map[string][]string)
	v, ok := flash[category]
	delete(flash, category)
	if !ok {
		return make([]string, 0, 10)
	}
	return v
}

// Returns a list of flash messages from the session.
//
//     session.AddFlash("info", "info message1")
//     session.AddFlash("info", "info message2")
//     session.AddFlash("error", "error message")
//     messages := session.Flashes()
//     // -> {"info":["info message1", "info message2"], "error":["error message"]}
func (self *Session) Flashes() map[string][]string {
	flash := self.Get(FlashKey).(map[string][]string)
	self.Set(FlashKey, make(map[string][]string))
	return flash
}

// SessionStore is an interface for custom session stores.
// See the MemorySessionStore for examples.
type SessionStore interface {
	Lock()
	Unlock()
	Init(*SessionMiddleware, interface{})
	Exists(string) bool
	NewSession() *Session
	Save(*Session)
	Load(string) *Session
	Delete(string)
	Gc()
	Count() int
}

type MemorySessionStore struct {
	sync.Mutex
	middleware *SessionMiddleware
	store      map[string]*Session
}

func (self *MemorySessionStore) Init(middleware *SessionMiddleware, cfg interface{}) {
	self.middleware = middleware
	self.store = make(map[string]*Session, 30)
}

func (self *MemorySessionStore) NewSessionId() string {
	for true {
		now := time.Now().Unix()
		random := strconv.Itoa(rand.Int())
		sessionId := fmt.Sprintf("%x", sha1.Sum([]byte(fmt.Sprintf("%v%v%v", now, random, self.middleware.Config.Secret))))
		if !self.Exists(sessionId) {
			return sessionId
		}
	}
	return ""
}

func (self *MemorySessionStore) Exists(sessionId string) bool {
	_, ok := self.store[sessionId]
	return ok
}

func (self *MemorySessionStore) NewSession() *Session {
	session := NewSession(self.NewSessionId())
	self.store[session.Id] = session
	return session
}

func (self *MemorySessionStore) Save(*Session) { /* Nothing to do */ }

func (self *MemorySessionStore) Load(sessionId string) *Session {
	session, ok := self.store[sessionId]
	if ok {
		return session
	}
	return self.NewSession()
}

func (self *MemorySessionStore) Delete(sessionId string) {
	delete(self.store, sessionId)
}

func (self *MemorySessionStore) Count() int {
	return len(self.store)
}

func (self *MemorySessionStore) Gc() {
	delkeys := make([]string, 0, len(self.store)/10)
	for k, v := range self.store {
		if (time.Now().Sub(v.LastAccessTime)) > self.middleware.Config.LifeTime {
			delkeys = append(delkeys, k)
		}
	}
	for _, key := range delkeys {
		self.Delete(key)
	}
}
<commit_msg>Now session cookie is a httpOnly cookie<commit_after>package cidre

import (
	"crypto/sha1"
	"fmt"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"
)

// SessionConfig is a configuration object for the SessionMiddleware
type SessionConfig struct {
	// default: gossessionid
	CookieName   string
	CookieDomain string
	// default: false
	CookieSecure  bool
	CookiePath    string
	CookieExpires time.Duration
	// A term used to authenticate the cookie value using HMAC
	Secret string
	// default: "cidre.MemorySessionStore"
	SessionStore string
	// default: 30m
	GcInterval time.Duration
	// default: 30m
	LifeTime time.Duration
}

// Returns a SessionConfig object that has default values set.
// If an 'init' function object argument is not nil, this function
// will call the function with the SessionConfig object.
func DefaultSessionConfig(init ...func(*SessionConfig)) *SessionConfig {
	self := &SessionConfig{
		CookieName:    "gosessionid",
		CookieDomain:  "",
		CookieSecure:  false,
		CookiePath:    "",
		CookieExpires: 0,
		Secret:        "",
		SessionStore:  "cidre.MemorySessionStore",
		GcInterval:    time.Minute * 30,
		LifeTime:      time.Minute * 30,
	}
	if len(init) > 0 {
		init[0](self)
	}
	return self
}

// Middleware for session management.
type SessionMiddleware struct {
	app    *App
	Config *SessionConfig
	Store  SessionStore
}

// Returns a new SessionMiddleware object.
func NewSessionMiddleware(app *App, config *SessionConfig, storeConfig interface{}) *SessionMiddleware {
	self := &SessionMiddleware{app: app, Config: config}
	if len(self.Config.Secret) == 0 {
		panic("Session secret must not be empty.")
	}
	DynamicObjectFactory.Register(MemorySessionStore{})
	store, _ := DynamicObjectFactory.New(self.Config.SessionStore).(SessionStore)
	self.Store = store
	self.Store.Init(self, storeConfig)

	app.Hooks.Add("start_server", func(w http.ResponseWriter, r *http.Request, data interface{}) {
		time.AfterFunc(self.Config.GcInterval, self.Gc)
	})

	return self
}

func (self *SessionMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ctx := RequestContext(r)
	if !ctx.IsDynamicRoute() {
		ctx.MiddlewareChain.DoNext(w, r)
	} else {
		if !strings.HasPrefix(r.URL.Path, self.Config.CookiePath) {
			return
		}
		func() {
			self.Store.Lock()
			defer self.Store.Unlock()
			signedString, _ := r.Cookie(self.Config.CookieName)
			var session *Session
			if signedString != nil {
				sessionId, err := ValidateSignedString(signedString.Value, self.Config.Secret)
				if err != nil {
					panic(err)
				}
				session = self.Store.Load(sessionId)
			} else {
				session = self.Store.NewSession()
			}
			if session != nil {
				ctx.Session = session
				session.UpdateLastAccessTime()
			}
		}()

		w.(ResponseWriter).Hooks().Add("before_write_header", func(w http.ResponseWriter, rnil *http.Request, statusCode interface{}) {
			if strings.Index(r.URL.Path, self.Config.CookiePath) != 0 {
				return
			}
			self.Store.Lock()
			defer self.Store.Unlock()
            domain := self.Config.CookieDomain
            if len(domain) == 0 {
              domain = strings.Split(r.Host,":")[0]
            }
			cookie := &http.Cookie{
				Domain: domain,
				Secure: self.Config.CookieSecure,
				Path:   self.Config.CookiePath,
                HttpOnly: true,
			}
			if self.Config.CookieExpires != 0 {
				cookie.Expires = time.Now().Add(self.Config.CookieExpires)
			}
			session := ctx.Session
			if session == nil {
				return
			}
			if session.Killed {
				cookie.MaxAge = -1
				self.Store.Delete(session.Id)
			} else {
				self.Store.Save(session)
			}
			cookie.Name = self.Config.CookieName
			cookie.Value = SignString(session.Id, self.Config.Secret)
			http.SetCookie(w, cookie)
		})

		ctx.MiddlewareChain.DoNext(w, r)
	}

}

func (self *SessionMiddleware) Gc() {
	self.Store.Lock()
	defer self.Store.Unlock()
	self.app.Logger(LogLevelDebug, "Session Gc")
	self.Store.Gc()
	time.AfterFunc(self.Config.GcInterval, self.Gc)
}

// Session value container.
type Session struct {
	Dict
	Killed         bool
	Id             string
	LastAccessTime time.Time
}

const FlashKey = "_flash"

func NewSession(id string) *Session {
	self := &Session{
		Dict:   NewDict(),
		Killed: false, Id: id,
		LastAccessTime: time.Now()}
	self.Set(FlashKey, make(map[string][]string))
	return self
}

func (self *Session) UpdateLastAccessTime() {
	self.LastAccessTime = time.Now()
}

func (self *Session) Kill() {
	self.Killed = true
}

// Adds a flash message to the session
func (self *Session) AddFlash(category string, message string) {
	flash := self.Get(FlashKey).(map[string][]string)
	if _, ok := flash[category]; !ok {
		flash[category] = make([]string, 0, 10)
	}
	flash[category] = append(flash[category], message)
}

// Returns a flash message associated with the given category.
func (self *Session) Flash(category string) []string {
	flash := self.Get(FlashKey).(map[string][]string)
	v, ok := flash[category]
	delete(flash, category)
	if !ok {
		return make([]string, 0, 10)
	}
	return v
}

// Returns a list of flash messages from the session.
//
//     session.AddFlash("info", "info message1")
//     session.AddFlash("info", "info message2")
//     session.AddFlash("error", "error message")
//     messages := session.Flashes()
//     // -> {"info":["info message1", "info message2"], "error":["error message"]}
func (self *Session) Flashes() map[string][]string {
	flash := self.Get(FlashKey).(map[string][]string)
	self.Set(FlashKey, make(map[string][]string))
	return flash
}

// SessionStore is an interface for custom session stores.
// See the MemorySessionStore for examples.
type SessionStore interface {
	Lock()
	Unlock()
	Init(*SessionMiddleware, interface{})
	Exists(string) bool
	NewSession() *Session
	Save(*Session)
	Load(string) *Session
	Delete(string)
	Gc()
	Count() int
}

type MemorySessionStore struct {
	sync.Mutex
	middleware *SessionMiddleware
	store      map[string]*Session
}

func (self *MemorySessionStore) Init(middleware *SessionMiddleware, cfg interface{}) {
	self.middleware = middleware
	self.store = make(map[string]*Session, 30)
}

func (self *MemorySessionStore) NewSessionId() string {
	for true {
		now := time.Now().Unix()
		random := strconv.Itoa(rand.Int())
		sessionId := fmt.Sprintf("%x", sha1.Sum([]byte(fmt.Sprintf("%v%v%v", now, random, self.middleware.Config.Secret))))
		if !self.Exists(sessionId) {
			return sessionId
		}
	}
	return ""
}

func (self *MemorySessionStore) Exists(sessionId string) bool {
	_, ok := self.store[sessionId]
	return ok
}

func (self *MemorySessionStore) NewSession() *Session {
	session := NewSession(self.NewSessionId())
	self.store[session.Id] = session
	return session
}

func (self *MemorySessionStore) Save(*Session) { /* Nothing to do */ }

func (self *MemorySessionStore) Load(sessionId string) *Session {
	session, ok := self.store[sessionId]
	if ok {
		return session
	}
	return self.NewSession()
}

func (self *MemorySessionStore) Delete(sessionId string) {
	delete(self.store, sessionId)
}

func (self *MemorySessionStore) Count() int {
	return len(self.store)
}

func (self *MemorySessionStore) Gc() {
	delkeys := make([]string, 0, len(self.store)/10)
	for k, v := range self.store {
		if (time.Now().Sub(v.LastAccessTime)) > self.middleware.Config.LifeTime {
			delkeys = append(delkeys, k)
		}
	}
	for _, key := range delkeys {
		self.Delete(key)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Timo Savola. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package codegen

import (
	"bytes"
	"encoding/binary"
	"errors"
	"math"

	"github.com/tsavola/wag/compile/event"
	"github.com/tsavola/wag/internal/code"
	"github.com/tsavola/wag/internal/gen"
	"github.com/tsavola/wag/internal/gen/atomic"
	"github.com/tsavola/wag/internal/gen/debug"
	"github.com/tsavola/wag/internal/gen/link"
	"github.com/tsavola/wag/internal/gen/rodata"
	"github.com/tsavola/wag/internal/loader"
	"github.com/tsavola/wag/internal/module"
	"github.com/tsavola/wag/internal/obj"
	"github.com/tsavola/wag/object/abi"
	"github.com/tsavola/wag/trap"
)

func GenProgram(
	text code.Buffer,
	objMap obj.ObjectMapper,
	load loader.L,
	m *module.M,
	lib *module.Library,
	eventHandler func(event.Event),
	initFuncCount int,
	debugConfig *gen.DebuggerSupport,
) {
	funcStorage := gen.Func{
		Prog: gen.Prog{
			Module:    m,
			Text:      code.Buf{Buffer: text},
			Map:       objMap,
			FuncLinks: make([]link.FuncL, len(m.Funcs)),
		},
	}
	p := &funcStorage.Prog

	p.DebugMap, _ = objMap.(obj.DebugObjectMapper)
	if p.DebugMap != nil {
		p.Debugger = makeDebugger(debugConfig, load.R)
	}

	if debug.Enabled {
		if debug.Depth != 0 {
			debug.Printf("")
		}
		debug.Depth = 0
	}

	funcCodeCount := load.Varuint32()
	if needed := len(m.Funcs) - len(m.ImportFuncs); funcCodeCount != uint32(needed) {
		panic(module.Errorf("wrong number of function bodies: %d (should be: %d)", funcCodeCount, needed))
	}

	p.Map.InitObjectMap(len(m.ImportFuncs), int(funcCodeCount))

	if p.Text.Addr != abi.TextAddrNoFunction {
		panic(errors.New("unexpected initial text address"))
	}
	asm.TrapHandler(p, trap.NoFunction)

	if p.Text.Addr == abi.TextAddrNoFunction || p.Text.Addr > abi.TextAddrResume {
		panic("bad text address after NoFunction trap handler")
	}
	asm.PadUntil(p, abi.TextAddrResume)
	asm.Resume(p)

	if p.Text.Addr <= abi.TextAddrResume || p.Text.Addr > abi.TextAddrEnter {
		panic("bad text address after resume routine")
	}
	asm.PadUntil(p, abi.TextAddrEnter)
	// Virtual return point for resuming a program which was suspended before
	// execution started.  This call site must be at index 0, and its address
	// must match the TextAddrEnter routine.
	p.Map.PutCallSite(uint32(p.Text.Addr), obj.Word*2) // Depth includes start and entry addresses.
	asm.Enter(p)

	if p.Text.Addr > rodata.CommonsAddr {
		panic("bad text address after init routines")
	}
	genCommons(p)

	for id := trap.NoFunction + 1; id < trap.NumTraps; id++ {
		asm.AlignFunc(p)
		p.TrapLinks[id].Addr = p.Text.Addr

		switch id {
		case trap.CallStackExhausted:
			asm.TrapHandlerRewindCallStackExhausted(p)

		default:
			asm.TrapHandler(p, id)
		}
	}

	for i := range p.TrapLinkRewindSuspended {
		asm.AlignFunc(p)
		p.TrapLinkRewindSuspended[i].Addr = p.Text.Addr

		asm.TrapHandlerRewindSuspended(p, i)
	}

	p.ImportContext = lib // Generate import functions in library context.

	for i, imp := range m.ImportFuncs {
		code := bytes.NewReader(lib.CodeFuncs[imp.LibraryFunc-uint32(len(lib.ImportFuncs))])
		genFunction(&funcStorage, loader.L{R: code}, i, lib.Types[lib.Funcs[imp.LibraryFunc]], false)
	}

	p.ImportContext = nil

	if eventHandler == nil {
		initFuncCount = len(m.Funcs)
	}

	for i := len(m.ImportFuncs); i < initFuncCount; i++ {
		genFunction(&funcStorage, load, i, m.Types[m.Funcs[i]], false)
		linker.UpdateCalls(p.Text.Bytes(), &p.FuncLinks[i].L)
	}

	ptr := p.Text.Bytes()[rodata.TableAddr:]

	for i, funcIndex := range m.TableFuncs {
		var funcAddr uint32 // NoFunction trap by default

		if funcIndex < uint32(len(p.FuncLinks)) {
			ln := &p.FuncLinks[funcIndex]
			funcAddr = uint32(ln.Addr) // missing if not generated yet
			if funcAddr == 0 {
				ln.AddTableIndex(i)
			}
		}

		sigIndex := uint32(math.MaxInt32) // invalid signature index by default

		if funcIndex < uint32(len(m.Funcs)) {
			sigIndex = m.Funcs[funcIndex]
		}

		binary.LittleEndian.PutUint64(ptr[:8], (uint64(sigIndex)<<32)|uint64(funcAddr))
		ptr = ptr[8:]

		if debug.Enabled {
			debug.Printf("element %d: function %d at 0x%x with signature %d", i, funcIndex, funcAddr, sigIndex)
		}
	}

	if initFuncCount < len(m.Funcs) {
		eventHandler(event.Init)

		for i := initFuncCount; i < len(m.Funcs); i++ {
			genFunction(&funcStorage, load, i, m.Types[m.Funcs[i]], true)
		}

		eventHandler(event.FunctionBarrier)

		table := p.Text.Bytes()[rodata.TableAddr:]

		for i := initFuncCount; i < len(m.Funcs); i++ {
			ln := &p.FuncLinks[i]
			addr := uint32(ln.Addr)

			for _, tableIndex := range ln.TableIndexes {
				offset := tableIndex * 8
				atomic.PutUint32(table[offset:offset+4], addr) // overwrite only function addr
			}

			linker.UpdateCalls(p.Text.Bytes(), &ln.L)
		}
	}
}

// genCommons except the contents of the table.
func genCommons(p *gen.Prog) {
	asm.PadUntil(p, rodata.CommonsAddr)

	var (
		tableSize   = len(p.Module.TableFuncs) * 8
		commonsEnd  = rodata.TableAddr + tableSize
		commonsSize = commonsEnd - rodata.CommonsAddr
	)

	p.Text.Extend(commonsSize)
	text := p.Text.Bytes()

	binary.LittleEndian.PutUint32(text[rodata.Mask7fAddr32:], 0x7fffffff)
	binary.LittleEndian.PutUint64(text[rodata.Mask7fAddr64:], 0x7fffffffffffffff)
	binary.LittleEndian.PutUint32(text[rodata.Mask80Addr32:], 0x80000000)
	binary.LittleEndian.PutUint64(text[rodata.Mask80Addr64:], 0x8000000000000000)
	binary.LittleEndian.PutUint32(text[rodata.Mask5f00Addr32:], 0x5f000000)
	binary.LittleEndian.PutUint64(text[rodata.Mask43e0Addr64:], 0x43e0000000000000)
}
<commit_msg>codegen: fix instruction map offsets<commit_after>// Copyright (c) 2016 Timo Savola. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package codegen

import (
	"bytes"
	"encoding/binary"
	"errors"
	"math"

	"github.com/tsavola/wag/compile/event"
	"github.com/tsavola/wag/internal/code"
	"github.com/tsavola/wag/internal/gen"
	"github.com/tsavola/wag/internal/gen/atomic"
	"github.com/tsavola/wag/internal/gen/debug"
	"github.com/tsavola/wag/internal/gen/link"
	"github.com/tsavola/wag/internal/gen/rodata"
	"github.com/tsavola/wag/internal/loader"
	"github.com/tsavola/wag/internal/module"
	"github.com/tsavola/wag/internal/obj"
	"github.com/tsavola/wag/object/abi"
	"github.com/tsavola/wag/trap"
)

func GenProgram(
	text code.Buffer,
	objMap obj.ObjectMapper,
	load loader.L,
	m *module.M,
	lib *module.Library,
	eventHandler func(event.Event),
	initFuncCount int,
	debugConfig *gen.DebuggerSupport,
) {
	funcStorage := gen.Func{
		Prog: gen.Prog{
			Module:    m,
			Text:      code.Buf{Buffer: text},
			Map:       objMap,
			FuncLinks: make([]link.FuncL, len(m.Funcs)),
		},
	}
	p := &funcStorage.Prog

	userFuncCount := len(m.Funcs) - len(m.ImportFuncs)
	p.Map.InitObjectMap(len(m.ImportFuncs), userFuncCount)

	p.DebugMap, _ = objMap.(obj.DebugObjectMapper)
	if p.DebugMap != nil {
		p.Debugger = makeDebugger(debugConfig, load.R)
	}

	if debug.Enabled {
		if debug.Depth != 0 {
			debug.Printf("")
		}
		debug.Depth = 0
	}

	if n := load.Varuint32(); n != uint32(userFuncCount) {
		panic(module.Errorf("wrong number of function bodies: %d (should be: %d)", n, userFuncCount))
	}

	if p.Text.Addr != abi.TextAddrNoFunction {
		panic(errors.New("unexpected initial text address"))
	}
	asm.TrapHandler(p, trap.NoFunction)

	if p.Text.Addr == abi.TextAddrNoFunction || p.Text.Addr > abi.TextAddrResume {
		panic("bad text address after NoFunction trap handler")
	}
	asm.PadUntil(p, abi.TextAddrResume)
	asm.Resume(p)

	if p.Text.Addr <= abi.TextAddrResume || p.Text.Addr > abi.TextAddrEnter {
		panic("bad text address after resume routine")
	}
	asm.PadUntil(p, abi.TextAddrEnter)
	// Virtual return point for resuming a program which was suspended before
	// execution started.  This call site must be at index 0, and its address
	// must match the TextAddrEnter routine.
	p.Map.PutCallSite(uint32(p.Text.Addr), obj.Word*2) // Depth includes start and entry addresses.
	asm.Enter(p)

	if p.Text.Addr > rodata.CommonsAddr {
		panic("bad text address after init routines")
	}
	genCommons(p)

	for id := trap.NoFunction + 1; id < trap.NumTraps; id++ {
		asm.AlignFunc(p)
		p.TrapLinks[id].Addr = p.Text.Addr

		switch id {
		case trap.CallStackExhausted:
			asm.TrapHandlerRewindCallStackExhausted(p)

		default:
			asm.TrapHandler(p, id)
		}
	}

	for i := range p.TrapLinkRewindSuspended {
		asm.AlignFunc(p)
		p.TrapLinkRewindSuspended[i].Addr = p.Text.Addr

		asm.TrapHandlerRewindSuspended(p, i)
	}

	p.ImportContext = lib // Generate import functions in library context.

	for i, imp := range m.ImportFuncs {
		code := bytes.NewReader(lib.CodeFuncs[imp.LibraryFunc-uint32(len(lib.ImportFuncs))])
		genFunction(&funcStorage, loader.L{R: code}, i, lib.Types[lib.Funcs[imp.LibraryFunc]], false)
	}

	p.ImportContext = nil

	if eventHandler == nil {
		initFuncCount = len(m.Funcs)
	}

	for i := len(m.ImportFuncs); i < initFuncCount; i++ {
		genFunction(&funcStorage, load, i, m.Types[m.Funcs[i]], false)
		linker.UpdateCalls(p.Text.Bytes(), &p.FuncLinks[i].L)
	}

	ptr := p.Text.Bytes()[rodata.TableAddr:]

	for i, funcIndex := range m.TableFuncs {
		var funcAddr uint32 // NoFunction trap by default

		if funcIndex < uint32(len(p.FuncLinks)) {
			ln := &p.FuncLinks[funcIndex]
			funcAddr = uint32(ln.Addr) // missing if not generated yet
			if funcAddr == 0 {
				ln.AddTableIndex(i)
			}
		}

		sigIndex := uint32(math.MaxInt32) // invalid signature index by default

		if funcIndex < uint32(len(m.Funcs)) {
			sigIndex = m.Funcs[funcIndex]
		}

		binary.LittleEndian.PutUint64(ptr[:8], (uint64(sigIndex)<<32)|uint64(funcAddr))
		ptr = ptr[8:]

		if debug.Enabled {
			debug.Printf("element %d: function %d at 0x%x with signature %d", i, funcIndex, funcAddr, sigIndex)
		}
	}

	if initFuncCount < len(m.Funcs) {
		eventHandler(event.Init)

		for i := initFuncCount; i < len(m.Funcs); i++ {
			genFunction(&funcStorage, load, i, m.Types[m.Funcs[i]], true)
		}

		eventHandler(event.FunctionBarrier)

		table := p.Text.Bytes()[rodata.TableAddr:]

		for i := initFuncCount; i < len(m.Funcs); i++ {
			ln := &p.FuncLinks[i]
			addr := uint32(ln.Addr)

			for _, tableIndex := range ln.TableIndexes {
				offset := tableIndex * 8
				atomic.PutUint32(table[offset:offset+4], addr) // overwrite only function addr
			}

			linker.UpdateCalls(p.Text.Bytes(), &ln.L)
		}
	}
}

// genCommons except the contents of the table.
func genCommons(p *gen.Prog) {
	asm.PadUntil(p, rodata.CommonsAddr)

	var (
		tableSize   = len(p.Module.TableFuncs) * 8
		commonsEnd  = rodata.TableAddr + tableSize
		commonsSize = commonsEnd - rodata.CommonsAddr
	)

	p.Text.Extend(commonsSize)
	text := p.Text.Bytes()

	binary.LittleEndian.PutUint32(text[rodata.Mask7fAddr32:], 0x7fffffff)
	binary.LittleEndian.PutUint64(text[rodata.Mask7fAddr64:], 0x7fffffffffffffff)
	binary.LittleEndian.PutUint32(text[rodata.Mask80Addr32:], 0x80000000)
	binary.LittleEndian.PutUint64(text[rodata.Mask80Addr64:], 0x8000000000000000)
	binary.LittleEndian.PutUint32(text[rodata.Mask5f00Addr32:], 0x5f000000)
	binary.LittleEndian.PutUint64(text[rodata.Mask43e0Addr64:], 0x43e0000000000000)
}
<|endoftext|>


<commit_before>// Package nametransform encrypts and decrypts filenames.
package nametransform

import (
	"bytes"
	"crypto/aes"
	"encoding/base64"
	"syscall"

	"github.com/rfjakob/eme"

	"github.com/rfjakob/gocryptfs/internal/nametransform/dirivcache"
	"github.com/rfjakob/gocryptfs/internal/tlog"
)

// NameTransform is used to transform filenames.
type NameTransform struct {
	emeCipher  *eme.EMECipher
	longNames  bool
	DirIVCache dirivcache.DirIVCache
	// B64 = either base64.URLEncoding or base64.RawURLEncoding, depeding
	// on the Raw64 feature flag
	B64 *base64.Encoding
}

// New returns a new NameTransform instance.
func New(e *eme.EMECipher, longNames bool, raw64 bool) *NameTransform {
	b64 := base64.URLEncoding
	if raw64 {
		b64 = base64.RawURLEncoding
	}
	return &NameTransform{
		emeCipher: e,
		longNames: longNames,
		B64:       b64,
	}
}

// DecryptName decrypts a base64-encoded encrypted filename "cipherName" using the
// initialization vector "iv".
func (n *NameTransform) DecryptName(cipherName string, iv []byte) (string, error) {
	bin, err := n.B64.DecodeString(cipherName)
	if err != nil {
		return "", err
	}
	if len(bin) == 0 {
		tlog.Warn.Printf("DecryptName: empty input")
		return "", syscall.EBADMSG
	}
	if len(bin)%aes.BlockSize != 0 {
		tlog.Debug.Printf("DecryptName %q: decoded length %d is not a multiple of 16", cipherName, len(bin))
		return "", syscall.EBADMSG
	}
	bin = n.emeCipher.Decrypt(iv, bin)
	bin, err = unPad16(bin)
	if err != nil {
		tlog.Debug.Printf("DecryptName: unPad16 error detail: %v", err)
		// unPad16 returns detailed errors including the position of the
		// incorrect bytes. Kill the padding oracle by lumping everything into
		// a generic error.
		return "", syscall.EBADMSG
	}
	// A name can never contain a null byte or "/". Make sure we never return those
	// to the kernel, even when we read a corrupted (or fuzzed) filesystem.
	if bytes.Contains(bin, []byte{0}) || bytes.Contains(bin, []byte("/")) {
		return "", syscall.EBADMSG
	}
	plain := string(bin)
	return plain, err
}

// EncryptName encrypts "plainName", returns a base64-encoded "cipherName64".
// Used internally by EncryptPathDirIV().
// The encryption is either CBC or EME, depending on "useEME".
//
// This function is exported because fusefrontend needs access to the full (not hashed)
// name if longname is used. Otherwise you should use EncryptPathDirIV()
func (n *NameTransform) EncryptName(plainName string, iv []byte) (cipherName64 string) {
	bin := []byte(plainName)
	bin = pad16(bin)
	bin = n.emeCipher.Encrypt(iv, bin)
	cipherName64 = n.B64.EncodeToString(bin)
	return cipherName64
}
<commit_msg>nametransform: Return error if decrypted name is '.' or '..'<commit_after>// Package nametransform encrypts and decrypts filenames.
package nametransform

import (
	"bytes"
	"crypto/aes"
	"encoding/base64"
	"syscall"

	"github.com/rfjakob/eme"

	"github.com/rfjakob/gocryptfs/internal/nametransform/dirivcache"
	"github.com/rfjakob/gocryptfs/internal/tlog"
)

// NameTransform is used to transform filenames.
type NameTransform struct {
	emeCipher  *eme.EMECipher
	longNames  bool
	DirIVCache dirivcache.DirIVCache
	// B64 = either base64.URLEncoding or base64.RawURLEncoding, depeding
	// on the Raw64 feature flag
	B64 *base64.Encoding
}

// New returns a new NameTransform instance.
func New(e *eme.EMECipher, longNames bool, raw64 bool) *NameTransform {
	b64 := base64.URLEncoding
	if raw64 {
		b64 = base64.RawURLEncoding
	}
	return &NameTransform{
		emeCipher: e,
		longNames: longNames,
		B64:       b64,
	}
}

// DecryptName decrypts a base64-encoded encrypted filename "cipherName" using the
// initialization vector "iv".
func (n *NameTransform) DecryptName(cipherName string, iv []byte) (string, error) {
	bin, err := n.B64.DecodeString(cipherName)
	if err != nil {
		return "", err
	}
	if len(bin) == 0 {
		tlog.Warn.Printf("DecryptName: empty input")
		return "", syscall.EBADMSG
	}
	if len(bin)%aes.BlockSize != 0 {
		tlog.Debug.Printf("DecryptName %q: decoded length %d is not a multiple of 16", cipherName, len(bin))
		return "", syscall.EBADMSG
	}
	bin = n.emeCipher.Decrypt(iv, bin)
	bin, err = unPad16(bin)
	if err != nil {
		tlog.Debug.Printf("DecryptName: unPad16 error detail: %v", err)
		// unPad16 returns detailed errors including the position of the
		// incorrect bytes. Kill the padding oracle by lumping everything into
		// a generic error.
		return "", syscall.EBADMSG
	}
	// A name can never contain a null byte or "/". Make sure we never return those
	// to the kernel, even when we read a corrupted (or fuzzed) filesystem.
	if bytes.Contains(bin, []byte{0}) || bytes.Contains(bin, []byte("/")) {
		return "", syscall.EBADMSG
	}
	// The name should never be "." or "..".
	if bytes.Equal(bin, []byte(".")) || bytes.Equal(bin, []byte("..")) {
		return "", syscall.EBADMSG
	}
	plain := string(bin)
	return plain, err
}

// EncryptName encrypts "plainName", returns a base64-encoded "cipherName64".
// Used internally by EncryptPathDirIV().
// The encryption is either CBC or EME, depending on "useEME".
//
// This function is exported because fusefrontend needs access to the full (not hashed)
// name if longname is used. Otherwise you should use EncryptPathDirIV()
func (n *NameTransform) EncryptName(plainName string, iv []byte) (cipherName64 string) {
	bin := []byte(plainName)
	bin = pad16(bin)
	bin = n.emeCipher.Encrypt(iv, bin)
	cipherName64 = n.B64.EncodeToString(bin)
	return cipherName64
}
<|endoftext|>


<commit_before>package readpassword

import (
	"os"

	"github.com/rfjakob/gocryptfs/internal/exitcodes"
	"github.com/rfjakob/gocryptfs/internal/tlog"
)

const (
	// TrezorPayloadLen is the length of the payload data passed to Trezor's
	// CipherKeyValue function.
	TrezorPayloadLen = 32
)

// Trezor reads 16 deterministically derived bytes from a
// SatoshiLabs Trezor USB security module.
// The bytes are pseudorandom binary data and may contain null bytes.
// This function either succeeds and returns 16 bytes or calls os.Exit to end
// the application.
func Trezor(payload []byte) []byte {
	if len(payload) != TrezorPayloadLen {
		tlog.Fatal.Printf("Invalid TrezorPayload length: wanted %d, got %d bytes\n", TrezorPayloadLen, len(payload))
		os.Exit(exitcodes.LoadConf)
	}
	var err error
	// TODO try to read bytes here....
	// Handle errors
	if err != nil {
		tlog.Fatal.Printf("xxx some error was encountered...")
		os.Exit(exitcodes.TrezorError)
	}
	tlog.Warn.Println("XXX readpassword.Trezor(): not implemented yet - returning hardcoded dummy bytes XXX")
	return []byte("1234567890123456")
}
<commit_msg>Implemented the support of Trezor devices.<commit_after>package readpassword

import (
	"os"

	"github.com/rfjakob/gocryptfs/internal/exitcodes"
	"github.com/rfjakob/gocryptfs/internal/tlog"

	"github.com/xaionaro-go/cryptoWallet"
	"github.com/xaionaro-go/cryptoWallet/vendors"
)

const (
	// TrezorPayloadLen is the length of the payload data passed to Trezor's
	// CipherKeyValue function.
	TrezorPayloadLen        = 32
	trezorNonce             = "" // the "nonce" is optional and has no use in here
	trezorKeyName           = "gocryptfs"
	trezorKeyDerivationPath = `m/10019'/0'`
)

func trezorGetPin(title, description, ok, cancel string) ([]byte, error) {
	return Once("", title), nil
}
func trezorGetConfirm(title, description, ok, cancel string) (bool, error) {
	return false, nil // do not retry on connection failure
}

// Trezor reads 32 deterministically derived bytes from a
// SatoshiLabs Trezor USB security module.
// The bytes are pseudorandom binary data and may contain null bytes.
// This function either succeeds and returns 32 bytes or calls os.Exit to end
// the application.
func Trezor(payload []byte) []byte {
	if len(payload) != TrezorPayloadLen {
		tlog.Fatal.Printf("Invalid TrezorPayload length: wanted %d, got %d bytes\n", TrezorPayloadLen, len(payload))
		os.Exit(exitcodes.LoadConf)
	}

	// Find all trezor devices
	trezors := cryptoWallet.Find(cryptoWallet.Filter{
		VendorID:   &[]uint16{vendors.GetVendorID("satoshilabs")}[0],
		ProductIDs: []uint16{1 /* Trezor One */},
	})

	// ATM, we require to one and only one trezor device to be connected.
	// The support of multiple trezor devices is not implemented, yet.
	if len(trezors) == 0 {
		tlog.Fatal.Printf("Trezor device is not found. Check the connection.")
		os.Exit(exitcodes.TrezorError)
	}
	if len(trezors) > 1 {
		tlog.Fatal.Printf("It's more than one Trezor device connected. This case is not implemented, yet. The number of currently connected devices: %v.", len(trezors))
		os.Exit(exitcodes.TrezorError)
	}

	// Using the first found device
	trezor := trezors[0]

	// Trezor may ask for PIN or Passphrase. Setting the handler for this case.
	trezor.SetGetPinFunc(trezorGetPin)

	// In some cases (like lost connection to the Trezor device and cannot
	// reconnect) it's required to get a confirmation from the user to
	// retry to reconnect. Setting the handler for this case.
	trezor.SetGetConfirmFunc(trezorGetConfirm)

	// To reset the state of the device and check if it's initialized.
	// If device is not initialized then trezor.Reset() will return an
	// error.
	err := trezor.Reset()
	if err != nil {
		tlog.Fatal.Printf("Cannot reset the Trezor device. Error: %v", err.Error())
		os.Exit(exitcodes.TrezorError)
	}

	// To generate a deterministic key we trying to decrypt our
	// predefined constant key using the Trezor device. The resulting key
	// will depend on next variables:
	// * the Trezor master key;
	// * the passphrase (passed to the Trezor).
	//
	// The right key will be received only if both values (mentioned
	// above) are correct.
	//
	// Note:
	// Also the resulting key depends on this values (that we defined as
	// constants above):
	// * the key derivation path;
	// * the "encrypted" payload;
	// * the nonce;
	// * the key name.
	key, err := trezor.DecryptKey(trezorKeyDerivationPath, payload, []byte(trezorNonce), trezorKeyName)
	if err != nil {
		tlog.Fatal.Printf("Cannot get the key from the Trezor device. Error description:\n\t%v", err.Error())
		os.Exit(exitcodes.TrezorError)
	}

	// Everything ok
	return key
}
<|endoftext|>


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

package verification

import (
	"context"
	"fmt"
	"os"
	"time"

	"github.com/google/exposure-notifications-server/internal/android"
	"github.com/google/exposure-notifications-server/internal/logging"
	"github.com/google/exposure-notifications-server/internal/model"
)

var (
	// Is safetynet being enforced on this server.
	// TODO(mikehelmick): Remove after client verification.
	enforce = true
)

func init() {
	disableSN := os.Getenv("DISABLE_SAFETYNET")
	if disableSN != "" {
		logger := logging.FromContext(context.Background())
		logger.Errorf("SafetyNet verification disabled, to enable unset the DISABLE_SAFETYNET environment variable")
		enforce = false
	}
}

func VerifyRegions(cfg *model.APIConfig, data model.Publish) error {
	if cfg == nil {
		return fmt.Errorf("no allowed regions configured")
	}

	if cfg.AllowAllRegions {
		return nil
	}

	for _, r := range data.Regions {
		if v, ok := cfg.AllowedRegions[r]; !ok || !v {
			return fmt.Errorf("application '%v' tried to write unauthorized region: '%v'", cfg.AppPackageName, r)
		}
	}

	// no error - application didn't try to write for regions that it isn't allowed
	return nil
}

func VerifySafetyNet(ctx context.Context, requestTime time.Time, cfg *model.APIConfig, data model.Publish) error {
	logger := logging.FromContext(ctx)
	if !enforce {
		logger.Error("skipping safetynet verification, disabled by override")
		return nil
	}

	if cfg == nil {
		logger.Errorf("safetynet enabled, but no config for application: %v", data.AppPackageName)
		// TODO(mikehelmick): Should this be a default configuration?
		return fmt.Errorf("cannot enforce safetynet, no application config")
	}

	opts := cfg.VerifyOpts(requestTime.UTC())
	err := android.ValidateAttestation(ctx, data.Verification, opts)
	if err != nil {
		if cfg.BypassSafetynet {
			logger.Errorf("safetynet failed, but bypass enabled for app: '%v', failure: %v", data.AppPackageName, err)
			return nil
		}
		return fmt.Errorf("android.ValidateAttestation: %v", err)
	}

	return nil
}
<commit_msg>remove atttttestation enforcement override capabilities. This can be done via individual application config now. (#39)<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package verification

import (
	"context"
	"fmt"
	"time"

	"github.com/google/exposure-notifications-server/internal/android"
	"github.com/google/exposure-notifications-server/internal/logging"
	"github.com/google/exposure-notifications-server/internal/model"
)

// VerifyRegions checks the request regions against the regions allowed by
// the configuration for the application.
func VerifyRegions(cfg *model.APIConfig, data model.Publish) error {
	if cfg == nil {
		return fmt.Errorf("no allowed regions configured")
	}

	if cfg.AllowAllRegions {
		return nil
	}

	for _, r := range data.Regions {
		if v, ok := cfg.AllowedRegions[r]; !ok || !v {
			return fmt.Errorf("application '%v' tried to write unauthorized region: '%v'", cfg.AppPackageName, r)
		}
	}

	// no error - application didn't try to write for regions that it isn't allowed
	return nil
}

// VerifySafetyNet verifies the SafetyNet device attestation against the allowed configuration for the application.
func VerifySafetyNet(ctx context.Context, requestTime time.Time, cfg *model.APIConfig, data model.Publish) error {
	logger := logging.FromContext(ctx)

	if cfg == nil {
		logger.Errorf("safetynet enabled, but no config for application: %v", data.AppPackageName)
		// TODO(mikehelmick): Should this be a default configuration?
		return fmt.Errorf("cannot enforce safetynet, no application config")
	}

	opts := cfg.VerifyOpts(requestTime.UTC())
	err := android.ValidateAttestation(ctx, data.Verification, opts)
	if err != nil {
		if cfg.BypassSafetynet {
			logger.Errorf("safetynet failed, but bypass enabled for app: '%v', failure: %v", data.AppPackageName, err)
			return nil
		}
		return fmt.Errorf("android.ValidateAttestation: %v", err)
	}

	return nil
}
<|endoftext|>


<commit_before>// Package set implements a Set using map.
package set

import "sync"

// Set stores distinct items.
// An empty Set struct is not valid for use.
// Use NewSet instead.
type Set struct {
	m map[interface{}]struct{}
	sync.RWMutex
}

// NewSet creates a new Set.
func NewSet() *Set {
	return &Set{make(map[interface{}]struct{}), sync.RWMutex{}}
}

// Add adds a value to the set.
func (s *Set) Add(value interface{}) {
	s.Lock()
	s.m[value] = struct{}{}
	s.Unlock()
}

// AddAll adds all values to the set distinctly.
func (s *Set) AddAll(values ...interface{}) {
	s.Lock()
	for _, value := range values {
		s.m[value] = struct{}{}
	}
	s.Unlock()
}

// Remove removes value from the set.
func (s *Set) Remove(value interface{}) {
	s.Lock()
	delete(s.m, value)
	s.Unlock()
}

// RemoveAll removes all values from the set.
func (s *Set) RemoveAll(values ...interface{}) {
	s.Lock()
	for _, value := range values {
		delete(s.m, value)
	}
	s.Unlock()
}

// Contains check if value exists in the set.
func (s *Set) Contains(value interface{}) bool {
	s.RLock()
	defer s.RUnlock()
	_, ok := s.m[value]
	return ok
}

// ContainsAll checks if all values exist in the set.
func (s *Set) ContainsAll(values ...interface{}) bool {
	s.RLock()
	defer s.RUnlock()
	for _, value := range values {
		_, ok := s.m[value]
		if !ok {
			return false
		}
	}
	return true
}

// ContainsFunc iterates all the items in the set and passes
// each to f. It returns true the first time a call to f returns
// true and false if no call to f returns true.
func (s *Set) ContainsFunc(f func(interface{}) bool) bool {
	s.RLock()
	defer s.RUnlock()
	for k := range s.m {
		if f(k) {
			return true
		}
	}
	return false
}

// Size returns the number of items in the set.
func (s *Set) Size() int {
	s.RLock()
	defer s.RUnlock()
	return len(s.m)
}

// Clear empties the set.
func (s *Set) Clear() {
	s.Lock()
	s.m = make(map[interface{}]struct{})
	s.Unlock()
}

// Iterator returns a new Iterator to iterate through values in the set.
func (s *Set) Iterator() Iterator {
	iterChan := make(chan interface{})
	go func() {
		for k := range s.m {
			iterChan <- k
		}
		close(iterChan)
	}()
	return IterFunc(func() (interface{}, bool) {
		value, ok := <-iterChan
		return value, ok
	})
}

// IteratorFunc is similar to Iterator but it only iterates through values
// that when passed to f, f returns true.
func (s *Set) IteratorFunc(f func(value interface{}) bool) Iterator {
	iterChan := make(chan interface{})
	go func() {
		for k := range s.m {
			if f(k) {
				iterChan <- k
			}
		}
		close(iterChan)
	}()
	return IterFunc(func() (interface{}, bool) {
		value, ok := <-iterChan
		return value, ok
	})
}

// Items returns slice of all items in the set.
func (s *Set) Items() []interface{} {
	s.RLock()
	defer s.RUnlock()
	items := make([]interface{}, len(s.m))
	i := 0
	for k := range s.m {
		items[i] = k
	}
	return items
}

// ItemsFunc returns slice of all items that when passed to f, f returns true.
func (s *Set) ItemsFunc(f func(value interface{}) bool) []interface{} {
	s.RLock()
	defer s.RUnlock()
	var items []interface{}
	for k := range s.m {
		if f(k) {
			items = append(items, k)
		}
	}
	return items
}

// Iterator iterates through a group of items.
type Iterator interface {
	// HasNext checks if there is a next value and moves to it.
	HasNext() bool
	// Value returns the current item. The initial value is nil and requires a call
	// to HasNext before usage. If HasNext returns false, it returns nil.
	Value() interface{}
}

type iterable struct {
	value interface{}
	next  func() (interface{}, bool)
}

func (i *iterable) HasNext() bool {
	value, ok := i.next()
	i.value = value
	return ok
}

func (i *iterable) Value() interface{} {
	return i.value
}

// IterFunc creates an Iterator using f
func IterFunc(f func() (interface{}, bool)) Iterator {
	return &iterable{next: f}
}
<commit_msg>Use mutex for iterators<commit_after>// Package set implements a Set using map.
package set

import "sync"

// Set stores distinct items.
// An empty Set struct is not valid for use.
// Use NewSet instead.
type Set struct {
	m map[interface{}]struct{}
	sync.RWMutex
}

// NewSet creates a new Set.
func NewSet() *Set {
	return &Set{make(map[interface{}]struct{}), sync.RWMutex{}}
}

// Add adds a value to the set.
func (s *Set) Add(value interface{}) {
	s.Lock()
	defer s.Unlock()
	s.m[value] = struct{}{}
}

// AddAll adds all values to the set distinctly.
func (s *Set) AddAll(values ...interface{}) {
	s.Lock()
	defer s.Unlock()
	for _, value := range values {
		s.m[value] = struct{}{}
	}
}

// Remove removes value from the set.
func (s *Set) Remove(value interface{}) {
	s.Lock()
	defer s.Unlock()
	delete(s.m, value)
}

// RemoveAll removes all values from the set.
func (s *Set) RemoveAll(values ...interface{}) {
	s.Lock()
	defer s.Unlock()
	for _, value := range values {
		delete(s.m, value)
	}
}

// Contains check if value exists in the set.
func (s *Set) Contains(value interface{}) bool {
	s.RLock()
	defer s.RUnlock()
	_, ok := s.m[value]
	return ok
}

// ContainsAll checks if all values exist in the set.
func (s *Set) ContainsAll(values ...interface{}) bool {
	s.RLock()
	defer s.RUnlock()
	for _, value := range values {
		_, ok := s.m[value]
		if !ok {
			return false
		}
	}
	return true
}

// ContainsFunc iterates all the items in the set and passes
// each to f. It returns true the first time a call to f returns
// true and false if no call to f returns true.
func (s *Set) ContainsFunc(f func(interface{}) bool) bool {
	s.RLock()
	defer s.RUnlock()
	for k := range s.m {
		if f(k) {
			return true
		}
	}
	return false
}

// Size returns the number of items in the set.
func (s *Set) Size() int {
	s.RLock()
	defer s.RUnlock()
	return len(s.m)
}

// Clear empties the set.
func (s *Set) Clear() {
	s.Lock()
	defer s.Unlock()
	s.m = make(map[interface{}]struct{})
}

// Iterator returns a new Iterator to iterate through values in the set.
func (s *Set) Iterator() Iterator {
	iterChan := make(chan interface{})
	go func() {
		s.RLock()
		defer s.RUnlock()
		for k := range s.m {
			iterChan <- k
		}
		close(iterChan)
	}()
	return IterFunc(func() (interface{}, bool) {
		value, ok := <-iterChan
		return value, ok
	})
}

// IteratorFunc is similar to Iterator but it only iterates through values
// that when passed to f, f returns true.
func (s *Set) IteratorFunc(f func(value interface{}) bool) Iterator {
	iterChan := make(chan interface{})
	go func() {
		s.RLock()
		defer s.RUnlock()
		for k := range s.m {
			if f(k) {
				iterChan <- k
			}
		}
		close(iterChan)
	}()
	return IterFunc(func() (interface{}, bool) {
		value, ok := <-iterChan
		return value, ok
	})
}

// Items returns slice of all items in the set.
func (s *Set) Items() []interface{} {
	s.RLock()
	defer s.RUnlock()
	items := make([]interface{}, len(s.m))
	i := 0
	for k := range s.m {
		items[i] = k
		i++
	}
	return items
}

// ItemsFunc returns slice of all items that when passed to f, f returns true.
func (s *Set) ItemsFunc(f func(value interface{}) bool) []interface{} {
	s.RLock()
	defer s.RUnlock()
	var items []interface{}
	for k := range s.m {
		if f(k) {
			items = append(items, k)
		}
	}
	return items
}

// Iterator iterates through a group of items.
type Iterator interface {
	// HasNext checks if there is a next value and moves to it.
	HasNext() bool
	// Value returns the current item. The initial value is nil and requires a call
	// to HasNext before usage. If HasNext returns false, it returns nil.
	Value() interface{}
}

type iterable struct {
	value interface{}
	next  func() (interface{}, bool)
}

func (i *iterable) HasNext() bool {
	value, ok := i.next()
	i.value = value
	return ok
}

func (i *iterable) Value() interface{} {
	return i.value
}

// IterFunc creates an Iterator using f
func IterFunc(f func() (interface{}, bool)) Iterator {
	return &iterable{next: f}
}
<|endoftext|>


<commit_before>package poissondisc

import (
	"math"
	"math/rand"
	"time"
)

func Sample(x0, y0, x1, y1, r float64, k int, rnd *rand.Rand) []Point {
	if rnd == nil {
		rnd = rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
	}

	var result []Point
	var active []Point
	grid := newGrid(x0, y0, x1, y1, r)

	// add the first point
	{
		x := x0 + (x1-x0)/2
		y := y0 + (y1-y0)/2
		p := Point{x, y}
		grid.insert(p)
		active = append(active, p)
		result = append(result, p)
	}

	// try to add points until no more are active
	for len(active) > 0 {
		// pick a random active point
		index := rnd.Intn(len(active))
		point := active[index]
		ok := false

		// make k attempts to place a nearby point
		for i := 0; i < k; i++ {
			a := rnd.Float64() * 2 * math.Pi
			d := rnd.Float64()*r + r
			x := point.X + math.Cos(a)*d
			y := point.Y + math.Sin(a)*d
			if x < x0 || y < y0 || x > x1 || y > y1 {
				continue
			}
			p := Point{x, y}
			if !grid.insert(p) {
				continue
			}
			result = append(result, p)
			active = append(active, p)
			ok = true
			break
		}

		// make this point inactive if we failed to add a new point
		if !ok {
			active[index] = active[len(active)-1]
			active = active[:len(active)-1]
		}
	}

	return result
}
<commit_msg>Add doc<commit_after>package poissondisc

import (
	"math"
	"math/rand"
	"time"
)

// Sample produces points via Poisson-disc sampling.
// The points will all be within the box defined by `x0`, `y0`, `x1`, `y1`.
// No two points will be closer than the defined radius `r`.
// For each point, the algorithm will make `k` attempts to place a
// neighboring point. Increase this value for a better sampling or decrease
// it to reduce algorithm runtime.
// You may provide your own `*rand.Rand` instance or `nil` to have one
// created for you.
// Learn more about Poisson-disc sampling from the links below:
// https://www.jasondavies.com/poisson-disc/
// https://bl.ocks.org/mbostock/dbb02448b0f93e4c82c3
func Sample(x0, y0, x1, y1, r float64, k int, rnd *rand.Rand) []Point {
	if rnd == nil {
		rnd = rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
	}

	var result []Point
	var active []Point
	grid := newGrid(x0, y0, x1, y1, r)

	// add the first point
	{
		x := x0 + (x1-x0)/2
		y := y0 + (y1-y0)/2
		p := Point{x, y}
		grid.insert(p)
		active = append(active, p)
		result = append(result, p)
	}

	// try to add points until no more are active
	for len(active) > 0 {
		// pick a random active point
		index := rnd.Intn(len(active))
		point := active[index]
		ok := false

		// make k attempts to place a nearby point
		for i := 0; i < k; i++ {
			a := rnd.Float64() * 2 * math.Pi
			d := rnd.Float64()*r + r
			x := point.X + math.Cos(a)*d
			y := point.Y + math.Sin(a)*d
			if x < x0 || y < y0 || x > x1 || y > y1 {
				continue
			}
			p := Point{x, y}
			if !grid.insert(p) {
				continue
			}
			result = append(result, p)
			active = append(active, p)
			ok = true
			break
		}

		// make this point inactive if we failed to add a new point
		if !ok {
			active[index] = active[len(active)-1]
			active = active[:len(active)-1]
		}
	}

	return result
}
<|endoftext|>


<commit_before>// Copyright © 2011 Steve McCoy
// Licensed under the MIT License. See LICENSE for details.

package main

import (
	"appengine"
	"appengine/datastore"
	"http"
	"regexp"
	"strconv"
	"template"
	"time"
)

var maxPasteLen = 8192
var templates = make(map[string]*template.Template)
var viewValidator = regexp.MustCompile("^/([0-9]+)(/([a-z]+)?)?$")

func init() {
	for _, tmpl := range []string{"paste", "plain", "fancy"} {
		t := "tmplt/" + tmpl + ".html"
		templates[tmpl] = template.MustParseFile(t, nil)
	}

	http.HandleFunc("/", pasteHandler)
}

func pasteHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	c.Debugf("path = %s", r.URL.Path)

	if r.Method == "POST" && r.URL.Path == "/" {
		c.Debugf("posting")
		saveHandler(w, r)
		return
	}

	if r.Method == "GET" && r.URL.Path == "/" {
		renderTemplate(w, "paste", new(Page))
		return
	}

	parts := viewValidator.FindStringSubmatch(r.URL.Path)
	if parts == nil {
		http.NotFound(w, r)
		return
	}

	id, err := strconv.Atoi64(parts[1])
	if err != nil {
		http.NotFound(w, r)
		return
	}

	view := parts[3]

	if r.Method == "POST" && view == "" {
		p, err := loadPage(r, id)
		if err != nil {
			p = new(Page)
		} // Oh well
		renderTemplate(w, "paste", p)
		return
	}

	if r.Method == "GET" {
		if view == "" {
			view = "plain"
		}
		p, err := loadPage(r, id)
		if err != nil || view != "plain" && view != "fancy" {
			http.NotFound(w, r)
			return
		}
		renderTemplate(w, view, p)
		return
	}
}

func saveHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	body := r.FormValue("body")
	p := &Page{
		Time: datastore.SecondsToTime(time.Seconds()),
		Body: []byte(body),
	}
	id, err := p.save(c)
	if err != nil {
		c.Errorf("Error saving paste %d\n", id)
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}
	c.Debugf("Saving paste %v\n", id)
	http.Redirect(w, r, strconv.Itoa64(id), http.StatusFound)
}

func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
	err := templates[tmpl].Execute(w, p)
	if err != nil {
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}
}
<commit_msg>Increase the paste limit.<commit_after>// Copyright © 2011 Steve McCoy
// Licensed under the MIT License. See LICENSE for details.

package main

import (
	"appengine"
	"appengine/datastore"
	"http"
	"regexp"
	"strconv"
	"template"
	"time"
)

var maxPasteLen = 32768
var templates = make(map[string]*template.Template)
var viewValidator = regexp.MustCompile("^/([0-9]+)(/([a-z]+)?)?$")

func init() {
	for _, tmpl := range []string{"paste", "plain", "fancy"} {
		t := "tmplt/" + tmpl + ".html"
		templates[tmpl] = template.MustParseFile(t, nil)
	}

	http.HandleFunc("/", pasteHandler)
}

func pasteHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	c.Debugf("path = %s", r.URL.Path)

	if r.Method == "POST" && r.URL.Path == "/" {
		c.Debugf("posting")
		saveHandler(w, r)
		return
	}

	if r.Method == "GET" && r.URL.Path == "/" {
		renderTemplate(w, "paste", new(Page))
		return
	}

	parts := viewValidator.FindStringSubmatch(r.URL.Path)
	if parts == nil {
		http.NotFound(w, r)
		return
	}

	id, err := strconv.Atoi64(parts[1])
	if err != nil {
		http.NotFound(w, r)
		return
	}

	view := parts[3]

	if r.Method == "POST" && view == "" {
		p, err := loadPage(r, id)
		if err != nil {
			p = new(Page)
		} // Oh well
		renderTemplate(w, "paste", p)
		return
	}

	if r.Method == "GET" {
		if view == "" {
			view = "plain"
		}
		p, err := loadPage(r, id)
		if err != nil || view != "plain" && view != "fancy" {
			http.NotFound(w, r)
			return
		}
		renderTemplate(w, view, p)
		return
	}
}

func saveHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	body := r.FormValue("body")
	p := &Page{
		Time: datastore.SecondsToTime(time.Seconds()),
		Body: []byte(body),
	}
	id, err := p.save(c)
	if err != nil {
		c.Errorf("Error saving paste %d\n", id)
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}
	c.Debugf("Saving paste %v\n", id)
	http.Redirect(w, r, strconv.Itoa64(id), http.StatusFound)
}

func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
	err := templates[tmpl].Execute(w, p)
	if err != nil {
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package build

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/spf13/cobra"

	"github.com/kubernetes-incubator/apiserver-builder/cmd/apiserver-boot/boot/util"
)

var versionedAPIs []string
var unversionedAPIs []string
var copyright string = "boilerplate.go.txt"
var skipGenerators []string

var generateCmd = &cobra.Command{
	Use:   "generated",
	Short: "Run code generators against repo.",
	Long:  `Automatically run by most build commands.  Writes generated source code for a repo.`,
	Example: `# Run code generators.
apiserver-boot build generated`,
	Run: RunGenerate,
}

var genericAPI = strings.Join([]string{
	"k8s.io/client-go/pkg/api/v1",
	"k8s.io/client-go/pkg/apis/apps/v1beta1",
	"k8s.io/client-go/pkg/apis/authentication/v1",
	"k8s.io/client-go/pkg/apis/authentication/v1beta1",
	"k8s.io/client-go/pkg/apis/authorization/v1",
	"k8s.io/client-go/pkg/apis/authorization/v1beta1",
	"k8s.io/client-go/pkg/apis/autoscaling/v1",
	"k8s.io/client-go/pkg/apis/autoscaling/v2alpha1",
	"k8s.io/client-go/pkg/apis/batch/v1",
	"k8s.io/client-go/pkg/apis/batch/v2alpha1",
	"k8s.io/client-go/pkg/apis/certificates/v1beta1",
	"k8s.io/client-go/pkg/apis/extensions/v1beta1",
	"k8s.io/client-go/pkg/apis/policy/v1beta1",
	"k8s.io/client-go/pkg/apis/rbac/v1alpha1",
	"k8s.io/client-go/pkg/apis/rbac/v1beta1",
	"k8s.io/client-go/pkg/apis/settings/v1alpha1",
	"k8s.io/client-go/pkg/apis/storage/v1",
	"k8s.io/client-go/pkg/apis/storage/v1beta1",
	"k8s.io/apimachinery/pkg/apis/meta/v1",
	"k8s.io/apimachinery/pkg/api/resource",
	"k8s.io/apimachinery/pkg/version",
	"k8s.io/apimachinery/pkg/runtime",
	"k8s.io/apimachinery/pkg/util/intstr"}, ",")

var extraAPI = strings.Join([]string{
	"k8s.io/apimachinery/pkg/apis/meta/v1",
	"k8s.io/apimachinery/pkg/conversion",
	"k8s.io/apimachinery/pkg/runtime"}, ",")

func AddGenerate(cmd *cobra.Command) {
	cmd.AddCommand(generateCmd)
	generateCmd.Flags().StringArrayVar(&versionedAPIs, "api-versions", []string{}, "comma separated list of APIs Versions.  e.g. foo/v1beta1,bar/v1  defaults to all directories under pkd/apis/group/version")
	generateCmd.Flags().StringArrayVar(&skipGenerators, "skip-generators", []string{}, "List of generators to skip.  If using apiserver-boot on a repo that does not use the apiserver-build code generation, specify --skip-generators=apiregister-gen")
	generateCmd.AddCommand(generateCleanCmd)
}

var generateCleanCmd = &cobra.Command{
	Use:   "clean",
	Short: "Removes generated source code",
	Long:  `Removes generated source code`,
	Run:   RunCleanGenerate,
}

func RunCleanGenerate(cmd *cobra.Command, args []string) {
	os.RemoveAll(filepath.Join("pkg", "client", "clientset_generated"))
	os.RemoveAll(filepath.Join("pkg", "client", "informers_generated"))
	os.RemoveAll(filepath.Join("pkg", "client", "listers_generated"))
	os.Remove(filepath.Join("pkg", "openapi", "openapi_generated.go"))

	filepath.Walk("pkg", func(path string, info os.FileInfo, err error) error {
		if !info.IsDir() && strings.HasPrefix(info.Name(), "zz_generated.") {
			return os.Remove(path)
		}
		return nil
	})
}

func RunGenerate(cmd *cobra.Command, args []string) {
	initApis()

	skip := map[string]interface{}{}
	for _, s := range skipGenerators {
		skip[s] = nil
	}

	if _, f1 := skip["client-gen"]; f1 {
		if _, f2 := skip["lister-gen"]; !f2 {
			log.Fatalf("Must skip lister-gen if client-gen is skipped")
		}
	}

	if _, f2 := skip["lister-gen"]; f2 {
		if _, f3 := skip["informer-gen"]; !f3 {
			log.Fatalf("Must skip informer-gen if lister-gen is skipped")
		}
	}

	util.GetCopyright(copyright)

	root, err := os.Executable()
	if err != nil {
		log.Fatalf("error: %v", err)
	}
	root = filepath.Dir(root)

	all := []string{}
	versioned := []string{}
	for _, v := range versionedAPIs {
		v = filepath.Join(util.Repo, "pkg", "apis", v)
		versioned = append(versioned, "--input-dirs", v)
		all = append(all, "--input-dirs", v)
	}
	unversioned := []string{}
	for _, u := range unversionedAPIs {
		u = filepath.Join(util.Repo, "pkg", "apis", u)
		unversioned = append(unversioned, "--input-dirs", u)
		all = append(all, "--input-dirs", u)
	}

	if _, f := skip["apiregister-gen"]; !f {
		c := exec.Command(filepath.Join(root, "apiregister-gen"),
			"--input-dirs", filepath.Join(util.Repo, "pkg", "apis", "..."),
			"--input-dirs", filepath.Join(util.Repo, "pkg", "controller", "..."),
		)
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err := c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run apiregister-gen %s %v", out, err)
		}
	}

	if _, f := skip["conversion-gen"]; !f {
		c := exec.Command(filepath.Join(root, "conversion-gen"),
			append(all,
				"-o", util.GoSrc,
				"--go-header-file", copyright,
				"-O", "zz_generated.conversion",
				"--extra-peer-dirs", extraAPI)...,
		)
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err := c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run conversion-gen %s %v", out, err)
		}
	}

	if _, f := skip["deepcopy-gen"]; !f {
		c := exec.Command(filepath.Join(root, "deepcopy-gen"),
			append(all,
				"-o", util.GoSrc,
				"--go-header-file", copyright,
				"-O", "zz_generated.deepcopy")...,
		)
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err := c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run deepcopy-gen %s %v", out, err)
		}
	}

	if _, f := skip["openapi-gen"]; !f {
		c := exec.Command(filepath.Join(root, "openapi-gen"),
			append(all,
				"-o", util.GoSrc,
				"--go-header-file", copyright,
				"-i", genericAPI,
				"--output-package", filepath.Join(util.Repo, "pkg", "openapi"))...,
		)
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err := c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run openapi-gen %s %v", out, err)
		}
	}

	if _, f := skip["defaulter-gen"]; !f {
		c := exec.Command(filepath.Join(root, "defaulter-gen"),
			append(all,
				"-o", util.GoSrc,
				"--go-header-file", copyright,
				"-O", "zz_generated.defaults",
				"--extra-peer-dirs=", extraAPI)...,
		)
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err := c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run defaulter-gen %s %v", out, err)
		}
	}

	if _, f := skip["client-gen"]; !f {
		// Builder the versioned apis client
		clientPkg := filepath.Join(util.Repo, "pkg", "client")
		clientset := filepath.Join(clientPkg, "clientset_generated")
		c := exec.Command(filepath.Join(root, "client-gen"),
			"-o", util.GoSrc,
			"--go-header-file", copyright,
			"--input-base", filepath.Join(util.Repo, "pkg", "apis"),
			"--input", strings.Join(versionedAPIs, ","),
			"--clientset-path", clientset,
			"--clientset-name", "clientset",
		)
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err := c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run client-gen %s %v", out, err)
		}

		c = exec.Command(filepath.Join(root, "client-gen"),
			"-o", util.GoSrc,
			"--go-header-file", copyright,
			"--input-base", filepath.Join(util.Repo, "pkg", "apis"),
			"--input", strings.Join(unversionedAPIs, ","),
			"--clientset-path", clientset,
			"--clientset-name", "internalclientset")
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err = c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run client-gen for unversioned APIs %s %v", out, err)
		}

		if _, f := skip["lister-gen"]; !f {
			listerPkg := filepath.Join(clientPkg, "listers_generated")
			c = exec.Command(filepath.Join(root, "lister-gen"),
				append(all,
					"-o", util.GoSrc,
					"--go-header-file", copyright,
					"--output-package", listerPkg)...,
			)
			fmt.Printf("%s\n", strings.Join(c.Args, " "))
			out, err = c.CombinedOutput()
			if err != nil {
				log.Fatalf("failed to run lister-gen %s %v", out, err)
			}

			if _, f := skip["informer-gen"]; !f {
				informerPkg := filepath.Join(clientPkg, "informers_generated")
				c = exec.Command(filepath.Join(root, "informer-gen"),
					append(all,
						"-o", util.GoSrc,
						"--go-header-file", copyright,
						"--output-package", informerPkg,
						"--listers-package", listerPkg,
						"--versioned-clientset-package", filepath.Join(clientset, "clientset"),
						"--internal-clientset-package", filepath.Join(clientset, "internalclientset"))...,
				)
				fmt.Printf("%s\n", strings.Join(c.Args, " "))
				out, err := c.CombinedOutput()
				if err != nil {
					log.Fatalf("failed to run informer-gen %s %v", out, err)
				}
			}
		}
	}
}

func initApis() {
	if len(versionedAPIs) == 0 {
		groups, err := ioutil.ReadDir(filepath.Join("pkg", "apis"))
		if err != nil {
			log.Fatalf("could not read pkg/apis directory to find api Versions")
		}
		for _, g := range groups {
			if g.IsDir() {
				versionFiles, err := ioutil.ReadDir(filepath.Join("pkg", "apis", g.Name()))
				if err != nil {
					log.Fatalf("could not read pkg/apis/%s directory to find api Versions", g.Name())
				}
				versionMatch := regexp.MustCompile("^v\\d+(alpha\\d+|beta\\d+)*$")
				for _, v := range versionFiles {
					if v.IsDir() && versionMatch.MatchString(v.Name()) {
						versionedAPIs = append(versionedAPIs, filepath.Join(g.Name(), v.Name()))
					}
				}
			}
		}
	}
	u := map[string]bool{}
	for _, a := range versionedAPIs {
		u[path.Dir(a)] = true
	}
	for a, _ := range u {
		unversionedAPIs = append(unversionedAPIs, a)
	}
}
<commit_msg>`apiserver-boot build generated` now accepts the --generator flag to run only certain generators.<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package build

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/kubernetes-incubator/apiserver-builder/cmd/apiserver-boot/boot/util"
	"github.com/spf13/cobra"
	"k8s.io/apimachinery/pkg/util/sets"
)

var versionedAPIs []string
var unversionedAPIs []string
var codegenerators []string
var copyright string = "boilerplate.go.txt"
var generators = sets.String{}

var generateCmd = &cobra.Command{
	Use:   "generated",
	Short: "Run code generators against repo.",
	Long:  `Automatically run by most build commands.  Writes generated source code for a repo.`,
	Example: `# Run code generators.
apiserver-boot build generated`,
	Run: RunGenerate,
}

var genericAPI = strings.Join([]string{
	"k8s.io/client-go/pkg/api/v1",
	"k8s.io/client-go/pkg/apis/apps/v1beta1",
	"k8s.io/client-go/pkg/apis/authentication/v1",
	"k8s.io/client-go/pkg/apis/authentication/v1beta1",
	"k8s.io/client-go/pkg/apis/authorization/v1",
	"k8s.io/client-go/pkg/apis/authorization/v1beta1",
	"k8s.io/client-go/pkg/apis/autoscaling/v1",
	"k8s.io/client-go/pkg/apis/autoscaling/v2alpha1",
	"k8s.io/client-go/pkg/apis/batch/v1",
	"k8s.io/client-go/pkg/apis/batch/v2alpha1",
	"k8s.io/client-go/pkg/apis/certificates/v1beta1",
	"k8s.io/client-go/pkg/apis/extensions/v1beta1",
	"k8s.io/client-go/pkg/apis/policy/v1beta1",
	"k8s.io/client-go/pkg/apis/rbac/v1alpha1",
	"k8s.io/client-go/pkg/apis/rbac/v1beta1",
	"k8s.io/client-go/pkg/apis/settings/v1alpha1",
	"k8s.io/client-go/pkg/apis/storage/v1",
	"k8s.io/client-go/pkg/apis/storage/v1beta1",
	"k8s.io/apimachinery/pkg/apis/meta/v1",
	"k8s.io/apimachinery/pkg/api/resource",
	"k8s.io/apimachinery/pkg/version",
	"k8s.io/apimachinery/pkg/runtime",
	"k8s.io/apimachinery/pkg/util/intstr"}, ",")

var extraAPI = strings.Join([]string{
	"k8s.io/apimachinery/pkg/apis/meta/v1",
	"k8s.io/apimachinery/pkg/conversion",
	"k8s.io/apimachinery/pkg/runtime"}, ",")

func AddGenerate(cmd *cobra.Command) {
	cmd.AddCommand(generateCmd)
	generateCmd.Flags().StringArrayVar(&versionedAPIs, "api-versions", []string{}, "API version to generate code for.  Can be specified multiple times.  e.g. --api-versions foo/v1beta1 --api-versions bar/v1  defaults to all versions found under directories pkg/apis/<group>/<version>")
	generateCmd.Flags().StringArrayVar(&codegenerators, "generator", []string{}, "list of generators to run.  e.g. --generator apiregister --generator conversion Valid values: [apiregister,conversion,client,deepcopy,defaulter,openapi]")
	generateCmd.AddCommand(generateCleanCmd)
}

var generateCleanCmd = &cobra.Command{
	Use:   "clean",
	Short: "Removes generated source code",
	Long:  `Removes generated source code`,
	Run:   RunCleanGenerate,
}

func RunCleanGenerate(cmd *cobra.Command, args []string) {
	os.RemoveAll(filepath.Join("pkg", "client", "clientset_generated"))
	os.RemoveAll(filepath.Join("pkg", "client", "informers_generated"))
	os.RemoveAll(filepath.Join("pkg", "client", "listers_generated"))
	os.Remove(filepath.Join("pkg", "openapi", "openapi_generated.go"))

	filepath.Walk("pkg", func(path string, info os.FileInfo, err error) error {
		if !info.IsDir() && strings.HasPrefix(info.Name(), "zz_generated.") {
			return os.Remove(path)
		}
		return nil
	})
}

func doGen(g string) bool {
	g = strings.Replace(g, "-gen", "", -1)
	return generators.Has(g) || generators.Len() == 0
}

func RunGenerate(cmd *cobra.Command, args []string) {
	initApis()

	for _, g := range codegenerators {
		generators.Insert(strings.Replace(g, "-gen", "", -1))
	}

	util.GetCopyright(copyright)

	root, err := os.Executable()
	if err != nil {
		log.Fatalf("error: %v", err)
	}
	root = filepath.Dir(root)

	all := []string{}
	versioned := []string{}
	for _, v := range versionedAPIs {
		v = filepath.Join(util.Repo, "pkg", "apis", v)
		versioned = append(versioned, "--input-dirs", v)
		all = append(all, "--input-dirs", v)
	}
	unversioned := []string{}
	for _, u := range unversionedAPIs {
		u = filepath.Join(util.Repo, "pkg", "apis", u)
		unversioned = append(unversioned, "--input-dirs", u)
		all = append(all, "--input-dirs", u)
	}

	if doGen("apiregister-gen") {
		c := exec.Command(filepath.Join(root, "apiregister-gen"),
			"--input-dirs", filepath.Join(util.Repo, "pkg", "apis", "..."),
			"--input-dirs", filepath.Join(util.Repo, "pkg", "controller", "..."),
		)
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err := c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run apiregister-gen %s %v", out, err)
		}
	}

	if doGen("conversion-gen") {
		c := exec.Command(filepath.Join(root, "conversion-gen"),
			append(all,
				"-o", util.GoSrc,
				"--go-header-file", copyright,
				"-O", "zz_generated.conversion",
				"--extra-peer-dirs", extraAPI)...,
		)
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err := c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run conversion-gen %s %v", out, err)
		}
	}

	if doGen("deepcopy-gen") {
		c := exec.Command(filepath.Join(root, "deepcopy-gen"),
			append(all,
				"-o", util.GoSrc,
				"--go-header-file", copyright,
				"-O", "zz_generated.deepcopy")...,
		)
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err := c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run deepcopy-gen %s %v", out, err)
		}
	}

	if doGen("openapi-gen") {
		c := exec.Command(filepath.Join(root, "openapi-gen"),
			append(all,
				"-o", util.GoSrc,
				"--go-header-file", copyright,
				"-i", genericAPI,
				"--output-package", filepath.Join(util.Repo, "pkg", "openapi"))...,
		)
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err := c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run openapi-gen %s %v", out, err)
		}
	}

	if doGen("defaulter-gen") {
		c := exec.Command(filepath.Join(root, "defaulter-gen"),
			append(all,
				"-o", util.GoSrc,
				"--go-header-file", copyright,
				"-O", "zz_generated.defaults",
				"--extra-peer-dirs=", extraAPI)...,
		)
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err := c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run defaulter-gen %s %v", out, err)
		}
	}

	if doGen("client-gen") {
		// Builder the versioned apis client
		clientPkg := filepath.Join(util.Repo, "pkg", "client")
		clientset := filepath.Join(clientPkg, "clientset_generated")
		c := exec.Command(filepath.Join(root, "client-gen"),
			"-o", util.GoSrc,
			"--go-header-file", copyright,
			"--input-base", filepath.Join(util.Repo, "pkg", "apis"),
			"--input", strings.Join(versionedAPIs, ","),
			"--clientset-path", clientset,
			"--clientset-name", "clientset",
		)
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err := c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run client-gen %s %v", out, err)
		}

		c = exec.Command(filepath.Join(root, "client-gen"),
			"-o", util.GoSrc,
			"--go-header-file", copyright,
			"--input-base", filepath.Join(util.Repo, "pkg", "apis"),
			"--input", strings.Join(unversionedAPIs, ","),
			"--clientset-path", clientset,
			"--clientset-name", "internalclientset")
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err = c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run client-gen for unversioned APIs %s %v", out, err)
		}

		listerPkg := filepath.Join(clientPkg, "listers_generated")
		c = exec.Command(filepath.Join(root, "lister-gen"),
			append(all,
				"-o", util.GoSrc,
				"--go-header-file", copyright,
				"--output-package", listerPkg)...,
		)
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err = c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run lister-gen %s %v", out, err)
		}

		informerPkg := filepath.Join(clientPkg, "informers_generated")
		c = exec.Command(filepath.Join(root, "informer-gen"),
			append(all,
				"-o", util.GoSrc,
				"--go-header-file", copyright,
				"--output-package", informerPkg,
				"--listers-package", listerPkg,
				"--versioned-clientset-package", filepath.Join(clientset, "clientset"),
				"--internal-clientset-package", filepath.Join(clientset, "internalclientset"))...,
		)
		fmt.Printf("%s\n", strings.Join(c.Args, " "))
		out, err = c.CombinedOutput()
		if err != nil {
			log.Fatalf("failed to run informer-gen %s %v", out, err)
		}
	}
}

func initApis() {
	if len(versionedAPIs) == 0 {
		groups, err := ioutil.ReadDir(filepath.Join("pkg", "apis"))
		if err != nil {
			log.Fatalf("could not read pkg/apis directory to find api Versions")
		}
		for _, g := range groups {
			if g.IsDir() {
				versionFiles, err := ioutil.ReadDir(filepath.Join("pkg", "apis", g.Name()))
				if err != nil {
					log.Fatalf("could not read pkg/apis/%s directory to find api Versions", g.Name())
				}
				versionMatch := regexp.MustCompile("^v\\d+(alpha\\d+|beta\\d+)*$")
				for _, v := range versionFiles {
					if v.IsDir() && versionMatch.MatchString(v.Name()) {
						versionedAPIs = append(versionedAPIs, filepath.Join(g.Name(), v.Name()))
					}
				}
			}
		}
	}
	u := map[string]bool{}
	for _, a := range versionedAPIs {
		u[path.Dir(a)] = true
	}
	for a, _ := range u {
		unversionedAPIs = append(unversionedAPIs, a)
	}
}
<|endoftext|>


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

package main

import (
	"encoding/base64"
	"fmt"
	"github.com/andres-erbsen/dename/client"
	"github.com/andres-erbsen/dename/dnmgr"
	"io/ioutil"
	"os"
)

func usageAndExit() {
	fmt.Fprintf(os.Stderr, `Missing arguments. Usage:
To create a new profile:
  %s init <name> <invite>
To set the value of a field on an existing profile:
  %s set <name> <field> <value>
Value '-' indicates standard input (useful if the value contains nul characters).
`, os.Args[0], os.Args[0])
	os.Exit(2)
}

func main() {
	if len(os.Args) < 2 {
		usageAndExit()
	}
	args := os.Args[2:]
	switch os.Args[1] {
	case "init":
		if len(args) < 2 {
			usageAndExit()
		}
		name := []byte(args[0])
		invite, err := base64.StdEncoding.DecodeString(args[1])
		if err != nil {
			fmt.Fprintf(os.Stderr, "invalid invite (base64 decoding failed: %s)\n", err)
			os.Exit(1)
		}
		profile, sk, err := client.NewProfile(nil, nil)
		if err != nil {
			panic(err)
		}
		if err := dnmgr.Register(sk, profile, name, invite, "", nil); err != nil {
			fmt.Fprintf(os.Stderr, "registration failed: %s\n", err)
			os.Exit(1)
		}
	case "set":
		if len(args) < 3 {
			usageAndExit()
		}
		name, fieldName, value := []byte(args[0]), args[1], []byte(args[2])
		fieldNumber, err := client.FieldByName(fieldName)
		if err != nil {
			fmt.Fprintf(os.Stderr, "unknown field \"%s\" (%s)\n", fieldName, err)
			os.Exit(1)
		}
		if len(value) == 1 && value[0] == '-' {
			value, err = ioutil.ReadAll(os.Stdin)
			if err != nil {
				fmt.Fprintf(os.Stderr, "failed to read input: %s\n", err)
				os.Exit(1)
			}
		}
		if err := dnmgr.SetProfileField(name, fieldNumber, value, "", nil); err != nil {
			fmt.Fprintf(os.Stderr, "operation failed: %s\n", err)
			os.Exit(1)
		}
	}
}
<commit_msg>dnmgr: unambigous set-from-stdin<commit_after>// Copyright 2014 The Dename Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// 	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

package main

import (
	"encoding/base64"
	"fmt"
	"github.com/andres-erbsen/dename/client"
	"github.com/andres-erbsen/dename/dnmgr"
	"io/ioutil"
	"os"
)

func usageAndExit() {
	fmt.Fprintf(os.Stderr, `Missing arguments. Usage:
To create a new profile:
  %s init <name> <invite>
To set the value of a field on an existing profile:
  %s set <name> <field> [value]
If a value is not provided in the arguments, it will be read from standard input.
This is(useful if the value contains nul characters.
`, os.Args[0], os.Args[0])
	os.Exit(2)
}

func main() {
	if len(os.Args) < 2 {
		usageAndExit()
	}
	args := os.Args[2:]
	switch os.Args[1] {
	case "init":
		if len(args) < 2 {
			usageAndExit()
		}
		name := []byte(args[0])
		invite, err := base64.StdEncoding.DecodeString(args[1])
		if err != nil {
			fmt.Fprintf(os.Stderr, "invalid invite (base64 decoding failed: %s)\n", err)
			os.Exit(1)
		}
		profile, sk, err := client.NewProfile(nil, nil)
		if err != nil {
			panic(err)
		}
		if err := dnmgr.Register(sk, profile, name, invite, "", nil); err != nil {
			fmt.Fprintf(os.Stderr, "registration failed: %s\n", err)
			os.Exit(1)
		}
	case "set":
		var name, fieldName string
		var value []byte
		if len(args) == 2 || len(args) == 3 {
			name, fieldName = args[0], args[1]
			if len(args) == 3 {
				value = []byte(args[3])
			}
		} else {
			usageAndExit()
		}
		if value == nil {
			var err error
			value, err = ioutil.ReadAll(os.Stdin)
			if err != nil {
				fmt.Fprintf(os.Stderr, "failed to read input: %s\n", err)
				os.Exit(1)
			}
		}
		fieldNumber, err := client.FieldByName(fieldName)
		if err != nil {
			fmt.Fprintf(os.Stderr, "unknown field \"%s\" (%s)\n", fieldName, err)
			os.Exit(1)
		}
		if err := dnmgr.SetProfileField([]byte(name), fieldNumber, value, "", nil); err != nil {
			fmt.Fprintf(os.Stderr, "operation failed: %s\n", err)
			os.Exit(1)
		}
	}
}
<|endoftext|>


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

package git

import (
	"fmt"
	"path"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
)

type EntryMode int

// There are only a few file modes in Git. They look like unix file modes, but they can only be
// one of these.
const (
	ENTRY_MODE_BLOB    EntryMode = 0100644
	ENTRY_MODE_EXEC    EntryMode = 0100755
	ENTRY_MODE_SYMLINK EntryMode = 0120000
	ENTRY_MODE_COMMIT  EntryMode = 0160000
	ENTRY_MODE_TREE    EntryMode = 0040000
)

type TreeEntry struct {
	ID   sha1
	Type ObjectType

	mode EntryMode
	name string

	ptree *Tree

	commited bool

	size  int64
	sized bool
}

func (te *TreeEntry) Name() string {
	return te.name
}

func (te *TreeEntry) Size() int64 {
	if te.IsDir() {
		return 0
	} else if te.sized {
		return te.size
	}

	stdout, err := NewCommand("cat-file", "-s", te.ID.String()).RunInDir(te.ptree.repo.Path)
	if err != nil {
		return 0
	}

	te.sized = true
	te.size, _ = strconv.ParseInt(strings.TrimSpace(stdout), 10, 64)
	return te.size
}

func (te *TreeEntry) IsSubModule() bool {
	return te.mode == ENTRY_MODE_COMMIT
}

func (te *TreeEntry) IsDir() bool {
	return te.mode == ENTRY_MODE_TREE
}

func (te *TreeEntry) Blob() *Blob {
	return &Blob{
		repo:      te.ptree.repo,
		TreeEntry: te,
	}
}

type Entries []*TreeEntry

var sorter = []func(t1, t2 *TreeEntry) bool{
	func(t1, t2 *TreeEntry) bool {
		return (t1.IsDir() || t1.IsSubModule()) && !t2.IsDir() && !t2.IsSubModule()
	},
	func(t1, t2 *TreeEntry) bool {
		return t1.name < t2.name
	},
}

func (tes Entries) Len() int      { return len(tes) }
func (tes Entries) Swap(i, j int) { tes[i], tes[j] = tes[j], tes[i] }
func (tes Entries) Less(i, j int) bool {
	t1, t2 := tes[i], tes[j]
	var k int
	for k = 0; k < len(sorter)-1; k++ {
		sort := sorter[k]
		switch {
		case sort(t1, t2):
			return true
		case sort(t2, t1):
			return false
		}
	}
	return sorter[k](t1, t2)
}

func (tes Entries) Sort() {
	sort.Sort(tes)
}

type commitInfo struct {
	id        string
	entryName string
	infos     []interface{}
	err       error
}

// GetCommitsInfo takes advantages of concurrey to speed up getting information
// of all commits that are corresponding to these entries.
// TODO: limit max goroutines at same time
func (tes Entries) GetCommitsInfo(commit *Commit, treePath string) ([][]interface{}, error) {
	if len(tes) == 0 {
		return nil, nil
	}

	revChan := make(chan commitInfo, 10)

	infoMap := make(map[string][]interface{}, len(tes))
	for i := range tes {
		if tes[i].Type != OBJECT_COMMIT {
			go func(i int) {
				cinfo := commitInfo{id: tes[i].ID.String(), entryName: tes[i].Name()}
				c, err := commit.GetCommitByPath(filepath.Join(treePath, tes[i].Name()))
				if err != nil {
					cinfo.err = fmt.Errorf("GetCommitByPath (%s/%s): %v", treePath, tes[i].Name(), err)
				} else {
					cinfo.infos = []interface{}{tes[i], c}
				}
				revChan <- cinfo
			}(i)
			continue
		}

		// Handle submodule
		go func(i int) {
			cinfo := commitInfo{id: tes[i].ID.String(), entryName: tes[i].Name()}
			sm, err := commit.GetSubModule(path.Join(treePath, tes[i].Name()))
			if err != nil {
				cinfo.err = fmt.Errorf("GetSubModule (%s/%s): %v", treePath, tes[i].Name(), err)
				revChan <- cinfo
				return
			}

			smUrl := ""
			if sm != nil {
				smUrl = sm.Url
			}

			c, err := commit.GetCommitByPath(filepath.Join(treePath, tes[i].Name()))
			if err != nil {
				cinfo.err = fmt.Errorf("GetCommitByPath (%s/%s): %v", treePath, tes[i].Name(), err)
			} else {
				cinfo.infos = []interface{}{tes[i], NewSubModuleFile(c, smUrl, tes[i].ID.String())}
			}
			revChan <- cinfo
		}(i)
	}

	i := 0
	for info := range revChan {
		if info.err != nil {
			return nil, info.err
		}

		infoMap[info.entryName] = info.infos
		i++
		if i == len(tes) {
			break
		}
	}

	commitsInfo := make([][]interface{}, len(tes))
	for i := 0; i < len(tes); i++ {
		commitsInfo[i] = infoMap[tes[i].Name()]
	}
	return commitsInfo, nil
}
<commit_msg>remove commitInfo.id (not need anymore)<commit_after>// Copyright 2015 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package git

import (
	"fmt"
	"path"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
)

type EntryMode int

// There are only a few file modes in Git. They look like unix file modes, but they can only be
// one of these.
const (
	ENTRY_MODE_BLOB    EntryMode = 0100644
	ENTRY_MODE_EXEC    EntryMode = 0100755
	ENTRY_MODE_SYMLINK EntryMode = 0120000
	ENTRY_MODE_COMMIT  EntryMode = 0160000
	ENTRY_MODE_TREE    EntryMode = 0040000
)

type TreeEntry struct {
	ID   sha1
	Type ObjectType

	mode EntryMode
	name string

	ptree *Tree

	commited bool

	size  int64
	sized bool
}

func (te *TreeEntry) Name() string {
	return te.name
}

func (te *TreeEntry) Size() int64 {
	if te.IsDir() {
		return 0
	} else if te.sized {
		return te.size
	}

	stdout, err := NewCommand("cat-file", "-s", te.ID.String()).RunInDir(te.ptree.repo.Path)
	if err != nil {
		return 0
	}

	te.sized = true
	te.size, _ = strconv.ParseInt(strings.TrimSpace(stdout), 10, 64)
	return te.size
}

func (te *TreeEntry) IsSubModule() bool {
	return te.mode == ENTRY_MODE_COMMIT
}

func (te *TreeEntry) IsDir() bool {
	return te.mode == ENTRY_MODE_TREE
}

func (te *TreeEntry) Blob() *Blob {
	return &Blob{
		repo:      te.ptree.repo,
		TreeEntry: te,
	}
}

type Entries []*TreeEntry

var sorter = []func(t1, t2 *TreeEntry) bool{
	func(t1, t2 *TreeEntry) bool {
		return (t1.IsDir() || t1.IsSubModule()) && !t2.IsDir() && !t2.IsSubModule()
	},
	func(t1, t2 *TreeEntry) bool {
		return t1.name < t2.name
	},
}

func (tes Entries) Len() int      { return len(tes) }
func (tes Entries) Swap(i, j int) { tes[i], tes[j] = tes[j], tes[i] }
func (tes Entries) Less(i, j int) bool {
	t1, t2 := tes[i], tes[j]
	var k int
	for k = 0; k < len(sorter)-1; k++ {
		sort := sorter[k]
		switch {
		case sort(t1, t2):
			return true
		case sort(t2, t1):
			return false
		}
	}
	return sorter[k](t1, t2)
}

func (tes Entries) Sort() {
	sort.Sort(tes)
}

type commitInfo struct {
	entryName string
	infos     []interface{}
	err       error
}

// GetCommitsInfo takes advantages of concurrey to speed up getting information
// of all commits that are corresponding to these entries.
// TODO: limit max goroutines at same time
func (tes Entries) GetCommitsInfo(commit *Commit, treePath string) ([][]interface{}, error) {
	if len(tes) == 0 {
		return nil, nil
	}

	revChan := make(chan commitInfo, 10)

	infoMap := make(map[string][]interface{}, len(tes))
	for i := range tes {
		if tes[i].Type != OBJECT_COMMIT {
			go func(i int) {
				cinfo := commitInfo{entryName: tes[i].Name()}
				c, err := commit.GetCommitByPath(filepath.Join(treePath, tes[i].Name()))
				if err != nil {
					cinfo.err = fmt.Errorf("GetCommitByPath (%s/%s): %v", treePath, tes[i].Name(), err)
				} else {
					cinfo.infos = []interface{}{tes[i], c}
				}
				revChan <- cinfo
			}(i)
			continue
		}

		// Handle submodule
		go func(i int) {
			cinfo := commitInfo{entryName: tes[i].Name()}
			sm, err := commit.GetSubModule(path.Join(treePath, tes[i].Name()))
			if err != nil {
				cinfo.err = fmt.Errorf("GetSubModule (%s/%s): %v", treePath, tes[i].Name(), err)
				revChan <- cinfo
				return
			}

			smUrl := ""
			if sm != nil {
				smUrl = sm.Url
			}

			c, err := commit.GetCommitByPath(filepath.Join(treePath, tes[i].Name()))
			if err != nil {
				cinfo.err = fmt.Errorf("GetCommitByPath (%s/%s): %v", treePath, tes[i].Name(), err)
			} else {
				cinfo.infos = []interface{}{tes[i], NewSubModuleFile(c, smUrl, tes[i].ID.String())}
			}
			revChan <- cinfo
		}(i)
	}

	i := 0
	for info := range revChan {
		if info.err != nil {
			return nil, info.err
		}

		infoMap[info.entryName] = info.infos
		i++
		if i == len(tes) {
			break
		}
	}

	commitsInfo := make([][]interface{}, len(tes))
	for i := 0; i < len(tes); i++ {
		commitsInfo[i] = infoMap[tes[i].Name()]
	}
	return commitsInfo, nil
}
<|endoftext|>