<commit_before>// The algorithm implemented here is based on "An O(NP) Sequence Comparison Algorithm"
// by described by Sun Wu, Udi Manber and Gene Myers

package gonp

import (
	"container/list"
	"fmt"
	"unicode/utf8"
)

const (
	// SesDelete is manipulaton type of deleting element in SES
	SesDelete SesType = iota
	// SesCommon is manipulaton type of same element in SES
	SesCommon
	// SesAdd is manipulaton type of adding element in SES
	SesAdd
)

// SesType is manipulaton type
type SesType int

// Point is coordinate in edit graph
type Point struct {
	x, y, k int
}

// SesElem is element of SES
type SesElem struct {
	c rune
	t SesType
}

// Diff is context for calculating difference between a and b
type Diff struct {
	a    []rune
	b    []rune
	m, n int
	ed   int
	meta Meta
	lcs  *list.List
	ses  *list.List
}

// Meta is internal state for calculating difference
type Meta struct {
	reverse  bool
	path     []int
	onlyEd   bool
	pathposi map[int]Point
}

func max(x, y int) int {
	if x < y {
		return y
	}
	return x
}

// New is initializer of Diff
func New(a string, b string) *Diff {
	m, n := utf8.RuneCountInString(a), utf8.RuneCountInString(b)
	diff := new(Diff)
	diff.a, diff.b = []rune(a), []rune(b)
	diff.m, diff.n = m, n
	diff.meta.reverse = false
	if m >= n {
		diff.a, diff.b = diff.b, diff.a
		diff.m, diff.n = n, m
		diff.meta.reverse = true
	}
	diff.meta.onlyEd = false
	return diff
}

// OnlyEd enables to calculate only edit distance
func (diff *Diff) OnlyEd() {
	diff.meta.onlyEd = true
}

// Editdistance returns edit distance between a and b
func (diff *Diff) Editdistance() int {
	return diff.ed
}

// Lcs returns LCS (Longest Common Subsequence) between a and b
func (diff *Diff) Lcs() string {
	var b = make([]rune, diff.lcs.Len())
	for i, e := 0, diff.lcs.Front(); e != nil; i, e = i+1, e.Next() {
		b[i] = e.Value.(rune)
	}
	return string(b)
}

// Ses return SES (Shortest Edit Script) between a and b
func (diff *Diff) Ses() []SesElem {
	seq := make([]SesElem, diff.ses.Len())
	for i, e := 0, diff.ses.Front(); e != nil; i, e = i+1, e.Next() {
		seq[i].c = e.Value.(SesElem).c
		seq[i].t = e.Value.(SesElem).t
	}
	return seq
}

// PrintSes prints shortest edit script between a and b
func (diff *Diff) PrintSes() {
	for _, e := 0, diff.ses.Front(); e != nil; e = e.Next() {
		ee := e.Value.(SesElem)
		switch ee.t {
		case SesDelete:
			fmt.Printf("- %c\n", ee.c)
		case SesAdd:
			fmt.Printf("+ %c\n", ee.c)
		case SesCommon:
			fmt.Printf("  %c\n", ee.c)
		}
	}
}

// Compose composes diff between a and b
func (diff *Diff) Compose() {
	fp := make([]int, diff.m+diff.n+3)
	diff.meta.path = make([]int, diff.m+diff.n+3)
	diff.meta.pathposi = make(map[int]Point)
	diff.lcs = list.New()
	diff.ses = list.New()

	for i := range fp {
		fp[i] = -1
		diff.meta.path[i] = -1
	}

	offset := diff.m + 1
	delta := diff.n - diff.m
	for p := 0; ; p++ {

		for k := -p; k <= delta-1; k++ {
			fp[k+offset] = diff.snake(k, fp[k-1+offset]+1, fp[k+1+offset], offset)
		}

		for k := delta + p; k >= delta+1; k-- {
			fp[k+offset] = diff.snake(k, fp[k-1+offset]+1, fp[k+1+offset], offset)
		}

		fp[delta+offset] = diff.snake(delta, fp[delta-1+offset]+1, fp[delta+1+offset], offset)

		if fp[delta+offset] >= diff.n {
			diff.ed = delta + 2*p
			break
		}
	}

	if diff.meta.onlyEd {
		return
	}

	r := diff.meta.path[delta+offset]
	epc := make(map[int]Point)
	for r != -1 {
		epc[len(epc)] = Point{x: diff.meta.pathposi[r].x, y: diff.meta.pathposi[r].y, k: -1}
		r = diff.meta.pathposi[r].k
	}
	diff.recordSeq(epc)
}

func (diff *Diff) snake(k, p, pp, offset int) int {
	r := 0
	if p > pp {
		r = diff.meta.path[k-1+offset]
	} else {
		r = diff.meta.path[k+1+offset]
	}

	y := max(p, pp)
	x := y - k

	for x < diff.m && y < diff.n && diff.a[x] == diff.b[y] {
		x++
		y++
	}

	if !diff.meta.onlyEd {
		diff.meta.path[k+offset] = len(diff.meta.pathposi)
		diff.meta.pathposi[len(diff.meta.pathposi)] = Point{x: x, y: y, k: r}
	}

	return y
}

func (diff *Diff) recordSeq(epc map[int]Point) {
	xIdx, yIdx := 1, 1
	pxIdx, pyIdx := 0, 0
	for i := len(epc) - 1; i >= 0; i-- {
		for (pxIdx < epc[i].x) || (pyIdx < epc[i].y) {
			if (epc[i].y - epc[i].x) > (pyIdx - pxIdx) {
				elem := diff.b[pyIdx]
				t := SesAdd
				if diff.meta.reverse {
					t = SesDelete
				}
				diff.ses.PushBack(SesElem{c: elem, t: t})
				yIdx++
				pyIdx++
			} else if epc[i].y-epc[i].x < pyIdx-pxIdx {
				elem := diff.a[pxIdx]
				t := SesDelete
				if diff.meta.reverse {
					t = SesAdd
				}
				diff.ses.PushBack(SesElem{c: elem, t: t})
				xIdx++
				pxIdx++
			} else {
				elem := diff.a[pxIdx]
				diff.lcs.PushBack(elem)
				diff.ses.PushBack(SesElem{c: elem, t: SesCommon})
				xIdx++
				yIdx++
				pxIdx++
				pyIdx++
			}
		}
	}
}
<commit_msg>refactored.<commit_after>// The algorithm implemented here is based on "An O(NP) Sequence Comparison Algorithm"
// by described by Sun Wu, Udi Manber and Gene Myers

package gonp

import (
	"container/list"
	"fmt"
	"unicode/utf8"
)

const (
	// SesDelete is manipulaton type of deleting element in SES
	SesDelete SesType = iota
	// SesCommon is manipulaton type of same element in SES
	SesCommon
	// SesAdd is manipulaton type of adding element in SES
	SesAdd
)

// SesType is manipulaton type
type SesType int

// Point is coordinate in edit graph
type Point struct {
	x, y int
}

// PointCordinate is coordinate in edit graph attached route
type PointCordinate struct {
	x, y, r int
}

// SesElem is element of SES
type SesElem struct {
	c rune
	t SesType
}

// Diff is context for calculating difference between a and b
type Diff struct {
	a    []rune
	b    []rune
	m, n int
	ed   int
	meta Meta
	lcs  *list.List
	ses  *list.List
}

// Meta is internal state for calculating difference
type Meta struct {
	reverse       bool
	path          []int
	onlyEd        bool
	pathCordinate []PointCordinate
}

func max(x, y int) int {
	if x < y {
		return y
	}
	return x
}

// New is initializer of Diff
func New(a string, b string) *Diff {
	m, n := utf8.RuneCountInString(a), utf8.RuneCountInString(b)
	diff := new(Diff)
	diff.a, diff.b = []rune(a), []rune(b)
	diff.m, diff.n = m, n
	diff.meta.reverse = false
	if m >= n {
		diff.a, diff.b = diff.b, diff.a
		diff.m, diff.n = n, m
		diff.meta.reverse = true
	}
	diff.meta.onlyEd = false
	return diff
}

// OnlyEd enables to calculate only edit distance
func (diff *Diff) OnlyEd() {
	diff.meta.onlyEd = true
}

// Editdistance returns edit distance between a and b
func (diff *Diff) Editdistance() int {
	return diff.ed
}

// Lcs returns LCS (Longest Common Subsequence) between a and b
func (diff *Diff) Lcs() string {
	var b = make([]rune, diff.lcs.Len())
	for i, e := 0, diff.lcs.Front(); e != nil; i, e = i+1, e.Next() {
		b[i] = e.Value.(rune)
	}
	return string(b)
}

// Ses return SES (Shortest Edit Script) between a and b
func (diff *Diff) Ses() []SesElem {
	seq := make([]SesElem, diff.ses.Len())
	for i, e := 0, diff.ses.Front(); e != nil; i, e = i+1, e.Next() {
		seq[i].c = e.Value.(SesElem).c
		seq[i].t = e.Value.(SesElem).t
	}
	return seq
}

// PrintSes prints shortest edit script between a and b
func (diff *Diff) PrintSes() {
	for _, e := 0, diff.ses.Front(); e != nil; e = e.Next() {
		ee := e.Value.(SesElem)
		switch ee.t {
		case SesDelete:
			fmt.Printf("- %c\n", ee.c)
		case SesAdd:
			fmt.Printf("+ %c\n", ee.c)
		case SesCommon:
			fmt.Printf("  %c\n", ee.c)
		}
	}
}

// Compose composes diff between a and b
func (diff *Diff) Compose() {
	fp := make([]int, diff.m+diff.n+3)
	diff.meta.path = make([]int, diff.m+diff.n+3)
	diff.meta.pathCordinate = make([]PointCordinate, 0)
	diff.lcs = list.New()
	diff.ses = list.New()

	for i := range fp {
		fp[i] = -1
		diff.meta.path[i] = -1
	}

	offset := diff.m + 1
	delta := diff.n - diff.m
	for p := 0; ; p++ {

		for k := -p; k <= delta-1; k++ {
			fp[k+offset] = diff.snake(k, fp[k-1+offset]+1, fp[k+1+offset], offset)
		}

		for k := delta + p; k >= delta+1; k-- {
			fp[k+offset] = diff.snake(k, fp[k-1+offset]+1, fp[k+1+offset], offset)
		}

		fp[delta+offset] = diff.snake(delta, fp[delta-1+offset]+1, fp[delta+1+offset], offset)

		if fp[delta+offset] >= diff.n {
			diff.ed = delta + 2*p
			break
		}
	}

	if diff.meta.onlyEd {
		return
	}

	r := diff.meta.path[delta+offset]
	epc := make([]Point, 0)
	for r != -1 {
		epc = append(epc, Point{x: diff.meta.pathCordinate[r].x, y: diff.meta.pathCordinate[r].y})
		r = diff.meta.pathCordinate[r].r
	}
	diff.recordSeq(epc)
}

func (diff *Diff) snake(k, p, pp, offset int) int {
	r := 0
	if p > pp {
		r = diff.meta.path[k-1+offset]
	} else {
		r = diff.meta.path[k+1+offset]
	}

	y := max(p, pp)
	x := y - k

	for x < diff.m && y < diff.n && diff.a[x] == diff.b[y] {
		x++
		y++
	}

	if !diff.meta.onlyEd {
		diff.meta.path[k+offset] = len(diff.meta.pathCordinate)
		diff.meta.pathCordinate = append(diff.meta.pathCordinate, PointCordinate{x, y, r})
	}

	return y
}

func (diff *Diff) recordSeq(epc []Point) {
	xIdx, yIdx := 1, 1
	pxIdx, pyIdx := 0, 0
	for i := len(epc) - 1; i >= 0; i-- {
		for (pxIdx < epc[i].x) || (pyIdx < epc[i].y) {
			if (epc[i].y - epc[i].x) > (pyIdx - pxIdx) {
				elem := diff.b[pyIdx]
				t := SesAdd
				if diff.meta.reverse {
					t = SesDelete
				}
				diff.ses.PushBack(SesElem{c: elem, t: t})
				yIdx++
				pyIdx++
			} else if epc[i].y-epc[i].x < pyIdx-pxIdx {
				elem := diff.a[pxIdx]
				t := SesDelete
				if diff.meta.reverse {
					t = SesAdd
				}
				diff.ses.PushBack(SesElem{c: elem, t: t})
				xIdx++
				pxIdx++
			} else {
				elem := diff.a[pxIdx]
				diff.lcs.PushBack(elem)
				diff.ses.PushBack(SesElem{c: elem, t: SesCommon})
				xIdx++
				yIdx++
				pxIdx++
				pyIdx++
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import "fmt"
import "math/rand"
import "strings"
import "errors"
import "strconv"

type Grid struct {
	Points     []Point
	TargetX    int
	TargetY    int
	GuessCount int
}

func randInt(min int, max int) int {
	return min + rand.Intn(max-min)
}

func (g *Grid) IsPointTarget(x int, y int) bool {
	result := false

	for _, point := range g.Points {
		if point.x == x && point.y == y {
			point.explored = true
			if point.target {
				result = true
				break
			}
		}
	}
	return result
}

func (g *Grid) ProcessGuess(raw_guess string) (GuessResult, error) {
	result := GuessResult{}
	err_msg := "Guess must be in the format #,#.  Example:  5.5"
	guess_x := 0
	guess_y := 0
	err_x := errors.New("")
	err_y := errors.New("")
	parts := strings.Split(raw_guess, ",")
	g.GuessCount += 1
	_ = guess_y
	if len(parts) != 2 {
		err := errors.New(err_msg)
		return result, err
	}
	if guess_x, err_x = strconv.Atoi(parts[0]); err_x != nil {
		err := errors.New(err_msg)
		return result, err
	}

	if guess_y, err_y = strconv.Atoi(parts[1]); err_y != nil {
		err := errors.New(err_msg)
		return result, err
	}

	if guess_x > g.TargetX {
		result.HorizontalPosition = cWest
	}
	if guess_x < g.TargetX {
		result.HorizontalPosition = cEast
	}
	if guess_x == g.TargetX {
		result.HorizontalPosition = cFound
	}

	if guess_y > g.TargetY {
		result.VerticalPosition = cNorth
	}
	if guess_y < g.TargetY {
		result.VerticalPosition = cSouth
	}
	if guess_y == g.TargetY {
		result.VerticalPosition = cFound
	}
	result.GuessCount = g.GuessCount
	return result, nil
}

func (g *Grid) Build() {
	target_x := randInt(1, *width)
	target_y := randInt(1, *height)

	g.TargetX = target_x
	g.TargetY = target_y

	set_x := 1
	set_y := 1
	for set_x <= *width {
		for set_y <= *height {
			tempPoint := Point{}
			tempPoint.x = set_x
			tempPoint.y = set_y
			set_y += 1

			if tempPoint.x == target_x && tempPoint.y == target_y {
				tempPoint.target = true
			}

			g.Points = append(g.Points, tempPoint)
		}
		set_x += 1
		set_y = 1
	}

	gresult := GuessResult{}
	gresult.VerticalPosition = cNorth
}
<commit_msg>removed fmt reference<commit_after>package main

import "math/rand"
import "strings"
import "errors"
import "strconv"

type Grid struct {
	Points     []Point
	TargetX    int
	TargetY    int
	GuessCount int
}

func randInt(min int, max int) int {
	return min + rand.Intn(max-min)
}

func (g *Grid) IsPointTarget(x int, y int) bool {
	result := false

	for _, point := range g.Points {
		if point.x == x && point.y == y {
			point.explored = true
			if point.target {
				result = true
				break
			}
		}
	}
	return result
}

func (g *Grid) ProcessGuess(raw_guess string) (GuessResult, error) {
	result := GuessResult{}
	err_msg := "Guess must be in the format #,#.  Example:  5.5"
	guess_x := 0
	guess_y := 0
	err_x := errors.New("")
	err_y := errors.New("")
	parts := strings.Split(raw_guess, ",")
	g.GuessCount += 1
	_ = guess_y
	if len(parts) != 2 {
		err := errors.New(err_msg)
		return result, err
	}
	if guess_x, err_x = strconv.Atoi(parts[0]); err_x != nil {
		err := errors.New(err_msg)
		return result, err
	}

	if guess_y, err_y = strconv.Atoi(parts[1]); err_y != nil {
		err := errors.New(err_msg)
		return result, err
	}

	if guess_x > g.TargetX {
		result.HorizontalPosition = cWest
	}
	if guess_x < g.TargetX {
		result.HorizontalPosition = cEast
	}
	if guess_x == g.TargetX {
		result.HorizontalPosition = cFound
	}

	if guess_y > g.TargetY {
		result.VerticalPosition = cNorth
	}
	if guess_y < g.TargetY {
		result.VerticalPosition = cSouth
	}
	if guess_y == g.TargetY {
		result.VerticalPosition = cFound
	}
	result.GuessCount = g.GuessCount
	return result, nil
}

func (g *Grid) Build() {
	target_x := randInt(1, *width)
	target_y := randInt(1, *height)

	g.TargetX = target_x
	g.TargetY = target_y

	set_x := 1
	set_y := 1
	for set_x <= *width {
		for set_y <= *height {
			tempPoint := Point{}
			tempPoint.x = set_x
			tempPoint.y = set_y
			set_y += 1

			if tempPoint.x == target_x && tempPoint.y == target_y {
				tempPoint.target = true
			}

			g.Points = append(g.Points, tempPoint)
		}
		set_x += 1
		set_y = 1
	}

	gresult := GuessResult{}
	gresult.VerticalPosition = cNorth
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/jmckaskill/gospdy"
	"io/ioutil"
	"log"
	"net/http"
	"runtime"
	"time"
)

import _ "crypto/sha512"

var VERBOSE = 0

// Connect loop
func connect(start, done chan bool, url string) {

	for {
		<-start

		tr := &spdy.Transport{}
		tr.FallbackClient = &http.Client{}
		client := &http.Client{Transport: tr}

		r, err := client.Get(url)

		if err != nil {
			log.Fatal(err)
			done <- true
			return
		}

		defer r.Body.Close()

		// Print the body
		if VERBOSE > 0 {

			body, err := ioutil.ReadAll(r.Body)

			if err != nil {
				log.Fatal(err)
				done <- true
				return
			}

			fmt.Printf(string(body))
		}

		done <- true
	}
}

// Goroutine to keep consuming <-done
func requests_done(done, end chan bool, n, c int) {

	start_time := time.Now().UnixNano()
	last_now := start_time

	for i := 0; i < n; i++ {
		<-done
		if i%c == 0 && i != 0 {
			now := time.Now().UnixNano()
			interval := (now - last_now) / 1e6
			fmt.Printf("%d reqs done. + %d msecs\n", i, interval)
			last_now = now
		}
	}

	interval := (time.Now().UnixNano() - last_now) / 1e6
	fmt.Printf("%d reqs done. + %d msecs\n", n, interval)

	total_time := time.Now().UnixNano() - start_time
	fmt.Printf("\ntotal time: %f secs\n", float64(total_time)/1e9)

	end <- true
}

func main() {

	url := flag.String("url", "", "url to connect to: 'https://www.google.com'")
	n := flag.Int("n", 1, "total number of requests")
	c := flag.Int("c", 1, "number of parallel requests")

	verbose := flag.Int("v", 0, "verbosity")

	flag.Parse()

	VERBOSE = *verbose

	// Enable debug in gospdy code
	if VERBOSE > 2 {
		spdy.Log = func(format string, args ...interface{}) {
			fmt.Printf(format, args...)
		}
	}

	start := make(chan bool, *n)
	done := make(chan bool, *n)

	end := make(chan bool, *n)

	// just one request
	if *n == 1 && *c == 1 {

		if VERBOSE == 0 {
			VERBOSE = 1
		}

		go connect(start, done, *url)
		start <- true
		<-done

		return
	}

	// Multiple Requests

	// Setting Max Procs to the Number of CPU Cores
	fmt.Printf("Max procs %d\n", runtime.GOMAXPROCS(runtime.NumCPU()))
	fmt.Printf("Max procs %d\n\n", runtime.GOMAXPROCS(0))

	go requests_done(done, end, *n, *c)

	for i := 0; i < *c; i++ {
		go connect(start, done, *url)
		// start some goroutines immediately
		start <- true
	}

	for i := *c; i < *n; i++ {
		// fill in the chan so everybody can work
		start <- true
	}

	// wait for all the requests to be terminated
	<-end
}
<commit_msg>close http response.Body appropriately to stop memory leaks from file descriptor exhaustion<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/jmckaskill/gospdy"
	"io/ioutil"
	"log"
	"net/http"
	"runtime"
	"time"
)

import _ "crypto/sha512"

var VERBOSE = 0

// Connect loop
func connect(start, done chan bool, url string) {

	for {
		<-start

		tr := &spdy.Transport{}
		tr.FallbackClient = &http.Client{}
		client := &http.Client{Transport: tr}

		r, err := client.Get(url)

		if err != nil {
			log.Fatal(err)
			done <- true
			return
		}

		// Print the body
		if VERBOSE > 0 {

			body, err := ioutil.ReadAll(r.Body)

			if err != nil {
				log.Fatal(err)
				done <- true
				return
			}

			fmt.Printf(string(body))
		}
		r.Body.Close()

		done <- true
	}
}

// Goroutine to keep consuming <-done
func requests_done(done, end chan bool, n, c int) {

	start_time := time.Now().UnixNano()
	last_now := start_time

	for i := 0; i < n; i++ {
		<-done
		if i%c == 0 && i != 0 {
			now := time.Now().UnixNano()
			interval := (now - last_now) / 1e6
			fmt.Printf("%d reqs done. + %d msecs\n", i, interval)
			last_now = now
		}
	}

	interval := (time.Now().UnixNano() - last_now) / 1e6
	fmt.Printf("%d reqs done. + %d msecs\n", n, interval)

	total_time := time.Now().UnixNano() - start_time
	fmt.Printf("\ntotal time: %f secs\n", float64(total_time)/1e9)

	end <- true
}

func main() {

	url := flag.String("url", "", "url to connect to: 'https://www.google.com'")
	n := flag.Int("n", 1, "total number of requests")
	c := flag.Int("c", 1, "number of parallel requests")

	verbose := flag.Int("v", 0, "verbosity")

	flag.Parse()

	VERBOSE = *verbose

	// Enable debug in gospdy code
	if VERBOSE > 2 {
		spdy.Log = func(format string, args ...interface{}) {
			fmt.Printf(format, args...)
		}
	}

	start := make(chan bool, *n)
	done := make(chan bool, *n)

	end := make(chan bool, *n)

	// just one request
	if *n == 1 && *c == 1 {

		if VERBOSE == 0 {
			VERBOSE = 1
		}

		go connect(start, done, *url)
		start <- true
		<-done

		return
	}

	// Multiple Requests

	// Setting Max Procs to the Number of CPU Cores
	fmt.Printf("Max procs %d\n", runtime.GOMAXPROCS(runtime.NumCPU()))
	fmt.Printf("Max procs %d\n\n", runtime.GOMAXPROCS(0))

	go requests_done(done, end, *n, *c)

	for i := 0; i < *c; i++ {
		go connect(start, done, *url)
		// start some goroutines immediately
		start <- true
	}

	for i := *c; i < *n; i++ {
		// fill in the chan so everybody can work
		start <- true
	}

	// wait for all the requests to be terminated
	<-end
}
<|endoftext|>


<commit_before>package dhcp6

import (
	"encoding/binary"
	"errors"
	"net"
	"time"
)

const (
	// ethernet10Mb is the default IANA hardware type used in DUID generation,
	// if a hardware type cannot be parsed from a network interface.
	ethernet10Mb uint16 = 1
)

var (
	// errInvalidDUID is returned when not enough bytes are present
	// to parse a valid DUID from a byte slice.
	errInvalidDUID = errors.New("not enough bytes for valid DUID")

	// errInvalidDUIDLLT is returned when not enough bytes are present
	// to parse a valid DUIDLLT from a byte slice, or when the DUID type
	// found in the byte slice is incorrect.
	errInvalidDUIDLLT = errors.New("invalid DUID-LLT")

	// errInvalidDUIDEN is returned when not enough bytes are present
	// to parse a valid DUIDEN from a byte slice, or when the DUID type
	// found in the byte slice is incorrect.
	errInvalidDUIDEN = errors.New("invalid DUID-EN")

	// errInvalidDUIDLL is returned when not enough bytes are present
	// to parse a valid DUIDLL from a byte slice, or when the DUID type
	// found in the byte slice is incorrect.
	errInvalidDUIDLL = errors.New("invalid DUID-LL")

	// errUnknownDUID is returned when an unknown DUID type is
	// encountered, and thus, a DUID cannot be parsed.
	errUnknownDUID = errors.New("unknown DUID type")
)

var (
	// duidLLTTime is the date specified in RFC 3315, Section 9.2, for use
	// with DUIT-LLT generation.  It is used to calculate a duration from an
	// input time after this date.  Dates before this time are not valid for
	// creation of DUIDLLT values.
	duidLLTTime = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)
)

// DUIDType is a type of DHCP Unique Identifier, as defined in RFC
// 3315, Section 9.  DUIDs are used to uniquely identify a client to a
// server, or vice-versa.
type DUIDType uint16

// DUIDType constants which indicate DUID-LLT, DUID-EN, or DUID-LL.
// Additional DUID types are defined in IANA's DHCPv6 parameters registry:
// http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml.
const (
	DUIDTypeLLT DUIDType = 1
	DUIDTypeEN  DUIDType = 2
	DUIDTypeLL  DUIDType = 3

	// BUG(mdlayher): add additional DUID types defined by IANA
)

// DUID represents a DHCP Unique Identifier, as defined in RFC
// 3315, Section 9.  A DUID is used by a DHCP server to identify
// unique clients.  A DUID can also be used by a DHCP client to identify
// a unique server, when needed.
//
// The DUID interface represents a generic DUID, but DUIDs can be
// type-asserted to one of three specific types outlined in RFC 3315,
// Section 9.2, 9.3, and 9.4:
//   - DUIDLLT - DUID Based on Link-layer Address Plus Time
//   -  DUIDEN - DUID Assigned by Vendor Based on Enterprise Number
//   -  DUIDLL - DUID Based on Link-layer Address
//
// If further introspection of the DUID is needed, a type switch is
// recommended:
//	switch d := duid.(type) {
//	case *dhcp6.DUIDLLT:
//		fmt.Println(d.Time)
//	case *dhcp6.DUIDEN:
//		fmt.Println(d.EnterpriseNumber)
//	case *dhcp6.DUIDLL:
//		fmt.Println(d.HardwareAddr)
//	}
type DUID Byteser

// DUIDLLT represents a DUID Based on Link-layer Address Plus Time [DUID-LLT],
// as defined in RFC 3315, Section 9.2.
//
// This DUID type must only be used with clients and servers with stable,
// persistent storage.  It is the recommended DUID type for all general
// purpose computing devices.
type DUIDLLT struct {
	// Type specifies the DUID type.  For a DUIDLLT, this should always be
	// DUIDTypeLLT.
	Type DUIDType

	// HardwareType specifies an IANA-assigned hardware type, as described
	// in RFC 826.
	HardwareType uint16

	// Time specifies the duration of the time this DUID was generated, minus
	// midnight (UTC), January 1, 2000.
	Time time.Duration

	// HardwareAddr specifies the hardware address for an arbitrary link-layer
	// interface on a device, used in generating the DUIDLLT.  This value
	// could represent any arbitrary interface on a system, and should not be
	// treated as a client or server's communicating hardware address.
	HardwareAddr net.HardwareAddr
}

// NewDUIDLLT generates a new DUIDLLT from an input IANA-assigned hardware
// type, time value, and a hardware address.
//
// The time value must be greater than midnight (UTC), January 1, 2000.
func NewDUIDLLT(hardwareType uint16, time time.Time, hardwareAddr net.HardwareAddr) (*DUIDLLT, error) {
	// Do not accept dates before duidLLTTime.
	if time.Before(duidLLTTime) {
		return nil, ErrInvalidDUIDLLTTime
	}

	return &DUIDLLT{
		Type:         DUIDTypeLLT,
		HardwareType: hardwareType,
		Time:         time.Sub(duidLLTTime),
		HardwareAddr: hardwareAddr,
	}, nil
}

// Bytes implements DUID, and allocates a byte slice containing the data
// from a DUIDLLT.
func (d *DUIDLLT) Bytes() []byte {
	// 2 bytes: DUID type
	// 2 bytes: hardware type
	// 4 bytes: time duration
	// N bytes: hardware address
	b := make([]byte, 8+len(d.HardwareAddr))

	binary.BigEndian.PutUint16(b[0:2], uint16(d.Type))
	binary.BigEndian.PutUint16(b[2:4], d.HardwareType)
	binary.BigEndian.PutUint32(b[4:8], uint32(d.Time/time.Second))
	copy(b[8:], d.HardwareAddr)

	return b
}

// parseDUIDLLT parses a raw byte slice into a DUIDLLT.  If the byte slice
// does not contain enough data to form a valid DUIDLLT, or another DUID type
// is indicated, errInvalidDUIDLLT is returned.
func parseDUIDLLT(b []byte) (*DUIDLLT, error) {
	// Too short to be valid DUIDLLT
	if len(b) < 8 {
		return nil, errInvalidDUIDLLT
	}

	// Verify DUID type
	dType := DUIDType(binary.BigEndian.Uint16(b[0:2]))
	if dType != DUIDTypeLLT {
		return nil, errInvalidDUIDLLT
	}

	mac := make(net.HardwareAddr, len(b[8:]))
	copy(mac, b[8:])

	return &DUIDLLT{
		Type:         dType,
		HardwareType: binary.BigEndian.Uint16(b[2:4]),
		Time:         time.Duration(binary.BigEndian.Uint32(b[4:8])) * time.Second,
		HardwareAddr: mac,
	}, nil
}

// DUIDEN represents a DUID Assigned by Vendor Based on Enterprise Number
// [DUID-EN], as defined in RFC 3315, Section 9.3.  This DUID type
// uses an IANA-assigned Private Enterprise Number for a given vendor.
type DUIDEN struct {
	// Type specifies the DUID type.  For a DUIDLLT, this should always be
	// DUIDTypeLLT.
	Type DUIDType

	// EnterpriseNumber specifies an IANA-assigned vendor Private Enterprise
	// Number.
	EnterpriseNumber uint32

	// Identifier specifies a unique identifier of arbitrary length.  This
	// value is typically assigned when a device is manufactured.
	Identifier []byte
}

// NewDUIDEN generates a new DUIDEN from an input IANA-assigned Private
// Enterprise Number and a variable length unique identifier byte slice.
func NewDUIDEN(enterpriseNumber uint32, identifier []byte) *DUIDEN {
	return &DUIDEN{
		Type:             DUIDTypeEN,
		EnterpriseNumber: enterpriseNumber,
		Identifier:       identifier,
	}
}

// Bytes implements DUID, and allocates a byte slice containing the data
// from a DUIDEN.
func (d *DUIDEN) Bytes() []byte {
	// 2 bytes: DUID type
	// 4 bytes: enterprise number
	// N bytes: identifier
	b := make([]byte, 6+len(d.Identifier))

	binary.BigEndian.PutUint16(b[0:2], uint16(d.Type))
	binary.BigEndian.PutUint32(b[2:6], d.EnterpriseNumber)
	copy(b[6:], d.Identifier)

	return b
}

// parseDUIDEN parses a raw byte slice into a DUIDEN.  If the byte slice
// does not contain enough data to form a valid DUIDEN, or another DUID type
// is indicated, errInvalidDUIDEN is returned.
func parseDUIDEN(b []byte) (*DUIDEN, error) {
	// Too short to be valid DUIDEN
	if len(b) < 6 {
		return nil, errInvalidDUIDEN
	}

	// Verify DUID type
	dType := DUIDType(binary.BigEndian.Uint16(b[0:2]))
	if dType != DUIDTypeEN {
		return nil, errInvalidDUIDEN
	}

	id := make([]byte, len(b[6:]))
	copy(id, b[6:])

	return &DUIDEN{
		Type:             dType,
		EnterpriseNumber: binary.BigEndian.Uint32(b[2:6]),
		Identifier:       id,
	}, nil
}

// DUIDLL represents a DUID Based on Link-layer Address [DUID-LL],
// as defined in RFC 3315, Section 9.4.
//
// This DUID type is recommended for devices with a
// permanently-connected network interface, but without stable,
// persistent storage.
//
// DUIDLL values are generated automatically for Servers which are not
// created with a ServerID, using the hardware type found by HardwareType
// and the hardware address of the listening network interface.
type DUIDLL struct {
	// Type specifies the DUID type.  For a DUIDLL, this should always be
	// DUIDTypeLL.
	Type DUIDType

	// HardwareType specifies an IANA-assigned hardware type, as described
	// in RFC 826.
	HardwareType uint16

	// HardwareAddr specifies the hardware address for an arbitrary link-layer
	// interface on a device, used in generating the DUIDLL.  This value
	// could represent any arbitrary interface on a system, and should not be
	// treated as a client or server's communicating hardware address.
	HardwareAddr net.HardwareAddr
}

// NewDUIDLL generates a new DUIDLL from an input IANA-assigned hardware
// type and a hardware address.
func NewDUIDLL(hardwareType uint16, hardwareAddr net.HardwareAddr) *DUIDLL {
	return &DUIDLL{
		Type:         DUIDTypeLL,
		HardwareType: hardwareType,
		HardwareAddr: hardwareAddr,
	}
}

// Bytes implements DUID, and allocates a byte slice containing the data
// from a DUIDLL.
func (d *DUIDLL) Bytes() []byte {
	// 2 bytes: DUID type
	// 2 bytes: hardware type
	// N bytes: hardware address
	b := make([]byte, 4+len(d.HardwareAddr))

	binary.BigEndian.PutUint16(b[0:2], uint16(d.Type))
	binary.BigEndian.PutUint16(b[2:4], d.HardwareType)
	copy(b[4:], d.HardwareAddr)

	return b
}

// parseDUIDLL parses a raw byte slice into a DUIDLL.  If the byte slice
// does not contain enough data to form a valid DUIDLL, or another DUID type
// is indicated, errInvalidDUIDLL is returned.
func parseDUIDLL(b []byte) (*DUIDLL, error) {
	// Too short to be DUIDLL
	if len(b) < 4 {
		return nil, errInvalidDUIDLL
	}

	// Verify DUID type
	dType := DUIDType(binary.BigEndian.Uint16(b[0:2]))
	if dType != DUIDTypeLL {
		return nil, errInvalidDUIDLL
	}

	mac := make(net.HardwareAddr, len(b[4:]))
	copy(mac, b[4:])

	return &DUIDLL{
		Type:         dType,
		HardwareType: binary.BigEndian.Uint16(b[2:4]),
		HardwareAddr: mac,
	}, nil
}

// parseDUID returns the correct DUID type of the input byte slice as a
// DUID interface type.
func parseDUID(d []byte) (DUID, error) {
	// DUID must have enough bytes to determine its type
	if len(d) < 2 {
		return nil, errInvalidDUID
	}

	// BUG(mdlayher): add DUID-UUID to this in the future.
	switch DUIDType(binary.BigEndian.Uint16(d[0:2])) {
	case DUIDTypeLLT:
		return parseDUIDLLT(d)
	case DUIDTypeEN:
		return parseDUIDEN(d)
	case DUIDTypeLL:
		return parseDUIDLL(d)
	}

	return nil, errUnknownDUID
}

// interfaceDUID generates a DUIDLL for an input net.Interface, using its
// IANA-assigned hardware type and its hardware address.
func interfaceDUID(ifi *net.Interface) (DUID, error) {
	// Attempt to check for IANA hardware type, default to Ethernet (10Mb)
	// on failure (this relies on syscalls which only work on Linux)
	// Hardware types can be found here:
	// http://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml.
	htype, err := HardwareType(ifi)
	if err != nil {
		// Return syscall errors
		if err != ErrParseHardwareType && err != ErrHardwareTypeNotImplemented {
			return nil, err
		}

		// Use default value if hardware type can't be found or
		// detection isn't implemented
		htype = ethernet10Mb
	}

	return NewDUIDLL(htype, ifi.HardwareAddr), nil
}
<commit_msg>duid: fix DUIDEN.Type documentation<commit_after>package dhcp6

import (
	"encoding/binary"
	"errors"
	"net"
	"time"
)

const (
	// ethernet10Mb is the default IANA hardware type used in DUID generation,
	// if a hardware type cannot be parsed from a network interface.
	ethernet10Mb uint16 = 1
)

var (
	// errInvalidDUID is returned when not enough bytes are present
	// to parse a valid DUID from a byte slice.
	errInvalidDUID = errors.New("not enough bytes for valid DUID")

	// errInvalidDUIDLLT is returned when not enough bytes are present
	// to parse a valid DUIDLLT from a byte slice, or when the DUID type
	// found in the byte slice is incorrect.
	errInvalidDUIDLLT = errors.New("invalid DUID-LLT")

	// errInvalidDUIDEN is returned when not enough bytes are present
	// to parse a valid DUIDEN from a byte slice, or when the DUID type
	// found in the byte slice is incorrect.
	errInvalidDUIDEN = errors.New("invalid DUID-EN")

	// errInvalidDUIDLL is returned when not enough bytes are present
	// to parse a valid DUIDLL from a byte slice, or when the DUID type
	// found in the byte slice is incorrect.
	errInvalidDUIDLL = errors.New("invalid DUID-LL")

	// errUnknownDUID is returned when an unknown DUID type is
	// encountered, and thus, a DUID cannot be parsed.
	errUnknownDUID = errors.New("unknown DUID type")
)

var (
	// duidLLTTime is the date specified in RFC 3315, Section 9.2, for use
	// with DUIT-LLT generation.  It is used to calculate a duration from an
	// input time after this date.  Dates before this time are not valid for
	// creation of DUIDLLT values.
	duidLLTTime = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)
)

// DUIDType is a type of DHCP Unique Identifier, as defined in RFC
// 3315, Section 9.  DUIDs are used to uniquely identify a client to a
// server, or vice-versa.
type DUIDType uint16

// DUIDType constants which indicate DUID-LLT, DUID-EN, or DUID-LL.
// Additional DUID types are defined in IANA's DHCPv6 parameters registry:
// http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml.
const (
	DUIDTypeLLT DUIDType = 1
	DUIDTypeEN  DUIDType = 2
	DUIDTypeLL  DUIDType = 3

	// BUG(mdlayher): add additional DUID types defined by IANA
)

// DUID represents a DHCP Unique Identifier, as defined in RFC
// 3315, Section 9.  A DUID is used by a DHCP server to identify
// unique clients.  A DUID can also be used by a DHCP client to identify
// a unique server, when needed.
//
// The DUID interface represents a generic DUID, but DUIDs can be
// type-asserted to one of three specific types outlined in RFC 3315,
// Section 9.2, 9.3, and 9.4:
//   - DUIDLLT - DUID Based on Link-layer Address Plus Time
//   -  DUIDEN - DUID Assigned by Vendor Based on Enterprise Number
//   -  DUIDLL - DUID Based on Link-layer Address
//
// If further introspection of the DUID is needed, a type switch is
// recommended:
//	switch d := duid.(type) {
//	case *dhcp6.DUIDLLT:
//		fmt.Println(d.Time)
//	case *dhcp6.DUIDEN:
//		fmt.Println(d.EnterpriseNumber)
//	case *dhcp6.DUIDLL:
//		fmt.Println(d.HardwareAddr)
//	}
type DUID Byteser

// DUIDLLT represents a DUID Based on Link-layer Address Plus Time [DUID-LLT],
// as defined in RFC 3315, Section 9.2.
//
// This DUID type must only be used with clients and servers with stable,
// persistent storage.  It is the recommended DUID type for all general
// purpose computing devices.
type DUIDLLT struct {
	// Type specifies the DUID type.  For a DUIDLLT, this should always be
	// DUIDTypeLLT.
	Type DUIDType

	// HardwareType specifies an IANA-assigned hardware type, as described
	// in RFC 826.
	HardwareType uint16

	// Time specifies the duration of the time this DUID was generated, minus
	// midnight (UTC), January 1, 2000.
	Time time.Duration

	// HardwareAddr specifies the hardware address for an arbitrary link-layer
	// interface on a device, used in generating the DUIDLLT.  This value
	// could represent any arbitrary interface on a system, and should not be
	// treated as a client or server's communicating hardware address.
	HardwareAddr net.HardwareAddr
}

// NewDUIDLLT generates a new DUIDLLT from an input IANA-assigned hardware
// type, time value, and a hardware address.
//
// The time value must be greater than midnight (UTC), January 1, 2000.
func NewDUIDLLT(hardwareType uint16, time time.Time, hardwareAddr net.HardwareAddr) (*DUIDLLT, error) {
	// Do not accept dates before duidLLTTime.
	if time.Before(duidLLTTime) {
		return nil, ErrInvalidDUIDLLTTime
	}

	return &DUIDLLT{
		Type:         DUIDTypeLLT,
		HardwareType: hardwareType,
		Time:         time.Sub(duidLLTTime),
		HardwareAddr: hardwareAddr,
	}, nil
}

// Bytes implements DUID, and allocates a byte slice containing the data
// from a DUIDLLT.
func (d *DUIDLLT) Bytes() []byte {
	// 2 bytes: DUID type
	// 2 bytes: hardware type
	// 4 bytes: time duration
	// N bytes: hardware address
	b := make([]byte, 8+len(d.HardwareAddr))

	binary.BigEndian.PutUint16(b[0:2], uint16(d.Type))
	binary.BigEndian.PutUint16(b[2:4], d.HardwareType)
	binary.BigEndian.PutUint32(b[4:8], uint32(d.Time/time.Second))
	copy(b[8:], d.HardwareAddr)

	return b
}

// parseDUIDLLT parses a raw byte slice into a DUIDLLT.  If the byte slice
// does not contain enough data to form a valid DUIDLLT, or another DUID type
// is indicated, errInvalidDUIDLLT is returned.
func parseDUIDLLT(b []byte) (*DUIDLLT, error) {
	// Too short to be valid DUIDLLT
	if len(b) < 8 {
		return nil, errInvalidDUIDLLT
	}

	// Verify DUID type
	dType := DUIDType(binary.BigEndian.Uint16(b[0:2]))
	if dType != DUIDTypeLLT {
		return nil, errInvalidDUIDLLT
	}

	mac := make(net.HardwareAddr, len(b[8:]))
	copy(mac, b[8:])

	return &DUIDLLT{
		Type:         dType,
		HardwareType: binary.BigEndian.Uint16(b[2:4]),
		Time:         time.Duration(binary.BigEndian.Uint32(b[4:8])) * time.Second,
		HardwareAddr: mac,
	}, nil
}

// DUIDEN represents a DUID Assigned by Vendor Based on Enterprise Number
// [DUID-EN], as defined in RFC 3315, Section 9.3.  This DUID type
// uses an IANA-assigned Private Enterprise Number for a given vendor.
type DUIDEN struct {
	// Type specifies the DUID type.  For a DUIDEN, this should always be
	// DUIDTypeEN.
	Type DUIDType

	// EnterpriseNumber specifies an IANA-assigned vendor Private Enterprise
	// Number.
	EnterpriseNumber uint32

	// Identifier specifies a unique identifier of arbitrary length.  This
	// value is typically assigned when a device is manufactured.
	Identifier []byte
}

// NewDUIDEN generates a new DUIDEN from an input IANA-assigned Private
// Enterprise Number and a variable length unique identifier byte slice.
func NewDUIDEN(enterpriseNumber uint32, identifier []byte) *DUIDEN {
	return &DUIDEN{
		Type:             DUIDTypeEN,
		EnterpriseNumber: enterpriseNumber,
		Identifier:       identifier,
	}
}

// Bytes implements DUID, and allocates a byte slice containing the data
// from a DUIDEN.
func (d *DUIDEN) Bytes() []byte {
	// 2 bytes: DUID type
	// 4 bytes: enterprise number
	// N bytes: identifier
	b := make([]byte, 6+len(d.Identifier))

	binary.BigEndian.PutUint16(b[0:2], uint16(d.Type))
	binary.BigEndian.PutUint32(b[2:6], d.EnterpriseNumber)
	copy(b[6:], d.Identifier)

	return b
}

// parseDUIDEN parses a raw byte slice into a DUIDEN.  If the byte slice
// does not contain enough data to form a valid DUIDEN, or another DUID type
// is indicated, errInvalidDUIDEN is returned.
func parseDUIDEN(b []byte) (*DUIDEN, error) {
	// Too short to be valid DUIDEN
	if len(b) < 6 {
		return nil, errInvalidDUIDEN
	}

	// Verify DUID type
	dType := DUIDType(binary.BigEndian.Uint16(b[0:2]))
	if dType != DUIDTypeEN {
		return nil, errInvalidDUIDEN
	}

	id := make([]byte, len(b[6:]))
	copy(id, b[6:])

	return &DUIDEN{
		Type:             dType,
		EnterpriseNumber: binary.BigEndian.Uint32(b[2:6]),
		Identifier:       id,
	}, nil
}

// DUIDLL represents a DUID Based on Link-layer Address [DUID-LL],
// as defined in RFC 3315, Section 9.4.
//
// This DUID type is recommended for devices with a
// permanently-connected network interface, but without stable,
// persistent storage.
//
// DUIDLL values are generated automatically for Servers which are not
// created with a ServerID, using the hardware type found by HardwareType
// and the hardware address of the listening network interface.
type DUIDLL struct {
	// Type specifies the DUID type.  For a DUIDLL, this should always be
	// DUIDTypeLL.
	Type DUIDType

	// HardwareType specifies an IANA-assigned hardware type, as described
	// in RFC 826.
	HardwareType uint16

	// HardwareAddr specifies the hardware address for an arbitrary link-layer
	// interface on a device, used in generating the DUIDLL.  This value
	// could represent any arbitrary interface on a system, and should not be
	// treated as a client or server's communicating hardware address.
	HardwareAddr net.HardwareAddr
}

// NewDUIDLL generates a new DUIDLL from an input IANA-assigned hardware
// type and a hardware address.
func NewDUIDLL(hardwareType uint16, hardwareAddr net.HardwareAddr) *DUIDLL {
	return &DUIDLL{
		Type:         DUIDTypeLL,
		HardwareType: hardwareType,
		HardwareAddr: hardwareAddr,
	}
}

// Bytes implements DUID, and allocates a byte slice containing the data
// from a DUIDLL.
func (d *DUIDLL) Bytes() []byte {
	// 2 bytes: DUID type
	// 2 bytes: hardware type
	// N bytes: hardware address
	b := make([]byte, 4+len(d.HardwareAddr))

	binary.BigEndian.PutUint16(b[0:2], uint16(d.Type))
	binary.BigEndian.PutUint16(b[2:4], d.HardwareType)
	copy(b[4:], d.HardwareAddr)

	return b
}

// parseDUIDLL parses a raw byte slice into a DUIDLL.  If the byte slice
// does not contain enough data to form a valid DUIDLL, or another DUID type
// is indicated, errInvalidDUIDLL is returned.
func parseDUIDLL(b []byte) (*DUIDLL, error) {
	// Too short to be DUIDLL
	if len(b) < 4 {
		return nil, errInvalidDUIDLL
	}

	// Verify DUID type
	dType := DUIDType(binary.BigEndian.Uint16(b[0:2]))
	if dType != DUIDTypeLL {
		return nil, errInvalidDUIDLL
	}

	mac := make(net.HardwareAddr, len(b[4:]))
	copy(mac, b[4:])

	return &DUIDLL{
		Type:         dType,
		HardwareType: binary.BigEndian.Uint16(b[2:4]),
		HardwareAddr: mac,
	}, nil
}

// parseDUID returns the correct DUID type of the input byte slice as a
// DUID interface type.
func parseDUID(d []byte) (DUID, error) {
	// DUID must have enough bytes to determine its type
	if len(d) < 2 {
		return nil, errInvalidDUID
	}

	// BUG(mdlayher): add DUID-UUID to this in the future.
	switch DUIDType(binary.BigEndian.Uint16(d[0:2])) {
	case DUIDTypeLLT:
		return parseDUIDLLT(d)
	case DUIDTypeEN:
		return parseDUIDEN(d)
	case DUIDTypeLL:
		return parseDUIDLL(d)
	}

	return nil, errUnknownDUID
}

// interfaceDUID generates a DUIDLL for an input net.Interface, using its
// IANA-assigned hardware type and its hardware address.
func interfaceDUID(ifi *net.Interface) (DUID, error) {
	// Attempt to check for IANA hardware type, default to Ethernet (10Mb)
	// on failure (this relies on syscalls which only work on Linux)
	// Hardware types can be found here:
	// http://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml.
	htype, err := HardwareType(ifi)
	if err != nil {
		// Return syscall errors
		if err != ErrParseHardwareType && err != ErrHardwareTypeNotImplemented {
			return nil, err
		}

		// Use default value if hardware type can't be found or
		// detection isn't implemented
		htype = ethernet10Mb
	}

	return NewDUIDLL(htype, ifi.HardwareAddr), nil
}
<|endoftext|>


<commit_before>package gofakes3

import (
	"bytes"
	"crypto/md5"
	"encoding/base64"
	"fmt"
	"hash"
	"io"
)

// hashingReader proxies an existing io.Reader, passing each read block to the
// given hash.Hash.
//
// If the expected hash is not empty, once the underlying reader returns EOF,
// the hash is checked.
type hashingReader struct {
	inner    io.Reader
	expected []byte
	hash     hash.Hash
	sum      []byte
}

func newHashingReader(inner io.Reader, expectedMD5Base64 string) (*hashingReader, error) {
	var md5Bytes []byte
	var err error

	if expectedMD5Base64 != "" {
		md5Bytes, err = base64.StdEncoding.DecodeString(expectedMD5Base64)
		if err != nil {
			return nil, ErrInvalidDigest
		}
	}

	return &hashingReader{
		inner:    inner,
		expected: md5Bytes,
		hash:     md5.New(),
	}, nil
}

// Sum returns the hash of the data read from the inner reader so far.
// If into is passed, it may be used if the hash needs to be computed.
func (h *hashingReader) Sum(into []byte) []byte {
	if h.sum != nil {
		return h.sum
	}
	return h.hash.Sum(into)
}

func (h *hashingReader) Read(p []byte) (n int, err error) {
	n, err = h.inner.Read(p)

	if n != 0 {
		wn, _ := h.hash.Write(p[:n]) // Hash.Write never returns an error.
		if wn != n {
			return n, fmt.Errorf("short write to hasher")
		}
	}

	if err != nil {
		if err == io.EOF {
			h.sum = h.hash.Sum(nil)

			if h.expected != nil && !bytes.Equal(h.sum, h.expected) {
				// FIXME: some more context here would be useful; need to flush out
				// what S3 responds with in this case.
				return n, ErrBadDigest
			}
		}
		return n, err
	}

	return n, nil
}
<commit_msg>Invalid digest check<commit_after>package gofakes3

import (
	"bytes"
	"crypto/md5"
	"encoding/base64"
	"fmt"
	"hash"
	"io"
)

// hashingReader proxies an existing io.Reader, passing each read block to the
// given hash.Hash.
//
// If the expected hash is not empty, once the underlying reader returns EOF,
// the hash is checked.
type hashingReader struct {
	inner    io.Reader
	expected []byte
	hash     hash.Hash
	sum      []byte
}

func newHashingReader(inner io.Reader, expectedMD5Base64 string) (*hashingReader, error) {
	var md5Bytes []byte
	var err error

	if expectedMD5Base64 != "" {
		md5Bytes, err = base64.StdEncoding.DecodeString(expectedMD5Base64)
		if err != nil {
			return nil, ErrInvalidDigest
		}
	}
	if len(md5Bytes) != 16 {
		return nil, ErrInvalidDigest
	}

	return &hashingReader{
		inner:    inner,
		expected: md5Bytes,
		hash:     md5.New(),
	}, nil
}

// Sum returns the hash of the data read from the inner reader so far.
// If into is passed, it may be used if the hash needs to be computed.
func (h *hashingReader) Sum(into []byte) []byte {
	if h.sum != nil {
		return h.sum
	}
	return h.hash.Sum(into)
}

func (h *hashingReader) Read(p []byte) (n int, err error) {
	n, err = h.inner.Read(p)

	if n != 0 {
		wn, _ := h.hash.Write(p[:n]) // Hash.Write never returns an error.
		if wn != n {
			return n, fmt.Errorf("short write to hasher")
		}
	}

	if err != nil {
		if err == io.EOF {
			h.sum = h.hash.Sum(nil)

			if h.expected != nil && !bytes.Equal(h.sum, h.expected) {
				// FIXME: some more context here would be useful; need to flush out
				// what S3 responds with in this case.
				return n, ErrBadDigest
			}
		}
		return n, err
	}

	return n, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os/exec"
	"syscall"

	"github.com/lxc/lxd/shared"
)

// cephOSDPoolExists checks whether a given OSD pool exists.
func cephOSDPoolExists(ClusterName string, poolName string) bool {
	_, err := shared.RunCommand(
		"ceph",
		"--cluster", ClusterName,
		"osd",
		"pool",
		"get",
		poolName,
		"size")
	if err != nil {
		return false
	}

	return true
}

// cephOSDPoolDestroy destroys an OSD pool.
// - A call to cephOSDPoolDestroy will destroy a pool including any storage
//   volumes that still exist in the pool.
// - In case the OSD pool that is supposed to be deleted does not exist this
//   command will still exit 0. This means that if the caller wants to be sure
//   that this call actually deleted an OSD pool it needs to check for the
//   existence of the pool first.
func cephOSDPoolDestroy(clusterName string, poolName string) error {
	_, err := shared.RunCommand("ceph",
		"--cluster", clusterName,
		"osd",
		"pool",
		"delete",
		poolName,
		poolName,
		"--yes-i-really-really-mean-it")
	if err != nil {
		return err
	}

	return nil
}

// getRBDDevPath constructs the path to a RBD block device.
// Note that for this path to be valid the corresponding volume has to be mapped
// first.
func getRBDDevPath(poolName string, volumeType string, volumeName string) string {
	if volumeType == "" {
		return fmt.Sprintf("/dev/rbd/%s/%s", poolName, volumeName)
	}

	return fmt.Sprintf("/dev/rbd/%s/%s_%s", poolName, volumeType, volumeName)
}

// cephRBDVolumeCreate creates an RBD storage volume.
// Note that the set of features is intentionally limited is intentionally
// limited by passing --image-feature explicitly. This is done to ensure that
// the chances of a conflict between the features supported by the userspace
// library and the kernel module are minimized. Otherwise random panics might
// occur.
func cephRBDVolumeCreate(clusterName string, poolName string, volumeName string,
	volumeType string, size string) error {
	_, err := shared.RunCommand(
		"rbd",
		"--image-feature", "layering,",
		"--cluster", clusterName,
		"--pool", poolName,
		"--size", size,
		"create",
		fmt.Sprintf("%s_%s", volumeType, volumeName))
	return err
}

// cephRBDVolumeMap maps a given RBD storage volume
// This will ensure that the RBD storage volume is accessible as a block device
// in the /dev directory and is therefore necessary in order to mount it.
func cephRBDVolumeMap(clusterName string, poolName string, volumeName string,
	volumeType string) error {
	_, err := shared.RunCommand(
		"rbd",
		"--cluster", clusterName,
		"--pool", poolName,
		"map",
		fmt.Sprintf("%s_%s", volumeType, volumeName))
	if err != nil {
		runError, ok := err.(shared.RunError)
		if ok {
			exitError, ok := runError.Err.(*exec.ExitError)
			if ok {
				waitStatus := exitError.Sys().(syscall.WaitStatus)
				if waitStatus.ExitStatus() == 22 {
					// EINVAL (already mapped)
					return nil
				}
			}
		}
		return err
	}

	return nil
}

// cephRBDSnapshotCreate creates a read-write snapshot of a given RBD storage
// volume
func cephRBDSnapshotCreate(clusterName string, poolName string,
	volumeName string, volumeType string, snapshotName string) error {
	_, err := shared.RunCommand(
		"rbd",
		"--cluster", clusterName,
		"--pool", poolName,
		"snap",
		"create",
		"--snap", snapshotName,
		fmt.Sprintf("%s_%s", volumeType, volumeName))
	if err != nil {
		return err
	}

	return nil
}

// cephRBDSnapshotProtect protects a given snapshot from being deleted
// This is a precondition to be able to create RBD clones from a given snapshot.
func cephRBDSnapshotProtect(clusterName string, poolName string,
	volumeName string, volumeType string, snapshotName string) error {
	_, err := shared.RunCommand(
		"rbd",
		"--cluster", clusterName,
		"--pool", poolName,
		"snap",
		"protect",
		"--snap", snapshotName,
		fmt.Sprintf("%s_%s", volumeType, volumeName))
	if err != nil {
		runError, ok := err.(shared.RunError)
		if ok {
			exitError, ok := runError.Err.(*exec.ExitError)
			if ok {
				waitStatus := exitError.Sys().(syscall.WaitStatus)
				if waitStatus.ExitStatus() == 16 {
					// EBUSY (snapshot already protected)
					return nil
				}
			}
		}
		return err
	}

	return nil
}

// cephRBDCloneCreate creates a clone from a protected RBD snapshot
func cephRBDCloneCreate(sourceClusterName string, sourcePoolName string,
	sourceVolumeName string, sourceVolumeType string,
	sourceSnapshotName string, targetPoolName string,
	targetVolumeName string, targetVolumeType string) error {
	_, err := shared.RunCommand(
		"rbd",
		"--cluster", sourceClusterName,
		"clone",
		fmt.Sprintf("%s/%s_%s@%s", sourcePoolName, sourceVolumeType,
			sourceVolumeName, sourceSnapshotName),
		fmt.Sprintf("%s/%s_%s", targetPoolName, targetVolumeType,
			targetVolumeName))
	if err != nil {
		return err
	}

	return nil
}

// getRBDSize returns the size the RBD storage volume is supposed to be created
// with
func (s *storageCeph) getRBDSize() (string, error) {
	sz, err := shared.ParseByteSizeString(s.volume.Config["size"])
	if err != nil {
		return "", err
	}

	// Safety net: Set to default value.
	if sz == 0 {
		sz, _ = shared.ParseByteSizeString("10GB")
	}

	return fmt.Sprintf("%dB", sz), nil
}

// getRBDFilesystem returns the filesystem the RBD storage volume is supposed to
// be created with
func (s *storageCeph) getRBDFilesystem() string {
	if s.volume.Config["block.filesystem"] != "" {
		return s.volume.Config["block.filesystem"]
	}

	if s.pool.Config["volume.block.filesystem"] != "" {
		return s.pool.Config["volume.block.filesystem"]
	}

	return "ext4"
}

// getRBDMountOptions returns the mount options the storage volume is supposed
// to be mounted with
// The option string that is returned needs to be passed to the approriate
// helper (currently named "lxdResolveMountoptions") which will take on the job
// of splitting it into appropriate flags and string options.
func (s *storageCeph) getRBDMountOptions() string {
	if s.volume.Config["block.mount_options"] != "" {
		return s.volume.Config["block.mount_options"]
	}

	if s.pool.Config["volume.block.mount_options"] != "" {
		return s.pool.Config["volume.block.mount_options"]
	}

	return "discard"
}
<commit_msg>ceph: cephRBDVolumeDelete()<commit_after>package main

import (
	"fmt"
	"os/exec"
	"syscall"

	"github.com/lxc/lxd/shared"
)

// cephOSDPoolExists checks whether a given OSD pool exists.
func cephOSDPoolExists(ClusterName string, poolName string) bool {
	_, err := shared.RunCommand(
		"ceph",
		"--cluster", ClusterName,
		"osd",
		"pool",
		"get",
		poolName,
		"size")
	if err != nil {
		return false
	}

	return true
}

// cephOSDPoolDestroy destroys an OSD pool.
// - A call to cephOSDPoolDestroy will destroy a pool including any storage
//   volumes that still exist in the pool.
// - In case the OSD pool that is supposed to be deleted does not exist this
//   command will still exit 0. This means that if the caller wants to be sure
//   that this call actually deleted an OSD pool it needs to check for the
//   existence of the pool first.
func cephOSDPoolDestroy(clusterName string, poolName string) error {
	_, err := shared.RunCommand("ceph",
		"--cluster", clusterName,
		"osd",
		"pool",
		"delete",
		poolName,
		poolName,
		"--yes-i-really-really-mean-it")
	if err != nil {
		return err
	}

	return nil
}

// getRBDDevPath constructs the path to a RBD block device.
// Note that for this path to be valid the corresponding volume has to be mapped
// first.
func getRBDDevPath(poolName string, volumeType string, volumeName string) string {
	if volumeType == "" {
		return fmt.Sprintf("/dev/rbd/%s/%s", poolName, volumeName)
	}

	return fmt.Sprintf("/dev/rbd/%s/%s_%s", poolName, volumeType, volumeName)
}

// cephRBDVolumeCreate creates an RBD storage volume.
// Note that the set of features is intentionally limited is intentionally
// limited by passing --image-feature explicitly. This is done to ensure that
// the chances of a conflict between the features supported by the userspace
// library and the kernel module are minimized. Otherwise random panics might
// occur.
func cephRBDVolumeCreate(clusterName string, poolName string, volumeName string,
	volumeType string, size string) error {
	_, err := shared.RunCommand(
		"rbd",
		"--image-feature", "layering,",
		"--cluster", clusterName,
		"--pool", poolName,
		"--size", size,
		"create",
		fmt.Sprintf("%s_%s", volumeType, volumeName))
	return err
}

// cephRBDVolumeExists checks whether a given RBD storage volume exists.
func cephRBDVolumeExists(clusterName string, poolName string, volumeName string,
	volumeType string) bool {
	_, err := shared.RunCommand(
		"rbd",
		"--cluster", clusterName,
		"--pool", poolName,
		"image-meta",
		"list",
		fmt.Sprintf("%s_%s", volumeType, volumeName))
	if err != nil {
		return false
	}
	return true
}

// cephRBDVolumeDelete deletes an RBD storage volume.
// - In case the RBD storage volume that is supposed to be deleted does not
//   exist this command will still exit 0. This means that if the caller wants
//   to be sure that this call actually deleted an RBD storage volume it needs
//   to check for the existence of the pool first.
func cephRBDVolumeDelete(clusterName string, poolName string, volumeName string,
	volumeType string) error {
	_, err := shared.RunCommand(
		"rbd",
		"--cluster", clusterName,
		"--pool", poolName,
		"rm",
		fmt.Sprintf("%s_%s", volumeType, volumeName))
	if err != nil {
		return err
	}

	return nil
}

// cephRBDVolumeMap maps a given RBD storage volume
// This will ensure that the RBD storage volume is accessible as a block device
// in the /dev directory and is therefore necessary in order to mount it.
func cephRBDVolumeMap(clusterName string, poolName string, volumeName string,
	volumeType string) error {
	_, err := shared.RunCommand(
		"rbd",
		"--cluster", clusterName,
		"--pool", poolName,
		"map",
		fmt.Sprintf("%s_%s", volumeType, volumeName))
	if err != nil {
		runError, ok := err.(shared.RunError)
		if ok {
			exitError, ok := runError.Err.(*exec.ExitError)
			if ok {
				waitStatus := exitError.Sys().(syscall.WaitStatus)
				if waitStatus.ExitStatus() == 22 {
					// EINVAL (already mapped)
					return nil
				}
			}
		}
		return err
	}

	return nil
}

// cephRBDSnapshotCreate creates a read-write snapshot of a given RBD storage
// volume
func cephRBDSnapshotCreate(clusterName string, poolName string,
	volumeName string, volumeType string, snapshotName string) error {
	_, err := shared.RunCommand(
		"rbd",
		"--cluster", clusterName,
		"--pool", poolName,
		"snap",
		"create",
		"--snap", snapshotName,
		fmt.Sprintf("%s_%s", volumeType, volumeName))
	if err != nil {
		return err
	}

	return nil
}

// cephRBDSnapshotProtect protects a given snapshot from being deleted
// This is a precondition to be able to create RBD clones from a given snapshot.
func cephRBDSnapshotProtect(clusterName string, poolName string,
	volumeName string, volumeType string, snapshotName string) error {
	_, err := shared.RunCommand(
		"rbd",
		"--cluster", clusterName,
		"--pool", poolName,
		"snap",
		"protect",
		"--snap", snapshotName,
		fmt.Sprintf("%s_%s", volumeType, volumeName))
	if err != nil {
		runError, ok := err.(shared.RunError)
		if ok {
			exitError, ok := runError.Err.(*exec.ExitError)
			if ok {
				waitStatus := exitError.Sys().(syscall.WaitStatus)
				if waitStatus.ExitStatus() == 16 {
					// EBUSY (snapshot already protected)
					return nil
				}
			}
		}
		return err
	}

	return nil
}

// cephRBDCloneCreate creates a clone from a protected RBD snapshot
func cephRBDCloneCreate(sourceClusterName string, sourcePoolName string,
	sourceVolumeName string, sourceVolumeType string,
	sourceSnapshotName string, targetPoolName string,
	targetVolumeName string, targetVolumeType string) error {
	_, err := shared.RunCommand(
		"rbd",
		"--cluster", sourceClusterName,
		"clone",
		fmt.Sprintf("%s/%s_%s@%s", sourcePoolName, sourceVolumeType,
			sourceVolumeName, sourceSnapshotName),
		fmt.Sprintf("%s/%s_%s", targetPoolName, targetVolumeType,
			targetVolumeName))
	if err != nil {
		return err
	}

	return nil
}

// getRBDSize returns the size the RBD storage volume is supposed to be created
// with
func (s *storageCeph) getRBDSize() (string, error) {
	sz, err := shared.ParseByteSizeString(s.volume.Config["size"])
	if err != nil {
		return "", err
	}

	// Safety net: Set to default value.
	if sz == 0 {
		sz, _ = shared.ParseByteSizeString("10GB")
	}

	return fmt.Sprintf("%dB", sz), nil
}

// getRBDFilesystem returns the filesystem the RBD storage volume is supposed to
// be created with
func (s *storageCeph) getRBDFilesystem() string {
	if s.volume.Config["block.filesystem"] != "" {
		return s.volume.Config["block.filesystem"]
	}

	if s.pool.Config["volume.block.filesystem"] != "" {
		return s.pool.Config["volume.block.filesystem"]
	}

	return "ext4"
}

// getRBDMountOptions returns the mount options the storage volume is supposed
// to be mounted with
// The option string that is returned needs to be passed to the approriate
// helper (currently named "lxdResolveMountoptions") which will take on the job
// of splitting it into appropriate flags and string options.
func (s *storageCeph) getRBDMountOptions() string {
	if s.volume.Config["block.mount_options"] != "" {
		return s.volume.Config["block.mount_options"]
	}

	if s.pool.Config["volume.block.mount_options"] != "" {
		return s.pool.Config["volume.block.mount_options"]
	}

	return "discard"
}
<|endoftext|>


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

package main

import (
	"os"
	"path/filepath"

	"github.com/Masterminds/semver"
	"github.com/sdboyer/gps"
)

type analyzer struct{}

// TODO: If we decide to support other tools manifest, this is where we would need
// to add that support.
func (a analyzer) DeriveManifestAndLock(path string, n gps.ProjectRoot) (gps.Manifest, gps.Lock, error) {
	mf := filepath.Join(path, manifestName)
	if fileOK, err := isRegular(mf); err != nil || !fileOK {
		// Do not return an error, when does not exist
		return nil, nil, nil
	}
	f, err := os.Open(mf)
	if err != nil {
		return nil, nil, nil
	}
	m, err := readManifest(f)
	if err != nil {
		return nil, nil, err
	}
	// TODO: no need to return lock til we decide about preferred versions
	// https://github.com/sdboyer/gps/wiki/gps-for-Implementors#preferred-versions
	return m, nil, nil
}

func (a analyzer) Info() (name string, version *semver.Version) {
	v, _ := semver.NewVersion("v0.0.1")
	return "hoard", v
}
<commit_msg>close file<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"os"
	"path/filepath"

	"github.com/Masterminds/semver"
	"github.com/sdboyer/gps"
)

type analyzer struct{}

// TODO: If we decide to support other tools manifest, this is where we would need
// to add that support.
func (a analyzer) DeriveManifestAndLock(path string, n gps.ProjectRoot) (gps.Manifest, gps.Lock, error) {
	mf := filepath.Join(path, manifestName)
	if fileOK, err := isRegular(mf); err != nil || !fileOK {
		// Do not return an error, when does not exist
		return nil, nil, nil
	}
	f, err := os.Open(mf)
	if err != nil {
		return nil, nil, nil
	}
	defer f.Close()

	m, err := readManifest(f)
	if err != nil {
		return nil, nil, err
	}
	// TODO: no need to return lock til we decide about preferred versions
	// https://github.com/sdboyer/gps/wiki/gps-for-Implementors#preferred-versions
	return m, nil, nil
}

func (a analyzer) Info() (name string, version *semver.Version) {
	v, _ := semver.NewVersion("v0.0.1")
	return "hoard", v
}
<|endoftext|>


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

package main

import (
	"os"
	"path/filepath"

	"github.com/Masterminds/semver"
	"github.com/sdboyer/gps"
)

type analyzer struct{}

// TODO: If we decide to support other tools manifest, this is where we would need
// to add that support.
func (a analyzer) DeriveManifestAndLock(path string, n gps.ProjectRoot) (gps.Manifest, gps.Lock, error) {
	mf := filepath.Join(path, manifestName)
	if fileOK, err := isRegular(mf); err != nil || !fileOK {
		// Do not return an error, when does not exist
		return nil, nil, nil
	}
	f, err := os.Open(mf)
	if err != nil {
		return nil, nil, nil
	}
	m, err := readManifest(f)
	if err != nil {
		return nil, nil, err
	}
	// TODO: no need to return lock til we decide about preferred versions
	// https://github.com/sdboyer/gps/wiki/gps-for-Implementors#preferred-versions
	return m, nil, nil
}

func (a analyzer) Info() (name string, version *semver.Version) {
	v, _ := semver.NewVersion("v0.0.1")
	return "example-analyzer", v
}
<commit_msg>Name the hoard analyzer appropriately<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"os"
	"path/filepath"

	"github.com/Masterminds/semver"
	"github.com/sdboyer/gps"
)

type analyzer struct{}

// TODO: If we decide to support other tools manifest, this is where we would need
// to add that support.
func (a analyzer) DeriveManifestAndLock(path string, n gps.ProjectRoot) (gps.Manifest, gps.Lock, error) {
	mf := filepath.Join(path, manifestName)
	if fileOK, err := isRegular(mf); err != nil || !fileOK {
		// Do not return an error, when does not exist
		return nil, nil, nil
	}
	f, err := os.Open(mf)
	if err != nil {
		return nil, nil, nil
	}
	m, err := readManifest(f)
	if err != nil {
		return nil, nil, err
	}
	// TODO: no need to return lock til we decide about preferred versions
	// https://github.com/sdboyer/gps/wiki/gps-for-Implementors#preferred-versions
	return m, nil, nil
}

func (a analyzer) Info() (name string, version *semver.Version) {
	v, _ := semver.NewVersion("v0.0.1")
	return "hoard", v
}
<|endoftext|>


<commit_before>package dht

// get_peers and announce_peers.

import (
	"time"

	"github.com/anacrolix/sync"
	"github.com/anacrolix/torrent/logonce"
	"github.com/willf/bloom"

	"github.com/anacrolix/dht/krpc"
)

// Maintains state for an ongoing Announce operation. An Announce is started
// by calling Server.Announce.
type Announce struct {
	mu    sync.Mutex
	Peers chan PeersValues
	// Inner chan is set to nil when on close.
	values     chan PeersValues
	stop       chan struct{}
	triedAddrs *bloom.BloomFilter
	// True when contact with all starting addrs has been initiated. This
	// prevents a race where the first transaction finishes before the rest
	// have been opened, sees no other transactions are pending and ends the
	// announce.
	contactedStartAddrs bool
	// How many transactions are still ongoing.
	pending  int
	server   *Server
	infoHash int160
	// Count of (probably) distinct addresses we've sent get_peers requests
	// to.
	numContacted int
	// The torrent port that we're announcing.
	announcePort int
	// The torrent port should be determined by the receiver in case we're
	// being NATed.
	announcePortImplied bool
}

// Returns the number of distinct remote addresses the announce has queried.
func (a *Announce) NumContacted() int {
	a.mu.Lock()
	defer a.mu.Unlock()
	return a.numContacted
}

func newBloomFilterForTraversal() *bloom.BloomFilter {
	return bloom.NewWithEstimates(1000, 0.5)
}

// This is kind of the main thing you want to do with DHT. It traverses the
// graph toward nodes that store peers for the infohash, streaming them to the
// caller, and announcing the local node to each node if allowed and
// specified.
func (s *Server) Announce(infoHash [20]byte, port int, impliedPort bool) (*Announce, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	startAddrs, err := s.traversalStartingAddrs()
	if err != nil {
		return nil, err
	}
	disc := &Announce{
		Peers:               make(chan PeersValues, 100),
		stop:                make(chan struct{}),
		values:              make(chan PeersValues),
		triedAddrs:          newBloomFilterForTraversal(),
		server:              s,
		infoHash:            int160FromByteArray(infoHash),
		announcePort:        port,
		announcePortImplied: impliedPort,
	}
	// Function ferries from values to Values until discovery is halted.
	go func() {
		defer close(disc.Peers)
		for {
			select {
			case psv := <-disc.values:
				select {
				case disc.Peers <- psv:
				case <-disc.stop:
					return
				}
			case <-disc.stop:
				return
			}
		}
	}()
	go func() {
		disc.mu.Lock()
		defer disc.mu.Unlock()
		for i, addr := range startAddrs {
			if i != 0 {
				disc.mu.Unlock()
				time.Sleep(time.Millisecond)
				disc.mu.Lock()
			}
			disc.contact(addr)
		}
		disc.contactedStartAddrs = true
		// If we failed to contact any of the starting addrs, no transactions
		// will complete triggering a check that there are no pending
		// responses.
		disc.maybeClose()
	}()
	return disc, nil
}

func validNodeAddr(addr Addr) bool {
	ua := addr.UDPAddr()
	if ua.Port == 0 {
		return false
	}
	if ip4 := ua.IP.To4(); ip4 != nil && ip4[0] == 0 {
		return false
	}
	return true
}

// TODO: Merge this with maybeGetPeersFromAddr.
func (a *Announce) gotNodeAddr(addr Addr) {
	if !validNodeAddr(addr) {
		return
	}
	if a.triedAddrs.Test([]byte(addr.String())) {
		return
	}
	if a.server.ipBlocked(addr.UDPAddr().IP) {
		return
	}
	a.contact(addr)
}

// TODO: Merge this with maybeGetPeersFromAddr.
func (a *Announce) contact(addr Addr) {
	a.numContacted++
	a.triedAddrs.Add([]byte(addr.String()))
	if err := a.getPeers(addr); err != nil {
		return
	}
	a.pending++
}

func (a *Announce) maybeClose() {
	if a.contactedStartAddrs && a.pending == 0 {
		a.close()
	}
}

func (a *Announce) transactionClosed() {
	a.pending--
	a.maybeClose()
}

func (a *Announce) responseNode(node krpc.NodeInfo) {
	a.gotNodeAddr(NewAddr(node.Addr.UDP()))
}

// Announce to a peer, if appropriate.
func (a *Announce) maybeAnnouncePeer(to Addr, token string, peerId *krpc.ID) {
	if !a.server.config.NoSecurity && (peerId == nil || !NodeIdSecure(*peerId, to.UDPAddr().IP)) {
		return
	}
	a.server.mu.Lock()
	defer a.server.mu.Unlock()
	err := a.server.announcePeer(to, a.infoHash, a.announcePort, token, a.announcePortImplied, nil)
	if err != nil {
		logonce.Stderr.Printf("error announcing peer: %s", err)
	}
}

func (a *Announce) getPeers(addr Addr) error {
	a.server.mu.Lock()
	defer a.server.mu.Unlock()
	return a.server.getPeers(addr, a.infoHash, func(m krpc.Msg, err error) {
		// Register suggested nodes closer to the target info-hash.
		if m.R != nil {
			expvars.Add("announce get_peers response nodes values", int64(len(m.R.Nodes)))
			expvars.Add("announce get_peers response nodes6 values", int64(len(m.R.Nodes6)))
			a.mu.Lock()
			for _, n := range m.R.Nodes {
				a.responseNode(n)
			}
			for _, n := range m.R.Nodes6 {
				a.responseNode(n)
			}
			a.mu.Unlock()
			select {
			case a.values <- PeersValues{
				Peers: m.R.Values,
				NodeInfo: krpc.NodeInfo{
					Addr: addr.KRPC(),
					ID:   *m.SenderID(),
				},
			}:
			case <-a.stop:
			}
			a.maybeAnnouncePeer(addr, m.R.Token, m.SenderID())
		}
		a.mu.Lock()
		a.transactionClosed()
		a.mu.Unlock()
	})
}

// Corresponds to the "values" key in a get_peers KRPC response. A list of
// peers that a node has reported as being in the swarm for a queried info
// hash.
type PeersValues struct {
	Peers         []Peer // Peers given in get_peers response.
	krpc.NodeInfo        // The node that gave the response.
}

// Stop the announce.
func (a *Announce) Close() {
	a.mu.Lock()
	defer a.mu.Unlock()
	a.close()
}

func (a *Announce) close() {
	select {
	case <-a.stop:
	default:
		close(a.stop)
	}
}
<commit_msg>Don't announce peer if the sender ID is nil<commit_after>package dht

// get_peers and announce_peers.

import (
	"time"

	"github.com/anacrolix/sync"
	"github.com/anacrolix/torrent/logonce"
	"github.com/willf/bloom"

	"github.com/anacrolix/dht/krpc"
)

// Maintains state for an ongoing Announce operation. An Announce is started
// by calling Server.Announce.
type Announce struct {
	mu    sync.Mutex
	Peers chan PeersValues
	// Inner chan is set to nil when on close.
	values     chan PeersValues
	stop       chan struct{}
	triedAddrs *bloom.BloomFilter
	// True when contact with all starting addrs has been initiated. This
	// prevents a race where the first transaction finishes before the rest
	// have been opened, sees no other transactions are pending and ends the
	// announce.
	contactedStartAddrs bool
	// How many transactions are still ongoing.
	pending  int
	server   *Server
	infoHash int160
	// Count of (probably) distinct addresses we've sent get_peers requests
	// to.
	numContacted int
	// The torrent port that we're announcing.
	announcePort int
	// The torrent port should be determined by the receiver in case we're
	// being NATed.
	announcePortImplied bool
}

// Returns the number of distinct remote addresses the announce has queried.
func (a *Announce) NumContacted() int {
	a.mu.Lock()
	defer a.mu.Unlock()
	return a.numContacted
}

func newBloomFilterForTraversal() *bloom.BloomFilter {
	return bloom.NewWithEstimates(1000, 0.5)
}

// This is kind of the main thing you want to do with DHT. It traverses the
// graph toward nodes that store peers for the infohash, streaming them to the
// caller, and announcing the local node to each node if allowed and
// specified.
func (s *Server) Announce(infoHash [20]byte, port int, impliedPort bool) (*Announce, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	startAddrs, err := s.traversalStartingAddrs()
	if err != nil {
		return nil, err
	}
	disc := &Announce{
		Peers:               make(chan PeersValues, 100),
		stop:                make(chan struct{}),
		values:              make(chan PeersValues),
		triedAddrs:          newBloomFilterForTraversal(),
		server:              s,
		infoHash:            int160FromByteArray(infoHash),
		announcePort:        port,
		announcePortImplied: impliedPort,
	}
	// Function ferries from values to Values until discovery is halted.
	go func() {
		defer close(disc.Peers)
		for {
			select {
			case psv := <-disc.values:
				select {
				case disc.Peers <- psv:
				case <-disc.stop:
					return
				}
			case <-disc.stop:
				return
			}
		}
	}()
	go func() {
		disc.mu.Lock()
		defer disc.mu.Unlock()
		for i, addr := range startAddrs {
			if i != 0 {
				disc.mu.Unlock()
				time.Sleep(time.Millisecond)
				disc.mu.Lock()
			}
			disc.contact(addr)
		}
		disc.contactedStartAddrs = true
		// If we failed to contact any of the starting addrs, no transactions
		// will complete triggering a check that there are no pending
		// responses.
		disc.maybeClose()
	}()
	return disc, nil
}

func validNodeAddr(addr Addr) bool {
	ua := addr.UDPAddr()
	if ua.Port == 0 {
		return false
	}
	if ip4 := ua.IP.To4(); ip4 != nil && ip4[0] == 0 {
		return false
	}
	return true
}

// TODO: Merge this with maybeGetPeersFromAddr.
func (a *Announce) gotNodeAddr(addr Addr) {
	if !validNodeAddr(addr) {
		return
	}
	if a.triedAddrs.Test([]byte(addr.String())) {
		return
	}
	if a.server.ipBlocked(addr.UDPAddr().IP) {
		return
	}
	a.contact(addr)
}

// TODO: Merge this with maybeGetPeersFromAddr.
func (a *Announce) contact(addr Addr) {
	a.numContacted++
	a.triedAddrs.Add([]byte(addr.String()))
	if err := a.getPeers(addr); err != nil {
		return
	}
	a.pending++
}

func (a *Announce) maybeClose() {
	if a.contactedStartAddrs && a.pending == 0 {
		a.close()
	}
}

func (a *Announce) transactionClosed() {
	a.pending--
	a.maybeClose()
}

func (a *Announce) responseNode(node krpc.NodeInfo) {
	a.gotNodeAddr(NewAddr(node.Addr.UDP()))
}

// Announce to a peer, if appropriate.
func (a *Announce) maybeAnnouncePeer(to Addr, token string, peerId *krpc.ID) {
	if !a.server.config.NoSecurity && (peerId == nil || !NodeIdSecure(*peerId, to.UDPAddr().IP)) {
		return
	}
	a.server.mu.Lock()
	defer a.server.mu.Unlock()
	err := a.server.announcePeer(to, a.infoHash, a.announcePort, token, a.announcePortImplied, nil)
	if err != nil {
		logonce.Stderr.Printf("error announcing peer: %s", err)
	}
}

func (a *Announce) getPeers(addr Addr) error {
	a.server.mu.Lock()
	defer a.server.mu.Unlock()
	return a.server.getPeers(addr, a.infoHash, func(m krpc.Msg, err error) {
		// Register suggested nodes closer to the target info-hash.
		if m.R != nil && m.SenderID() != nil {
			expvars.Add("announce get_peers response nodes values", int64(len(m.R.Nodes)))
			expvars.Add("announce get_peers response nodes6 values", int64(len(m.R.Nodes6)))
			a.mu.Lock()
			for _, n := range m.R.Nodes {
				a.responseNode(n)
			}
			for _, n := range m.R.Nodes6 {
				a.responseNode(n)
			}
			a.mu.Unlock()
			select {
			case a.values <- PeersValues{
				Peers: m.R.Values,
				NodeInfo: krpc.NodeInfo{
					Addr: addr.KRPC(),
					ID:   *m.SenderID(),
				},
			}:
			case <-a.stop:
			}
			a.maybeAnnouncePeer(addr, m.R.Token, m.SenderID())
		}
		a.mu.Lock()
		a.transactionClosed()
		a.mu.Unlock()
	})
}

// Corresponds to the "values" key in a get_peers KRPC response. A list of
// peers that a node has reported as being in the swarm for a queried info
// hash.
type PeersValues struct {
	Peers         []Peer // Peers given in get_peers response.
	krpc.NodeInfo        // The node that gave the response.
}

// Stop the announce.
func (a *Announce) Close() {
	a.mu.Lock()
	defer a.mu.Unlock()
	a.close()
}

func (a *Announce) close() {
	select {
	case <-a.stop:
	default:
		close(a.stop)
	}
}
<|endoftext|>


<commit_before>package gluahttp

import "github.com/yuin/gopher-lua"
import "net/http"
import "fmt"
import "errors"
import "io/ioutil"
import "strings"

type httpModule struct {
	client *http.Client
}

type empty struct{}

func NewHttpModule(client *http.Client) *httpModule {
	return &httpModule{
		client: client,
	}
}

func (h *httpModule) Loader(L *lua.LState) int {
	mod := L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{
		"get":           h.get,
		"delete":        h.delete,
		"head":          h.head,
		"patch":         h.patch,
		"post":          h.post,
		"put":           h.put,
		"request":       h.request,
		"request_batch": h.requestBatch,
	})
	registerHttpResponseType(mod, L)
	L.Push(mod)
	return 1
}

func (h *httpModule) get(L *lua.LState) int {
	return h.doRequestAndPush(L, "get", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) delete(L *lua.LState) int {
	return h.doRequestAndPush(L, "delete", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) head(L *lua.LState) int {
	return h.doRequestAndPush(L, "head", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) patch(L *lua.LState) int {
	return h.doRequestAndPush(L, "patch", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) post(L *lua.LState) int {
	return h.doRequestAndPush(L, "post", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) put(L *lua.LState) int {
	return h.doRequestAndPush(L, "put", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) request(L *lua.LState) int {
	return h.doRequestAndPush(L, L.ToString(1), L.ToString(2), L.ToTable(3))
}

func (h *httpModule) requestBatch(L *lua.LState) int {
	requests := L.ToTable(1)
	amountRequests := requests.Len()

	errs := make([]error, amountRequests)
	responses := make([]*lua.LUserData, amountRequests)
	sem := make(chan empty, amountRequests)

	i := 0

	requests.ForEach(func(_ lua.LValue, value lua.LValue) {
		requestTable := toTable(value)

		if requestTable != nil {
			method := requestTable.RawGet(lua.LNumber(1)).String()
			url := requestTable.RawGet(lua.LNumber(2)).String()
			options := toTable(requestTable.RawGet(lua.LNumber(3)))

			go func(i int, L *lua.LState, method string, url string, options *lua.LTable) {
				response, err := h.doRequest(L, method, url, options)

				if err == nil {
					errs[i] = nil
					responses[i] = response
				} else {
					errs[i] = err
					responses[i] = nil
				}

				sem <- empty{}
			}(i, L, method, url, options)
		} else {
			errs[i] = errors.New("Request must be a table")
			responses[i] = nil
			sem <- empty{}
		}

		i = i + 1
	})

	for i = 0; i < amountRequests; i++ {
		<-sem
	}

	hasErrors := false
	errorsTable := L.NewTable()
	responsesTable := L.NewTable()
	for i = 0; i < amountRequests; i++ {
		if errs[i] == nil {
			responsesTable.Append(responses[i])
			errorsTable.Append(lua.LNil)
		} else {
			responsesTable.Append(lua.LNil)
			errorsTable.Append(lua.LString(fmt.Sprintf("%s", errs[i])))
			hasErrors = true
		}
	}

	if hasErrors {
		L.Push(responsesTable)
		L.Push(errorsTable)
		return 2
	} else {
		L.Push(responsesTable)
		return 1
	}
}

func (h *httpModule) doRequest(L *lua.LState, method string, url string, options *lua.LTable) (*lua.LUserData, error) {
	req, err := http.NewRequest(strings.ToUpper(method), url, nil)
	if err != nil {
		return nil, err
	}

	if options != nil {
		if reqCookies, ok := options.RawGet(lua.LString("cookies")).(*lua.LTable); ok {
			reqCookies.ForEach(func(key lua.LValue, value lua.LValue) {
				req.AddCookie(&http.Cookie{Name: key.String(), Value: value.String()})
			})
		}

		switch reqQuery := options.RawGet(lua.LString("query")).(type) {
		case lua.LString:
			req.URL.RawQuery = reqQuery.String()
		}

		body := options.RawGet(lua.LString("body"))
		if _, ok := body.(lua.LString); !ok {
			// "form" is deprecated.
			body = options.RawGet(lua.LString("form"))
			// Only set the Content-Type to application/x-www-form-urlencoded
			// when someone uses "form", not for "body".
			if _, ok := body.(lua.LString); ok {
				req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
			}
		}

		switch reqBody := body.(type) {
		case lua.LString:
			body := reqBody.String()
			req.ContentLength = int64(len(body))
			req.Body = ioutil.NopCloser(strings.NewReader(body))
		}

		// Set these last. That way the code above doesn't overwrite them.
		if reqHeaders, ok := options.RawGet(lua.LString("headers")).(*lua.LTable); ok {
			reqHeaders.ForEach(func(key lua.LValue, value lua.LValue) {
				req.Header.Set(key.String(), value.String())
			})
		}
	}

	res, err := h.client.Do(req)

	if err != nil {
		return nil, err
	}

	defer res.Body.Close()
	body, err := ioutil.ReadAll(res.Body)

	if err != nil {
		return nil, err
	}

	return newHttpResponse(res, &body, len(body), L), nil
}

func (h *httpModule) doRequestAndPush(L *lua.LState, method string, url string, options *lua.LTable) int {
	response, err := h.doRequest(L, method, url, options)

	if err != nil {
		L.Push(lua.LNil)
		L.Push(lua.LString(fmt.Sprintf("%s", err)))
		return 2
	}

	L.Push(response)
	return 1
}

func toTable(v lua.LValue) *lua.LTable {
	if lv, ok := v.(*lua.LTable); ok {
		return lv
	}
	return nil
}
<commit_msg>add func NewHttpModuleWithDo, to allow passing do func instead of http client<commit_after>package gluahttp

import "github.com/yuin/gopher-lua"
import "net/http"
import "fmt"
import "errors"
import "io/ioutil"
import "strings"

type httpModule struct {
	client *http.Client

	do func(req *http.Request) (*http.Response, error)
}

type empty struct{}

func NewHttpModule(client *http.Client) *httpModule {
	return &httpModule{
		client: client,
	}
}

func NewHttpModuleWithDo(do func(req *http.Request) (*http.Response, error)) *httpModule {
	return &httpModule{
		do: do,
	}
}

func (h *httpModule) Loader(L *lua.LState) int {
	mod := L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{
		"get":           h.get,
		"delete":        h.delete,
		"head":          h.head,
		"patch":         h.patch,
		"post":          h.post,
		"put":           h.put,
		"request":       h.request,
		"request_batch": h.requestBatch,
	})
	registerHttpResponseType(mod, L)
	L.Push(mod)
	return 1
}

func (h *httpModule) get(L *lua.LState) int {
	return h.doRequestAndPush(L, "get", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) delete(L *lua.LState) int {
	return h.doRequestAndPush(L, "delete", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) head(L *lua.LState) int {
	return h.doRequestAndPush(L, "head", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) patch(L *lua.LState) int {
	return h.doRequestAndPush(L, "patch", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) post(L *lua.LState) int {
	return h.doRequestAndPush(L, "post", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) put(L *lua.LState) int {
	return h.doRequestAndPush(L, "put", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) request(L *lua.LState) int {
	return h.doRequestAndPush(L, L.ToString(1), L.ToString(2), L.ToTable(3))
}

func (h *httpModule) requestBatch(L *lua.LState) int {
	requests := L.ToTable(1)
	amountRequests := requests.Len()

	errs := make([]error, amountRequests)
	responses := make([]*lua.LUserData, amountRequests)
	sem := make(chan empty, amountRequests)

	i := 0

	requests.ForEach(func(_ lua.LValue, value lua.LValue) {
		requestTable := toTable(value)

		if requestTable != nil {
			method := requestTable.RawGet(lua.LNumber(1)).String()
			url := requestTable.RawGet(lua.LNumber(2)).String()
			options := toTable(requestTable.RawGet(lua.LNumber(3)))

			go func(i int, L *lua.LState, method string, url string, options *lua.LTable) {
				response, err := h.doRequest(L, method, url, options)

				if err == nil {
					errs[i] = nil
					responses[i] = response
				} else {
					errs[i] = err
					responses[i] = nil
				}

				sem <- empty{}
			}(i, L, method, url, options)
		} else {
			errs[i] = errors.New("Request must be a table")
			responses[i] = nil
			sem <- empty{}
		}

		i = i + 1
	})

	for i = 0; i < amountRequests; i++ {
		<-sem
	}

	hasErrors := false
	errorsTable := L.NewTable()
	responsesTable := L.NewTable()
	for i = 0; i < amountRequests; i++ {
		if errs[i] == nil {
			responsesTable.Append(responses[i])
			errorsTable.Append(lua.LNil)
		} else {
			responsesTable.Append(lua.LNil)
			errorsTable.Append(lua.LString(fmt.Sprintf("%s", errs[i])))
			hasErrors = true
		}
	}

	if hasErrors {
		L.Push(responsesTable)
		L.Push(errorsTable)
		return 2
	} else {
		L.Push(responsesTable)
		return 1
	}
}

func (h *httpModule) doRequest(L *lua.LState, method string, url string, options *lua.LTable) (*lua.LUserData, error) {
	req, err := http.NewRequest(strings.ToUpper(method), url, nil)
	if err != nil {
		return nil, err
	}

	if options != nil {
		if reqCookies, ok := options.RawGet(lua.LString("cookies")).(*lua.LTable); ok {
			reqCookies.ForEach(func(key lua.LValue, value lua.LValue) {
				req.AddCookie(&http.Cookie{Name: key.String(), Value: value.String()})
			})
		}

		switch reqQuery := options.RawGet(lua.LString("query")).(type) {
		case lua.LString:
			req.URL.RawQuery = reqQuery.String()
		}

		body := options.RawGet(lua.LString("body"))
		if _, ok := body.(lua.LString); !ok {
			// "form" is deprecated.
			body = options.RawGet(lua.LString("form"))
			// Only set the Content-Type to application/x-www-form-urlencoded
			// when someone uses "form", not for "body".
			if _, ok := body.(lua.LString); ok {
				req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
			}
		}

		switch reqBody := body.(type) {
		case lua.LString:
			body := reqBody.String()
			req.ContentLength = int64(len(body))
			req.Body = ioutil.NopCloser(strings.NewReader(body))
		}

		// Set these last. That way the code above doesn't overwrite them.
		if reqHeaders, ok := options.RawGet(lua.LString("headers")).(*lua.LTable); ok {
			reqHeaders.ForEach(func(key lua.LValue, value lua.LValue) {
				req.Header.Set(key.String(), value.String())
			})
		}
	}

	var res *http.Response

	if h.do != nil {
		res, err = h.do(req)
	} else {
		res, err = h.client.Do(req)
	}

	if err != nil {
		return nil, err
	}

	defer res.Body.Close()
	body, err := ioutil.ReadAll(res.Body)

	if err != nil {
		return nil, err
	}

	return newHttpResponse(res, &body, len(body), L), nil
}

func (h *httpModule) doRequestAndPush(L *lua.LState, method string, url string, options *lua.LTable) int {
	response, err := h.doRequest(L, method, url, options)

	if err != nil {
		L.Push(lua.LNil)
		L.Push(lua.LString(fmt.Sprintf("%s", err)))
		return 2
	}

	L.Push(response)
	return 1
}

func toTable(v lua.LValue) *lua.LTable {
	if lv, ok := v.(*lua.LTable); ok {
		return lv
	}
	return nil
}
<|endoftext|>


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

package github

import (
	"context"
	"fmt"
	"net/http"
	"testing"

	"github.com/google/go-cmp/cmp"
)

const (
	manifestJSON = `{
	"id": 1,
  "client_id": "a" ,
  "client_secret": "b",
  "webhook_secret": "c",
  "pem": "key"
}
`
)

func TestGetConfig(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/app-manifests/code/conversions", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "POST")
		fmt.Fprint(w, manifestJSON)
	})

	ctx := context.Background()
	cfg, _, err := client.Apps.CompleteAppManifest(ctx, "code")
	if err != nil {
		t.Errorf("AppManifest.GetConfig returned error: %v", err)
	}

	want := &AppConfig{
		ID:            Int64(1),
		ClientID:      String("a"),
		ClientSecret:  String("b"),
		WebhookSecret: String("c"),
		PEM:           String("key"),
	}

	if !cmp.Equal(cfg, want) {
		t.Errorf("GetConfig returned %+v, want %+v", cfg, want)
	}

	const methodName = "CompleteAppManifest"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Apps.CompleteAppManifest(ctx, "\n")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Apps.CompleteAppManifest(ctx, "code")
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}
<commit_msg>Add test cases for JSON resource marshaling (#1926)<commit_after>// Copyright 2019 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package github

import (
	"context"
	"fmt"
	"net/http"
	"testing"

	"github.com/google/go-cmp/cmp"
)

const (
	manifestJSON = `{
	"id": 1,
  "client_id": "a" ,
  "client_secret": "b",
  "webhook_secret": "c",
  "pem": "key"
}
`
)

func TestGetConfig(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/app-manifests/code/conversions", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "POST")
		fmt.Fprint(w, manifestJSON)
	})

	ctx := context.Background()
	cfg, _, err := client.Apps.CompleteAppManifest(ctx, "code")
	if err != nil {
		t.Errorf("AppManifest.GetConfig returned error: %v", err)
	}

	want := &AppConfig{
		ID:            Int64(1),
		ClientID:      String("a"),
		ClientSecret:  String("b"),
		WebhookSecret: String("c"),
		PEM:           String("key"),
	}

	if !cmp.Equal(cfg, want) {
		t.Errorf("GetConfig returned %+v, want %+v", cfg, want)
	}

	const methodName = "CompleteAppManifest"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Apps.CompleteAppManifest(ctx, "\n")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Apps.CompleteAppManifest(ctx, "code")
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestAppConfig_Marshal(t *testing.T) {
	testJSONMarshal(t, &AppConfig{}, "{}")

	u := &AppConfig{
		ID:     Int64(1),
		NodeID: String("nid"),
		Owner: &User{
			Login:           String("l"),
			ID:              Int64(1),
			URL:             String("u"),
			AvatarURL:       String("a"),
			GravatarID:      String("g"),
			Name:            String("n"),
			Company:         String("c"),
			Blog:            String("b"),
			Location:        String("l"),
			Email:           String("e"),
			Hireable:        Bool(true),
			Bio:             String("b"),
			TwitterUsername: String("t"),
			PublicRepos:     Int(1),
			Followers:       Int(1),
			Following:       Int(1),
			CreatedAt:       &Timestamp{referenceTime},
			SuspendedAt:     &Timestamp{referenceTime},
		},
		Name:          String("n"),
		Description:   String("d"),
		ExternalURL:   String("eu"),
		HTMLURL:       String("hu"),
		CreatedAt:     &Timestamp{referenceTime},
		UpdatedAt:     &Timestamp{referenceTime},
		ClientID:      String("ci"),
		ClientSecret:  String("cs"),
		WebhookSecret: String("ws"),
		PEM:           String("pem"),
	}

	want := `{
		"id": 1,
		"node_id": "nid",
		"owner": {
			"login": "l",
			"id": 1,
			"avatar_url": "a",
			"gravatar_id": "g",
			"name": "n",
			"company": "c",
			"blog": "b",
			"location": "l",
			"email": "e",
			"hireable": true,
			"bio": "b",
			"twitter_username": "t",
			"public_repos": 1,
			"followers": 1,
			"following": 1,
			"created_at": ` + referenceTimeStr + `,
			"suspended_at": ` + referenceTimeStr + `,
			"url": "u"
		},
		"name": "n",
		"description": "d",
		"external_url": "eu",
		"html_url": "hu",
		"created_at": ` + referenceTimeStr + `,
		"updated_at": ` + referenceTimeStr + `,
		"client_id": "ci",
		"client_secret": "cs",
		"webhook_secret": "ws",
		"pem": "pem"
	}`

	testJSONMarshal(t, u, want)
}
<|endoftext|>


<commit_before>package sudoku

import (
	"fmt"
	"math/rand"
)

type xwingTechnique struct {
	*basicSolveTechnique
}

func (self *xwingTechnique) Difficulty() float64 {
	return self.difficultyHelper(5.0)
}

func (self *xwingTechnique) Description(step *SolveStep) string {
	majorAxis := "NONE"
	minorAxis := "NONE"
	var majorGroups IntSlice
	var minorGroups IntSlice
	switch self.groupType {
	case GROUP_ROW:
		majorAxis = "rows"
		minorAxis = "columns"
		majorGroups = step.PointerCells.CollectNums(getRow).Unique()
		minorGroups = step.PointerCells.CollectNums(getCol).Unique()
	case GROUP_COL:
		majorAxis = "columns"
		minorAxis = "rows"
		majorGroups = step.PointerCells.CollectNums(getCol).Unique()
		minorGroups = step.PointerCells.CollectNums(getRow).Unique()
	}

	return fmt.Sprintf("in %s %s, %d is only possible in %s %s, and %d must be in one of those cells per %s, so it can't be in any other cells in those %s",
		majorAxis,
		majorGroups.Description(),
		step.TargetNums[0],
		minorAxis,
		minorGroups.Description(),
		step.TargetNums[0],
		majorAxis,
		minorAxis,
	)
}

func (self *xwingTechnique) Find(grid *Grid) []*SolveStep {

	var results []*SolveStep

	getter := self.getter(grid)

	//For each number
	for _, i := range rand.Perm(DIM) {
		//In comments we'll say "Row" for the major group type, and "col" for minor group type, just for easier comprehension.
		//Look for each row that has that number possible in only two cells.

		//i is zero indexed right now
		i++

		var majorGroups []CellList

		for groupIndex := 0; groupIndex < DIM; groupIndex++ {
			group := getter(groupIndex)
			cells := group.FilterByPossible(i)
			if len(cells) == 2 {
				//Found a row that might fit the bill.
				majorGroups = append(majorGroups, cells)
			}
		}

		//Okay, did we have two rows?
		if len(majorGroups) < 2 {
			//Not enough rows
			continue
		}

		//Now look at each pair of rows and see if their numbers line up.
		for _, subsets := range subsetIndexes(len(majorGroups), 2) {
			var targetCells CellList

			currentGroups := []CellList{majorGroups[subsets[0]], majorGroups[subsets[1]]}

			//Are the possibilities in each row in the same column as the one above?
			//We need to do this differently depending on if we're row or col.
			if self.groupType == GROUP_ROW {
				//TODO: figure out a way to factor group row and col better so we don't duplicate code like this.
				if currentGroups[0][0].Col != currentGroups[1][0].Col || currentGroups[0][1].Col != currentGroups[1][1].Col {
					//Nope, the cells didn't line up.
					continue
				}
				//All of the cells in those two columns
				targetCells = append(grid.Col(currentGroups[0][0].Col), grid.Col(currentGroups[0][1].Col)...)

			} else if self.groupType == GROUP_COL {
				if currentGroups[0][0].Row != currentGroups[1][0].Row || currentGroups[0][1].Row != currentGroups[1][1].Row {
					//Nope, the cells didn't line up.
					continue
				}
				//All of the cells in those two columns
				targetCells = append(grid.Row(currentGroups[0][0].Row), grid.Row(currentGroups[0][1].Row)...)

			}

			//Then remove the cells that are the pointerCells
			targetCells = targetCells.RemoveCells(currentGroups[0])
			targetCells = targetCells.RemoveCells(currentGroups[1])

			//And remove the cells that don't have the target number to remove (to keep the set tight; technically it's OK to include them it would just be a no-op for those cells)
			targetCells = targetCells.FilterByPossible(i)

			//Okay, we found a pair that works. Create a step for it (if it's useful)
			step := &SolveStep{targetCells, append(currentGroups[0], currentGroups[1]...), IntSlice{i}, nil, self}
			if step.IsUseful(grid) {
				results = append(results, step)
			}
		}

	}
	return results
}
<commit_msg>Made the xwing description not be flaky.<commit_after>package sudoku

import (
	"fmt"
	"math/rand"
)

type xwingTechnique struct {
	*basicSolveTechnique
}

func (self *xwingTechnique) Difficulty() float64 {
	return self.difficultyHelper(5.0)
}

func (self *xwingTechnique) Description(step *SolveStep) string {
	majorAxis := "NONE"
	minorAxis := "NONE"
	var majorGroups IntSlice
	var minorGroups IntSlice
	switch self.groupType {
	case GROUP_ROW:
		majorAxis = "rows"
		minorAxis = "columns"
		majorGroups = step.PointerCells.CollectNums(getRow).Unique()
		minorGroups = step.PointerCells.CollectNums(getCol).Unique()
	case GROUP_COL:
		majorAxis = "columns"
		minorAxis = "rows"
		majorGroups = step.PointerCells.CollectNums(getCol).Unique()
		minorGroups = step.PointerCells.CollectNums(getRow).Unique()
	}

	//Ensure a stable description; Unique() doesn't have a guranteed order.
	majorGroups.Sort()
	minorGroups.Sort()

	return fmt.Sprintf("in %s %s, %d is only possible in %s %s, and %d must be in one of those cells per %s, so it can't be in any other cells in those %s",
		majorAxis,
		majorGroups.Description(),
		step.TargetNums[0],
		minorAxis,
		minorGroups.Description(),
		step.TargetNums[0],
		majorAxis,
		minorAxis,
	)
}

func (self *xwingTechnique) Find(grid *Grid) []*SolveStep {

	var results []*SolveStep

	getter := self.getter(grid)

	//For each number
	for _, i := range rand.Perm(DIM) {
		//In comments we'll say "Row" for the major group type, and "col" for minor group type, just for easier comprehension.
		//Look for each row that has that number possible in only two cells.

		//i is zero indexed right now
		i++

		var majorGroups []CellList

		for groupIndex := 0; groupIndex < DIM; groupIndex++ {
			group := getter(groupIndex)
			cells := group.FilterByPossible(i)
			if len(cells) == 2 {
				//Found a row that might fit the bill.
				majorGroups = append(majorGroups, cells)
			}
		}

		//Okay, did we have two rows?
		if len(majorGroups) < 2 {
			//Not enough rows
			continue
		}

		//Now look at each pair of rows and see if their numbers line up.
		for _, subsets := range subsetIndexes(len(majorGroups), 2) {
			var targetCells CellList

			currentGroups := []CellList{majorGroups[subsets[0]], majorGroups[subsets[1]]}

			//Are the possibilities in each row in the same column as the one above?
			//We need to do this differently depending on if we're row or col.
			if self.groupType == GROUP_ROW {
				//TODO: figure out a way to factor group row and col better so we don't duplicate code like this.
				if currentGroups[0][0].Col != currentGroups[1][0].Col || currentGroups[0][1].Col != currentGroups[1][1].Col {
					//Nope, the cells didn't line up.
					continue
				}
				//All of the cells in those two columns
				targetCells = append(grid.Col(currentGroups[0][0].Col), grid.Col(currentGroups[0][1].Col)...)

			} else if self.groupType == GROUP_COL {
				if currentGroups[0][0].Row != currentGroups[1][0].Row || currentGroups[0][1].Row != currentGroups[1][1].Row {
					//Nope, the cells didn't line up.
					continue
				}
				//All of the cells in those two columns
				targetCells = append(grid.Row(currentGroups[0][0].Row), grid.Row(currentGroups[0][1].Row)...)

			}

			//Then remove the cells that are the pointerCells
			targetCells = targetCells.RemoveCells(currentGroups[0])
			targetCells = targetCells.RemoveCells(currentGroups[1])

			//And remove the cells that don't have the target number to remove (to keep the set tight; technically it's OK to include them it would just be a no-op for those cells)
			targetCells = targetCells.FilterByPossible(i)

			//Okay, we found a pair that works. Create a step for it (if it's useful)
			step := &SolveStep{targetCells, append(currentGroups[0], currentGroups[1]...), IntSlice{i}, nil, self}
			if step.IsUseful(grid) {
				results = append(results, step)
			}
		}

	}
	return results
}
<|endoftext|>