<commit_before>package main

import (
	"encoding/json"
	"errors"
	"log"
	"net/http"
	"path"
	"strconv"
	"strings"
	"sync"

	r "github.com/dancannon/gorethink"
	"github.com/dchest/uniuri"
	"github.com/lavab/goji"
	"github.com/lavab/goji/web"
	"github.com/lavab/raven-go"
	"github.com/namsral/flag"
	"github.com/neelance/sourcemap"

	"github.com/lavab/lavatrace/models"
)

var (
	configFlag        = flag.String("config", "", "config file to load")
	rethinkdbAddress  = flag.String("rethinkdb_address", "127.0.0.1:28015", "RethinkDB address")
	rethinkdbDatabase = flag.String("rethinkdb_database", "lavatrace", "Name of the RethinkDB database to use")
	adminToken        = flag.String("admin_token", uniuri.NewLen(uniuri.UUIDLen), "Admin token for source map uploads")
	ravenDSN          = flag.String("raven_dsn", "", "Raven DSN")
)

var (
	session *r.Session
)

type Map struct {
	ID     string `json:"id" gorethink:"id"`
	Commit string `json:"commit" gorethink:"commit"`
	Name   string `json:"name" gorethink:"name"`
	Body   string `json:"body" gorethink:"body"`
}

func main() {
	// Parse the flags
	flag.Parse()

	// Connect to RethinkDB
	var err error
	session, err = r.Connect(r.ConnectOpts{
		Address: *rethinkdbAddress,
	})
	if err != nil {
		log.Fatal(err)
	}

	// Set up the database
	r.DbCreate(*rethinkdbDatabase).Exec(session)
	r.Db(*rethinkdbDatabase).TableCreate("maps").Exec(session)
	r.Db(*rethinkdbDatabase).Table("maps").IndexCreateFunc("commitName", func(row r.Term) interface{} {
		return []interface{}{
			row.Field("commit"),
			row.Field("name"),
		}
	}).Exec(session)
	r.Db(*rethinkdbDatabase).TableCreate("reports").Exec(session)
	r.Db(*rethinkdbDatabase).Table("reports").IndexCreate("version").Exec(session)

	// Connect to Raven
	rc, err := raven.NewClient(*ravenDSN, nil)
	if err != nil {
		log.Fatal(err)
	}

	// Index page
	goji.Get("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("lavab/lavatrace 0.1.0"))
	})

	// Map uploading header (alloc it here so that it won't be alloc'd in each request)
	tokenHeader := "Bearer " + *adminToken

	goji.Post("/maps/:commit", func(c web.C, w http.ResponseWriter, req *http.Request) {
		// Check if the token is valid
		if header := req.Header.Get("Authorization"); header == "" || header != tokenHeader {
			w.WriteHeader(403)
			w.Write([]byte("Invalid authorization token"))
			return
		}

		// Decode the body
		var request map[string]string
		if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
			w.WriteHeader(400)
			w.Write([]byte(err.Error()))
			return
		}

		// Try to get the commit hash from the URL params
		commit, ok := c.URLParams["commit"]
		if !ok {
			w.WriteHeader(400)
			w.Write([]byte("Invalid commit ID"))
			return
		}

		// Insert every map into the database
		for key, value := range request {
			if err := r.Db(*rethinkdbDatabase).Table("maps").Insert(&Map{
				ID:     uniuri.NewLen(uniuri.UUIDLen),
				Commit: commit,
				Name:   key,
				Body:   value,
			}).Exec(session); err != nil {
				w.WriteHeader(500)
				w.Write([]byte(err.Error()))
				return
			}
		}

		// Return some dumb text
		w.Write([]byte("Success"))
		return
	})

	// Report - registers a new event
	goji.Post("/report", func(w http.ResponseWriter, req *http.Request) {
		// Parse the request body
		var report *models.Report
		if err := json.NewDecoder(req.Body).Decode(&report); err != nil {
			w.WriteHeader(400)
			w.Write([]byte(err.Error()))
			return
		}

		// Prepare a new packet
		packet := &raven.Packet{
			Interfaces: []raven.Interface{},
			Extra: map[string]interface{}{
				"commit_id": report.CommitID,
				"version":   report.Version,
				"assets":    report.Assets,
			},
			Platform: "javascript",
			Release:  report.CommitID,
		}

		// Transform entries into exceptions
		for _, entry := range report.Entries {
			// Prepare a new Exception
			ex := &raven.Exception{
				Type:  entry.Type,
				Value: entry.Message,
				Stacktrace: &raven.Stacktrace{
					Frames: []*raven.StacktraceFrame{},
				},
			}

			// Stacktrace is a string with format:
			//   fileIndex:line:column
			for _, part := range strings.Split(entry.Stacktrace, ";") {
				// Parse each call
				call := strings.Split(part, ":")
				if len(call) < 3 {
					w.WriteHeader(400)
					w.Write([]byte("Invalid stacktrace"))
					return
				}

				// Integer parsing helper
				err = nil
				mustParse := func(input string) int {
					if err == nil {
						x, e := strconv.Atoi(input)
						if e != nil {
							err = e
							return 0
						}

						return x
					}

					return 0
				}

				// Parse the fields
				var (
					fileIndex = call[0]
					lineNo    = mustParse(call[1])
					columnNo  = mustParse(call[2])
				)
				if err != nil {
					w.WriteHeader(400)
					w.Write([]byte(err.Error()))
					return
				}

				// First case - we don't know the source
				switch fileIndex {
				case "/":
					ex.Stacktrace.Frames = append(ex.Stacktrace.Frames, &raven.StacktraceFrame{
						Filename: "unknown",
						Function: "unknown",
						Lineno:   lineNo,
						Colno:    columnNo,
						InApp:    true,
					})
				case "native":
					ex.Stacktrace.Frames = append(ex.Stacktrace.Frames, &raven.StacktraceFrame{
						Filename: "native",
						Function: "native",
						Lineno:   lineNo,
						Colno:    columnNo,
						InApp:    false,
					})
				default:
					// Convert file index to an int
					fii, err := strconv.Atoi(fileIndex)
					if err != nil {
						w.WriteHeader(400)
						w.Write([]byte(err.Error()))
						return
					}

					// Map index to file path
					if len(report.Assets) < fii+1 {
						w.WriteHeader(400)
						w.Write([]byte("Invalid asset ID"))
						return
					}
					asset := report.Assets[fii]

					// Get the asset's filename
					filename := path.Base(asset) + ".map"

					// Map the data
					mapping, err := getMapping(report.CommitID, filename, lineNo, columnNo)
					if err != nil {
						w.WriteHeader(500)
						w.Write([]byte(err.Error()))
						return
					}

					// Append it to the stacktrace
					ex.Stacktrace.Frames = append(ex.Stacktrace.Frames, &raven.StacktraceFrame{
						Filename:     mapping.OriginalFile,
						Function:     mapping.OriginalName,
						Lineno:       mapping.OriginalLine,
						Colno:        mapping.OriginalColumn,
						Module:       mapping.OriginalFile + "." + mapping.OriginalName,
						AbsolutePath: asset,
						InApp:        true,
					})
				}
			}

			// Use filename as module of exception
			ex.Module = ex.Stacktrace.Frames[len(ex.Stacktrace.Frames)-1].Filename

			// Transform objects
			objects := map[string]interface{}{}
			for i, v := range entry.Objects {
				objects[strconv.Itoa(i)] = v
			}

			// Set Vars of the last frame
			ex.Stacktrace.Frames[len(ex.Stacktrace.Frames)-1].Vars = objects

			// Append the Exception to interfaces
			packet.Interfaces = append(packet.Interfaces, ex)
		}

		// Set the culprit and message
		packet.Culprit =
			packet.Interfaces[len(packet.Interfaces)-1].(raven.Culpriter).Culprit()
		packet.Message =
			packet.Interfaces[len(packet.Interfaces)-1].(*raven.Exception).Value

		// Send the packet to Sentry
		eid, ch := rc.Capture(packet, nil)
		err = <-ch
		if err != nil {
			w.WriteHeader(500)
			w.Write([]byte(err.Error()))
			return
		}

		w.Write([]byte(eid))
		return
	})

	// Print out the current admin token
	log.Printf("Current admin token is %s", *adminToken)

	// Start the server
	goji.Serve()
}

var (
	lineCache = map[string]*sourcemap.Mapping{}
	lineLock  sync.RWMutex

	mapCache = map[string]*EMap{}
	mapLock  sync.RWMutex

	stateMap  = map[string]bool{}
	stateLock sync.RWMutex
)

type EMap struct {
	Map   *sourcemap.Map
	Lines map[int]map[int]*sourcemap.Mapping
}

func (e *EMap) GetMapping(row, col int) (*sourcemap.Mapping, error) {
	if col < 0 {
		return nil, errors.New("No such column")
	}

	if _, ok := e.Lines[row]; !ok {
		return nil, errors.New("No such line")
	}

	if _, ok := e.Lines[row][col]; !ok {
		return e.GetMapping(row, col-1)
	}

	return e.Lines[row][col], nil
}

func getMapping(commit, filename string, row, col int) (*sourcemap.Mapping, error) {
	// First look for the line cache
	lineLock.RLock()
	li := commit + "~" + filename + "~" + strconv.Itoa(row) + "~" + strconv.Itoa(col)
	log.Print(li)
	c1, ok := lineCache[li]
	lineLock.RUnlock()
	if ok {
		return c1, nil
	}

	// Then for the map cache
	mapLock.RLock()
	mi := commit + "~" + filename
	c2, ok := mapCache[mi]
	mapLock.RUnlock()
	if ok {
		return c2.GetMapping(row, col)
	}

	stateLock.Lock()
	defer stateLock.Unlock()

	// Get the map from database
	cursor, err := r.Db(*rethinkdbDatabase).Table("maps").GetAllByIndex("commitName", []interface{}{commit, filename}).Run(session)
	if err != nil {
		return nil, err
	}
	var result []*Map
	if err := cursor.All(&result); err != nil {
		return nil, err
	}
	if len(result) < 1 {
		m := &sourcemap.Mapping{
			OriginalFile:   "unknown",
			OriginalName:   "unknown",
			OriginalLine:   row,
			OriginalColumn: col,
		}

		lineLock.Lock()
		lineCache[li] = m
		lineLock.Unlock()

		return m, nil
	}

	// Parse the map
	sm, err := sourcemap.ReadFrom(strings.NewReader(result[0].Body))
	if err != nil {
		return nil, err
	}

	em := &EMap{
		Map:   sm,
		Lines: map[int]map[int]*sourcemap.Mapping{},
	}

	for _, mapping := range sm.DecodedMappings() {
		if _, ok := em.Lines[mapping.GeneratedLine]; !ok {
			em.Lines[mapping.GeneratedLine] = map[int]*sourcemap.Mapping{}
		}
		if _, ok := em.Lines[mapping.GeneratedLine][mapping.GeneratedColumn]; !ok {
			em.Lines[mapping.GeneratedLine][mapping.GeneratedColumn] = mapping
		}
	}

	mapLock.Lock()
	mapCache[mi] = em
	mapLock.Unlock()

	m, err := em.GetMapping(row, col)
	if err != nil {
		return nil, err
	}

	lineLock.Lock()
	lineCache[li] = m
	lineLock.Unlock()

	return m, nil
}
<commit_msg>Fallback parsing<commit_after>package main

import (
	"encoding/json"
	"errors"
	"log"
	"net/http"
	"path"
	"strconv"
	"strings"
	"sync"

	r "github.com/dancannon/gorethink"
	"github.com/dchest/uniuri"
	"github.com/lavab/goji"
	"github.com/lavab/goji/web"
	"github.com/lavab/raven-go"
	"github.com/namsral/flag"
	"github.com/neelance/sourcemap"

	"github.com/lavab/lavatrace/models"
)

var (
	configFlag        = flag.String("config", "", "config file to load")
	rethinkdbAddress  = flag.String("rethinkdb_address", "127.0.0.1:28015", "RethinkDB address")
	rethinkdbDatabase = flag.String("rethinkdb_database", "lavatrace", "Name of the RethinkDB database to use")
	adminToken        = flag.String("admin_token", uniuri.NewLen(uniuri.UUIDLen), "Admin token for source map uploads")
	ravenDSN          = flag.String("raven_dsn", "", "Raven DSN")
)

var (
	session *r.Session
)

type Map struct {
	ID     string `json:"id" gorethink:"id"`
	Commit string `json:"commit" gorethink:"commit"`
	Name   string `json:"name" gorethink:"name"`
	Body   string `json:"body" gorethink:"body"`
}

func main() {
	// Parse the flags
	flag.Parse()

	// Connect to RethinkDB
	var err error
	session, err = r.Connect(r.ConnectOpts{
		Address: *rethinkdbAddress,
	})
	if err != nil {
		log.Fatal(err)
	}

	// Set up the database
	r.DbCreate(*rethinkdbDatabase).Exec(session)
	r.Db(*rethinkdbDatabase).TableCreate("maps").Exec(session)
	r.Db(*rethinkdbDatabase).Table("maps").IndexCreateFunc("commitName", func(row r.Term) interface{} {
		return []interface{}{
			row.Field("commit"),
			row.Field("name"),
		}
	}).Exec(session)
	r.Db(*rethinkdbDatabase).TableCreate("reports").Exec(session)
	r.Db(*rethinkdbDatabase).Table("reports").IndexCreate("version").Exec(session)

	// Connect to Raven
	rc, err := raven.NewClient(*ravenDSN, nil)
	if err != nil {
		log.Fatal(err)
	}

	// Index page
	goji.Get("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("lavab/lavatrace 0.1.0"))
	})

	// Map uploading header (alloc it here so that it won't be alloc'd in each request)
	tokenHeader := "Bearer " + *adminToken

	goji.Post("/maps/:commit", func(c web.C, w http.ResponseWriter, req *http.Request) {
		// Check if the token is valid
		if header := req.Header.Get("Authorization"); header == "" || header != tokenHeader {
			w.WriteHeader(403)
			w.Write([]byte("Invalid authorization token"))
			return
		}

		// Decode the body
		var request map[string]string
		if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
			w.WriteHeader(400)
			w.Write([]byte(err.Error()))
			return
		}

		// Try to get the commit hash from the URL params
		commit, ok := c.URLParams["commit"]
		if !ok {
			w.WriteHeader(400)
			w.Write([]byte("Invalid commit ID"))
			return
		}

		// Insert every map into the database
		for key, value := range request {
			if err := r.Db(*rethinkdbDatabase).Table("maps").Insert(&Map{
				ID:     uniuri.NewLen(uniuri.UUIDLen),
				Commit: commit,
				Name:   key,
				Body:   value,
			}).Exec(session); err != nil {
				w.WriteHeader(500)
				w.Write([]byte(err.Error()))
				return
			}
		}

		// Return some dumb text
		w.Write([]byte("Success"))
		return
	})

	// Report - registers a new event
	goji.Post("/report", func(w http.ResponseWriter, req *http.Request) {
		// Parse the request body
		var report *models.Report
		if err := json.NewDecoder(req.Body).Decode(&report); err != nil {
			w.WriteHeader(400)
			w.Write([]byte(err.Error()))
			return
		}

		// Prepare a new packet
		packet := &raven.Packet{
			Interfaces: []raven.Interface{},
			Extra: map[string]interface{}{
				"commit_id": report.CommitID,
				"version":   report.Version,
				"assets":    report.Assets,
			},
			Platform: "javascript",
			Release:  report.CommitID,
		}

		// Transform entries into exceptions
		for _, entry := range report.Entries {
			// Prepare a new Exception
			ex := &raven.Exception{
				Type:  entry.Type,
				Value: entry.Message,
				Stacktrace: &raven.Stacktrace{
					Frames: []*raven.StacktraceFrame{},
				},
			}

			// Stacktrace is a string with format:
			//   fileIndex:line:column
			for _, part := range strings.Split(entry.Stacktrace, ";") {
				// Parse each call
				call := strings.Split(part, ":")
				if len(call) < 3 {
					w.WriteHeader(400)
					w.Write([]byte("Invalid stacktrace"))
					return
				}

				// Integer parsing helper
				err = nil
				mustParse := func(input string) int {
					if err == nil {
						x, e := strconv.Atoi(input)
						if e != nil {
							err = e
							return 0
						}

						return x
					}

					return 0
				}

				// Parse the fields
				var (
					fileIndex = call[0]
					lineNo    = mustParse(call[1])
					columnNo  = mustParse(call[2])
				)

				// First case - we don't know the source
				switch fileIndex {
				case "/":
					ex.Stacktrace.Frames = append(ex.Stacktrace.Frames, &raven.StacktraceFrame{
						Filename: "unknown",
						Function: "unknown",
						Lineno:   lineNo,
						Colno:    columnNo,
						InApp:    true,
					})
				case "native":
					ex.Stacktrace.Frames = append(ex.Stacktrace.Frames, &raven.StacktraceFrame{
						Filename: "native",
						Function: "native",
						Lineno:   lineNo,
						Colno:    columnNo,
						InApp:    false,
					})
				default:
					// Convert file index to an int
					fii, err := strconv.Atoi(fileIndex)
					if err != nil {
						w.WriteHeader(400)
						w.Write([]byte(err.Error()))
						return
					}

					// Map index to file path
					if len(report.Assets) < fii+1 {
						w.WriteHeader(400)
						w.Write([]byte("Invalid asset ID"))
						return
					}
					asset := report.Assets[fii]

					// Get the asset's filename
					filename := path.Base(asset) + ".map"

					// Map the data
					mapping, err := getMapping(report.CommitID, filename, lineNo, columnNo)
					if err != nil {
						w.WriteHeader(500)
						w.Write([]byte(err.Error()))
						return
					}

					// Append it to the stacktrace
					ex.Stacktrace.Frames = append(ex.Stacktrace.Frames, &raven.StacktraceFrame{
						Filename:     mapping.OriginalFile,
						Function:     mapping.OriginalName,
						Lineno:       mapping.OriginalLine,
						Colno:        mapping.OriginalColumn,
						Module:       mapping.OriginalFile + "." + mapping.OriginalName,
						AbsolutePath: asset,
						InApp:        true,
					})
				}
			}

			// Use filename as module of exception
			ex.Module = ex.Stacktrace.Frames[len(ex.Stacktrace.Frames)-1].Filename

			// Transform objects
			objects := map[string]interface{}{}
			for i, v := range entry.Objects {
				objects[strconv.Itoa(i)] = v
			}

			// Set Vars of the last frame
			ex.Stacktrace.Frames[len(ex.Stacktrace.Frames)-1].Vars = objects

			// Append the Exception to interfaces
			packet.Interfaces = append(packet.Interfaces, ex)
		}

		// Set the culprit and message
		packet.Culprit =
			packet.Interfaces[len(packet.Interfaces)-1].(raven.Culpriter).Culprit()
		packet.Message =
			packet.Interfaces[len(packet.Interfaces)-1].(*raven.Exception).Value

		// Send the packet to Sentry
		eid, ch := rc.Capture(packet, nil)
		err = <-ch
		if err != nil {
			w.WriteHeader(500)
			w.Write([]byte(err.Error()))
			return
		}

		w.Write([]byte(eid))
		return
	})

	// Print out the current admin token
	log.Printf("Current admin token is %s", *adminToken)

	// Start the server
	goji.Serve()
}

var (
	lineCache = map[string]*sourcemap.Mapping{}
	lineLock  sync.RWMutex

	mapCache = map[string]*EMap{}
	mapLock  sync.RWMutex

	stateMap  = map[string]bool{}
	stateLock sync.RWMutex
)

type EMap struct {
	Map   *sourcemap.Map
	Lines map[int]map[int]*sourcemap.Mapping
}

func (e *EMap) GetMapping(row, col int) (*sourcemap.Mapping, error) {
	if col < 0 {
		return nil, errors.New("No such column")
	}

	if _, ok := e.Lines[row]; !ok {
		return nil, errors.New("No such line")
	}

	if _, ok := e.Lines[row][col]; !ok {
		return e.GetMapping(row, col-1)
	}

	return e.Lines[row][col], nil
}

func getMapping(commit, filename string, row, col int) (*sourcemap.Mapping, error) {
	// First look for the line cache
	lineLock.RLock()
	li := commit + "~" + filename + "~" + strconv.Itoa(row) + "~" + strconv.Itoa(col)
	log.Print(li)
	c1, ok := lineCache[li]
	lineLock.RUnlock()
	if ok {
		return c1, nil
	}

	// Then for the map cache
	mapLock.RLock()
	mi := commit + "~" + filename
	c2, ok := mapCache[mi]
	mapLock.RUnlock()
	if ok {
		return c2.GetMapping(row, col)
	}

	stateLock.Lock()
	defer stateLock.Unlock()

	// Get the map from database
	cursor, err := r.Db(*rethinkdbDatabase).Table("maps").GetAllByIndex("commitName", []interface{}{commit, filename}).Run(session)
	if err != nil {
		return nil, err
	}
	var result []*Map
	if err := cursor.All(&result); err != nil {
		return nil, err
	}
	if len(result) < 1 {
		m := &sourcemap.Mapping{
			OriginalFile:   "unknown",
			OriginalName:   "unknown",
			OriginalLine:   row,
			OriginalColumn: col,
		}

		lineLock.Lock()
		lineCache[li] = m
		lineLock.Unlock()

		return m, nil
	}

	// Parse the map
	sm, err := sourcemap.ReadFrom(strings.NewReader(result[0].Body))
	if err != nil {
		return nil, err
	}

	em := &EMap{
		Map:   sm,
		Lines: map[int]map[int]*sourcemap.Mapping{},
	}

	for _, mapping := range sm.DecodedMappings() {
		if _, ok := em.Lines[mapping.GeneratedLine]; !ok {
			em.Lines[mapping.GeneratedLine] = map[int]*sourcemap.Mapping{}
		}
		if _, ok := em.Lines[mapping.GeneratedLine][mapping.GeneratedColumn]; !ok {
			em.Lines[mapping.GeneratedLine][mapping.GeneratedColumn] = mapping
		}
	}

	mapLock.Lock()
	mapCache[mi] = em
	mapLock.Unlock()

	m, err := em.GetMapping(row, col)
	if err != nil {
		return nil, err
	}

	lineLock.Lock()
	lineCache[li] = m
	lineLock.Unlock()

	return m, nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
)

const usageText = `NOTI
    display a notification in os x after a terminal process finishes.

USAGE
    noti [options] [utility [args...]]

OPTIONS
    -t, -title
        Set the notification title. If no arguments passed, default is "noti",
        else default is utility name.

    -m, -message
        Set notification message. Default is "Done!"

    -s, -sound
        Set notification sound. Default is Ping. Possible options are Basso,
        Blow, Bottle, Frog, Funk, Glass, Hero, Morse, Ping, Pop, Purr, Sosumi,
        Submarine, Tink. Check /System/Library/Sounds for available sounds.

    -v, -version
        Print noti version and exit.

    -h, -help
        Display usage information and exit.

EXAMPLES
    Display a notification when tar finishes compressing files.
        noti tar -cjf music.tar.bz2 Music/

    Display a notification when apt-get finishes updating on a remote server.
        noti ssh you@server.com apt-get update

    Set the notification title to "homebrew" and message to "Up to date" and
    display it after Homebrew finishes updating.
        noti -t "homebrew" -m "up to date" brew update

    You can also add noti after a command, in case you forgot at the beginning.
        clang foo.c -Wall -lm -L/usr/X11R6/lib -lX11 -o bizz; noti
`

func main() {
	title := flag.String("t", "", "")
	mesg := flag.String("m", "Done!", "")
	sound := flag.String("s", "Ping", "")
	version := flag.Bool("v", false, "")
	help := flag.Bool("h", false, "")
	flag.StringVar(title, "title", "", "")
	flag.StringVar(mesg, "message", "Done!", "")
	flag.StringVar(sound, "sound", "Ping", "")
	flag.BoolVar(version, "version", false, "")
	flag.BoolVar(help, "help", false, "")
	flag.Usage = func() { log.Println(usageText) }
	flag.Parse()

	if *help {
		fmt.Println(usageText)
		return
	}

	if *version {
		fmt.Println("noti version 1.0.1")
		return
	}

	if len(flag.Args()) == 0 {
		if err := notify("noti", *mesg, *sound); err != nil {
			log.Fatal(err)
		}

		return
	}

	if *title == "" {
		// title = utility's name
		*title = flag.Args()[0]
	}

	// run a binary and its arguments
	if err := run(flag.Args()[0], flag.Args()[1:]); err != nil {
		notify(*title, "Failed. See terminal.", "Basso")
		os.Exit(1)
	}

	if err := notify(*title, *mesg, *sound); err != nil {
		log.Fatal(err)
	}
}

// run executes a program and waits for it to finish. The stdin, stdout, and
// stderr of noti are passed to the program.
func run(bin string, args []string) error {
	cmd := exec.Command(bin, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	return cmd.Run()
}

// notify displays a notification in OS X's notification center with a given
// title, message, and sound.
func notify(title, mesg, sound string) error {
	script := fmt.Sprintf("display notification %q with title %q sound name %q",
		mesg, title, sound)

	cmd := exec.Command("osascript", "-e", script)
	return cmd.Run()
}
<commit_msg>Add flag to bring Terminal to foreground<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
)

const usageText = `NOTI
    display a notification in os x after a terminal process finishes.

USAGE
    noti [options] [utility [args...]]

OPTIONS
    -f, -foreground
        Bring the terminal to the foreground.

    -t, -title
        Set the notification title. If no arguments passed, default is "noti",
        else default is utility name.

    -m, -message
        Set notification message. Default is "Done!"

    -s, -sound
        Set notification sound. Default is Ping. Possible options are Basso,
        Blow, Bottle, Frog, Funk, Glass, Hero, Morse, Ping, Pop, Purr, Sosumi,
        Submarine, Tink. Check /System/Library/Sounds for available sounds.

    -v, -version
        Print noti version and exit.

    -h, -help
        Display usage information and exit.

EXAMPLES
    Display a notification when tar finishes compressing files.
        noti tar -cjf music.tar.bz2 Music/

    Display a notification when apt-get finishes updating on a remote server.
        noti ssh you@server.com apt-get update

    Set the notification title to "homebrew" and message to "Up to date" and
    display it after Homebrew finishes updating.
        noti -t "homebrew" -m "up to date" brew update

    You can also add noti after a command, in case you forgot at the beginning.
        clang foo.c -Wall -lm -L/usr/X11R6/lib -lX11 -o bizz; noti
`

const (
	activateReopen = `tell application "Terminal"
	activate
	reopen
end tell`

	displayNotification = "display notification %q with title %q sound name %q"
)

func main() {
	foreground := flag.Bool("f", false, "")
	title := flag.String("t", "", "")
	mesg := flag.String("m", "Done!", "")
	sound := flag.String("s", "Ping", "")
	version := flag.Bool("v", false, "")
	help := flag.Bool("h", false, "")
	flag.BoolVar(foreground, "foreground", false, "")
	flag.StringVar(title, "title", "", "")
	flag.StringVar(mesg, "message", "Done!", "")
	flag.StringVar(sound, "sound", "Ping", "")
	flag.BoolVar(version, "version", false, "")
	flag.BoolVar(help, "help", false, "")
	flag.Usage = func() { log.Println(usageText) }
	flag.Parse()

	if *help {
		fmt.Println(usageText)
		return
	}

	if *version {
		fmt.Println("noti version 1.1.0")
		return
	}

	// noti called by itself
	if len(flag.Args()) == 0 {
		if err := notify("noti", *mesg, *sound, *foreground); err != nil {
			log.Fatal(err)
		}

		return
	}

	if *title == "" {
		// title = utility's name
		*title = flag.Args()[0]
	}

	// run a binary and its arguments
	if err := run(flag.Args()[0], flag.Args()[1:]); err != nil {
		notify(*title, "Failed. See terminal.", "Basso", *foreground)
		os.Exit(1)
	}

	if err := notify(*title, *mesg, *sound, *foreground); err != nil {
		log.Fatal(err)
	}
}

// run executes a program and waits for it to finish. The stdin, stdout, and
// stderr of noti are passed to the program.
func run(bin string, args []string) error {
	cmd := exec.Command(bin, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	return cmd.Run()
}

// notify displays a notification in OS X's notification center with a given
// title, message, and sound.
func notify(title, mesg, sound string, foreground bool) error {
	if foreground {
		cmd := exec.Command("osascript", "-e", activateReopen)
		if err := cmd.Run(); err != nil {
			return err
		}
	}

	script := fmt.Sprintf(displayNotification, mesg, title, sound)
	cmd := exec.Command("osascript", "-e", script)
	return cmd.Run()
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 TSUYUSATO Kitsune
// This software is released under the MIT License.
// http://opensource.org/licenses/mit-license.php

// Package hotkey provides HotKey for Go Language.
package hotkey

import (
	"fmt"
	"time"

	"github.com/lxn/win"
)

import "github.com/MakeNowJust/hotkey/win"

// A hotkey.Id is a identity number of registered hotkey.
type Id int32

// A hotkey.Modifier specifies the keyboard modifier for hotkey.Register.
type Modifier uint32

// These are all members of hotkey.Modifier.
const (
	Alt   Modifier = hotkey_win.MOD_ALT
	Ctrl  Modifier = hotkey_win.MOD_CONTROL
	Shift Modifier = hotkey_win.MOD_SHIFT
	Win   Modifier = hotkey_win.MOD_WIN
)

type reservedHotKey struct {
	id              int32
	fsModifiers, vk uint32
}

var (
	started = false

	currentId = int32(1)
	id2handle = make(map[Id]func())

	reservedHotKeys = make([]reservedHotKey, 0)

	threadId     uint32
	chRegister   = make(chan reservedHotKey, 100)
	chUnregister = make(chan int32, 100)
)

// Register a hotkey with modifiers and vk.
//
// mods are hotkey's modifiers such as hotkey.Alt, hotkey.Ctrl+hotkey.Shift.
//
// vk is a hotkey's virtual key code. See also
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
func Register(mods Modifier, vk uint32, handle func()) (id Id, err error) {
	reserved := reservedHotKey{
		id:          currentId,
		fsModifiers: uint32(mods),
		vk:          uint32(vk),
	}
	id = Id(currentId)
	id2handle[id] = handle

	currentId += 1

	if started {
		chRegister <- reserved
		hotkey_win.PostThreadMessage(threadId, win.WM_USER, 0, 0)
	} else {
		reservedHotKeys = append(reservedHotKeys, reserved)
	}
	return
}

// Unregister a hotkey from id.
func Unregister(id Id) {
	if started {
		chUnregister <- int32(id)
		delete(id2handle, id)
		hotkey_win.PostThreadMessage(threadId, win.WM_USER, 0, 0)
	} else {
		for idx, reserved := range reservedHotKeys {
			if reserved.id == int32(id) {
				reservedHotKeys[idx] = reservedHotKey{0, 0, 0}
			}
		}
	}
}

// Start hotkey's loop. It is non-blocking.
//
// The returned channel notices finish hotkey's loop.
// If occured error in hotkey's loop, it send error object.
func Start() <-chan error {
	chErr := make(chan error)
	chThreadId := make(chan uint32)

	go func() {
		// register and reserve to unregister hotkeys
		count := 0
		for _, reserved := range reservedHotKeys {
			if reserved.id == 0 {
				continue
			}

			if !hotkey_win.RegisterHotKey(0, reserved.id, reserved.fsModifiers, reserved.vk) {
				chErr <- fmt.Errorf("failed to register hotkey %v", reserved)
				return
			}
			defer hotkey_win.UnregisterHotKey(0, reserved.id)
			count += 1
		}

		chThreadId <- hotkey_win.GetThreadId(hotkey_win.GetCurrentThread())

		// hotkey's loop
		for {
			select {
			case <-time.After(time.Millisecond * 10):
				var msg win.MSG
				res := win.GetMessage(&msg, 0, 0, 0)

				if res == 0 || res == -1 {
					// TODO: get system error message
					chErr <- nil
					return
				}

				switch msg.Message {
				case win.WM_HOTKEY:
					if handle := id2handle[Id(msg.WParam)]; handle != nil {
						handle()
					}
				default:
					win.TranslateMessage(&msg)
					win.DispatchMessage(&msg)
				}

			case reserved := <-chRegister:
				if !hotkey_win.RegisterHotKey(0, reserved.id, reserved.fsModifiers, reserved.vk) {
					chErr <- fmt.Errorf("failed to register hotkey %v", reserved)
					return
				}
				defer hotkey_win.UnregisterHotKey(0, reserved.id)
				count += 1

			case id := <-chUnregister:
				hotkey_win.UnregisterHotKey(0, id)
				if count -= 1; count == 0 && len(chRegister) == 0 {
					chErr <- nil
					return
				}
			}
		}
	}()

	threadId = <-chThreadId
	started = true
	return chErr
}
<commit_msg>Lock hotkey's loop thread for win32api<commit_after>// Copyright (c) 2014 TSUYUSATO Kitsune
// This software is released under the MIT License.
// http://opensource.org/licenses/mit-license.php

// Package hotkey provides HotKey for Go Language.
package hotkey

import (
	"fmt"
	"time"
	"runtime"

	"github.com/lxn/win"
)

import "github.com/MakeNowJust/hotkey/win"

// A hotkey.Id is a identity number of registered hotkey.
type Id int32

// A hotkey.Modifier specifies the keyboard modifier for hotkey.Register.
type Modifier uint32

// These are all members of hotkey.Modifier.
const (
	Alt   Modifier = hotkey_win.MOD_ALT
	Ctrl  Modifier = hotkey_win.MOD_CONTROL
	Shift Modifier = hotkey_win.MOD_SHIFT
	Win   Modifier = hotkey_win.MOD_WIN
)

type reservedHotKey struct {
	id              int32
	fsModifiers, vk uint32
}

var (
	started = false

	currentId = int32(1)
	id2handle = make(map[Id]func())

	reservedHotKeys = make([]reservedHotKey, 0)

	threadId     uint32
	chRegister   = make(chan reservedHotKey, 100)
	chUnregister = make(chan int32, 100)
)

// Register a hotkey with modifiers and vk.
//
// mods are hotkey's modifiers such as hotkey.Alt, hotkey.Ctrl+hotkey.Shift.
//
// vk is a hotkey's virtual key code. See also
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
func Register(mods Modifier, vk uint32, handle func()) (id Id, err error) {
	reserved := reservedHotKey{
		id:          currentId,
		fsModifiers: uint32(mods),
		vk:          uint32(vk),
	}
	id = Id(currentId)
	id2handle[id] = handle

	currentId += 1

	if started {
		chRegister <- reserved
		hotkey_win.PostThreadMessage(threadId, win.WM_USER, 0, 0)
	} else {
		reservedHotKeys = append(reservedHotKeys, reserved)
	}
	return
}

// Unregister a hotkey from id.
func Unregister(id Id) {
	if started {
		chUnregister <- int32(id)
		delete(id2handle, id)
		hotkey_win.PostThreadMessage(threadId, win.WM_USER, 0, 0)
	} else {
		for idx, reserved := range reservedHotKeys {
			if reserved.id == int32(id) {
				reservedHotKeys[idx] = reservedHotKey{0, 0, 0}
			}
		}
	}
}

// Start hotkey's loop. It is non-blocking.
//
// The returned channel notices finish hotkey's loop.
// If occured error in hotkey's loop, it send error object.
func Start() <-chan error {
	chErr := make(chan error)
	chThreadId := make(chan uint32)

	go func() {
		runtime.LockOSThread()

		// register and reserve to unregister hotkeys
		count := 0
		for _, reserved := range reservedHotKeys {
			if reserved.id == 0 {
				continue
			}

			if !hotkey_win.RegisterHotKey(0, reserved.id, reserved.fsModifiers, reserved.vk) {
				chErr <- fmt.Errorf("failed to register hotkey %v", reserved)
				return
			}
			defer hotkey_win.UnregisterHotKey(0, reserved.id)
			count += 1
		}

		chThreadId <- hotkey_win.GetThreadId(hotkey_win.GetCurrentThread())

		// hotkey's loop
		for {
			select {
			case <-time.After(time.Millisecond * 10):
				var msg win.MSG
				res := win.GetMessage(&msg, 0, 0, 0)

				if res == 0 || res == -1 {
					// TODO: get system error message
					chErr <- nil
					return
				}

				switch msg.Message {
				case win.WM_HOTKEY:
					if handle := id2handle[Id(msg.WParam)]; handle != nil {
						handle()
					}
				default:
					win.TranslateMessage(&msg)
					win.DispatchMessage(&msg)
				}

			case reserved := <-chRegister:
				if !hotkey_win.RegisterHotKey(0, reserved.id, reserved.fsModifiers, reserved.vk) {
					chErr <- fmt.Errorf("failed to register hotkey %v", reserved)
					return
				}
				defer hotkey_win.UnregisterHotKey(0, reserved.id)
				count += 1

			case id := <-chUnregister:
				hotkey_win.UnregisterHotKey(0, id)
				if count -= 1; count == 0 && len(chRegister) == 0 {
					chErr <- nil
					return
				}
			}
		}
	}()

	threadId = <-chThreadId
	started = true
	return chErr
}
<|endoftext|>


<commit_before>package dataloader

import (
	"fmt"
	"log"
	"reflect"
	"strconv"
	"sync"
	"testing"
)

///////////////////////////////////////////////////
// Tests
///////////////////////////////////////////////////
func TestLoader(t *testing.T) {
	t.Run("test Load method", func(t *testing.T) {
		t.Parallel()
		identityLoader, _ := IDLoader(0)
		future := identityLoader.Load("1")
		value, _ := future()
		if value != "1" {
			t.Error("load didn't return the right value")
		}
	})

	t.Run("test LoadMany method", func(t *testing.T) {
		t.Parallel()
		errorLoader, _ := ErrorLoader(0)
		future := errorLoader.LoadMany([]string{"1", "2", "3"})
		_, err := future()
		if len(err) != 3 {
			t.Error("loadmany didn't return right number of errors")
		}
	})

	t.Run("test LoadMany method", func(t *testing.T) {
		t.Parallel()
		identityLoader, _ := IDLoader(0)
		future := identityLoader.LoadMany([]string{"1", "2", "3"})
		results, _ := future()
		if results[0].(string) != "1" || results[1].(string) != "2" || results[2].(string) != "3" {
			t.Error("loadmany didn't return the right value")
		}
	})

	t.Run("batches many requests", func(t *testing.T) {
		t.Parallel()
		identityLoader, loadCalls := IDLoader(0)
		future1 := identityLoader.Load("1")
		future2 := identityLoader.Load("2")

		future1()
		future2()

		calls := *loadCalls
		inner := []string{"1", "2"}
		expected := [][]string{inner}
		if !reflect.DeepEqual(calls, expected) {
			t.Errorf("did not call batchFn in right order. Expected %#v, got %#v", expected, calls)
		}
	})

	t.Run("number of results matches number of keys", func(t *testing.T) {
		t.Parallel()
		faultyLoader, _ := FaultyLoader()

		n := 10
		reqs := []Thunk{}
		keys := []string{}
		for i := 0; i < n; i++ {
			key := strconv.Itoa(i)
			reqs = append(reqs, faultyLoader.Load(key))
			keys = append(keys, key)
		}

		for _, future := range reqs {
			future()
		}

		// TODO: expect to get some kind of warning
	})

	t.Run("responds to max batch size", func(t *testing.T) {
		t.Parallel()
		identityLoader, loadCalls := IDLoader(2)
		future1 := identityLoader.Load("1")
		future2 := identityLoader.Load("2")
		future3 := identityLoader.Load("3")

		future1()
		future2()
		future3()

		calls := *loadCalls
		inner1 := []string{"1", "2"}
		inner2 := []string{"3"}
		expected := [][]string{inner1, inner2}
		if !reflect.DeepEqual(calls, expected) {
			t.Errorf("did not respect max batch size. Expected %#v, got %#v", expected, calls)
		}
	})

	t.Run("caches repeated requests", func(t *testing.T) {
		t.Parallel()
		identityLoader, loadCalls := IDLoader(0)
		future1 := identityLoader.Load("1")
		future2 := identityLoader.Load("1")

		future1()
		future2()

		calls := *loadCalls
		inner := []string{"1"}
		expected := [][]string{inner}
		if !reflect.DeepEqual(calls, expected) {
			t.Errorf("did not respect max batch size. Expected %#v, got %#v", expected, calls)
		}
	})

	t.Run("allows primed cache", func(t *testing.T) {
		t.Parallel()
		identityLoader, loadCalls := IDLoader(0)
		identityLoader.Prime("A", "Cached")
		future1 := identityLoader.Load("1")
		future2 := identityLoader.Load("A")

		future1()
		value, _ := future2()

		calls := *loadCalls
		inner := []string{"1"}
		expected := [][]string{inner}
		if !reflect.DeepEqual(calls, expected) {
			t.Errorf("did not respect max batch size. Expected %#v, got %#v", expected, calls)
		}

		if value.(string) != "Cached" {
			t.Errorf("did not use primed cache value. Expected '%#v', got '%#v'", "Cached", value)
		}
	})

	t.Run("allows clear value in cache", func(t *testing.T) {
		t.Parallel()
		identityLoader, loadCalls := IDLoader(0)
		identityLoader.Prime("A", "Cached")
		identityLoader.Prime("B", "B")
		future1 := identityLoader.Load("1")
		future2 := identityLoader.Clear("A").Load("A")
		future3 := identityLoader.Load("B")

		future1()
		value, _ := future2()
		future3()

		calls := *loadCalls
		inner := []string{"1", "A"}
		expected := [][]string{inner}
		if !reflect.DeepEqual(calls, expected) {
			t.Errorf("did not respect max batch size. Expected %#v, got %#v", expected, calls)
		}

		if value.(string) != "A" {
			t.Errorf("did not use primed cache value. Expected '%#v', got '%#v'", "Cached", value)
		}
	})

	t.Run("allows clearAll values in cache", func(t *testing.T) {
		t.Parallel()
		identityLoader, loadCalls := IDLoader(0)
		identityLoader.Prime("A", "Cached")
		identityLoader.Prime("B", "B")

		identityLoader.ClearAll()

		future1 := identityLoader.Load("1")
		future2 := identityLoader.Load("A")
		future3 := identityLoader.Load("B")

		future1()
		future2()
		future3()

		calls := *loadCalls
		inner := []string{"1", "A", "B"}
		expected := [][]string{inner}
		if !reflect.DeepEqual(calls, expected) {
			t.Errorf("did not respect max batch size. Expected %#v, got %#v", expected, calls)
		}
	})

	t.Run("all methods on NoCache are Noops", func(t *testing.T) {
		t.Parallel()
		identityLoader, loadCalls := NoCacheLoader(0)
		identityLoader.Prime("A", "Cached")
		identityLoader.Prime("B", "B")

		identityLoader.ClearAll()

		future1 := identityLoader.Clear("1").Load("1")
		future2 := identityLoader.Load("A")
		future3 := identityLoader.Load("B")

		future1()
		future2()
		future3()

		calls := *loadCalls
		inner := []string{"1", "A", "B"}
		expected := [][]string{inner}
		if !reflect.DeepEqual(calls, expected) {
			t.Errorf("did not respect max batch size. Expected %#v, got %#v", expected, calls)
		}
	})

	t.Run("no cache does not cache anything", func(t *testing.T) {
		t.Parallel()
		identityLoader, loadCalls := NoCacheLoader(0)
		identityLoader.Prime("A", "Cached")
		identityLoader.Prime("B", "B")

		future1 := identityLoader.Load("1")
		future2 := identityLoader.Load("A")
		future3 := identityLoader.Load("B")

		future1()
		future2()
		future3()

		calls := *loadCalls
		inner := []string{"1", "A", "B"}
		expected := [][]string{inner}
		if !reflect.DeepEqual(calls, expected) {
			t.Errorf("did not respect max batch size. Expected %#v, got %#v", expected, calls)
		}
	})

}

// test helpers
func IDLoader(max int) (*Loader, *[][]string) {
	var mu sync.Mutex
	var loadCalls [][]string
	identityLoader := NewBatchedLoader(func(keys []string) []*Result {
		var results []*Result
		mu.Lock()
		loadCalls = append(loadCalls, keys)
		mu.Unlock()
		for _, key := range keys {
			results = append(results, &Result{key, nil})
		}
		return results
	}, WithBatchCapacity(max))
	return identityLoader, &loadCalls
}
func ErrorLoader(max int) (*Loader, *[][]string) {
	var mu sync.Mutex
	var loadCalls [][]string
	identityLoader := NewBatchedLoader(func(keys []string) []*Result {
		var results []*Result
		mu.Lock()
		loadCalls = append(loadCalls, keys)
		mu.Unlock()
		for _, key := range keys {
			results = append(results, &Result{key, fmt.Errorf("this is a test error")})
		}
		return results
	}, WithBatchCapacity(max))
	return identityLoader, &loadCalls
}
func BadLoader(max int) (*Loader, *[][]string) {
	var mu sync.Mutex
	var loadCalls [][]string
	identityLoader := NewBatchedLoader(func(keys []string) []*Result {
		var results []*Result
		mu.Lock()
		loadCalls = append(loadCalls, keys)
		mu.Unlock()
		results = append(results, &Result{keys[0], nil})
		return results
	}, WithBatchCapacity(max))
	return identityLoader, &loadCalls
}
func NoCacheLoader(max int) (*Loader, *[][]string) {
	var mu sync.Mutex
	var loadCalls [][]string
	cache := &NoCache{}
	identityLoader := NewBatchedLoader(func(keys []string) []*Result {
		var results []*Result
		mu.Lock()
		loadCalls = append(loadCalls, keys)
		mu.Unlock()
		for _, key := range keys {
			results = append(results, &Result{key, nil})
		}
		return results
	}, WithCache(cache), WithBatchCapacity(max))
	return identityLoader, &loadCalls
}

// FaultyLoader gives len(keys)-1 results.
func FaultyLoader() (*Loader, *[][]string) {
	var mu sync.Mutex
	var loadCalls [][]string

	loader := NewBatchedLoader(func(keys []string) []*Result {
		var results []*Result
		mu.Lock()
		loadCalls = append(loadCalls, keys)
		mu.Unlock()

		lastKeyIndex := len(keys) - 1
		for i, key := range keys {
			if i == lastKeyIndex {
				break
			}

			results = append(results, &Result{key, nil})
		}
		return results
	})

	return loader, &loadCalls
}

///////////////////////////////////////////////////
// Benchmarks
///////////////////////////////////////////////////
var a = &Avg{}

func batchIdentity(keys []string) (results []*Result) {
	a.Add(len(keys))
	for _, key := range keys {
		results = append(results, &Result{key, nil})
	}
	return
}

func BenchmarkLoader(b *testing.B) {
	UserLoader := NewBatchedLoader(batchIdentity)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		UserLoader.Load(strconv.Itoa(i))
	}
	log.Printf("avg: %f", a.Avg())
}

type Avg struct {
	total  float64
	length float64
}

func (a *Avg) Add(v int) {
	a.total += float64(v)
	a.length++
}

func (a *Avg) Avg() float64 {
	if a.total == 0 {
		return 0
	} else if a.length == 0 {
		return 0
	}
	return a.total / a.length
}
<commit_msg>catch more errors in tests, cought by gometalinter<commit_after>package dataloader

import (
	"fmt"
	"log"
	"reflect"
	"strconv"
	"sync"
	"testing"
)

///////////////////////////////////////////////////
// Tests
///////////////////////////////////////////////////
func TestLoader(t *testing.T) {
	t.Run("test Load method", func(t *testing.T) {
		t.Parallel()
		identityLoader, _ := IDLoader(0)
		future := identityLoader.Load("1")
		value, err := future()
		if err != nil {
			t.Error(err.Error())
		}
		if value != "1" {
			t.Error("load didn't return the right value")
		}
	})

	t.Run("test LoadMany method", func(t *testing.T) {
		t.Parallel()
		errorLoader, _ := ErrorLoader(0)
		future := errorLoader.LoadMany([]string{"1", "2", "3"})
		_, err := future()
		if len(err) != 3 {
			t.Error("loadmany didn't return right number of errors")
		}
	})

	t.Run("test LoadMany method", func(t *testing.T) {
		t.Parallel()
		identityLoader, _ := IDLoader(0)
		future := identityLoader.LoadMany([]string{"1", "2", "3"})
		results, _ := future()
		if results[0].(string) != "1" || results[1].(string) != "2" || results[2].(string) != "3" {
			t.Error("loadmany didn't return the right value")
		}
	})

	t.Run("batches many requests", func(t *testing.T) {
		t.Parallel()
		identityLoader, loadCalls := IDLoader(0)
		future1 := identityLoader.Load("1")
		future2 := identityLoader.Load("2")

		_, err := future1()
		if err != nil {
			t.Error(err.Error())
		}
		_, err = future2()
		if err != nil {
			t.Error(err.Error())
		}

		calls := *loadCalls
		inner := []string{"1", "2"}
		expected := [][]string{inner}
		if !reflect.DeepEqual(calls, expected) {
			t.Errorf("did not call batchFn in right order. Expected %#v, got %#v", expected, calls)
		}
	})

	t.Run("number of results matches number of keys", func(t *testing.T) {
		t.Parallel()
		faultyLoader, _ := FaultyLoader()

		n := 10
		reqs := []Thunk{}
		keys := []string{}
		for i := 0; i < n; i++ {
			key := strconv.Itoa(i)
			reqs = append(reqs, faultyLoader.Load(key))
			keys = append(keys, key)
		}

		for _, future := range reqs {
			_, err := future()
			if err == nil {
				t.Error("if number of results doesn't match keys, all keys should contain error")
			}
		}

		// TODO: expect to get some kind of warning
	})

	t.Run("responds to max batch size", func(t *testing.T) {
		t.Parallel()
		identityLoader, loadCalls := IDLoader(2)
		future1 := identityLoader.Load("1")
		future2 := identityLoader.Load("2")
		future3 := identityLoader.Load("3")

		_, err := future1()
		if err != nil {
			t.Error(err.Error())
		}
		_, err = future2()
		if err != nil {
			t.Error(err.Error())
		}
		_, err = future3()
		if err != nil {
			t.Error(err.Error())
		}

		calls := *loadCalls
		inner1 := []string{"1", "2"}
		inner2 := []string{"3"}
		expected := [][]string{inner1, inner2}
		if !reflect.DeepEqual(calls, expected) {
			t.Errorf("did not respect max batch size. Expected %#v, got %#v", expected, calls)
		}
	})

	t.Run("caches repeated requests", func(t *testing.T) {
		t.Parallel()
		identityLoader, loadCalls := IDLoader(0)
		future1 := identityLoader.Load("1")
		future2 := identityLoader.Load("1")

		_, err := future1()
		if err != nil {
			t.Error(err.Error())
		}
		_, err = future2()
		if err != nil {
			t.Error(err.Error())
		}

		calls := *loadCalls
		inner := []string{"1"}
		expected := [][]string{inner}
		if !reflect.DeepEqual(calls, expected) {
			t.Errorf("did not respect max batch size. Expected %#v, got %#v", expected, calls)
		}
	})

	t.Run("allows primed cache", func(t *testing.T) {
		t.Parallel()
		identityLoader, loadCalls := IDLoader(0)
		identityLoader.Prime("A", "Cached")
		future1 := identityLoader.Load("1")
		future2 := identityLoader.Load("A")

		_, err := future1()
		if err != nil {
			t.Error(err.Error())
		}
		value, err := future2()
		if err != nil {
			t.Error(err.Error())
		}

		calls := *loadCalls
		inner := []string{"1"}
		expected := [][]string{inner}
		if !reflect.DeepEqual(calls, expected) {
			t.Errorf("did not respect max batch size. Expected %#v, got %#v", expected, calls)
		}

		if value.(string) != "Cached" {
			t.Errorf("did not use primed cache value. Expected '%#v', got '%#v'", "Cached", value)
		}
	})

	t.Run("allows clear value in cache", func(t *testing.T) {
		t.Parallel()
		identityLoader, loadCalls := IDLoader(0)
		identityLoader.Prime("A", "Cached")
		identityLoader.Prime("B", "B")
		future1 := identityLoader.Load("1")
		future2 := identityLoader.Clear("A").Load("A")
		future3 := identityLoader.Load("B")

		_, err := future1()
		if err != nil {
			t.Error(err.Error())
		}
		value, err := future2()
		if err != nil {
			t.Error(err.Error())
		}
		_, err = future3()
		if err != nil {
			t.Error(err.Error())
		}

		calls := *loadCalls
		inner := []string{"1", "A"}
		expected := [][]string{inner}
		if !reflect.DeepEqual(calls, expected) {
			t.Errorf("did not respect max batch size. Expected %#v, got %#v", expected, calls)
		}

		if value.(string) != "A" {
			t.Errorf("did not use primed cache value. Expected '%#v', got '%#v'", "Cached", value)
		}
	})

	t.Run("allows clearAll values in cache", func(t *testing.T) {
		t.Parallel()
		identityLoader, loadCalls := IDLoader(0)
		identityLoader.Prime("A", "Cached")
		identityLoader.Prime("B", "B")

		identityLoader.ClearAll()

		future1 := identityLoader.Load("1")
		future2 := identityLoader.Load("A")
		future3 := identityLoader.Load("B")

		_, err := future1()
		if err != nil {
			t.Error(err.Error())
		}
		_, err = future2()
		if err != nil {
			t.Error(err.Error())
		}
		_, err = future3()
		if err != nil {
			t.Error(err.Error())
		}

		calls := *loadCalls
		inner := []string{"1", "A", "B"}
		expected := [][]string{inner}
		if !reflect.DeepEqual(calls, expected) {
			t.Errorf("did not respect max batch size. Expected %#v, got %#v", expected, calls)
		}
	})

	t.Run("all methods on NoCache are Noops", func(t *testing.T) {
		t.Parallel()
		identityLoader, loadCalls := NoCacheLoader(0)
		identityLoader.Prime("A", "Cached")
		identityLoader.Prime("B", "B")

		identityLoader.ClearAll()

		future1 := identityLoader.Clear("1").Load("1")
		future2 := identityLoader.Load("A")
		future3 := identityLoader.Load("B")

		_, err := future1()
		if err != nil {
			t.Error(err.Error())
		}
		_, err = future2()
		if err != nil {
			t.Error(err.Error())
		}
		_, err = future3()
		if err != nil {
			t.Error(err.Error())
		}

		calls := *loadCalls
		inner := []string{"1", "A", "B"}
		expected := [][]string{inner}
		if !reflect.DeepEqual(calls, expected) {
			t.Errorf("did not respect max batch size. Expected %#v, got %#v", expected, calls)
		}
	})

	t.Run("no cache does not cache anything", func(t *testing.T) {
		t.Parallel()
		identityLoader, loadCalls := NoCacheLoader(0)
		identityLoader.Prime("A", "Cached")
		identityLoader.Prime("B", "B")

		future1 := identityLoader.Load("1")
		future2 := identityLoader.Load("A")
		future3 := identityLoader.Load("B")

		_, err := future1()
		if err != nil {
			t.Error(err.Error())
		}
		_, err = future2()
		if err != nil {
			t.Error(err.Error())
		}
		_, err = future3()
		if err != nil {
			t.Error(err.Error())
		}

		calls := *loadCalls
		inner := []string{"1", "A", "B"}
		expected := [][]string{inner}
		if !reflect.DeepEqual(calls, expected) {
			t.Errorf("did not respect max batch size. Expected %#v, got %#v", expected, calls)
		}
	})

}

// test helpers
func IDLoader(max int) (*Loader, *[][]string) {
	var mu sync.Mutex
	var loadCalls [][]string
	identityLoader := NewBatchedLoader(func(keys []string) []*Result {
		var results []*Result
		mu.Lock()
		loadCalls = append(loadCalls, keys)
		mu.Unlock()
		for _, key := range keys {
			results = append(results, &Result{key, nil})
		}
		return results
	}, WithBatchCapacity(max))
	return identityLoader, &loadCalls
}
func ErrorLoader(max int) (*Loader, *[][]string) {
	var mu sync.Mutex
	var loadCalls [][]string
	identityLoader := NewBatchedLoader(func(keys []string) []*Result {
		var results []*Result
		mu.Lock()
		loadCalls = append(loadCalls, keys)
		mu.Unlock()
		for _, key := range keys {
			results = append(results, &Result{key, fmt.Errorf("this is a test error")})
		}
		return results
	}, WithBatchCapacity(max))
	return identityLoader, &loadCalls
}
func BadLoader(max int) (*Loader, *[][]string) {
	var mu sync.Mutex
	var loadCalls [][]string
	identityLoader := NewBatchedLoader(func(keys []string) []*Result {
		var results []*Result
		mu.Lock()
		loadCalls = append(loadCalls, keys)
		mu.Unlock()
		results = append(results, &Result{keys[0], nil})
		return results
	}, WithBatchCapacity(max))
	return identityLoader, &loadCalls
}
func NoCacheLoader(max int) (*Loader, *[][]string) {
	var mu sync.Mutex
	var loadCalls [][]string
	cache := &NoCache{}
	identityLoader := NewBatchedLoader(func(keys []string) []*Result {
		var results []*Result
		mu.Lock()
		loadCalls = append(loadCalls, keys)
		mu.Unlock()
		for _, key := range keys {
			results = append(results, &Result{key, nil})
		}
		return results
	}, WithCache(cache), WithBatchCapacity(max))
	return identityLoader, &loadCalls
}

// FaultyLoader gives len(keys)-1 results.
func FaultyLoader() (*Loader, *[][]string) {
	var mu sync.Mutex
	var loadCalls [][]string

	loader := NewBatchedLoader(func(keys []string) []*Result {
		var results []*Result
		mu.Lock()
		loadCalls = append(loadCalls, keys)
		mu.Unlock()

		lastKeyIndex := len(keys) - 1
		for i, key := range keys {
			if i == lastKeyIndex {
				break
			}

			results = append(results, &Result{key, nil})
		}
		return results
	})

	return loader, &loadCalls
}

///////////////////////////////////////////////////
// Benchmarks
///////////////////////////////////////////////////
var a = &Avg{}

func batchIdentity(keys []string) (results []*Result) {
	a.Add(len(keys))
	for _, key := range keys {
		results = append(results, &Result{key, nil})
	}
	return
}

func BenchmarkLoader(b *testing.B) {
	UserLoader := NewBatchedLoader(batchIdentity)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		UserLoader.Load(strconv.Itoa(i))
	}
	log.Printf("avg: %f", a.Avg())
}

type Avg struct {
	total  float64
	length float64
}

func (a *Avg) Add(v int) {
	a.total += float64(v)
	a.length++
}

func (a *Avg) Avg() float64 {
	if a.total == 0 {
		return 0
	} else if a.length == 0 {
		return 0
	}
	return a.total / a.length
}
<|endoftext|>


<commit_before>package goja

// Ported from Rhino (https://github.com/mozilla/rhino/blob/master/src/org/mozilla/javascript/DToA.java)

import (
	"bytes"
	"fmt"
	"math"
	"math/big"
	"strconv"
)

const (
	frac_mask = 0xfffff
	exp_shift = 20
	exp_msk1  = 0x100000

	exp_shiftL       = 52
	exp_mask_shifted = 0x7ff
	frac_maskL       = 0xfffffffffffff
	exp_msk1L        = 0x10000000000000
	exp_shift1       = 20
	exp_mask         = 0x7ff00000
	bias             = 1023
	p                = 53
	bndry_mask       = 0xfffff
	log2P            = 1

	digits = "0123456789abcdefghijklmnopqrstuvwxyz"
)

func lo0bits(x uint32) (k uint32) {

	if (x & 7) != 0 {
		if (x & 1) != 0 {
			return 0
		}
		if (x & 2) != 0 {
			return 1
		}
		return 2
	}
	if (x & 0xffff) == 0 {
		k = 16
		x >>= 16
	}
	if (x & 0xff) == 0 {
		k += 8
		x >>= 8
	}
	if (x & 0xf) == 0 {
		k += 4
		x >>= 4
	}
	if (x & 0x3) == 0 {
		k += 2
		x >>= 2
	}
	if (x & 1) == 0 {
		k++
		x >>= 1
		if (x & 1) == 0 {
			return 32
		}
	}
	return
}

func hi0bits(x uint32) (k uint32) {

	if (x & 0xffff0000) == 0 {
		k = 16
		x <<= 16
	}
	if (x & 0xff000000) == 0 {
		k += 8
		x <<= 8
	}
	if (x & 0xf0000000) == 0 {
		k += 4
		x <<= 4
	}
	if (x & 0xc0000000) == 0 {
		k += 2
		x <<= 2
	}
	if (x & 0x80000000) == 0 {
		k++
		if (x & 0x40000000) == 0 {
			return 32
		}
	}
	return
}

func stuffBits(bits []byte, offset int, val uint32) {
	bits[offset] = byte(val >> 24)
	bits[offset+1] = byte(val >> 16)
	bits[offset+2] = byte(val >> 8)
	bits[offset+3] = byte(val)
}

func d2b(d float64) (b *big.Int, e int32, bits uint32) {
	dBits := math.Float64bits(d)
	d0 := uint32(dBits >> 32)
	d1 := uint32(dBits)

	z := d0 & frac_mask
	d0 &= 0x7fffffff /* clear sign bit, which we ignore */

	var de, k, i uint32
	var dbl_bits []byte
	if de = (d0 >> exp_shift); de != 0 {
		z |= exp_msk1
	}

	y := d1
	if y != 0 {
		dbl_bits = make([]byte, 8)
		k = lo0bits(y)
		y >>= k
		if k != 0 {
			stuffBits(dbl_bits, 4, y|z<<(32-k))
			z >>= k
		} else {
			stuffBits(dbl_bits, 4, y)
		}
		stuffBits(dbl_bits, 0, z)
		if z != 0 {
			i = 2
		} else {
			i = 1
		}
	} else {
		dbl_bits = make([]byte, 4)
		k = lo0bits(z)
		z >>= k
		stuffBits(dbl_bits, 0, z)
		k += 32
		i = 1
	}

	if de != 0 {
		e = int32(de - bias - (p - 1) + k)
		bits = p - k
	} else {
		e = int32(de - bias - (p - 1) + 1 + k)
		bits = 32*i - hi0bits(z)
	}
	b = (&big.Int{}).SetBytes(dbl_bits)
	return
}

func dtobasestr(num float64, radix int) string {
	var negative bool
	if num < 0 {
		num = -num
		negative = true
	}

	dfloor := math.Floor(num)
	ldfloor := int64(dfloor)
	var intDigits string
	if dfloor == float64(ldfloor) {
		if negative {
			ldfloor = -ldfloor
		}
		intDigits = strconv.FormatInt(ldfloor, radix)
	} else {
		floorBits := math.Float64bits(num)
		exp := int(floorBits>>exp_shiftL) & exp_mask_shifted
		var mantissa int64
		if exp == 0 {
			mantissa = int64((floorBits & frac_maskL) << 1)
		} else {
			mantissa = int64((floorBits & frac_maskL) | exp_msk1L)
		}

		if negative {
			mantissa = -mantissa
		}
		exp -= 1075
		x := big.NewInt(mantissa)
		if exp > 0 {
			x.Lsh(x, uint(exp))
		} else if exp < 0 {
			x.Rsh(x, uint(-exp))
		}
		intDigits = x.Text(radix)
	}

	if num == dfloor {
		// No fraction part
		return intDigits
	} else {
		/* We have a fraction. */
		var buffer bytes.Buffer
		buffer.WriteString(intDigits)
		buffer.WriteByte('.')
		df := num - dfloor

		dBits := math.Float64bits(num)
		word0 := uint32(dBits >> 32)
		word1 := uint32(dBits)

		b, e, _ := d2b(df)
		//            JS_ASSERT(e < 0);
		/* At this point df = b * 2^e.  e must be less than zero because 0 < df < 1. */

		s2 := -int32((word0 >> exp_shift1) & (exp_mask >> exp_shift1))
		if s2 == 0 {
			s2 = -1
		}
		s2 += bias + p
		/* 1/2^s2 = (nextDouble(d) - d)/2 */
		//            JS_ASSERT(-s2 < e);
		if -s2 >= e {
			panic(fmt.Errorf("-s2 >= e: %d, %d", -s2, e))
		}
		mlo := big.NewInt(1)
		mhi := mlo
		if (word1 == 0) && ((word0 & bndry_mask) == 0) && ((word0 & (exp_mask & exp_mask << 1)) != 0) {
			/* The special case.  Here we want to be within a quarter of the last input
			   significant digit instead of one half of it when the output string's value is less than d.  */
			s2 += log2P
			mhi = big.NewInt(1 << log2P)
		}

		b.Lsh(b, uint(e+s2))
		s := big.NewInt(1)
		s.Lsh(s, uint(s2))
		/* At this point we have the following:
		 *   s = 2^s2;
		 *   1 > df = b/2^s2 > 0;
		 *   (d - prevDouble(d))/2 = mlo/2^s2;
		 *   (nextDouble(d) - d)/2 = mhi/2^s2. */
		bigBase := big.NewInt(int64(radix))

		done := false
		m := &big.Int{}
		delta := &big.Int{}
		for !done {
			b.Mul(b, bigBase)
			b.DivMod(b, s, m)
			digit := byte(b.Int64())
			b, m = m, b
			mlo.Mul(mlo, bigBase)
			if mlo != mhi {
				mhi.Mul(mhi, bigBase)
			}

			/* Do we yet have the shortest string that will round to d? */
			j := b.Cmp(mlo)
			/* j is b/2^s2 compared with mlo/2^s2. */

			delta.Sub(s, mhi)
			var j1 int
			if delta.Sign() <= 0 {
				j1 = 1
			} else {
				j1 = b.Cmp(delta)
			}
			/* j1 is b/2^s2 compared with 1 - mhi/2^s2. */
			if j1 == 0 && (word1&1) == 0 {
				if j > 0 {
					digit++
				}
				done = true
			} else if j < 0 || (j == 0 && ((word1 & 1) == 0)) {
				if j1 > 0 {
					/* Either dig or dig+1 would work here as the least significant digit.
					Use whichever would produce an output value closer to d. */
					b.Lsh(b, 1)
					j1 = b.Cmp(s)
					if j1 > 0 { /* The even test (|| (j1 == 0 && (digit & 1))) is not here because it messes up odd base output such as 3.5 in base 3.  */
						digit++
					}
				}
				done = true
			} else if j1 > 0 {
				digit++
				done = true
			}
			//                JS_ASSERT(digit < (uint32)base);
			buffer.WriteByte(digits[digit])
		}

		return buffer.String()
	}
}
<commit_msg>Fixed operator precedence.<commit_after>package goja

// Ported from Rhino (https://github.com/mozilla/rhino/blob/master/src/org/mozilla/javascript/DToA.java)

import (
	"bytes"
	"fmt"
	"math"
	"math/big"
	"strconv"
)

const (
	frac_mask = 0xfffff
	exp_shift = 20
	exp_msk1  = 0x100000

	exp_shiftL       = 52
	exp_mask_shifted = 0x7ff
	frac_maskL       = 0xfffffffffffff
	exp_msk1L        = 0x10000000000000
	exp_shift1       = 20
	exp_mask         = 0x7ff00000
	bias             = 1023
	p                = 53
	bndry_mask       = 0xfffff
	log2P            = 1

	digits = "0123456789abcdefghijklmnopqrstuvwxyz"
)

func lo0bits(x uint32) (k uint32) {

	if (x & 7) != 0 {
		if (x & 1) != 0 {
			return 0
		}
		if (x & 2) != 0 {
			return 1
		}
		return 2
	}
	if (x & 0xffff) == 0 {
		k = 16
		x >>= 16
	}
	if (x & 0xff) == 0 {
		k += 8
		x >>= 8
	}
	if (x & 0xf) == 0 {
		k += 4
		x >>= 4
	}
	if (x & 0x3) == 0 {
		k += 2
		x >>= 2
	}
	if (x & 1) == 0 {
		k++
		x >>= 1
		if (x & 1) == 0 {
			return 32
		}
	}
	return
}

func hi0bits(x uint32) (k uint32) {

	if (x & 0xffff0000) == 0 {
		k = 16
		x <<= 16
	}
	if (x & 0xff000000) == 0 {
		k += 8
		x <<= 8
	}
	if (x & 0xf0000000) == 0 {
		k += 4
		x <<= 4
	}
	if (x & 0xc0000000) == 0 {
		k += 2
		x <<= 2
	}
	if (x & 0x80000000) == 0 {
		k++
		if (x & 0x40000000) == 0 {
			return 32
		}
	}
	return
}

func stuffBits(bits []byte, offset int, val uint32) {
	bits[offset] = byte(val >> 24)
	bits[offset+1] = byte(val >> 16)
	bits[offset+2] = byte(val >> 8)
	bits[offset+3] = byte(val)
}

func d2b(d float64) (b *big.Int, e int32, bits uint32) {
	dBits := math.Float64bits(d)
	d0 := uint32(dBits >> 32)
	d1 := uint32(dBits)

	z := d0 & frac_mask
	d0 &= 0x7fffffff /* clear sign bit, which we ignore */

	var de, k, i uint32
	var dbl_bits []byte
	if de = (d0 >> exp_shift); de != 0 {
		z |= exp_msk1
	}

	y := d1
	if y != 0 {
		dbl_bits = make([]byte, 8)
		k = lo0bits(y)
		y >>= k
		if k != 0 {
			stuffBits(dbl_bits, 4, y|z<<(32-k))
			z >>= k
		} else {
			stuffBits(dbl_bits, 4, y)
		}
		stuffBits(dbl_bits, 0, z)
		if z != 0 {
			i = 2
		} else {
			i = 1
		}
	} else {
		dbl_bits = make([]byte, 4)
		k = lo0bits(z)
		z >>= k
		stuffBits(dbl_bits, 0, z)
		k += 32
		i = 1
	}

	if de != 0 {
		e = int32(de - bias - (p - 1) + k)
		bits = p - k
	} else {
		e = int32(de - bias - (p - 1) + 1 + k)
		bits = 32*i - hi0bits(z)
	}
	b = (&big.Int{}).SetBytes(dbl_bits)
	return
}

func dtobasestr(num float64, radix int) string {
	var negative bool
	if num < 0 {
		num = -num
		negative = true
	}

	dfloor := math.Floor(num)
	ldfloor := int64(dfloor)
	var intDigits string
	if dfloor == float64(ldfloor) {
		if negative {
			ldfloor = -ldfloor
		}
		intDigits = strconv.FormatInt(ldfloor, radix)
	} else {
		floorBits := math.Float64bits(num)
		exp := int(floorBits>>exp_shiftL) & exp_mask_shifted
		var mantissa int64
		if exp == 0 {
			mantissa = int64((floorBits & frac_maskL) << 1)
		} else {
			mantissa = int64((floorBits & frac_maskL) | exp_msk1L)
		}

		if negative {
			mantissa = -mantissa
		}
		exp -= 1075
		x := big.NewInt(mantissa)
		if exp > 0 {
			x.Lsh(x, uint(exp))
		} else if exp < 0 {
			x.Rsh(x, uint(-exp))
		}
		intDigits = x.Text(radix)
	}

	if num == dfloor {
		// No fraction part
		return intDigits
	} else {
		/* We have a fraction. */
		var buffer bytes.Buffer
		buffer.WriteString(intDigits)
		buffer.WriteByte('.')
		df := num - dfloor

		dBits := math.Float64bits(num)
		word0 := uint32(dBits >> 32)
		word1 := uint32(dBits)

		b, e, _ := d2b(df)
		//            JS_ASSERT(e < 0);
		/* At this point df = b * 2^e.  e must be less than zero because 0 < df < 1. */

		s2 := -int32((word0 >> exp_shift1) & (exp_mask >> exp_shift1))
		if s2 == 0 {
			s2 = -1
		}
		s2 += bias + p
		/* 1/2^s2 = (nextDouble(d) - d)/2 */
		//            JS_ASSERT(-s2 < e);
		if -s2 >= e {
			panic(fmt.Errorf("-s2 >= e: %d, %d", -s2, e))
		}
		mlo := big.NewInt(1)
		mhi := mlo
		if (word1 == 0) && ((word0 & bndry_mask) == 0) && ((word0 & (exp_mask & (exp_mask << 1))) != 0) {
			/* The special case.  Here we want to be within a quarter of the last input
			   significant digit instead of one half of it when the output string's value is less than d.  */
			s2 += log2P
			mhi = big.NewInt(1 << log2P)
		}

		b.Lsh(b, uint(e+s2))
		s := big.NewInt(1)
		s.Lsh(s, uint(s2))
		/* At this point we have the following:
		 *   s = 2^s2;
		 *   1 > df = b/2^s2 > 0;
		 *   (d - prevDouble(d))/2 = mlo/2^s2;
		 *   (nextDouble(d) - d)/2 = mhi/2^s2. */
		bigBase := big.NewInt(int64(radix))

		done := false
		m := &big.Int{}
		delta := &big.Int{}
		for !done {
			b.Mul(b, bigBase)
			b.DivMod(b, s, m)
			digit := byte(b.Int64())
			b, m = m, b
			mlo.Mul(mlo, bigBase)
			if mlo != mhi {
				mhi.Mul(mhi, bigBase)
			}

			/* Do we yet have the shortest string that will round to d? */
			j := b.Cmp(mlo)
			/* j is b/2^s2 compared with mlo/2^s2. */

			delta.Sub(s, mhi)
			var j1 int
			if delta.Sign() <= 0 {
				j1 = 1
			} else {
				j1 = b.Cmp(delta)
			}
			/* j1 is b/2^s2 compared with 1 - mhi/2^s2. */
			if j1 == 0 && (word1&1) == 0 {
				if j > 0 {
					digit++
				}
				done = true
			} else if j < 0 || (j == 0 && ((word1 & 1) == 0)) {
				if j1 > 0 {
					/* Either dig or dig+1 would work here as the least significant digit.
					Use whichever would produce an output value closer to d. */
					b.Lsh(b, 1)
					j1 = b.Cmp(s)
					if j1 > 0 { /* The even test (|| (j1 == 0 && (digit & 1))) is not here because it messes up odd base output such as 3.5 in base 3.  */
						digit++
					}
				}
				done = true
			} else if j1 > 0 {
				digit++
				done = true
			}
			//                JS_ASSERT(digit < (uint32)base);
			buffer.WriteByte(digits[digit])
		}

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


<commit_before>// Copyright 2017 John Scherff
//
// 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 (
	`bytes`
	`encoding/json`
	`fmt`
	`io/ioutil`
	`net/http`
	`time`
	`github.com/jscherff/gocmdb`
)

var (
	transport = &http.Transport{ResponseHeaderTimeout: 10 * time.Second}
	client = &http.Client{Transport: transport}

	HttpStatusMap = map[int]string {
		http.StatusOK:			`request processed, no errors`,		// 200
		http.StatusCreated:		`request processed, object created`,	// 201
		http.StatusAccepted:		`request processed, data accepted`,	// 202
		http.StatusNoContent:		`request processed, no action taken`,	// 204
		http.StatusNotModified:		`request processed, no changes found`,	// 302
		http.StatusBadRequest:		`unsupported or malformed request`,	// 400
		http.StatusNotAcceptable:	`insufficient or incorrect data`,	// 406
		http.StatusUnprocessableEntity:	`unable to decode request`,		// 422
		http.StatusFailedDependency:	`unsatisfied prerequisite`,		// 424
		http.StatusInternalServerError:	`unable to process request`,		// 500
	}
)

// getNewSN obtains a serial number from the gocmdbd server.
func getNewSN(o gocmdb.Registerable) (s string, err error) {

	var (
		j []byte
		sc int
	)

	url := fmt.Sprintf(`%s/%s/%s/%s/%s`, conf.Server.URL,
		conf.Server.NewSNPath, o.Host(), o.VID(), o.PID(),
	)

	if j, err = o.JSON(); err != nil {
		elog.Print(err)
		return s, err
	}

	if j, sc, err = httpPost(url, j); err != nil {
		return s, err
	}

	switch sc {
	case http.StatusCreated:
		err = json.Unmarshal(j, &s)
	default:
		err = fmt.Errorf(`serial number not generated - %q`, http.StatusText(sc))
	}

	if err != nil {
		elog.Print(err)
	}

	return s, err
}

// checkinDevice checks a device in with the gocmdbd server.
func checkinDevice(o gocmdb.Registerable) (err error) {

	var (
		j []byte
		sc int
	)

	url := fmt.Sprintf(`%s/%s/%s/%s/%s`, conf.Server.URL,
		conf.Server.CheckinPath, o.Host(), o.VID(), o.PID(),
	)

	if j, err = o.JSON(); err != nil {
		elog.Print(err)
		return err
	}

	if _, sc, err = httpPost(url, j); err != nil {
		elog.Print(err)
		return err
	}

	switch sc {
	case http.StatusOK:
	case http.StatusCreated:
	case http.StatusAccepted:
	case http.StatusNoContent:
	case http.StatusNotModified:
	default:
		err = fmt.Errorf(`checkin not accepted - %q`, http.StatusText(sc))
	}

	if err != nil {
		elog.Print(err)
	}

	return err
}

// checkoutDevice obtains the JSON representation of a serialized device object
// from the server using the unique key combination VID+PID+SN.
func checkoutDevice(o gocmdb.Auditable) (j []byte, err error) {

	var (
		sc int
	)

	if o.ID() == `` {
		slog.Print(`device %s-%s fetch: skipping, no SN`,
			o.VID(), o.PID(),
		)
		return j, err
	}

	url := fmt.Sprintf(`%s/%s/%s/%s/%s/%s`, conf.Server.URL,
		conf.Server.CheckoutPath, o.Host(), o.VID(), o.PID(), o.ID(),
	)

	if j, sc, err = httpGet(url); err != nil {
		elog.Print(err)
		return j, err
	}

	switch sc {
	case http.StatusOK:
	case http.StatusCreated:
	case http.StatusAccepted:
	case http.StatusNoContent:
	case http.StatusNotModified:
	default:
		err = fmt.Errorf(`device not returned - %q`, http.StatusText(sc))
	}

	if err != nil {
		elog.Print(err)
	}

	return j, err
}

// submitAudit submits changes from audit to the server in JSON format.
func submitAudit(o gocmdb.Auditable) (err error) {

	var (
		j []byte
		sc int
	)

	url := fmt.Sprintf(`%s/%s/%s/%s/%s/%s`, conf.Server.URL,
		conf.Server.AuditPath, o.Host(), o.VID(), o.PID(), o.ID(),
	)

	if j, err = json.Marshal(o.GetChanges()); err != nil {
		elog.Print(err)
		return err
	}

	if _, sc, err = httpPost(url, j); err != nil {
		elog.Print(err)
		return err
	}

	switch sc {
	case http.StatusOK:
	case http.StatusCreated:
	case http.StatusAccepted:
	case http.StatusNoContent:
	case http.StatusNotModified:
	default:
		err = fmt.Errorf(`audit not accepted - %q`, http.StatusText(sc))
	}

	if err != nil {
		elog.Print(err)
	}

	return err
}

// httpPost sends http POST requests to gocmdbd server endpoints for other functions.
func httpPost(url string, j []byte ) (b []byte, sc int, err error) {

	if req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(j)); err != nil {
		return b, sc, err
	} else {
		req.Header.Add(`Content-Type`, `application/json; charset=UTF8`)
		return httpRequest(req)
	}
}

// httpGet sends http GET requests to gocmdbd server endpoints for other functions.
func httpGet(url string) (b []byte, sc int, err error) {

	if req, err := http.NewRequest(http.MethodGet, url, nil); err != nil {
		return b, sc, err
	} else {
		return httpRequest(req)
	}
}

// httpRequest sends http requests to gocmdbd server endpoints for other functions.
func httpRequest(req *http.Request) (b []byte, sc int, err error) {

	req.Header.Add(`Accept`, `application/json; charset=UTF8`)
	req.Header.Add(`X-Custom-Header`, `gocmdb`)

	resp, err := client.Do(req)

	if err == nil {
		defer resp.Body.Close()
		sc = resp.StatusCode
		b, err = ioutil.ReadAll(resp.Body)
	}

	if err != nil {
		elog.Print(err)
	}

	return b, sc, err
}
<commit_msg>Re-applied Nick Meverden's recommendations after deleting httpio.go<commit_after>// Copyright 2017 John Scherff
//
// 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 (
	`bytes`
	`encoding/json`
	`fmt`
	`io/ioutil`
	`net/http`
	`time`
	`github.com/jscherff/gocmdb`
)

const (
	acceptedMask =
		http.StatusOK |
		http.StatusCreated |
		http.StatusAccepted |
		http.StatusNoContent |
		http.StatusNotModified
)

var (
	transport = &http.Transport{ResponseHeaderTimeout: 10 * time.Second}
	client = &http.Client{Transport: transport}
)

type httpStatus int

func (this httpStatus) Accepted() bool {
	return this & acceptedMask == this
}

func (this httpStatus) String() (s string) {

	switch this {

	case http.StatusOK:
		s = `request processed, no errors`
	case http.StatusCreated:
		s = `request processed, object created`
	case http.StatusAccepted:
		s = `request processed, data accepted`
	case http.StatusNoContent:
		s = `request processed, no action taken`
	case http.StatusNotModified:
		s = `request processed, no changes found`
	case http.StatusBadRequest:
		s = `unsupported or malformed request`
	case http.StatusNotAcceptable:
		s = `insufficient or incorrect data`
	case http.StatusUnprocessableEntity:
		s = `unable to decode request`
	case http.StatusFailedDependency:
		s = `unsatisfied prerequisite`
	case http.StatusInternalServerError:
		s = `unable to process request`
	default:
		s = `status unknown`
	}

	return s
}

func (this httpStatus) StatusText() (s string) {
	return http.StatusText(int(this))
}

// getNewSN obtains a serial number from the gocmdbd server.
func getNewSN(o gocmdb.Registerable) (s string, err error) {

	var (
		j []byte
		hs httpStatus
	)

	url := fmt.Sprintf(`%s/%s/%s/%s/%s`, conf.Server.URL,
		conf.Server.NewSNPath, o.Host(), o.VID(), o.PID(),
	)

	if j, err = o.JSON(); err != nil {
		elog.Print(err)
		return s, err
	}

	if j, hs, err = httpPost(url, j); err != nil {
		return s, err
	}

	if hs.Accepted() {
		err = json.Unmarshal(j, &s)
	} else {
		err = fmt.Errorf(`serial number not generated - %s`, hs)
	}

	if err != nil {
		elog.Print(err)
	}

	return s, err
}

// checkinDevice checks a device in with the gocmdbd server.
func checkinDevice(o gocmdb.Registerable) (err error) {

	var (
		j []byte
		hs httpStatus
	)

	url := fmt.Sprintf(`%s/%s/%s/%s/%s`, conf.Server.URL,
		conf.Server.CheckinPath, o.Host(), o.VID(), o.PID(),
	)

	if j, err = o.JSON(); err != nil {
		elog.Print(err)
		return err
	}

	if _, hs, err = httpPost(url, j); err != nil {
		elog.Print(err)
		return err
	}

	if !hs.Accepted() {
		err = fmt.Errorf(`checkin not accepted - %s`, hs)
		elog.Print(err)
	}

	return err
}

// checkoutDevice obtains the JSON representation of a serialized device object
// from the server using the unique key combination VID+PID+SN.
func checkoutDevice(o gocmdb.Auditable) (j []byte, err error) {

	var (
		hs httpStatus
	)

	if o.ID() == `` {
		slog.Print(`device %s-%s fetch: skipping, no SN`,
			o.VID(), o.PID(),
		)
		return j, err
	}

	url := fmt.Sprintf(`%s/%s/%s/%s/%s/%s`, conf.Server.URL,
		conf.Server.CheckoutPath, o.Host(), o.VID(), o.PID(), o.ID(),
	)

	if j, hs, err = httpGet(url); err != nil {
		elog.Print(err)
		return j, err
	}

	if !hs.Accepted() {
		err = fmt.Errorf(`device not returned - %q`, hs)
		elog.Print(err)
	}

	return j, err
}

// submitAudit submits changes from audit to the server in JSON format.
func submitAudit(o gocmdb.Auditable) (err error) {

	var (
		j []byte
		hs httpStatus
	)

	url := fmt.Sprintf(`%s/%s/%s/%s/%s/%s`, conf.Server.URL,
		conf.Server.AuditPath, o.Host(), o.VID(), o.PID(), o.ID(),
	)

	if j, err = json.Marshal(o.GetChanges()); err != nil {
		elog.Print(err)
		return err
	}

	if _, hs, err = httpPost(url, j); err != nil {
		elog.Print(err)
		return err
	}

	if !hs.Accepted() {
		err = fmt.Errorf(`audit not accepted - %s`, hs)
		elog.Print(err)
	}

	return err
}

// httpPost sends http POST requests to gocmdbd server endpoints for other functions.
func httpPost(url string, j []byte ) (b []byte, hs httpStatus, err error) {

	if req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(j)); err != nil {
		return b, hs, err
	} else {
		req.Header.Add(`Content-Type`, `application/json; charset=UTF8`)
		return httpRequest(req)
	}
}

// httpGet sends http GET requests to gocmdbd server endpoints for other functions.
func httpGet(url string) (b []byte, hs httpStatus, err error) {

	if req, err := http.NewRequest(http.MethodGet, url, nil); err != nil {
		return b, hs, err
	} else {
		return httpRequest(req)
	}
}

// httpRequest sends http requests to gocmdbd server endpoints for other functions.
func httpRequest(req *http.Request) (b []byte, hs httpStatus, err error) {

	req.Header.Add(`Accept`, `application/json; charset=UTF8`)
	req.Header.Add(`X-Custom-Header`, `gocmdb`)

	resp, err := client.Do(req)

	if err == nil {
		defer resp.Body.Close()
		hs = httpStatus(resp.StatusCode)
		b, err = ioutil.ReadAll(resp.Body)
	}

	if err != nil {
		elog.Print(err)
	}

	return b, hs, err
}
<|endoftext|>


<commit_before>package allocdir

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	cstructs "github.com/hashicorp/nomad/client/structs"
)

type TaskDir struct {
	// Dir is the path to Task directory on the host
	Dir string

	// SharedAllocDir is the path to shared alloc directory on the host
	// <alloc_dir>/alloc/
	SharedAllocDir string

	// SharedTaskDir is the path to the shared alloc directory linked into
	// the task directory on the host.
	// <task_dir>/alloc/
	SharedTaskDir string

	// LocalDir is the path to the task's local directory on the host
	// <task_dir>/local/
	LocalDir string

	// LogDir is the path to the task's log directory on the host
	// <alloc_dir>/alloc/logs/
	LogDir string

	// SecretsDir is the path to secrets/ directory on the host
	// <task_dir>/secrets/
	SecretsDir string
}

// newTaskDir creates a TaskDir struct with paths set. Call Build() to
// create paths on disk.
//
// Call AllocDir.NewTaskDir to create new TaskDirs
func newTaskDir(allocDir, taskName string) *TaskDir {
	taskDir := filepath.Join(allocDir, taskName)
	return &TaskDir{
		Dir:            taskDir,
		SharedAllocDir: filepath.Join(allocDir, SharedAllocName),
		LogDir:         filepath.Join(allocDir, SharedAllocName, LogDirName),
		SharedTaskDir:  filepath.Join(taskDir, SharedAllocName),
		LocalDir:       filepath.Join(taskDir, TaskLocal),
		SecretsDir:     filepath.Join(taskDir, TaskSecrets),
	}
}

// Build default directories and permissions in a task directory.
func (t *TaskDir) Build(chroot map[string]string, fsi cstructs.FSIsolation) error {
	if err := os.MkdirAll(t.Dir, 0777); err != nil {
		return err
	}

	// Make the task directory have non-root permissions.
	if err := dropDirPermissions(t.Dir); err != nil {
		return err
	}

	// Create a local directory that each task can use.
	if err := os.MkdirAll(t.LocalDir, 0777); err != nil {
		return err
	}

	if err := dropDirPermissions(t.LocalDir); err != nil {
		return err
	}

	// Create the directories that should be in every task.
	for _, dir := range TaskDirs {
		absdir := filepath.Join(t.Dir, dir)
		if err := os.MkdirAll(absdir, 0777); err != nil {
			return err
		}

		if err := dropDirPermissions(absdir); err != nil {
			return err
		}
	}

	// Always link the shared task directory even though image based
	// filesystem isolalation doesn't require it. This way we have a
	// consistent task dir.
	if err := linkDir(t.SharedAllocDir, t.SharedTaskDir); err != nil {
		return fmt.Errorf("Failed to mount shared directory for task: %v", err)
	}

	// Create the secret directory
	if err := createSecretDir(t.SecretsDir); err != nil {
		return err
	}

	if err := dropDirPermissions(t.SecretsDir); err != nil {
		return err
	}

	// Build chroot if chroot filesystem isolation is going to be used
	if fsi == cstructs.FSIsolationChroot {
		if err := t.buildChroot(chroot); err != nil {
			return err
		}
	}

	return nil
}

// buildChroot takes a mapping of absolute directory or file paths on the host
// to their intended, relative location within the task directory. This
// attempts hardlink and then defaults to copying. If the path exists on the
// host and can't be embedded an error is returned.
func (t *TaskDir) buildChroot(entries map[string]string) error {
	// Link/copy chroot entries
	if err := t.embedDirs(entries); err != nil {
		return err
	}

	// Mount special dirs
	if err := t.mountSpecialDirs(); err != nil {
		return err
	}

	return nil
}

func (t *TaskDir) embedDirs(entries map[string]string) error {
	subdirs := make(map[string]string)
	for source, dest := range entries {
		// Check to see if directory exists on host.
		s, err := os.Stat(source)
		if os.IsNotExist(err) {
			continue
		}

		// Embedding a single file
		if !s.IsDir() {
			if err := createDir(t.Dir, filepath.Dir(dest)); err != nil {
				return fmt.Errorf("Couldn't create destination directory %v: %v", dest, err)
			}

			// Copy the file.
			taskEntry := filepath.Join(t.Dir, dest)
			if err := linkOrCopy(source, taskEntry, s.Mode().Perm()); err != nil {
				return err
			}

			continue
		}

		// Create destination directory.
		destDir := filepath.Join(t.Dir, dest)

		if err := createDir(t.Dir, dest); err != nil {
			return fmt.Errorf("Couldn't create destination directory %v: %v", destDir, err)
		}

		// Enumerate the files in source.
		dirEntries, err := ioutil.ReadDir(source)
		if err != nil {
			return fmt.Errorf("Couldn't read directory %v: %v", source, err)
		}

		for _, entry := range dirEntries {
			hostEntry := filepath.Join(source, entry.Name())
			taskEntry := filepath.Join(destDir, filepath.Base(hostEntry))
			if entry.IsDir() {
				subdirs[hostEntry] = filepath.Join(dest, filepath.Base(hostEntry))
				continue
			}

			// Check if entry exists. This can happen if restarting a failed
			// task.
			if _, err := os.Lstat(taskEntry); err == nil {
				continue
			}

			if !entry.Mode().IsRegular() {
				// If it is a symlink we can create it, otherwise we skip it.
				if entry.Mode()&os.ModeSymlink == 0 {
					continue
				}

				link, err := os.Readlink(hostEntry)
				if err != nil {
					return fmt.Errorf("Couldn't resolve symlink for %v: %v", source, err)
				}

				if err := os.Symlink(link, taskEntry); err != nil {
					// Symlinking twice
					if err.(*os.LinkError).Err.Error() != "file exists" {
						return fmt.Errorf("Couldn't create symlink: %v", err)
					}
				}
				continue
			}

			if err := linkOrCopy(hostEntry, taskEntry, entry.Mode().Perm()); err != nil {
				return err
			}
		}
	}

	// Recurse on self to copy subdirectories.
	if len(subdirs) != 0 {
		return t.embedDirs(subdirs)
	}

	return nil
}
<commit_msg>Add comments to TaskDir<commit_after>package allocdir

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	cstructs "github.com/hashicorp/nomad/client/structs"
)

// TaskDir contains all of the paths relevant to a task. All paths are on the
// host system so drivers should mount/link into task containers as necessary.
type TaskDir struct {
	// Dir is the path to Task directory on the host
	Dir string

	// SharedAllocDir is the path to shared alloc directory on the host
	// <alloc_dir>/alloc/
	SharedAllocDir string

	// SharedTaskDir is the path to the shared alloc directory linked into
	// the task directory on the host.
	// <task_dir>/alloc/
	SharedTaskDir string

	// LocalDir is the path to the task's local directory on the host
	// <task_dir>/local/
	LocalDir string

	// LogDir is the path to the task's log directory on the host
	// <alloc_dir>/alloc/logs/
	LogDir string

	// SecretsDir is the path to secrets/ directory on the host
	// <task_dir>/secrets/
	SecretsDir string
}

// newTaskDir creates a TaskDir struct with paths set. Call Build() to
// create paths on disk.
//
// Call AllocDir.NewTaskDir to create new TaskDirs
func newTaskDir(allocDir, taskName string) *TaskDir {
	taskDir := filepath.Join(allocDir, taskName)
	return &TaskDir{
		Dir:            taskDir,
		SharedAllocDir: filepath.Join(allocDir, SharedAllocName),
		LogDir:         filepath.Join(allocDir, SharedAllocName, LogDirName),
		SharedTaskDir:  filepath.Join(taskDir, SharedAllocName),
		LocalDir:       filepath.Join(taskDir, TaskLocal),
		SecretsDir:     filepath.Join(taskDir, TaskSecrets),
	}
}

// Build default directories and permissions in a task directory.
func (t *TaskDir) Build(chroot map[string]string, fsi cstructs.FSIsolation) error {
	if err := os.MkdirAll(t.Dir, 0777); err != nil {
		return err
	}

	// Make the task directory have non-root permissions.
	if err := dropDirPermissions(t.Dir); err != nil {
		return err
	}

	// Create a local directory that each task can use.
	if err := os.MkdirAll(t.LocalDir, 0777); err != nil {
		return err
	}

	if err := dropDirPermissions(t.LocalDir); err != nil {
		return err
	}

	// Create the directories that should be in every task.
	for _, dir := range TaskDirs {
		absdir := filepath.Join(t.Dir, dir)
		if err := os.MkdirAll(absdir, 0777); err != nil {
			return err
		}

		if err := dropDirPermissions(absdir); err != nil {
			return err
		}
	}

	// Always link the shared task directory even though image based
	// filesystem isolalation doesn't require it. This way we have a
	// consistent task dir.
	if err := linkDir(t.SharedAllocDir, t.SharedTaskDir); err != nil {
		return fmt.Errorf("Failed to mount shared directory for task: %v", err)
	}

	// Create the secret directory
	if err := createSecretDir(t.SecretsDir); err != nil {
		return err
	}

	if err := dropDirPermissions(t.SecretsDir); err != nil {
		return err
	}

	// Build chroot if chroot filesystem isolation is going to be used
	if fsi == cstructs.FSIsolationChroot {
		if err := t.buildChroot(chroot); err != nil {
			return err
		}
	}

	return nil
}

// buildChroot takes a mapping of absolute directory or file paths on the host
// to their intended, relative location within the task directory. This
// attempts hardlink and then defaults to copying. If the path exists on the
// host and can't be embedded an error is returned.
func (t *TaskDir) buildChroot(entries map[string]string) error {
	// Link/copy chroot entries
	if err := t.embedDirs(entries); err != nil {
		return err
	}

	// Mount special dirs
	if err := t.mountSpecialDirs(); err != nil {
		return err
	}

	return nil
}

func (t *TaskDir) embedDirs(entries map[string]string) error {
	subdirs := make(map[string]string)
	for source, dest := range entries {
		// Check to see if directory exists on host.
		s, err := os.Stat(source)
		if os.IsNotExist(err) {
			continue
		}

		// Embedding a single file
		if !s.IsDir() {
			if err := createDir(t.Dir, filepath.Dir(dest)); err != nil {
				return fmt.Errorf("Couldn't create destination directory %v: %v", dest, err)
			}

			// Copy the file.
			taskEntry := filepath.Join(t.Dir, dest)
			if err := linkOrCopy(source, taskEntry, s.Mode().Perm()); err != nil {
				return err
			}

			continue
		}

		// Create destination directory.
		destDir := filepath.Join(t.Dir, dest)

		if err := createDir(t.Dir, dest); err != nil {
			return fmt.Errorf("Couldn't create destination directory %v: %v", destDir, err)
		}

		// Enumerate the files in source.
		dirEntries, err := ioutil.ReadDir(source)
		if err != nil {
			return fmt.Errorf("Couldn't read directory %v: %v", source, err)
		}

		for _, entry := range dirEntries {
			hostEntry := filepath.Join(source, entry.Name())
			taskEntry := filepath.Join(destDir, filepath.Base(hostEntry))
			if entry.IsDir() {
				subdirs[hostEntry] = filepath.Join(dest, filepath.Base(hostEntry))
				continue
			}

			// Check if entry exists. This can happen if restarting a failed
			// task.
			if _, err := os.Lstat(taskEntry); err == nil {
				continue
			}

			if !entry.Mode().IsRegular() {
				// If it is a symlink we can create it, otherwise we skip it.
				if entry.Mode()&os.ModeSymlink == 0 {
					continue
				}

				link, err := os.Readlink(hostEntry)
				if err != nil {
					return fmt.Errorf("Couldn't resolve symlink for %v: %v", source, err)
				}

				if err := os.Symlink(link, taskEntry); err != nil {
					// Symlinking twice
					if err.(*os.LinkError).Err.Error() != "file exists" {
						return fmt.Errorf("Couldn't create symlink: %v", err)
					}
				}
				continue
			}

			if err := linkOrCopy(hostEntry, taskEntry, entry.Mode().Perm()); err != nil {
				return err
			}
		}
	}

	// Recurse on self to copy subdirectories.
	if len(subdirs) != 0 {
		return t.embedDirs(subdirs)
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2017 Zebra Project
//
// 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 rib

import (
	"encoding/json"
	"fmt"
	//"os"
	"net"
	"os/exec"
)

func TunnelProbe(vrfId string, peerId string) bool {
	jsonStr, err := exec.Command("curl", "-s", "http://localhost:9999/peers").Output()
	if err != nil {
		fmt.Println("File read error", err)
		return false
	}

	var jsonIntf interface{}
	err = json.Unmarshal([]byte(jsonStr), &jsonIntf)
	if err != nil {
		fmt.Println("json Unmarshal error", err)
		return false
	}

	if jsonMap, ok := jsonIntf.(map[string]interface{}); ok {
		if vrf, ok := jsonMap[vrfId]; ok {
			if vrfMap, ok := vrf.(map[string]interface{}); ok {
				if peer, ok := vrfMap[peerId]; ok {
					if peerMap, ok := peer.(map[string]interface{}); ok {
						for key, value := range peerMap {
							fmt.Println("key:", key)
							if valueMap, ok := value.(map[string]interface{}); ok {
								if state, ok := valueMap["state"]; ok {
									fmt.Println("state", state)
									if state == "ESTABLISHED" {
										return true
									}
								}
							}
						}
					}
				}
			}
		}
	}
	return false
}

func PeerId(vrfId string, nexthop string) string {
	jsonStr, err := exec.Command("curl", "-s", "http://localhost:9999/config").Output()
	if err != nil {
		fmt.Println("File read error", err)
		return ""
	}

	var jsonIntf interface{}
	err = json.Unmarshal([]byte(jsonStr), &jsonIntf)
	if err != nil {
		fmt.Println("json Unmarshal error", err)
		return ""
	}

	if jsonMap, ok := jsonIntf.(map[string]interface{}); ok {
		if links, ok := jsonMap["links"]; ok {
			if vrfs, ok := links.(map[string]interface{}); ok {
				if vrf, ok := vrfs[vrfId]; ok {
					if peers, ok := vrf.(map[string]interface{}); ok {
						if peer, ok := peers["peers"]; ok {
							if entry, ok := peer.(map[string]interface{}); ok {
								for peerId, item := range entry {
									if peer, ok := item.(map[string]interface{}); ok {
										if nhop, ok := peer["nexthop-id"]; ok {
											if nhop == nexthop {
												return peerId
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
	return ""
}

func DtlsNexthop(vrfId int, nexthop net.IP) bool {
	vrf := fmt.Sprintf("vrf%d", vrfId)
	peerId := PeerId(vrf, nexthop.String())
	fmt.Println("Peer ID:", peerId)
	if peerId == "" {
		fmt.Println("Peer does not exists")
		return false
	}
	result := TunnelProbe(vrf, peerId)
	if result {
		fmt.Println("Tunnel is UP")
		return true
	} else {
		fmt.Println("Tunnel is DOWN")
		return false
	}
}
<commit_msg>Change vrfID type from int to uint32.<commit_after>// Copyright 2017 Zebra Project
//
// 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 rib

import (
	"encoding/json"
	"fmt"
	//"os"
	"net"
	"os/exec"
)

func TunnelProbe(vrfId string, peerId string) bool {
	jsonStr, err := exec.Command("curl", "-s", "http://localhost:9999/peers").Output()
	if err != nil {
		fmt.Println("File read error", err)
		return false
	}

	var jsonIntf interface{}
	err = json.Unmarshal([]byte(jsonStr), &jsonIntf)
	if err != nil {
		fmt.Println("json Unmarshal error", err)
		return false
	}

	if jsonMap, ok := jsonIntf.(map[string]interface{}); ok {
		if vrf, ok := jsonMap[vrfId]; ok {
			if vrfMap, ok := vrf.(map[string]interface{}); ok {
				if peer, ok := vrfMap[peerId]; ok {
					if peerMap, ok := peer.(map[string]interface{}); ok {
						for key, value := range peerMap {
							fmt.Println("key:", key)
							if valueMap, ok := value.(map[string]interface{}); ok {
								if state, ok := valueMap["state"]; ok {
									fmt.Println("state", state)
									if state == "ESTABLISHED" {
										return true
									}
								}
							}
						}
					}
				}
			}
		}
	}
	return false
}

func PeerId(vrfId string, nexthop string) string {
	jsonStr, err := exec.Command("curl", "-s", "http://localhost:9999/config").Output()
	if err != nil {
		fmt.Println("File read error", err)
		return ""
	}

	var jsonIntf interface{}
	err = json.Unmarshal([]byte(jsonStr), &jsonIntf)
	if err != nil {
		fmt.Println("json Unmarshal error", err)
		return ""
	}

	if jsonMap, ok := jsonIntf.(map[string]interface{}); ok {
		if links, ok := jsonMap["links"]; ok {
			if vrfs, ok := links.(map[string]interface{}); ok {
				if vrf, ok := vrfs[vrfId]; ok {
					if peers, ok := vrf.(map[string]interface{}); ok {
						if peer, ok := peers["peers"]; ok {
							if entry, ok := peer.(map[string]interface{}); ok {
								for peerId, item := range entry {
									if peer, ok := item.(map[string]interface{}); ok {
										if nhop, ok := peer["nexthop-id"]; ok {
											if nhop == nexthop {
												return peerId
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
	return ""
}

func DtlsNexthop(vrfId uint32, nexthop net.IP) bool {
	vrf := fmt.Sprintf("vrf%d", vrfId)
	peerId := PeerId(vrf, nexthop.String())
	fmt.Println("Peer ID:", peerId)
	if peerId == "" {
		fmt.Println("Peer does not exists")
		return false
	}
	result := TunnelProbe(vrf, peerId)
	if result {
		fmt.Println("Tunnel is UP")
		return true
	} else {
		fmt.Println("Tunnel is DOWN")
		return false
	}
}
<|endoftext|>