<commit_before>package web

import (
	"fmt"
	"github.com/jhillyerd/inbucket/log"
	"html/template"
	"io"
	"net/http"
	"strconv"
	"time"
)

type JsonMessageHeader struct {
	Mailbox, Id, From, Subject string
	Date                    time.Time
}

func MailboxIndex(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
	name := req.FormValue("name")
	if len(name) == 0 {
		ctx.Session.AddFlash("Account name is required", "errors")
		http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther)
		return nil
	}

	return RenderTemplate("mailbox/index.html", w, map[string]interface{}{
		"ctx":  ctx,
		"name": name,
	})
}

func MailboxList(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
	// Don't have to validate these aren't empty, Gorilla returns 404
	name := ctx.Vars["name"]

	mb, err := ctx.DataStore.MailboxFor(name)
	if err != nil {
		return fmt.Errorf("Failed to get mailbox for %v: %v", name, err)
	}
	messages, err := mb.GetMessages()
	if err != nil {
		return fmt.Errorf("Failed to get messages for %v: %v", name, err)
	}
	log.LogTrace("Got %v messsages", len(messages))

	if ctx.IsJson {
		jmessages := make([]*JsonMessageHeader, len(messages))
		for i, msg := range messages {
			jmessages[i] = &JsonMessageHeader{
				Mailbox: name,
				Id:      msg.Id(),
				From:    msg.From(),
				Subject: msg.Subject(),
				Date:    msg.Date(),
			}
		}
		return RenderJson(w, jmessages)
	}

	return RenderPartial("mailbox/_list.html", w, map[string]interface{}{
		"ctx":      ctx,
		"name":     name,
		"messages": messages,
	})
}

func MailboxShow(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
	// Don't have to validate these aren't empty, Gorilla returns 404
	name := ctx.Vars["name"]
	id := ctx.Vars["id"]

	mb, err := ctx.DataStore.MailboxFor(name)
	if err != nil {
		return fmt.Errorf("MailboxFor('%v'): %v", name, err)
	}
	message, err := mb.GetMessage(id)
	if err != nil {
		return fmt.Errorf("GetMessage() failed: %v", err)
	}
	mime, err := message.ReadBody()
	if err != nil {
		return fmt.Errorf("ReadBody() failed: %v", err)
	}
	body := template.HTML(textToHtml(mime.Text))
	htmlAvailable := mime.Html != ""

	return RenderPartial("mailbox/_show.html", w, map[string]interface{}{
		"ctx":           ctx,
		"name":          name,
		"message":       message,
		"body":          body,
		"htmlAvailable": htmlAvailable,
		"attachments":   mime.Attachments,
	})
}

func MailboxPurge(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
	// Don't have to validate these aren't empty, Gorilla returns 404
	name := ctx.Vars["name"]

	mb, err := ctx.DataStore.MailboxFor(name)
	if err != nil {
		return fmt.Errorf("MailboxFor('%v'): %v", name, err)
	}
	if err := mb.Purge(); err != nil {
		return fmt.Errorf("Mailbox(%q) Purge: %v", name, err)
	}
	log.LogTrace("Purged mailbox for %q", name)

	if ctx.IsJson {
		return RenderJson(w, "OK")
	}

	w.Header().Set("Content-Type", "text/plain")
	io.WriteString(w, "OK")
	return nil
}

func MailboxHtml(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
	// Don't have to validate these aren't empty, Gorilla returns 404
	name := ctx.Vars["name"]
	id := ctx.Vars["id"]

	mb, err := ctx.DataStore.MailboxFor(name)
	if err != nil {
		return err
	}
	message, err := mb.GetMessage(id)
	if err != nil {
		return err
	}
	mime, err := message.ReadBody()
	if err != nil {
		return err
	}

	return RenderPartial("mailbox/_html.html", w, map[string]interface{}{
		"ctx":     ctx,
		"name":    name,
		"message": message,
		// TODO: It is not really safe to render, need to sanitize.
		"body": template.HTML(mime.Html),
	})
}

func MailboxSource(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
	// Don't have to validate these aren't empty, Gorilla returns 404
	name := ctx.Vars["name"]
	id := ctx.Vars["id"]

	mb, err := ctx.DataStore.MailboxFor(name)
	if err != nil {
		return err
	}
	message, err := mb.GetMessage(id)
	if err != nil {
		return err
	}
	raw, err := message.ReadRaw()
	if err != nil {
		return err
	}

	w.Header().Set("Content-Type", "text/plain")
	io.WriteString(w, *raw)
	return nil
}

func MailboxDownloadAttach(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
	// Don't have to validate these aren't empty, Gorilla returns 404
	name := ctx.Vars["name"]
	id := ctx.Vars["id"]
	numStr := ctx.Vars["num"]
	num, err := strconv.ParseUint(numStr, 10, 32)
	if err != nil {
		ctx.Session.AddFlash("Attachment number must be unsigned numeric", "errors")
		http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther)
		return nil
	}

	mb, err := ctx.DataStore.MailboxFor(name)
	if err != nil {
		return err
	}
	message, err := mb.GetMessage(id)
	if err != nil {
		return err
	}
	body, err := message.ReadBody()
	if err != nil {
		return err
	}
	if int(num) >= len(body.Attachments) {
		ctx.Session.AddFlash("Attachment number too high", "errors")
		http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther)
		return nil
	}
	part := body.Attachments[num]

	w.Header().Set("Content-Type", "application/octet-stream")
	w.Header().Set("Content-Disposition", "attachment")
	w.Write(part.Content())
	return nil
}

func MailboxViewAttach(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
	// Don't have to validate these aren't empty, Gorilla returns 404
	name := ctx.Vars["name"]
	id := ctx.Vars["id"]
	numStr := ctx.Vars["num"]
	num, err := strconv.ParseUint(numStr, 10, 32)
	if err != nil {
		ctx.Session.AddFlash("Attachment number must be unsigned numeric", "errors")
		http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther)
		return nil
	}

	mb, err := ctx.DataStore.MailboxFor(name)
	if err != nil {
		return err
	}
	message, err := mb.GetMessage(id)
	if err != nil {
		return err
	}
	body, err := message.ReadBody()
	if err != nil {
		return err
	}
	if int(num) >= len(body.Attachments) {
		ctx.Session.AddFlash("Attachment number too high", "errors")
		http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther)
		return nil
	}
	part := body.Attachments[num]

	w.Header().Set("Content-Type", part.ContentType())
	w.Write(part.Content())
	return nil
}

func MailboxDelete(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
	// Don't have to validate these aren't empty, Gorilla returns 404
	name := ctx.Vars["name"]
	id := ctx.Vars["id"]

	mb, err := ctx.DataStore.MailboxFor(name)
	if err != nil {
		return err
	}
	message, err := mb.GetMessage(id)
	if err != nil {
		return err
	}
	err = message.Delete()
	if err != nil {
		return err
	}

	if ctx.IsJson {
		return RenderJson(w, "OK")
	}

	w.Header().Set("Content-Type", "text/plain")
	io.WriteString(w, "OK")
	return nil
}
<commit_msg>Add size to mailbox listing JSON<commit_after>package web

import (
	"fmt"
	"github.com/jhillyerd/inbucket/log"
	"html/template"
	"io"
	"net/http"
	"strconv"
	"time"
)

type JsonMessageHeader struct {
	Mailbox, Id, From, Subject string
	Date                       time.Time
	Size                       int64
}

func MailboxIndex(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
	name := req.FormValue("name")
	if len(name) == 0 {
		ctx.Session.AddFlash("Account name is required", "errors")
		http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther)
		return nil
	}

	return RenderTemplate("mailbox/index.html", w, map[string]interface{}{
		"ctx":  ctx,
		"name": name,
	})
}

func MailboxList(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
	// Don't have to validate these aren't empty, Gorilla returns 404
	name := ctx.Vars["name"]

	mb, err := ctx.DataStore.MailboxFor(name)
	if err != nil {
		return fmt.Errorf("Failed to get mailbox for %v: %v", name, err)
	}
	messages, err := mb.GetMessages()
	if err != nil {
		return fmt.Errorf("Failed to get messages for %v: %v", name, err)
	}
	log.LogTrace("Got %v messsages", len(messages))

	if ctx.IsJson {
		jmessages := make([]*JsonMessageHeader, len(messages))
		for i, msg := range messages {
			jmessages[i] = &JsonMessageHeader{
				Mailbox: name,
				Id:      msg.Id(),
				From:    msg.From(),
				Subject: msg.Subject(),
				Date:    msg.Date(),
				Size:    msg.Size(),
			}
		}
		return RenderJson(w, jmessages)
	}

	return RenderPartial("mailbox/_list.html", w, map[string]interface{}{
		"ctx":      ctx,
		"name":     name,
		"messages": messages,
	})
}

func MailboxShow(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
	// Don't have to validate these aren't empty, Gorilla returns 404
	name := ctx.Vars["name"]
	id := ctx.Vars["id"]

	mb, err := ctx.DataStore.MailboxFor(name)
	if err != nil {
		return fmt.Errorf("MailboxFor('%v'): %v", name, err)
	}
	message, err := mb.GetMessage(id)
	if err != nil {
		return fmt.Errorf("GetMessage() failed: %v", err)
	}
	mime, err := message.ReadBody()
	if err != nil {
		return fmt.Errorf("ReadBody() failed: %v", err)
	}
	body := template.HTML(textToHtml(mime.Text))
	htmlAvailable := mime.Html != ""

	return RenderPartial("mailbox/_show.html", w, map[string]interface{}{
		"ctx":           ctx,
		"name":          name,
		"message":       message,
		"body":          body,
		"htmlAvailable": htmlAvailable,
		"attachments":   mime.Attachments,
	})
}

func MailboxPurge(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
	// Don't have to validate these aren't empty, Gorilla returns 404
	name := ctx.Vars["name"]

	mb, err := ctx.DataStore.MailboxFor(name)
	if err != nil {
		return fmt.Errorf("MailboxFor('%v'): %v", name, err)
	}
	if err := mb.Purge(); err != nil {
		return fmt.Errorf("Mailbox(%q) Purge: %v", name, err)
	}
	log.LogTrace("Purged mailbox for %q", name)

	if ctx.IsJson {
		return RenderJson(w, "OK")
	}

	w.Header().Set("Content-Type", "text/plain")
	io.WriteString(w, "OK")
	return nil
}

func MailboxHtml(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
	// Don't have to validate these aren't empty, Gorilla returns 404
	name := ctx.Vars["name"]
	id := ctx.Vars["id"]

	mb, err := ctx.DataStore.MailboxFor(name)
	if err != nil {
		return err
	}
	message, err := mb.GetMessage(id)
	if err != nil {
		return err
	}
	mime, err := message.ReadBody()
	if err != nil {
		return err
	}

	return RenderPartial("mailbox/_html.html", w, map[string]interface{}{
		"ctx":     ctx,
		"name":    name,
		"message": message,
		// TODO: It is not really safe to render, need to sanitize.
		"body": template.HTML(mime.Html),
	})
}

func MailboxSource(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
	// Don't have to validate these aren't empty, Gorilla returns 404
	name := ctx.Vars["name"]
	id := ctx.Vars["id"]

	mb, err := ctx.DataStore.MailboxFor(name)
	if err != nil {
		return err
	}
	message, err := mb.GetMessage(id)
	if err != nil {
		return err
	}
	raw, err := message.ReadRaw()
	if err != nil {
		return err
	}

	w.Header().Set("Content-Type", "text/plain")
	io.WriteString(w, *raw)
	return nil
}

func MailboxDownloadAttach(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
	// Don't have to validate these aren't empty, Gorilla returns 404
	name := ctx.Vars["name"]
	id := ctx.Vars["id"]
	numStr := ctx.Vars["num"]
	num, err := strconv.ParseUint(numStr, 10, 32)
	if err != nil {
		ctx.Session.AddFlash("Attachment number must be unsigned numeric", "errors")
		http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther)
		return nil
	}

	mb, err := ctx.DataStore.MailboxFor(name)
	if err != nil {
		return err
	}
	message, err := mb.GetMessage(id)
	if err != nil {
		return err
	}
	body, err := message.ReadBody()
	if err != nil {
		return err
	}
	if int(num) >= len(body.Attachments) {
		ctx.Session.AddFlash("Attachment number too high", "errors")
		http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther)
		return nil
	}
	part := body.Attachments[num]

	w.Header().Set("Content-Type", "application/octet-stream")
	w.Header().Set("Content-Disposition", "attachment")
	w.Write(part.Content())
	return nil
}

func MailboxViewAttach(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
	// Don't have to validate these aren't empty, Gorilla returns 404
	name := ctx.Vars["name"]
	id := ctx.Vars["id"]
	numStr := ctx.Vars["num"]
	num, err := strconv.ParseUint(numStr, 10, 32)
	if err != nil {
		ctx.Session.AddFlash("Attachment number must be unsigned numeric", "errors")
		http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther)
		return nil
	}

	mb, err := ctx.DataStore.MailboxFor(name)
	if err != nil {
		return err
	}
	message, err := mb.GetMessage(id)
	if err != nil {
		return err
	}
	body, err := message.ReadBody()
	if err != nil {
		return err
	}
	if int(num) >= len(body.Attachments) {
		ctx.Session.AddFlash("Attachment number too high", "errors")
		http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther)
		return nil
	}
	part := body.Attachments[num]

	w.Header().Set("Content-Type", part.ContentType())
	w.Write(part.Content())
	return nil
}

func MailboxDelete(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
	// Don't have to validate these aren't empty, Gorilla returns 404
	name := ctx.Vars["name"]
	id := ctx.Vars["id"]

	mb, err := ctx.DataStore.MailboxFor(name)
	if err != nil {
		return err
	}
	message, err := mb.GetMessage(id)
	if err != nil {
		return err
	}
	err = message.Delete()
	if err != nil {
		return err
	}

	if ctx.IsJson {
		return RenderJson(w, "OK")
	}

	w.Header().Set("Content-Type", "text/plain")
	io.WriteString(w, "OK")
	return nil
}
<|endoftext|>


<commit_before>package sorter

import (
	"errors"
	"fmt"
	"math/rand"
	"path/filepath"
	"reflect"
	"runtime"
	"testing"
	"time"
)

const (
	// this is used to define number size
	// all created number (x) will be between 0 < x < LENGTH
	LENGTH = 100
)

var (
	ErrNotSorted            = errors.New("Array is not sorted")
	ErrArrayNoLength        = errors.New("Array has no length")
	ErrArrayElementsNotSame = errors.New("Array elements are not same")
)

// GenerateArray generates an array that has length with the given number
// Also created numbers are between 0 and 100
func GenerateArray(n int) ([]int, error) {
	if n < 1 {
		return nil, ErrArrayNoLength
	}

	var arr []int
	rand.Seed(time.Now().UTC().UnixNano())
	for i := 0; i < n; i++ {
		arr = append(arr, rand.Intn(LENGTH))
	}

	return arr, nil
}

// IsSorted checks all consecutive array numbers
// if a[i] > a[i+1] then error occurs
func IsSorted(arr []int) (bool, error) {
	for i := 1; i < len(arr); i++ {
		if arr[i-1] > arr[i] {
			return false, ErrNotSorted
		}
	}
	return true, nil
}

//  IsArrayElementsSame checks the given arrays are the same or not
func IsArrayElementsSame(givenArray, expArray []int) (bool, error) {
	for _, giv := range givenArray {
		if !isInArray(giv, expArray) {
			return false, ErrArrayElementsNotSame
		}
	}
	return true, nil
}

func isInArray(element int, array []int) bool {
	for _, a := range array {
		if element == a {
			return true
		}
	}
	return false
}

// equals gets 3 parameters
// First, gets testing packages's itself
// Second parameter is the expectation of value
// Third parameter is the actual value
// Basicly, this is helper function for testing
func equals(tb testing.TB, exp, act interface{}) {
	if !reflect.DeepEqual(exp, act) {
		_, file, line, _ := runtime.Caller(1)
		fmt.Printf("\033[31m%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\033[39m\n\n", filepath.Base(file), line, exp, act)
		tb.Fail()
	}
}
<commit_msg>tests: isInArray func is commented<commit_after>package sorter

import (
	"errors"
	"fmt"
	"math/rand"
	"path/filepath"
	"reflect"
	"runtime"
	"testing"
	"time"
)

const (
	// this is used to define number size
	// all created number (x) will be between 0 < x < LENGTH
	LENGTH = 100
)

var (
	ErrNotSorted            = errors.New("Array is not sorted")
	ErrArrayNoLength        = errors.New("Array has no length")
	ErrArrayElementsNotSame = errors.New("Array elements are not same")
)

// GenerateArray generates an array that has length with the given number
// Also created numbers are between 0 and 100
func GenerateArray(n int) ([]int, error) {
	if n < 1 {
		return nil, ErrArrayNoLength
	}

	var arr []int
	rand.Seed(time.Now().UTC().UnixNano())
	for i := 0; i < n; i++ {
		arr = append(arr, rand.Intn(LENGTH))
	}

	return arr, nil
}

// IsSorted checks all consecutive array numbers
// if a[i] > a[i+1] then error occurs
func IsSorted(arr []int) (bool, error) {
	for i := 1; i < len(arr); i++ {
		if arr[i-1] > arr[i] {
			return false, ErrNotSorted
		}
	}
	return true, nil
}

//  IsArrayElementsSame checks the given arrays are the same or not
func IsArrayElementsSame(givenArray, expArray []int) (bool, error) {
	for _, giv := range givenArray {
		if !isInArray(giv, expArray) {
			return false, ErrArrayElementsNotSame
		}
	}
	return true, nil
}

// isInArray checks the given element is in the given array or not
func isInArray(element int, array []int) bool {
	for _, a := range array {
		if element == a {
			return true
		}
	}
	return false
}

// equals gets 3 parameters
// First, gets testing packages's itself
// Second parameter is the expectation of value
// Third parameter is the actual value
// Basicly, this is helper function for testing
func equals(tb testing.TB, exp, act interface{}) {
	if !reflect.DeepEqual(exp, act) {
		_, file, line, _ := runtime.Caller(1)
		fmt.Printf("\033[31m%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\033[39m\n\n", filepath.Base(file), line, exp, act)
		tb.Fail()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"

	"code.google.com/p/gopass"
	"golang.org/x/crypto/ssh/terminal"
)

type Paste struct {
	Id        int    `json:"id"`
	Code      string `json:"code"`
	Encrypted int    `json:"encrypted"`
	Language  string `json:"language"`
	DeleteAt  string `json:"delete_at"`
	CreatedAt string `json:"created_at"`
	Slug      string `json:"slug`
}

// Get paste from api by passing slug
// Will prompt password if paste is encrypted
func GetPaste(slug string) string {
	url := "https://vvt.nu/" + slug + ".json"
	result, err := http.Get(url)

	if err != nil {
		panic(err)
	}

	defer result.Body.Close()

	body, err := ioutil.ReadAll(result.Body)
	if err != nil {
		panic(err)
	}
	paste := decodeJSON(body)

	// Move this logix out of GetPaste..
	if paste.Encrypted == 1 {
		password, err := gopass.GetPass("This paste is encrypted, enter password: ")
		if err != nil {
			panic(err)
		}

		content, err := Decrypt(paste.Code, password)
		if err != nil {
			panic(err)
		}
		return content
	}

	return paste.Code
}

// Post paste trought api requires string and retunrs Paste object
func PostPaste(content string) Paste {
	data := Paste{Code: content, Encrypted: 0}
	body, err := json.Marshal(data)

	if err != nil {
		panic(err)
	}

	resp, err := http.Post("https://vvt.nu/save.json", "application/json", bytes.NewReader(body))
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	body, err = ioutil.ReadAll(resp.Body)

	if err != nil {
		panic(err)
	}

	paste := decodeJSON(body)
	return paste
}

// decode vvt's json response to struct
func decodeJSON(s []byte) Paste {
	var paste Paste

	if err := json.Unmarshal(s, &paste); err != nil {
		panic(err)
		fmt.Printf("Hmm.. problem")
		os.Exit(1)
	}
	return paste
}

func main() {
	outputFile := flag.String("o", "", "Output file (defaul is stdout)")
	flag.Parse()

	args := flag.Args()
	switch len(args) {
	case 0:
		if !terminal.IsTerminal(0) {
			bytes, err := ioutil.ReadAll(os.Stdin)
			if err != nil {
				panic(err)
			}
			paste := PostPaste(string(bytes))
			fmt.Printf("https://vvt.nu/" + paste.Slug + "\n")
		} else {
			fmt.Println("No piped data")
			flag.Usage()
			return
		}
	case 1:
		content := GetPaste(args[0])
		if *outputFile != "" {
			file, err := os.Create(*outputFile)
			if err != nil {
				panic(err)
			}
			file.WriteString(content)
			fmt.Println(*outputFile + " created")
		} else {
			fmt.Print(content)
		}
	default:
		flag.Usage()
		os.Exit(-1)
	}
}
<commit_msg>new backend..<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"

	"code.google.com/p/gopass"
	"golang.org/x/crypto/ssh/terminal"
)

type Paste struct {
	Code      string `json:"code"`
	Encrypted bool   `json:"encrypted"`
	Language  string `json:"language"`
	Slug      string `json:"slug"`
}

// Get paste from api by passing slug
// Will prompt password if paste is encrypted
func GetPaste(slug string) string {
	url := "https://vvt.nu/" + slug + ".json"
	result, err := http.Get(url)

	if err != nil {
		panic(err)
	}

	defer result.Body.Close()

	body, err := ioutil.ReadAll(result.Body)
	if err != nil {
		panic(err)
	}
	paste := decodeJSON(body)

	// Move this logix out of GetPaste..
	if paste.Encrypted == true {
		password, err := gopass.GetPass("This paste is encrypted, enter password: ")
		if err != nil {
			panic(err)
		}

		content, err := Decrypt(paste.Code, password)
		if err != nil {
			panic(err)
		}
		return content
	}

	return paste.Code
}

// Post paste trought api requires string and retunrs Paste object
func PostPaste(content string) Paste {
	data := Paste{Code: content, Encrypted: true}
	body, err := json.Marshal(data)

	if err != nil {
		panic(err)
	}

	resp, err := http.Post("https://vvt.nu/save.json", "application/json", bytes.NewReader(body))
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	body, err = ioutil.ReadAll(resp.Body)

	if err != nil {
		panic(err)
	}

	paste := decodeJSON(body)
	return paste
}

// decode vvt's json response to struct
func decodeJSON(s []byte) Paste {
	var paste Paste

	if err := json.Unmarshal(s, &paste); err != nil {
		panic(err)
		fmt.Printf("Hmm.. problem")
		os.Exit(1)
	}
	return paste
}

func main() {
	outputFile := flag.String("o", "", "Output file (defaul is stdout)")
	flag.Parse()

	args := flag.Args()
	switch len(args) {
	case 0:
		if !terminal.IsTerminal(0) {
			bytes, err := ioutil.ReadAll(os.Stdin)
			if err != nil {
				panic(err)
			}
			paste := PostPaste(string(bytes))
			fmt.Printf("https://vvt.nu/" + paste.Slug + "\n")
		} else {
			fmt.Println("No piped data")
			flag.Usage()
			return
		}
	case 1:
		content := GetPaste(args[0])
		if *outputFile != "" {
			file, err := os.Create(*outputFile)
			if err != nil {
				panic(err)
			}
			file.WriteString(content)
			fmt.Println(*outputFile + " created")
		} else {
			fmt.Print(content)
		}
	default:
		flag.Usage()
		os.Exit(-1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"path/filepath"
	"reflect"
	"time"

	"github.com/go-yaml/yaml"
	"github.com/gorilla/mux"
)

type Supervisor struct {
	ConfigDir string
	pgs       []*Program
	pgMap     map[string]*Program
	procMap   map[string]*Process
}

func (s *Supervisor) programPath() string {
	return filepath.Join(s.ConfigDir, "programs.yml")
}

func (s *Supervisor) addOrUpdateProgram(pg Program) {
	origPg, ok := s.pgMap[pg.Name]
	if ok {
		if !reflect.DeepEqual(origPg, &pg) {
			log.Println("Update:", pg.Name)
			origProc := s.procMap[pg.Name]
			isRunning := origProc.IsRunning()
			go func() {
				origProc.Operate(StopEvent)

				// TODO: wait state change
				time.Sleep(2 * time.Second)

				newProc := NewProcess(pg)
				s.procMap[pg.Name] = newProc
				if isRunning {
					newProc.Operate(StartEvent)
				}
			}()
		}
	} else {
		s.pgs = append(s.pgs, &pg)
		s.pgMap[pg.Name] = &pg
		s.procMap[pg.Name] = NewProcess(pg)
		log.Println("Add:", pg.Name)
	}
}

func (s *Supervisor) loadDB() error {
	data, err := ioutil.ReadFile(s.programPath())
	if err != nil {
		data = []byte("")
	}
	pgs := make([]Program, 0)
	if err = yaml.Unmarshal(data, pgs); err != nil {
		return err
	}
	for _, pg := range pgs {
		s.addOrUpdateProgram(pg)
	}
	return nil
}

func (s *Supervisor) saveDB() error {
	data, err := yaml.Marshal(s.pgs)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(s.programPath(), data, 0644)
}

func (s *Supervisor) Index(w http.ResponseWriter, r *http.Request) {
	t := template.Must(template.New("t").ParseFiles("./res/index.html"))
	t.ExecuteTemplate(w, "index.html", nil)
}

func (s *Supervisor) AddProgram(w http.ResponseWriter, r *http.Request) {
	pg := Program{
		Name:    r.FormValue("name"),
		Command: r.FormValue("command"),
		// TODO: missing other values
	}
	if err := pg.Check(); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	var data []byte
	if _, ok := s.pgMap[pg.Name]; ok {
		data, _ = json.Marshal(map[string]interface{}{
			"status": 1,
		})
	} else {
		s.addOrUpdateProgram(pg)

		data, _ = json.Marshal(map[string]interface{}{
			"status": 0,
		})
	}
	w.Write(data)
}

func init() {
	suv := &Supervisor{}
	r := mux.NewRouter()
	r.HandleFunc("/", suv.Index)
	r.HandleFunc("/api/programs", suv.AddProgram).Methods("POST")

	fs := http.FileServer(http.Dir("res"))
	http.Handle("/", r)
	http.Handle("/res/", http.StripPrefix("/res/", fs))
}
<commit_msg>fix reload<commit_after>package main

import (
	"encoding/json"
	"html/template"
	"io/ioutil"
	"net/http"
	"path/filepath"
	"reflect"
	"time"

	"github.com/go-yaml/yaml"
	"github.com/gorilla/mux"
	"github.com/qiniu/log"
)

type Supervisor struct {
	ConfigDir string
	pgs       []*Program
	pgMap     map[string]*Program
	procMap   map[string]*Process
}

func (s *Supervisor) programPath() string {
	return filepath.Join(s.ConfigDir, "programs.yml")
}

func (s *Supervisor) addOrUpdateProgram(pg Program) {
	origPg, ok := s.pgMap[pg.Name]
	if ok {
		if !reflect.DeepEqual(origPg, &pg) {
			log.Println("Update:", pg.Name)
			origProc := s.procMap[pg.Name]
			isRunning := origProc.IsRunning()
			go func() {
				origProc.Operate(StopEvent)

				// TODO: wait state change
				time.Sleep(2 * time.Second)

				newProc := NewProcess(pg)
				s.procMap[pg.Name] = newProc
				if isRunning {
					newProc.Operate(StartEvent)
				}
			}()
		}
	} else {
		s.pgs = append(s.pgs, &pg)
		s.pgMap[pg.Name] = &pg
		s.procMap[pg.Name] = NewProcess(pg)
		log.Println("Add:", pg.Name)
	}
}

func (s *Supervisor) loadDB() error {
	data, err := ioutil.ReadFile(s.programPath())
	if err != nil {
		data = []byte("")
	}
	pgs := make([]Program, 0)
	if err = yaml.Unmarshal(data, pgs); err != nil {
		return err
	}
	// add or update program
	visited := map[string]bool{}
	for _, pg := range pgs {
		if visited[pg.Name] {
			log.Warnf("Duplicated program name: %s", pg.Name)
			continue
		}
		visited[pg.Name] = true
		s.addOrUpdateProgram(pg)
	}
	// delete not exists program
	for _, pg := range s.pgs {
		if visited[pg.Name] {
			continue
		}
		name := pg.Name
		s.procMap[name].Operate(StopEvent)
		delete(s.procMap, name)
		delete(s.pgMap, name)
	}
	// update programs (because of delete)
	s.pgs = make([]*Program, 0, len(s.pgMap))
	for _, pg := range s.pgMap {
		s.pgs = append(s.pgs, pg)
	}
	return nil
}

func (s *Supervisor) saveDB() error {
	data, err := yaml.Marshal(s.pgs)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(s.programPath(), data, 0644)
}

func (s *Supervisor) Index(w http.ResponseWriter, r *http.Request) {
	t := template.Must(template.New("t").ParseFiles("./res/index.html"))
	t.ExecuteTemplate(w, "index.html", nil)
}

func (s *Supervisor) AddProgram(w http.ResponseWriter, r *http.Request) {
	pg := Program{
		Name:    r.FormValue("name"),
		Command: r.FormValue("command"),
		// TODO: missing other values
	}
	if err := pg.Check(); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	var data []byte
	if _, ok := s.pgMap[pg.Name]; ok {
		data, _ = json.Marshal(map[string]interface{}{
			"status": 1,
		})
	} else {
		s.addOrUpdateProgram(pg)

		data, _ = json.Marshal(map[string]interface{}{
			"status": 0,
		})
	}
	w.Write(data)
}

func init() {
	suv := &Supervisor{}
	r := mux.NewRouter()
	r.HandleFunc("/", suv.Index)
	r.HandleFunc("/api/programs", suv.AddProgram).Methods("POST")

	fs := http.FileServer(http.Dir("res"))
	http.Handle("/", r)
	http.Handle("/res/", http.StripPrefix("/res/", fs))
}
<|endoftext|>


<commit_before>package main

import "fmt"

func main() {
	// set up terminal interface
	fmt.Print("\x1b[9;0]") // blank 0
	fmt.Print("\x1b[14;0]") // powerdown 0
	fmt.Print("\x1b[41m") // red background (for troubleshooting)
	fmt.Print("\x1b[?25l\x1b[?1c") // disable cursor
	fmt.Print("\x1b[8]") // store defaults
	fmt.Print("\x1b[H\x1b[J") // clear screen

	b, err := decodeImageBGR8("/home/charrington/1.jpg")
	if err != nil {
		panic(err)
	}
	err = renderToFramebuffer(b)
	if err != nil {
		panic(err)
	}

	var c chan string
	c <- "wait forever"
}

<commit_msg>apparently 'block forever' means exit immediately<commit_after>package main

import "fmt"
import "time"

func main() {
	// set up terminal interface
	fmt.Print("\x1b[9;0]")         // blank 0
	fmt.Print("\x1b[14;0]")        // powerdown 0
	fmt.Print("\x1b[41m")          // red background (for troubleshooting)
	fmt.Print("\x1b[?25l\x1b[?1c") // disable cursor
	fmt.Print("\x1b[8]")           // store defaults
	fmt.Print("\x1b[H\x1b[J")      // clear screen

	b, err := decodeImageBGR8("/home/charrington/1.jpg")
	if err != nil {
		panic(err)
	}
	err = renderToFramebuffer(b)
	if err != nil {
		panic(err)
	}

	for {
		<-time.After(1 * time.Hour)
	}
}
<|endoftext|>


<commit_before>// +build windows

/*
Package wmi provides a WQL interface for WMI on Windows.

Example code to print names of running processes:

	type Win32_Process struct {
		Name string
	}

	func main() {
		var dst []Win32_Process
		q := wmi.CreateQuery(&dst, "")
		err := wmi.Query(q, &dst)
		if err != nil {
			log.Fatal(err)
		}
		for i, v := range dst {
			println(i, v.Name)
		}
	}

*/
package wmi

import (
	"bytes"
	"errors"
	"fmt"
	"log"
	"os"
	"reflect"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/go-ole/go-ole"
	"github.com/go-ole/go-ole/oleutil"
)

var l = log.New(os.Stdout, "", log.LstdFlags)

var (
	ErrInvalidEntityType = errors.New("wmi: invalid entity type")
	// ErrNilCreateObject is the error returned if CreateObject returns nil even
	// if the error was nil.
	ErrNilCreateObject = errors.New("wmi: create object returned nil")
	lock               sync.Mutex
)

// S_FALSE is returned by CoInitializeEx if it was already called on this thread.
const S_FALSE = 0x00000001

// QueryNamespace invokes Query with the given namespace on the local machine.
func QueryNamespace(query string, dst interface{}, namespace string) error {
	return Query(query, dst, nil, namespace)
}

// Query runs the WQL query and appends the values to dst.
//
// dst must have type *[]S or *[]*S, for some struct type S. Fields selected in
// the query must have the same name in dst. Supported types are all signed and
// unsigned integers, time.Time, string, bool, or a pointer to one of those.
// Array types are not supported.
//
// By default, the local machine and default namespace are used. These can be
// changed using connectServerArgs. See
// http://msdn.microsoft.com/en-us/library/aa393720.aspx for details.
//
// Query is a wrapper around DefaultClient.Query.
func Query(query string, dst interface{}, connectServerArgs ...interface{}) error {
	return DefaultClient.Query(query, dst, connectServerArgs...)
}

// A Client is an WMI query client.
//
// Its zero value (DefaultClient) is a usable client.
type Client struct {
	// NonePtrZero specifies if nil values for fields which aren't pointers
	// should be returned as the field types zero value.
	//
	// Setting this to true allows stucts without pointer fields to be used
	// without the risk failure should a nil value returned from WMI.
	NonePtrZero bool

	// PtrNil specifies if nil values for pointer fields should be returned
	// as nil.
	//
	// Setting this to true will set pointer fields to nil where WMI
	// returned nil, otherwise the types zero value will be returned.
	PtrNil bool

	// AllowMissingFields specifies that struct fields not present in the
	// query result should not result in an error.
	//
	// Setting this to true allows custom queries to be used with full
	// struct definitions instead of having to define multiple structs.
	AllowMissingFields bool
}

// DefaultClient is the default Client and is used by Query, QueryNamespace
var DefaultClient = &Client{}

// Query runs the WQL query and appends the values to dst.
//
// dst must have type *[]S or *[]*S, for some struct type S. Fields selected in
// the query must have the same name in dst. Supported types are all signed and
// unsigned integers, time.Time, string, bool, or a pointer to one of those.
// Array types are not supported.
//
// By default, the local machine and default namespace are used. These can be
// changed using connectServerArgs. See
// http://msdn.microsoft.com/en-us/library/aa393720.aspx for details.
func (c *Client) Query(query string, dst interface{}, connectServerArgs ...interface{}) error {
	dv := reflect.ValueOf(dst)
	if dv.Kind() != reflect.Ptr || dv.IsNil() {
		return ErrInvalidEntityType
	}
	dv = dv.Elem()
	mat, elemType := checkMultiArg(dv)
	if mat == multiArgTypeInvalid {
		return ErrInvalidEntityType
	}

	lock.Lock()
	defer lock.Unlock()
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED)
	if err != nil {
		oleCode := err.(*ole.OleError).Code()
		if oleCode != ole.S_OK && oleCode != S_FALSE {
			return err
		}
	}
	defer ole.CoUninitialize()

	unknown, err := oleutil.CreateObject("WbemScripting.SWbemLocator")
	if err != nil {
		return err
	} else if unknown == nil {
		return ErrNilCreateObject
	}
	defer unknown.Release()

	wmi, err := unknown.QueryInterface(ole.IID_IDispatch)
	if err != nil {
		return err
	}
	defer wmi.Release()

	// service is a SWbemServices
	serviceRaw, err := oleutil.CallMethod(wmi, "ConnectServer", connectServerArgs...)
	if err != nil {
		return err
	}
	service := serviceRaw.ToIDispatch()
	defer serviceRaw.Clear()

	// result is a SWBemObjectSet
	resultRaw, err := oleutil.CallMethod(service, "ExecQuery", query)
	if err != nil {
		return err
	}
	result := resultRaw.ToIDispatch()
	defer resultRaw.Clear()

	count, err := oleInt64(result, "Count")
	if err != nil {
		return err
	}

	enumProperty, err := result.GetProperty("_NewEnum")
	if err != nil {
		return err
	}
	defer enumProperty.Clear()

	enum, err := enumProperty.ToIUnknown().IEnumVARIANT(ole.IID_IEnumVariant)
	if err != nil {
		return err
	}
	if enum == nil {
		return fmt.Errorf("can't get IEnumVARIANT, enum is nil")
	}
	defer enum.Release()

	// Initialize a slice with Count capacity
	dv.Set(reflect.MakeSlice(dv.Type(), 0, int(count)))

	var errFieldMismatch error
	for itemRaw, length, err := enum.Next(1); length > 0; itemRaw, length, err = enum.Next(1) {
		if err != nil {
			return err
		}

		err := func() error {
			// item is a SWbemObject, but really a Win32_Process
			item := itemRaw.ToIDispatch()
			defer item.Release()

			ev := reflect.New(elemType)
			if err = c.loadEntity(ev.Interface(), item); err != nil {
				if _, ok := err.(*ErrFieldMismatch); ok {
					// We continue loading entities even in the face of field mismatch errors.
					// If we encounter any other error, that other error is returned. Otherwise,
					// an ErrFieldMismatch is returned.
					errFieldMismatch = err
				} else {
					return err
				}
			}
			if mat != multiArgTypeStructPtr {
				ev = ev.Elem()
			}
			dv.Set(reflect.Append(dv, ev))
			return nil
		}()
		if err != nil {
			return err
		}
	}
	return errFieldMismatch
}

// ErrFieldMismatch is returned when a field is to be loaded into a different
// type than the one it was stored from, or when a field is missing or
// unexported in the destination struct.
// StructType is the type of the struct pointed to by the destination argument.
type ErrFieldMismatch struct {
	StructType reflect.Type
	FieldName  string
	Reason     string
}

func (e *ErrFieldMismatch) Error() string {
	return fmt.Sprintf("wmi: cannot load field %q into a %q: %s",
		e.FieldName, e.StructType, e.Reason)
}

var timeType = reflect.TypeOf(time.Time{})

// loadEntity loads a SWbemObject into a struct pointer.
func (c *Client) loadEntity(dst interface{}, src *ole.IDispatch) (errFieldMismatch error) {
	v := reflect.ValueOf(dst).Elem()
	for i := 0; i < v.NumField(); i++ {
		f := v.Field(i)
		of := f
		isPtr := f.Kind() == reflect.Ptr
		if isPtr {
			ptr := reflect.New(f.Type().Elem())
			f.Set(ptr)
			f = f.Elem()
		}
		n := v.Type().Field(i).Name
		if !f.CanSet() {
			return &ErrFieldMismatch{
				StructType: of.Type(),
				FieldName:  n,
				Reason:     "CanSet() is false",
			}
		}
		prop, err := oleutil.GetProperty(src, n)
		if err != nil {
			if !c.AllowMissingFields {
				errFieldMismatch = &ErrFieldMismatch{
					StructType: of.Type(),
					FieldName:  n,
					Reason:     "no such struct field",
				}
			}
			continue
		}
		defer prop.Clear()

		switch val := prop.Value().(type) {
		case int8, int16, int32, int64, int:
			v := reflect.ValueOf(val).Int()
			switch f.Kind() {
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				f.SetInt(v)
			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
				f.SetUint(uint64(v))
			default:
				return &ErrFieldMismatch{
					StructType: of.Type(),
					FieldName:  n,
					Reason:     "not an integer class",
				}
			}
		case uint8, uint16, uint32, uint64:
			v := reflect.ValueOf(val).Uint()
			switch f.Kind() {
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				f.SetInt(int64(v))
			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
				f.SetUint(v)
			default:
				return &ErrFieldMismatch{
					StructType: of.Type(),
					FieldName:  n,
					Reason:     "not an integer class",
				}
			}
		case string:
			switch f.Kind() {
			case reflect.String:
				f.SetString(val)
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				iv, err := strconv.ParseInt(val, 10, 64)
				if err != nil {
					return err
				}
				f.SetInt(iv)
			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
				uv, err := strconv.ParseUint(val, 10, 64)
				if err != nil {
					return err
				}
				f.SetUint(uv)
			case reflect.Struct:
				switch f.Type() {
				case timeType:
					if len(val) == 25 {
						mins, err := strconv.Atoi(val[22:])
						if err != nil {
							return err
						}
						val = val[:22] + fmt.Sprintf("%02d%02d", mins/60, mins%60)
					}
					t, err := time.Parse("20060102150405.000000-0700", val)
					if err != nil {
						return err
					}
					f.Set(reflect.ValueOf(t))
				}
			}
		case bool:
			switch f.Kind() {
			case reflect.Bool:
				f.SetBool(val)
			default:
				return &ErrFieldMismatch{
					StructType: of.Type(),
					FieldName:  n,
					Reason:     "not a bool",
				}
			}
		default:
			typeof := reflect.TypeOf(val)
			if typeof == nil && (isPtr || c.NonePtrZero) {
				if (isPtr && c.PtrNil) || (!isPtr && c.NonePtrZero) {
					of.Set(reflect.Zero(of.Type()))
				}
				break
			}
			return &ErrFieldMismatch{
				StructType: of.Type(),
				FieldName:  n,
				Reason:     fmt.Sprintf("unsupported type (%T)", val),
			}
		}
	}
	return errFieldMismatch
}

type multiArgType int

const (
	multiArgTypeInvalid multiArgType = iota
	multiArgTypeStruct
	multiArgTypeStructPtr
)

// checkMultiArg checks that v has type []S, []*S for some struct type S.
//
// It returns what category the slice's elements are, and the reflect.Type
// that represents S.
func checkMultiArg(v reflect.Value) (m multiArgType, elemType reflect.Type) {
	if v.Kind() != reflect.Slice {
		return multiArgTypeInvalid, nil
	}
	elemType = v.Type().Elem()
	switch elemType.Kind() {
	case reflect.Struct:
		return multiArgTypeStruct, elemType
	case reflect.Ptr:
		elemType = elemType.Elem()
		if elemType.Kind() == reflect.Struct {
			return multiArgTypeStructPtr, elemType
		}
	}
	return multiArgTypeInvalid, nil
}

func oleInt64(item *ole.IDispatch, prop string) (int64, error) {
	v, err := oleutil.GetProperty(item, prop)
	if err != nil {
		return 0, err
	}
	defer v.Clear()

	i := int64(v.Val)
	return i, nil
}

// CreateQuery returns a WQL query string that queries all columns of src. where
// is an optional string that is appended to the query, to be used with WHERE
// clauses. In such a case, the "WHERE" string should appear at the beginning.
func CreateQuery(src interface{}, where string) string {
	var b bytes.Buffer
	b.WriteString("SELECT ")
	s := reflect.Indirect(reflect.ValueOf(src))
	t := s.Type()
	if s.Kind() == reflect.Slice {
		t = t.Elem()
	}
	if t.Kind() != reflect.Struct {
		return ""
	}
	var fields []string
	for i := 0; i < t.NumField(); i++ {
		fields = append(fields, t.Field(i).Name)
	}
	b.WriteString(strings.Join(fields, ", "))
	b.WriteString(" FROM ")
	b.WriteString(t.Name())
	b.WriteString(" " + where)
	return b.String()
}
<commit_msg>Revert "Partial Revert "Correct threading model used by wmi calls""<commit_after>// +build windows

/*
Package wmi provides a WQL interface for WMI on Windows.

Example code to print names of running processes:

	type Win32_Process struct {
		Name string
	}

	func main() {
		var dst []Win32_Process
		q := wmi.CreateQuery(&dst, "")
		err := wmi.Query(q, &dst)
		if err != nil {
			log.Fatal(err)
		}
		for i, v := range dst {
			println(i, v.Name)
		}
	}

*/
package wmi

import (
	"bytes"
	"errors"
	"fmt"
	"log"
	"os"
	"reflect"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/go-ole/go-ole"
	"github.com/go-ole/go-ole/oleutil"
)

var l = log.New(os.Stdout, "", log.LstdFlags)

var (
	ErrInvalidEntityType = errors.New("wmi: invalid entity type")
	// ErrNilCreateObject is the error returned if CreateObject returns nil even
	// if the error was nil.
	ErrNilCreateObject = errors.New("wmi: create object returned nil")
)

// S_FALSE is returned by CoInitializeEx if it was already called on this thread.
const S_FALSE = 0x00000001

// QueryNamespace invokes Query with the given namespace on the local machine.
func QueryNamespace(query string, dst interface{}, namespace string) error {
	return Query(query, dst, nil, namespace)
}

// Query runs the WQL query and appends the values to dst.
//
// dst must have type *[]S or *[]*S, for some struct type S. Fields selected in
// the query must have the same name in dst. Supported types are all signed and
// unsigned integers, time.Time, string, bool, or a pointer to one of those.
// Array types are not supported.
//
// By default, the local machine and default namespace are used. These can be
// changed using connectServerArgs. See
// http://msdn.microsoft.com/en-us/library/aa393720.aspx for details.
//
// Query is a wrapper around DefaultClient.Query.
func Query(query string, dst interface{}, connectServerArgs ...interface{}) error {
	return DefaultClient.Query(query, dst, connectServerArgs...)
}

// A Client is an WMI query client.
//
// Its zero value (DefaultClient) is a usable client.
type Client struct {
	// NonePtrZero specifies if nil values for fields which aren't pointers
	// should be returned as the field types zero value.
	//
	// Setting this to true allows stucts without pointer fields to be used
	// without the risk failure should a nil value returned from WMI.
	NonePtrZero bool

	// PtrNil specifies if nil values for pointer fields should be returned
	// as nil.
	//
	// Setting this to true will set pointer fields to nil where WMI
	// returned nil, otherwise the types zero value will be returned.
	PtrNil bool

	// AllowMissingFields specifies that struct fields not present in the
	// query result should not result in an error.
	//
	// Setting this to true allows custom queries to be used with full
	// struct definitions instead of having to define multiple structs.
	AllowMissingFields bool
}

// DefaultClient is the default Client and is used by Query, QueryNamespace
var DefaultClient = &Client{}

// Query runs the WQL query and appends the values to dst.
//
// dst must have type *[]S or *[]*S, for some struct type S. Fields selected in
// the query must have the same name in dst. Supported types are all signed and
// unsigned integers, time.Time, string, bool, or a pointer to one of those.
// Array types are not supported.
//
// By default, the local machine and default namespace are used. These can be
// changed using connectServerArgs. See
// http://msdn.microsoft.com/en-us/library/aa393720.aspx for details.
func (c *Client) Query(query string, dst interface{}, connectServerArgs ...interface{}) error {
	dv := reflect.ValueOf(dst)
	if dv.Kind() != reflect.Ptr || dv.IsNil() {
		return ErrInvalidEntityType
	}
	dv = dv.Elem()
	mat, elemType := checkMultiArg(dv)
	if mat == multiArgTypeInvalid {
		return ErrInvalidEntityType
	}

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	err := ole.CoInitializeEx(0, ole.COINIT_APARTMENTTHREADED)
	if err != nil {
		oleCode := err.(*ole.OleError).Code()
		if oleCode != ole.S_OK && oleCode != S_FALSE {
			return err
		}
	}
	defer ole.CoUninitialize()

	unknown, err := oleutil.CreateObject("WbemScripting.SWbemLocator")
	if err != nil {
		return err
	} else if unknown == nil {
		return ErrNilCreateObject
	}
	defer unknown.Release()

	wmi, err := unknown.QueryInterface(ole.IID_IDispatch)
	if err != nil {
		return err
	}
	defer wmi.Release()

	// service is a SWbemServices
	serviceRaw, err := oleutil.CallMethod(wmi, "ConnectServer", connectServerArgs...)
	if err != nil {
		return err
	}
	service := serviceRaw.ToIDispatch()
	defer serviceRaw.Clear()

	// result is a SWBemObjectSet
	resultRaw, err := oleutil.CallMethod(service, "ExecQuery", query)
	if err != nil {
		return err
	}
	result := resultRaw.ToIDispatch()
	defer resultRaw.Clear()

	count, err := oleInt64(result, "Count")
	if err != nil {
		return err
	}

	enumProperty, err := result.GetProperty("_NewEnum")
	if err != nil {
		return err
	}
	defer enumProperty.Clear()

	enum, err := enumProperty.ToIUnknown().IEnumVARIANT(ole.IID_IEnumVariant)
	if err != nil {
		return err
	}
	if enum == nil {
		return fmt.Errorf("can't get IEnumVARIANT, enum is nil")
	}
	defer enum.Release()

	// Initialize a slice with Count capacity
	dv.Set(reflect.MakeSlice(dv.Type(), 0, int(count)))

	var errFieldMismatch error
	for itemRaw, length, err := enum.Next(1); length > 0; itemRaw, length, err = enum.Next(1) {
		if err != nil {
			return err
		}

		err := func() error {
			// item is a SWbemObject, but really a Win32_Process
			item := itemRaw.ToIDispatch()
			defer item.Release()

			ev := reflect.New(elemType)
			if err = c.loadEntity(ev.Interface(), item); err != nil {
				if _, ok := err.(*ErrFieldMismatch); ok {
					// We continue loading entities even in the face of field mismatch errors.
					// If we encounter any other error, that other error is returned. Otherwise,
					// an ErrFieldMismatch is returned.
					errFieldMismatch = err
				} else {
					return err
				}
			}
			if mat != multiArgTypeStructPtr {
				ev = ev.Elem()
			}
			dv.Set(reflect.Append(dv, ev))
			return nil
		}()
		if err != nil {
			return err
		}
	}
	return errFieldMismatch
}

// ErrFieldMismatch is returned when a field is to be loaded into a different
// type than the one it was stored from, or when a field is missing or
// unexported in the destination struct.
// StructType is the type of the struct pointed to by the destination argument.
type ErrFieldMismatch struct {
	StructType reflect.Type
	FieldName  string
	Reason     string
}

func (e *ErrFieldMismatch) Error() string {
	return fmt.Sprintf("wmi: cannot load field %q into a %q: %s",
		e.FieldName, e.StructType, e.Reason)
}

var timeType = reflect.TypeOf(time.Time{})

// loadEntity loads a SWbemObject into a struct pointer.
func (c *Client) loadEntity(dst interface{}, src *ole.IDispatch) (errFieldMismatch error) {
	v := reflect.ValueOf(dst).Elem()
	for i := 0; i < v.NumField(); i++ {
		f := v.Field(i)
		of := f
		isPtr := f.Kind() == reflect.Ptr
		if isPtr {
			ptr := reflect.New(f.Type().Elem())
			f.Set(ptr)
			f = f.Elem()
		}
		n := v.Type().Field(i).Name
		if !f.CanSet() {
			return &ErrFieldMismatch{
				StructType: of.Type(),
				FieldName:  n,
				Reason:     "CanSet() is false",
			}
		}
		prop, err := oleutil.GetProperty(src, n)
		if err != nil {
			if !c.AllowMissingFields {
				errFieldMismatch = &ErrFieldMismatch{
					StructType: of.Type(),
					FieldName:  n,
					Reason:     "no such struct field",
				}
			}
			continue
		}
		defer prop.Clear()

		switch val := prop.Value().(type) {
		case int8, int16, int32, int64, int:
			v := reflect.ValueOf(val).Int()
			switch f.Kind() {
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				f.SetInt(v)
			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
				f.SetUint(uint64(v))
			default:
				return &ErrFieldMismatch{
					StructType: of.Type(),
					FieldName:  n,
					Reason:     "not an integer class",
				}
			}
		case uint8, uint16, uint32, uint64:
			v := reflect.ValueOf(val).Uint()
			switch f.Kind() {
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				f.SetInt(int64(v))
			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
				f.SetUint(v)
			default:
				return &ErrFieldMismatch{
					StructType: of.Type(),
					FieldName:  n,
					Reason:     "not an integer class",
				}
			}
		case string:
			switch f.Kind() {
			case reflect.String:
				f.SetString(val)
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				iv, err := strconv.ParseInt(val, 10, 64)
				if err != nil {
					return err
				}
				f.SetInt(iv)
			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
				uv, err := strconv.ParseUint(val, 10, 64)
				if err != nil {
					return err
				}
				f.SetUint(uv)
			case reflect.Struct:
				switch f.Type() {
				case timeType:
					if len(val) == 25 {
						mins, err := strconv.Atoi(val[22:])
						if err != nil {
							return err
						}
						val = val[:22] + fmt.Sprintf("%02d%02d", mins/60, mins%60)
					}
					t, err := time.Parse("20060102150405.000000-0700", val)
					if err != nil {
						return err
					}
					f.Set(reflect.ValueOf(t))
				}
			}
		case bool:
			switch f.Kind() {
			case reflect.Bool:
				f.SetBool(val)
			default:
				return &ErrFieldMismatch{
					StructType: of.Type(),
					FieldName:  n,
					Reason:     "not a bool",
				}
			}
		default:
			typeof := reflect.TypeOf(val)
			if typeof == nil && (isPtr || c.NonePtrZero) {
				if (isPtr && c.PtrNil) || (!isPtr && c.NonePtrZero) {
					of.Set(reflect.Zero(of.Type()))
				}
				break
			}
			return &ErrFieldMismatch{
				StructType: of.Type(),
				FieldName:  n,
				Reason:     fmt.Sprintf("unsupported type (%T)", val),
			}
		}
	}
	return errFieldMismatch
}

type multiArgType int

const (
	multiArgTypeInvalid multiArgType = iota
	multiArgTypeStruct
	multiArgTypeStructPtr
)

// checkMultiArg checks that v has type []S, []*S for some struct type S.
//
// It returns what category the slice's elements are, and the reflect.Type
// that represents S.
func checkMultiArg(v reflect.Value) (m multiArgType, elemType reflect.Type) {
	if v.Kind() != reflect.Slice {
		return multiArgTypeInvalid, nil
	}
	elemType = v.Type().Elem()
	switch elemType.Kind() {
	case reflect.Struct:
		return multiArgTypeStruct, elemType
	case reflect.Ptr:
		elemType = elemType.Elem()
		if elemType.Kind() == reflect.Struct {
			return multiArgTypeStructPtr, elemType
		}
	}
	return multiArgTypeInvalid, nil
}

func oleInt64(item *ole.IDispatch, prop string) (int64, error) {
	v, err := oleutil.GetProperty(item, prop)
	if err != nil {
		return 0, err
	}
	defer v.Clear()

	i := int64(v.Val)
	return i, nil
}

// CreateQuery returns a WQL query string that queries all columns of src. where
// is an optional string that is appended to the query, to be used with WHERE
// clauses. In such a case, the "WHERE" string should appear at the beginning.
func CreateQuery(src interface{}, where string) string {
	var b bytes.Buffer
	b.WriteString("SELECT ")
	s := reflect.Indirect(reflect.ValueOf(src))
	t := s.Type()
	if s.Kind() == reflect.Slice {
		t = t.Elem()
	}
	if t.Kind() != reflect.Struct {
		return ""
	}
	var fields []string
	for i := 0; i < t.NumField(); i++ {
		fields = append(fields, t.Field(i).Name)
	}
	b.WriteString(strings.Join(fields, ", "))
	b.WriteString(" FROM ")
	b.WriteString(t.Name())
	b.WriteString(" " + where)
	return b.String()
}
<|endoftext|>


<commit_before>/*
  log-scaled histogram. concurrency-safe, and performant.

  Based on the ideas in github.com/codahale/hdrhistogram, which itself
  is based on the ideas in some old java code. but not using any of the
  implementation because that one only handles ints, doesn't deal
  with outliers, isn't thread-safe, and doesn't have an API which
  allows calculating of multiple statistics in a single pass.

  Copyright 2017 Nicolas Dade
*/
package loghistogram

import (
	"fmt"
	"math"
	"sync/atomic"
)

const epsilon = 1E-16 // 1E-16 is chosen because it is close to the ~52 bit limit of a float64 mantissa

// Histogram is a log-scaled histogram. It holds the accumulated counts
type Histogram struct {
	shift, scale float64 // precalculated values

	n                        uint64   // total # of accumulated samples in counts[], including outliers at counts[0] and counts[N+1]
	counts                   []uint64 // buckets of counts + a low and high outlier bucket at [0] and [N+1]
	middle_bucket_percentile float64  // guess (crude approximation) of the percentile of the values are in the first len(counts)/2 buckets, or -1 if it isn't yet guesses
}

// map from a value to a bucket index in h.counts. returns indexes 0 and = len(h.counts-1) for outliers
func (h *Histogram) valueToBucket(value float64) int {
	v := value - h.shift
	if v < 1.0 {
		return 0
	}

	b := 1 + int(math.Log(v)*h.scale) // benchmarks on amd64 & go1.7 show math.Log is slightly faster than math.Log10 and much faster than math.Log2

	if b >= len(h.counts) {
		b = len(h.counts) - 1
	}

	return b
}

// map from a bucket index into h.counts to the lower bound of values which map to that bucket
// if the bucket is an outlier then the result is not well defined, except that it will be outside the low,high range
func (h *Histogram) bucketToValue(bucket int) float64 {
	v := math.Exp(float64(bucket)/h.scale) + h.shift
	return v
}

// New constructs a histogram to hold values between low and high using the given number of buckets
func New(low, high float64, num_buckets int) *Histogram {
	h := &Histogram{}
	h.init(low, high, num_buckets)
	return h
}

func (h *Histogram) init(low, high float64, num_buckets int) {
	// check for nonsense arguments from broken callers
	if high < low || num_buckets <= 0 {
		panic(fmt.Sprintf("loghistogram.New(%v, %v, %v): invalid arguments", low, high, num_buckets))
	}

	// we want log(low-shift) to be 0, and log(high-shift)*scale = num_buckets+1-epsilon (so it falls inside the last bucket and not right on the edge)
	// so low-shift = 1, or
	shift := low - 1
	// and then
	scale := float64(num_buckets) * (1 - epsilon) / math.Log(high-shift)

	h.counts = make([]uint64, 2+num_buckets)
	h.shift = shift
	h.scale = scale
	h.middle_bucket_percentile = -1
}

func (h *Histogram) swap(new_counts []uint64) (old_counts []uint64) {
	old_counts = h.counts
	h.counts = new_counts
	return old_counts
}

// Accumulate adds a sample with value x to the histogram
func (h *Histogram) Accumulate(x float64) {
	i := h.valueToBucket(x)

	atomic.AddUint64(&h.counts[i], 1)
	atomic.AddUint64(&h.n, 1)
}

// test to see how much the atomic ops hurt performance
// (the answer, for the curious, is that the atomic increments cost ~3 ns/Accumulate(), out of 19.8 ns/Accumulate())
func (h *Histogram) raceyAccumulate(x float64) {
	i := h.valueToBucket(x)

	h.counts[i]++
	h.n++
}

// Count returns the total number of samples accumulated, including outliers
func (h *Histogram) Count() uint64 { return atomic.LoadUint64(&h.n) }

// Outliers returns the number of outliers on either side (how may samples were outside the low...high bound)
func (h *Histogram) Outliers() (uint64, uint64) {
	return atomic.LoadUint64(&h.counts[0]), atomic.LoadUint64(&h.counts[len(h.counts)-1])
}

// Percentiles returns the values at each percentile. NaN is returned if Count is 0 or percentiles are outside the 0...100 range.
// pers argument MUST be sorted low-to-high. NOTE outliers are taken into account as best we can, so the results can be outside
// of low...high if the percentile requested lies within the outliers.
func (h *Histogram) Percentiles(pers ...float64) []float64 {
	// check for stupid args
	if len(pers) == 0 {
		return nil
	}

	values := make([]float64, len(pers))

	// if the data values are evenly spread then scalling for percentiles starting from the highest
	// values to lower ones would be faster (since the high buckets are larger and would have more
	// of the total for fewer buckets scanned). But if you're using this log-scaled histogram rather
	// than a linear histogram it's probably because the distribution of values is skewed. In a common
	// use case of latency measurements, it's often very very skewed, with only a few outliers at the
	// top of the scale. Scanning for the 90% or 99% percentiles (often those of interest) can be
	// more efficient from below as from above, depending on the distribution.
	// A first good guess is to do it from below, but keeping track of the percentile of the middle
	// bucket lets us guess properly next time.

	if h.middle_bucket_percentile >= 0 && pers[0] > h.middle_bucket_percentile {
		// find the percentiles from high to low. this can be more efficient when asking for things like the 99% percentile
		// because we only need to scan over 1% of the counts.
		// (the log-sized buckets can make the outliers efficient, even if there aren't a lot of them)
		n := atomic.LoadUint64(&h.n)
		a := n
		if n == 0 {
			goto return_nans
		}
		nf := float64(n)
		i := len(h.counts) - 1
		for j := len(pers) - 1; j >= 0; j-- {
			p := pers[j]
			pn := uint64(p * nf / 100)
			for a >= pn && i >= 0 {
				a -= atomic.LoadUint64(&h.counts[i])
				i--
			}
			values[j] = h.bucketToValue(i)
		}
	} else {
		// find the percentiles from low to high
		a := uint64(0)
		n := atomic.LoadUint64(&h.n)
		if n == 0 {
			goto return_nans
		}
		nf := float64(n)
		i := 0
		middle_bucket := len(h.counts) / 2
		for j, p := range pers {
			pn := uint64(p * nf / 100)
			for a < pn && i < len(h.counts) {
				a += atomic.LoadUint64(&h.counts[i])
				if i == middle_bucket {
					// update our estimate of the middle bucket's percentile
					h.middle_bucket_percentile = 100 * float64(a) / float64(n)
				}
				i++
			}
			values[j] = h.bucketToValue(i)
		}
	}

	return values

return_nans:
	nan := math.NaN()
	for i := range values {
		values[i] = nan
	}
	return values
}

// Percentile calculates one percentile
func (h *Histogram) Percentile(per float64) float64 {
	return h.Percentiles(per)[0]
}

// Dup returns a copy of h
func (h *Histogram) Dup() *Histogram {
	h2 := *h
	// we've copied the struct, but of course not the counts slice
	// so copy that, and while we are at it we need to recompute n, just in case the counts change while we are copying them
	counts := make([]uint64, len(h2.counts))
	n := uint64(0)
	for i := range counts {
		c := atomic.LoadUint64(&h2.counts[i])
		n += c
		counts[i] = c
	}
	h2.counts = counts
	h2.n = n
	return &h2
}

// Sub subtracts h2 from h in-place. h -= h2. h and h2 must be the same size or you're subtracting apples from oranges and you'll get garbage
// Subtracting an earlier copy of the histogram is useful when keeping a running histogram.
func (h *Histogram) Sub(h2 *Histogram) {
	if len(h.counts) != len(h2.counts) {
		panic("subtracting different-sized histograms")
	}
	// I could also check the low and high, but that's sometimes useful, so don't

	for i := range h2.counts {
		c := atomic.LoadUint64(&h2.counts[i])
		atomic.AddUint64(&h.counts[i], -c)
		atomic.AddUint64(&h.n, -c) // keep the 'n' as up-to-date as Accumulate does, rather than adjust n once at the end of the loop
	}
}

// Sub returns h1-h2 without changing h1 nor h2
func Sub(h1, h2 *Histogram) *Histogram {
	if len(h1.counts) != len(h2.counts) {
		panic("subtracting different-sized histograms")
	}
	// I could also check the low and high, but that's sometimes useful, so don't

	h := *h1
	h.counts = make([]uint64, len(h1.counts))
	n := uint64(0)
	for i := range h1.counts {
		c1 := atomic.LoadUint64(&h1.counts[i])
		c2 := atomic.LoadUint64(&h2.counts[i])
		h.counts[i] = c1 - c2
		n += c1 - c2
	}
	h.n = n

	return &h
}
<commit_msg>fix Dup()<commit_after>/*
  log-scaled histogram. concurrency-safe, and performant.

  Based on the ideas in github.com/codahale/hdrhistogram, which itself
  is based on the ideas in some old java code. but not using any of the
  implementation because that one only handles ints, doesn't deal
  with outliers, isn't thread-safe, and doesn't have an API which
  allows calculating of multiple statistics in a single pass.

  Copyright 2017 Nicolas Dade
*/
package loghistogram

import (
	"fmt"
	"math"
	"sync/atomic"
)

const epsilon = 1E-16 // 1E-16 is chosen because it is close to the ~52 bit limit of a float64 mantissa

// Histogram is a log-scaled histogram. It holds the accumulated counts
type Histogram struct {
	shift, scale float64 // precalculated values

	n                        uint64   // total # of accumulated samples in counts[], including outliers at counts[0] and counts[N+1]
	counts                   []uint64 // buckets of counts + a low and high outlier bucket at [0] and [N+1]
	middle_bucket_percentile float64  // guess (crude approximation) of the percentile of the values are in the first len(counts)/2 buckets, or -1 if it isn't yet guesses
}

// map from a value to a bucket index in h.counts. returns indexes 0 and = len(h.counts-1) for outliers
func (h *Histogram) valueToBucket(value float64) int {
	v := value - h.shift
	if v < 1.0 {
		return 0
	}

	b := 1 + int(math.Log(v)*h.scale) // benchmarks on amd64 & go1.7 show math.Log is slightly faster than math.Log10 and much faster than math.Log2

	if b >= len(h.counts) {
		b = len(h.counts) - 1
	}

	return b
}

// map from a bucket index into h.counts to the lower bound of values which map to that bucket
// if the bucket is an outlier then the result is not well defined, except that it will be outside the low,high range
func (h *Histogram) bucketToValue(bucket int) float64 {
	v := math.Exp(float64(bucket)/h.scale) + h.shift
	return v
}

// New constructs a histogram to hold values between low and high using the given number of buckets
func New(low, high float64, num_buckets int) *Histogram {
	h := &Histogram{}
	h.init(low, high, num_buckets)
	return h
}

func (h *Histogram) init(low, high float64, num_buckets int) {
	// check for nonsense arguments from broken callers
	if high < low || num_buckets <= 0 {
		panic(fmt.Sprintf("loghistogram.New(%v, %v, %v): invalid arguments", low, high, num_buckets))
	}

	// we want log(low-shift) to be 0, and log(high-shift)*scale = num_buckets+1-epsilon (so it falls inside the last bucket and not right on the edge)
	// so low-shift = 1, or
	shift := low - 1
	// and then
	scale := float64(num_buckets) * (1 - epsilon) / math.Log(high-shift)

	h.counts = make([]uint64, 2+num_buckets)
	h.shift = shift
	h.scale = scale
	h.middle_bucket_percentile = -1
}

func (h *Histogram) swap(new_counts []uint64) (old_counts []uint64) {
	old_counts = h.counts
	h.counts = new_counts
	return old_counts
}

// Accumulate adds a sample with value x to the histogram
func (h *Histogram) Accumulate(x float64) {
	i := h.valueToBucket(x)

	atomic.AddUint64(&h.counts[i], 1)
	atomic.AddUint64(&h.n, 1)
}

// test to see how much the atomic ops hurt performance
// (the answer, for the curious, is that the atomic increments cost ~3 ns/Accumulate(), out of 19.8 ns/Accumulate())
func (h *Histogram) raceyAccumulate(x float64) {
	i := h.valueToBucket(x)

	h.counts[i]++
	h.n++
}

// Count returns the total number of samples accumulated, including outliers
func (h *Histogram) Count() uint64 { return atomic.LoadUint64(&h.n) }

// Outliers returns the number of outliers on either side (how may samples were outside the low...high bound)
func (h *Histogram) Outliers() (uint64, uint64) {
	return atomic.LoadUint64(&h.counts[0]), atomic.LoadUint64(&h.counts[len(h.counts)-1])
}

// Percentiles returns the values at each percentile. NaN is returned if Count is 0 or percentiles are outside the 0...100 range.
// pers argument MUST be sorted low-to-high. NOTE outliers are taken into account as best we can, so the results can be outside
// of low...high if the percentile requested lies within the outliers.
func (h *Histogram) Percentiles(pers ...float64) []float64 {
	// check for stupid args
	if len(pers) == 0 {
		return nil
	}

	values := make([]float64, len(pers))

	// if the data values are evenly spread then scalling for percentiles starting from the highest
	// values to lower ones would be faster (since the high buckets are larger and would have more
	// of the total for fewer buckets scanned). But if you're using this log-scaled histogram rather
	// than a linear histogram it's probably because the distribution of values is skewed. In a common
	// use case of latency measurements, it's often very very skewed, with only a few outliers at the
	// top of the scale. Scanning for the 90% or 99% percentiles (often those of interest) can be
	// more efficient from below as from above, depending on the distribution.
	// A first good guess is to do it from below, but keeping track of the percentile of the middle
	// bucket lets us guess properly next time.

	if h.middle_bucket_percentile >= 0 && pers[0] > h.middle_bucket_percentile {
		// find the percentiles from high to low. this can be more efficient when asking for things like the 99% percentile
		// because we only need to scan over 1% of the counts.
		// (the log-sized buckets can make the outliers efficient, even if there aren't a lot of them)
		n := atomic.LoadUint64(&h.n)
		a := n
		if n == 0 {
			goto return_nans
		}
		nf := float64(n)
		i := len(h.counts) - 1
		for j := len(pers) - 1; j >= 0; j-- {
			p := pers[j]
			pn := uint64(p * nf / 100)
			for a >= pn && i >= 0 {
				a -= atomic.LoadUint64(&h.counts[i])
				i--
			}
			values[j] = h.bucketToValue(i)
		}
	} else {
		// find the percentiles from low to high
		a := uint64(0)
		n := atomic.LoadUint64(&h.n)
		if n == 0 {
			goto return_nans
		}
		nf := float64(n)
		i := 0
		middle_bucket := len(h.counts) / 2
		for j, p := range pers {
			pn := uint64(p * nf / 100)
			for a < pn && i < len(h.counts) {
				a += atomic.LoadUint64(&h.counts[i])
				if i == middle_bucket {
					// update our estimate of the middle bucket's percentile
					h.middle_bucket_percentile = 100 * float64(a) / float64(n)
				}
				i++
			}
			values[j] = h.bucketToValue(i)
		}
	}

	return values

return_nans:
	nan := math.NaN()
	for i := range values {
		values[i] = nan
	}
	return values
}

// Percentile calculates one percentile
func (h *Histogram) Percentile(per float64) float64 {
	return h.Percentiles(per)[0]
}

// Dup returns a copy of h
func (h *Histogram) Dup() *Histogram {
	h2 := *h
	// we've copied the struct, but of course not the counts slice
	// so copy that, and while we are at it we need to recompute n, just in case the counts change while we are copying them
	counts := make([]uint64, len(h.counts))
	n := uint64(0)
	for i := range counts {
		c := atomic.LoadUint64(&h.counts[i])
		n += c
		counts[i] = c
	}
	h2.counts = counts
	h2.n = n
	return &h2
}

// Sub subtracts h2 from h in-place. h -= h2. h and h2 must be the same size or you're subtracting apples from oranges and you'll get garbage
// Subtracting an earlier copy of the histogram is useful when keeping a running histogram.
func (h *Histogram) Sub(h2 *Histogram) {
	if len(h.counts) != len(h2.counts) {
		panic("subtracting different-sized histograms")
	}
	// I could also check the low and high, but that's sometimes useful, so don't

	for i := range h2.counts {
		c := atomic.LoadUint64(&h2.counts[i])
		atomic.AddUint64(&h.counts[i], -c)
		atomic.AddUint64(&h.n, -c) // keep the 'n' as up-to-date as Accumulate does, rather than adjust n once at the end of the loop
	}
}

// Sub returns h1-h2 without changing h1 nor h2
func Sub(h1, h2 *Histogram) *Histogram {
	if len(h1.counts) != len(h2.counts) {
		panic("subtracting different-sized histograms")
	}
	// I could also check the low and high, but that's sometimes useful, so don't

	h := *h1
	h.counts = make([]uint64, len(h1.counts))
	n := uint64(0)
	for i := range h1.counts {
		c1 := atomic.LoadUint64(&h1.counts[i])
		c2 := atomic.LoadUint64(&h2.counts[i])
		h.counts[i] = c1 - c2
		n += c1 - c2
	}
	h.n = n

	return &h
}
<|endoftext|>


<commit_before>package lottery

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

type DropItem struct {
	ItemID   int
	ItemName string
	DropProb int
}

func (d DropItem) Prob() int {
	return d.DropProb
}

var _ Interface = (*DropItem)(nil)

func TestLots(t *testing.T) {
	l := New(rand.New(rand.NewSource(time.Now().UnixNano())))

	dropItems := []Interface{
		DropItem{ItemID: 1, ItemName: "エリクサ", DropProb: 10},
		DropItem{ItemID: 2, ItemName: "エーテル", DropProb: 20},
		DropItem{ItemID: 3, ItemName: "ポーション", DropProb: 30},
		DropItem{ItemID: 4, ItemName: "ハズレ", DropProb: 40},
	}

	check := 2000000
	countMap := map[DropItem]int{}
	for i := 0; i < check; i++ {
		lotIdx := l.Lots(dropItems...)
		if lotIdx == -1 {
			t.Fatal("lot error")
		}

		switch d := dropItems[lotIdx].(type) {
		case DropItem:
			countMap[d]++
		}
	}

	for dropItem, count := range countMap {
		result := float64(count) / float64(check) * 100
		prob := float64(dropItem.Prob())
		// 誤差0.1チェック
		if (prob-0.1) <= result && result < (prob+0.1) {
			fmt.Printf("ok %3.5f%%(%7d) : %s\n", result, count, dropItem.ItemName)
		} else {
			t.Errorf("error %3.5f%%(%7d) : %s\n", result, count, dropItem.ItemName)
		}
	}
}

func TestLot(t *testing.T) {
	l := New(rand.New(rand.NewSource(time.Now().UnixNano())))

	check := 1000000
	prob := float64(4.0) // 4%
	count := 0
	for i := 0; i < check; i++ {
		if l.Lot(int(prob)) {
			count++
		}
	}
	result := float64(count) / float64(check) * 100

	// 誤差0.1チェック
	if (prob-0.1) <= result && result < (prob+0.1) {
		fmt.Printf("lottery ok %f%%\n", result)
	} else {
		t.Errorf("lottery error %f%%", result)
	}
}

func TestLotOf(t *testing.T) {
	l := New(rand.New(rand.NewSource(time.Now().UnixNano())))

	check := 1000000
	prob := float64(0.5) // 0.5%
	count := 0
	for i := 0; i < check; i++ {
		// 1万分率で計算
		if l.LotOf(int(prob/100*10000), 10000) {
			count++
		}
	}
	result := float64(count) / float64(check) * 100

	// 誤差0.1チェック
	if (prob-0.1) <= result && result < (prob+0.1) {
		fmt.Printf("lottery ok %f%%\n", result)
	} else {
		t.Errorf("lottery error %f%%", result)
	}
}

func TestLot_0to100(t *testing.T) {
	l := New(rand.New(rand.NewSource(time.Now().UnixNano())))

	testCases := []struct {
		prob   int
		result bool
	}{
		{prob: 120, result: true},
		{prob: 100, result: true},
		{prob: 0, result: false},
		{prob: -1, result: false},
	}

	for _, testCase := range testCases {
		if l.Lot(testCase.prob) != testCase.result {
			t.Errorf("lottery error not %f%%", testCase.prob)
		}
	}
}
<commit_msg>fix test<commit_after>package lottery_test

import (
	"fmt"
	"math/rand"
	"testing"
	"time"

	"github.com/kyokomi/lottery"
)

type DropItem struct {
	ItemID   int
	ItemName string
	DropProb int
}

func (d DropItem) Prob() int {
	return d.DropProb
}

var _ lottery.Interface = (*DropItem)(nil)

func TestLots(t *testing.T) {
	l := lottery.New(rand.New(rand.NewSource(time.Now().UnixNano())))

	dropItems := []lottery.Interface{
		DropItem{ItemID: 1, ItemName: "エリクサ", DropProb: 10},
		DropItem{ItemID: 2, ItemName: "エーテル", DropProb: 20},
		DropItem{ItemID: 3, ItemName: "ポーション", DropProb: 30},
		DropItem{ItemID: 4, ItemName: "ハズレ", DropProb: 40},
	}

	check := 2000000
	countMap := map[DropItem]int{}
	for i := 0; i < check; i++ {
		lotIdx := l.Lots(dropItems...)
		if lotIdx == -1 {
			t.Fatal("lot error")
		}

		switch d := dropItems[lotIdx].(type) {
		case DropItem:
			countMap[d]++
		}
	}

	for dropItem, count := range countMap {
		result := float64(count) / float64(check) * 100
		prob := float64(dropItem.Prob())
		// 誤差0.1チェック
		if (prob-0.1) <= result && result < (prob+0.1) {
			fmt.Printf("ok %3.5f%%(%7d) : %s\n", result, count, dropItem.ItemName)
		} else {
			t.Errorf("error %3.5f%%(%7d) : %s\n", result, count, dropItem.ItemName)
		}
	}
}

func TestLot(t *testing.T) {
	l := lottery.New(rand.New(rand.NewSource(time.Now().UnixNano())))

	check := 1000000
	prob := float64(4.0) // 4%
	count := 0
	for i := 0; i < check; i++ {
		if l.Lot(int(prob)) {
			count++
		}
	}
	result := float64(count) / float64(check) * 100

	// 誤差0.1チェック
	if (prob-0.1) <= result && result < (prob+0.1) {
		fmt.Printf("lottery ok %f%%\n", result)
	} else {
		t.Errorf("lottery error %f%%", result)
	}
}

func TestLotOf(t *testing.T) {
	l := lottery.New(rand.New(rand.NewSource(time.Now().UnixNano())))

	check := 1000000
	prob := float64(0.5) // 0.5%
	count := 0
	for i := 0; i < check; i++ {
		// 1万分率で計算
		if l.LotOf(int(prob/100*10000), 10000) {
			count++
		}
	}
	result := float64(count) / float64(check) * 100

	// 誤差0.1チェック
	if (prob-0.1) <= result && result < (prob+0.1) {
		fmt.Printf("lottery ok %f%%\n", result)
	} else {
		t.Errorf("lottery error %f%%", result)
	}
}

func TestLot_0to100(t *testing.T) {
	l := lottery.New(rand.New(rand.NewSource(time.Now().UnixNano())))

	testCases := []struct {
		prob   int
		result bool
	}{
		{prob: 120, result: true},
		{prob: 100, result: true},
		{prob: 0, result: false},
		{prob: -1, result: false},
	}

	for _, testCase := range testCases {
		if l.Lot(testCase.prob) != testCase.result {
			t.Errorf("lottery error not %f%%", testCase.prob)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"

	"github.com/lxc/lxd/lxd/cluster"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/response"
)

// forwardedResponseIfTargetIsRemote redirects a request to the request has a
// targetNode parameter pointing to a node which is not the local one.
func forwardedResponseIfTargetIsRemote(d *Daemon, request *http.Request) response.Response {
	targetNode := queryParam(request, "target")
	if targetNode == "" {
		return nil
	}

	// Figure out the address of the target node (which is possibly
	// this very same node).
	address, err := cluster.ResolveTarget(d.cluster, targetNode)
	if err != nil {
		return response.SmartError(err)
	}

	if address != "" {
		// Forward the response.
		cert := d.endpoints.NetworkCert()
		client, err := cluster.Connect(address, cert, false)
		if err != nil {
			return response.SmartError(err)
		}
		return response.ForwardedResponse(client, request)
	}

	return nil
}

// forwardedResponseIfInstanceIsRemote redirects a request to the node running
// the container with the given name. If the container is local, nothing gets
// done and nil is returned.
func forwardedResponseIfInstanceIsRemote(d *Daemon, r *http.Request, project, name string, instanceType instancetype.Type) (response.Response, error) {
	cert := d.endpoints.NetworkCert()
	client, err := cluster.ConnectIfInstanceIsRemote(d.cluster, project, name, cert, instanceType)
	if err != nil {
		return nil, err
	}
	if client == nil {
		return nil, nil
	}
	return response.ForwardedResponse(client, r), nil
}

// forwardedResponseIfVolumeIsRemote redirects a request to the node hosting
// the volume with the given pool ID, name and type. If the container is local,
// nothing gets done and nil is returned. If more than one node has a matching
// volume, an error is returned.
//
// This is used when no targetNode is specified, and saves users some typing
// when the volume name/type is unique to a node.
func forwardedResponseIfVolumeIsRemote(d *Daemon, r *http.Request, poolName string, projectName string, volumeName string, volumeType int) response.Response {
	if queryParam(r, "target") != "" {
		return nil
	}

	cert := d.endpoints.NetworkCert()
	client, err := cluster.ConnectIfVolumeIsRemote(d.cluster, poolName, projectName, volumeName, volumeType, cert)
	if err != nil {
		return response.SmartError(err)
	}

	if client == nil {
		return nil
	}

	return response.ForwardedResponse(client, r)
}
<commit_msg>lxd/response: cluster.ConnectIfVolumeIsRemote usage<commit_after>package main

import (
	"net/http"

	"github.com/lxc/lxd/lxd/cluster"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/response"
)

// forwardedResponseIfTargetIsRemote redirects a request to the request has a
// targetNode parameter pointing to a node which is not the local one.
func forwardedResponseIfTargetIsRemote(d *Daemon, request *http.Request) response.Response {
	targetNode := queryParam(request, "target")
	if targetNode == "" {
		return nil
	}

	// Figure out the address of the target node (which is possibly
	// this very same node).
	address, err := cluster.ResolveTarget(d.cluster, targetNode)
	if err != nil {
		return response.SmartError(err)
	}

	if address != "" {
		// Forward the response.
		cert := d.endpoints.NetworkCert()
		client, err := cluster.Connect(address, cert, false)
		if err != nil {
			return response.SmartError(err)
		}
		return response.ForwardedResponse(client, request)
	}

	return nil
}

// forwardedResponseIfInstanceIsRemote redirects a request to the node running
// the container with the given name. If the container is local, nothing gets
// done and nil is returned.
func forwardedResponseIfInstanceIsRemote(d *Daemon, r *http.Request, project, name string, instanceType instancetype.Type) (response.Response, error) {
	cert := d.endpoints.NetworkCert()
	client, err := cluster.ConnectIfInstanceIsRemote(d.cluster, project, name, cert, instanceType)
	if err != nil {
		return nil, err
	}
	if client == nil {
		return nil, nil
	}
	return response.ForwardedResponse(client, r), nil
}

// forwardedResponseIfVolumeIsRemote redirects a request to the node hosting
// the volume with the given pool ID, name and type. If the container is local,
// nothing gets done and nil is returned. If more than one node has a matching
// volume, an error is returned.
//
// This is used when no targetNode is specified, and saves users some typing
// when the volume name/type is unique to a node.
func forwardedResponseIfVolumeIsRemote(d *Daemon, r *http.Request, poolName string, projectName string, volumeName string, volumeType int) response.Response {
	if queryParam(r, "target") != "" {
		return nil
	}

	cert := d.endpoints.NetworkCert()
	client, err := cluster.ConnectIfVolumeIsRemote(d.State(), poolName, projectName, volumeName, volumeType, cert)
	if err != nil {
		return response.SmartError(err)
	}

	if client == nil {
		return nil
	}

	return response.ForwardedResponse(client, r)
}
<|endoftext|>


<commit_before>package xpp

import (
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"strings"
)

type XMLEventType int

const (
	StartDocument XMLEventType = iota
	EndDocument
	StartTag
	EndTag
	Text
	Comment
	ProcessingInstruction
	Directive
	IgnorableWhitespace // TODO: ?
	// TODO: CDSECT ?
)

type XMLPullParser struct {
	// Document State
	Spaces map[string]string

	// Token State
	Depth int
	Event XMLEventType
	Attrs []xml.Attr
	Name  string
	Space string
	Text  string

	decoder *xml.Decoder
	token   interface{}
}

func NewXMLPullParser(r io.Reader, strict bool) *XMLPullParser {
	d := xml.NewDecoder(r)
	d.Strict = strict
	return &XMLPullParser{
		decoder: d,
		Event:   StartDocument,
		Depth:   0,
		Spaces:  map[string]string{},
	}
}

func (p *XMLPullParser) NextTag() (event XMLEventType, err error) {
	t, err := p.Next()
	if err != nil {
		return event, err
	}

	for t == Text && p.IsWhitespace() {
		t, err = p.Next()
		if err != nil {
			return event, err
		}
	}

	if t != StartTag && t != EndTag {
		return event, fmt.Errorf("Expected StartTag or EndTag but got %s", p.EventName(t))
	}

	return t, nil
}

func (p *XMLPullParser) Next() (event XMLEventType, err error) {
	for {
		event, err = p.NextToken()
		if err != nil {
			return event, err
		}

		// Return immediately after encountering a StartTag
		// EndTag, Text, EndDocument
		if event == StartTag ||
			event == EndTag ||
			event == EndDocument ||
			event == Text {
			return event, nil
		}

		// Skip Comment/Directive and ProcessingInstruction
		if event == Comment ||
			event == Directive ||
			event == ProcessingInstruction {
			continue
		}
	}
	return event, nil
}

func (p *XMLPullParser) NextToken() (event XMLEventType, err error) {
	// Clear any state held for the previous token
	p.resetTokenState()

	token, err := p.decoder.Token()
	if err != nil {
		if err == io.EOF {
			// XML decoder returns the EOF as an error
			// but we want to return it as a valid
			// EndDocument token instead
			p.token = nil
			p.Event = EndDocument
			return p.Event, nil
		}
		return event, err
	}

	p.token = xml.CopyToken(token)
	p.processToken(p.token)
	p.Event = p.EventType(p.token)

	return p.Event, nil
}

func (p *XMLPullParser) NextText() (string, error) {
	if p.Event != StartTag {
		return "", errors.New("Parser must be on StartTag to get NextText()")
	}

	t, err := p.Next()
	if err != nil {
		return "", err
	}

	if t != EndTag && t != Text {
		return "", errors.New("Parser must be on EndTag or Text to read text")
	}

	var result string
	for t == Text {
		result = result + p.Text
		t, err = p.Next()
		if err != nil {
			return "", err
		}

		if t != EndTag && t != Text {
			errstr := fmt.Sprintf("Event Text must be immediately followed by EndTag or Text but got %s", p.EventName(t))
			return "", errors.New(errstr)
		}
	}

	return result, nil
}

func (p *XMLPullParser) Skip() error {
	for {
		tok, err := p.NextToken()
		if err != nil {
			return err
		}
		if tok == StartTag {
			if err := p.Skip(); err != nil {
				return err
			}
		} else if tok == EndTag {
			return nil
		}
	}
}

func (p *XMLPullParser) Attribute(name string) string {
	for _, attr := range p.Attrs {
		if attr.Name.Local == name {
			return attr.Value
		}
	}
	return ""
}

func (p *XMLPullParser) Expect(event XMLEventType, name string) (err error) {
	return p.ExpectAll(event, "*", name)
}

func (p *XMLPullParser) ExpectAll(event XMLEventType, space string, name string) (err error) {
	if !(p.Event == event && (strings.ToLower(p.Space) == strings.ToLower(space) || space == "*") && (strings.ToLower(p.Name) == strings.ToLower(name) || name == "*")) {
		err = fmt.Errorf("Expected Space:%s Name:%s Event:%s but got Space:%s Name:%s Event:%s", space, name, p.EventName(event), p.Space, p.Name, p.EventName(p.Event))
	}
	return
}

func (p *XMLPullParser) DecodeElement(v interface{}) error {
	if p.Event != StartTag {
		return errors.New("DecodeElement can only be called from a StartTag event")
	}

	//tok := &p.token

	startToken := p.token.(xml.StartElement)

	// Consumes all tokens until the matching end token.
	err := p.decoder.DecodeElement(v, &startToken)
	if err != nil {
		return err
	}

	name := p.Name

	// Need to set the "current" token name/event
	// to the previous StartTag event's name
	p.resetTokenState()
	p.Event = EndTag
	p.Name = name
	p.token = nil
	return nil
}

func (p *XMLPullParser) IsWhitespace() bool {
	return strings.TrimSpace(p.Text) == ""
}

func (p *XMLPullParser) EventName(e XMLEventType) (name string) {
	switch e {
	case StartTag:
		name = "StartTag"
	case EndTag:
		name = "EndTag"
	case StartDocument:
		name = "StartDocument"
	case EndDocument:
		name = "EndDocument"
	case ProcessingInstruction:
		name = "ProcessingInstruction"
	case Directive:
		name = "Directive"
	case Comment:
		name = "Comment"
	case Text:
		name = "Text"
	case IgnorableWhitespace:
		name = "IgnorableWhitespace"
	}
	return
}

func (p *XMLPullParser) EventType(t xml.Token) (event XMLEventType) {
	switch t.(type) {
	case xml.StartElement:
		event = StartTag
	case xml.EndElement:
		event = EndTag
	case xml.CharData:
		event = Text
	case xml.Comment:
		event = Comment
	case xml.ProcInst:
		event = ProcessingInstruction
	case xml.Directive:
		event = Directive
	}
	return
}

func (p *XMLPullParser) processToken(t xml.Token) {
	switch tt := t.(type) {
	case xml.StartElement:
		p.processStartToken(tt)
	case xml.EndElement:
		p.processEndToken(tt)
	case xml.CharData:
		p.processCharDataToken(tt)
	case xml.Comment:
		p.processCommentToken(tt)
	case xml.ProcInst:
		p.processProcInstToken(tt)
	case xml.Directive:
		p.processDirectiveToken(tt)
	}
}

func (p *XMLPullParser) processStartToken(t xml.StartElement) {
	p.Depth++
	p.Attrs = t.Attr
	p.Name = t.Name.Local
	p.Space = t.Name.Space
	p.trackNamespaces(t)
}

func (p *XMLPullParser) processEndToken(t xml.EndElement) {
	p.Depth--
	p.Name = t.Name.Local
}

func (p *XMLPullParser) processCharDataToken(t xml.CharData) {
	p.Text = string([]byte(t))
}

func (p *XMLPullParser) processCommentToken(t xml.Comment) {
	p.Text = string([]byte(t))
}

func (p *XMLPullParser) processProcInstToken(t xml.ProcInst) {
	p.Text = fmt.Sprintf("%s %s", t.Target, string(t.Inst))
}

func (p *XMLPullParser) processDirectiveToken(t xml.Directive) {
	p.Text = string([]byte(t))
}

func (p *XMLPullParser) resetTokenState() {
	p.Attrs = nil
	p.Name = ""
	p.Space = ""
	p.Text = ""
}

func (p *XMLPullParser) trackNamespaces(t xml.StartElement) {
	for _, attr := range t.Attr {
		if attr.Name.Space == "xmlns" {
			space := strings.TrimSpace(attr.Value)
			spacePrefix := strings.TrimSpace(strings.ToLower(attr.Name.Local))
			p.Spaces[space] = spacePrefix
		} else if attr.Name.Local == "xmlns" {
			space := strings.TrimSpace(attr.Value)
			p.Spaces[space] = ""
		}
	}
}
<commit_msg>A charset reader can now be specified<commit_after>package xpp

import (
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"strings"
)

type XMLEventType int
type CharsetReader func(charset string, input io.Reader) (io.Reader, error)

const (
	StartDocument XMLEventType = iota
	EndDocument
	StartTag
	EndTag
	Text
	Comment
	ProcessingInstruction
	Directive
	IgnorableWhitespace // TODO: ?
	// TODO: CDSECT ?
)

type XMLPullParser struct {
	// Document State
	Spaces map[string]string

	// Token State
	Depth int
	Event XMLEventType
	Attrs []xml.Attr
	Name  string
	Space string
	Text  string

	decoder *xml.Decoder
	token   interface{}
}

func NewXMLPullParser(r io.Reader, strict bool, cr CharsetReader) *XMLPullParser {
	d := xml.NewDecoder(r)
	d.Strict = strict
	d.CharsetReader = cr
	return &XMLPullParser{
		decoder: d,
		Event:   StartDocument,
		Depth:   0,
		Spaces:  map[string]string{},
	}
}

func (p *XMLPullParser) NextTag() (event XMLEventType, err error) {
	t, err := p.Next()
	if err != nil {
		return event, err
	}

	for t == Text && p.IsWhitespace() {
		t, err = p.Next()
		if err != nil {
			return event, err
		}
	}

	if t != StartTag && t != EndTag {
		return event, fmt.Errorf("Expected StartTag or EndTag but got %s", p.EventName(t))
	}

	return t, nil
}

func (p *XMLPullParser) Next() (event XMLEventType, err error) {
	for {
		event, err = p.NextToken()
		if err != nil {
			return event, err
		}

		// Return immediately after encountering a StartTag
		// EndTag, Text, EndDocument
		if event == StartTag ||
			event == EndTag ||
			event == EndDocument ||
			event == Text {
			return event, nil
		}

		// Skip Comment/Directive and ProcessingInstruction
		if event == Comment ||
			event == Directive ||
			event == ProcessingInstruction {
			continue
		}
	}
	return event, nil
}

func (p *XMLPullParser) NextToken() (event XMLEventType, err error) {
	// Clear any state held for the previous token
	p.resetTokenState()

	token, err := p.decoder.Token()
	if err != nil {
		if err == io.EOF {
			// XML decoder returns the EOF as an error
			// but we want to return it as a valid
			// EndDocument token instead
			p.token = nil
			p.Event = EndDocument
			return p.Event, nil
		}
		return event, err
	}

	p.token = xml.CopyToken(token)
	p.processToken(p.token)
	p.Event = p.EventType(p.token)

	return p.Event, nil
}

func (p *XMLPullParser) NextText() (string, error) {
	if p.Event != StartTag {
		return "", errors.New("Parser must be on StartTag to get NextText()")
	}

	t, err := p.Next()
	if err != nil {
		return "", err
	}

	if t != EndTag && t != Text {
		return "", errors.New("Parser must be on EndTag or Text to read text")
	}

	var result string
	for t == Text {
		result = result + p.Text
		t, err = p.Next()
		if err != nil {
			return "", err
		}

		if t != EndTag && t != Text {
			errstr := fmt.Sprintf("Event Text must be immediately followed by EndTag or Text but got %s", p.EventName(t))
			return "", errors.New(errstr)
		}
	}

	return result, nil
}

func (p *XMLPullParser) Skip() error {
	for {
		tok, err := p.NextToken()
		if err != nil {
			return err
		}
		if tok == StartTag {
			if err := p.Skip(); err != nil {
				return err
			}
		} else if tok == EndTag {
			return nil
		}
	}
}

func (p *XMLPullParser) Attribute(name string) string {
	for _, attr := range p.Attrs {
		if attr.Name.Local == name {
			return attr.Value
		}
	}
	return ""
}

func (p *XMLPullParser) Expect(event XMLEventType, name string) (err error) {
	return p.ExpectAll(event, "*", name)
}

func (p *XMLPullParser) ExpectAll(event XMLEventType, space string, name string) (err error) {
	if !(p.Event == event && (strings.ToLower(p.Space) == strings.ToLower(space) || space == "*") && (strings.ToLower(p.Name) == strings.ToLower(name) || name == "*")) {
		err = fmt.Errorf("Expected Space:%s Name:%s Event:%s but got Space:%s Name:%s Event:%s", space, name, p.EventName(event), p.Space, p.Name, p.EventName(p.Event))
	}
	return
}

func (p *XMLPullParser) DecodeElement(v interface{}) error {
	if p.Event != StartTag {
		return errors.New("DecodeElement can only be called from a StartTag event")
	}

	//tok := &p.token

	startToken := p.token.(xml.StartElement)

	// Consumes all tokens until the matching end token.
	err := p.decoder.DecodeElement(v, &startToken)
	if err != nil {
		return err
	}

	name := p.Name

	// Need to set the "current" token name/event
	// to the previous StartTag event's name
	p.resetTokenState()
	p.Event = EndTag
	p.Name = name
	p.token = nil
	return nil
}

func (p *XMLPullParser) IsWhitespace() bool {
	return strings.TrimSpace(p.Text) == ""
}

func (p *XMLPullParser) EventName(e XMLEventType) (name string) {
	switch e {
	case StartTag:
		name = "StartTag"
	case EndTag:
		name = "EndTag"
	case StartDocument:
		name = "StartDocument"
	case EndDocument:
		name = "EndDocument"
	case ProcessingInstruction:
		name = "ProcessingInstruction"
	case Directive:
		name = "Directive"
	case Comment:
		name = "Comment"
	case Text:
		name = "Text"
	case IgnorableWhitespace:
		name = "IgnorableWhitespace"
	}
	return
}

func (p *XMLPullParser) EventType(t xml.Token) (event XMLEventType) {
	switch t.(type) {
	case xml.StartElement:
		event = StartTag
	case xml.EndElement:
		event = EndTag
	case xml.CharData:
		event = Text
	case xml.Comment:
		event = Comment
	case xml.ProcInst:
		event = ProcessingInstruction
	case xml.Directive:
		event = Directive
	}
	return
}

func (p *XMLPullParser) processToken(t xml.Token) {
	switch tt := t.(type) {
	case xml.StartElement:
		p.processStartToken(tt)
	case xml.EndElement:
		p.processEndToken(tt)
	case xml.CharData:
		p.processCharDataToken(tt)
	case xml.Comment:
		p.processCommentToken(tt)
	case xml.ProcInst:
		p.processProcInstToken(tt)
	case xml.Directive:
		p.processDirectiveToken(tt)
	}
}

func (p *XMLPullParser) processStartToken(t xml.StartElement) {
	p.Depth++
	p.Attrs = t.Attr
	p.Name = t.Name.Local
	p.Space = t.Name.Space
	p.trackNamespaces(t)
}

func (p *XMLPullParser) processEndToken(t xml.EndElement) {
	p.Depth--
	p.Name = t.Name.Local
}

func (p *XMLPullParser) processCharDataToken(t xml.CharData) {
	p.Text = string([]byte(t))
}

func (p *XMLPullParser) processCommentToken(t xml.Comment) {
	p.Text = string([]byte(t))
}

func (p *XMLPullParser) processProcInstToken(t xml.ProcInst) {
	p.Text = fmt.Sprintf("%s %s", t.Target, string(t.Inst))
}

func (p *XMLPullParser) processDirectiveToken(t xml.Directive) {
	p.Text = string([]byte(t))
}

func (p *XMLPullParser) resetTokenState() {
	p.Attrs = nil
	p.Name = ""
	p.Space = ""
	p.Text = ""
}

func (p *XMLPullParser) trackNamespaces(t xml.StartElement) {
	for _, attr := range t.Attr {
		if attr.Name.Space == "xmlns" {
			space := strings.TrimSpace(attr.Value)
			spacePrefix := strings.TrimSpace(strings.ToLower(attr.Name.Local))
			p.Spaces[space] = spacePrefix
		} else if attr.Name.Local == "xmlns" {
			space := strings.TrimSpace(attr.Value)
			p.Spaces[space] = ""
		}
	}
}
<|endoftext|>