<commit_before>// Copyright 2013 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 simplifiedchinese

import (
	"errors"
	"unicode/utf8"

	"golang.org/x/text/encoding"
	"golang.org/x/text/transform"
)

var (
	// GB18030 is the GB18030 encoding.
	GB18030 encoding.Encoding = gbk{gb18030: true}
	// GBK is the GBK encoding. It encodes an extension of the GB2312 character set
	// and is also known as Code Page 936.
	GBK encoding.Encoding = gbk{gb18030: false}
)

type gbk struct {
	gb18030 bool
}

func (g gbk) NewDecoder() transform.Transformer {
	return gbkDecoder{gb18030: g.gb18030}
}

func (g gbk) NewEncoder() transform.Transformer {
	return gbkEncoder{gb18030: g.gb18030}
}

func (g gbk) String() string {
	if g.gb18030 {
		return "GB18030"
	}
	return "GBK"
}

var (
	errInvalidGB18030 = errors.New("simplifiedchinese: invalid GB18030 encoding")
	errInvalidGBK     = errors.New("simplifiedchinese: invalid GBK encoding")
)

type gbkDecoder struct {
	transform.NopResetter
	gb18030 bool
}

func (d gbkDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
	r, size := rune(0), 0
loop:
	for ; nSrc < len(src); nSrc += size {
		switch c0 := src[nSrc]; {
		case c0 < utf8.RuneSelf:
			r, size = rune(c0), 1

		// Microsoft's Code Page 936 extends GBK 1.0 to encode the euro sign U+20AC
		// as 0x80. The HTML5 specification at http://encoding.spec.whatwg.org/#gbk
		// says to treat "gbk" as Code Page 936.
		case c0 == 0x80:
			r, size = '€', 1

		case c0 < 0xff:
			if nSrc+1 >= len(src) {
				err = transform.ErrShortSrc
				break loop
			}
			c1 := src[nSrc+1]
			switch {
			case 0x40 <= c1 && c1 < 0x7f:
				c1 -= 0x40
			case 0x80 <= c1 && c1 < 0xff:
				c1 -= 0x41
			case d.gb18030 && 0x30 <= c1 && c1 < 0x40:
				if nSrc+3 >= len(src) {
					err = transform.ErrShortSrc
					break loop
				}
				c2 := src[nSrc+2]
				if c2 < 0x81 || 0xff <= c2 {
					err = errInvalidGB18030
					break loop
				}
				c3 := src[nSrc+3]
				if c3 < 0x30 || 0x3a <= c3 {
					err = errInvalidGB18030
					break loop
				}
				size = 4
				r = ((rune(c0-0x81)*10+rune(c1-0x30))*126+rune(c2-0x81))*10 + rune(c3-0x30)
				if r < 39420 {
					i, j := 0, len(gb18030)
					for i < j {
						h := i + (j-i)/2
						if r >= rune(gb18030[h][0]) {
							i = h + 1
						} else {
							j = h
						}
					}
					dec := &gb18030[i-1]
					r += rune(dec[1]) - rune(dec[0])
					goto write
				}
				r -= 189000
				if 0 <= r && r < 0x100000 {
					r += 0x10000
					goto write
				}
				err = errInvalidGB18030
				break loop
			default:
				if d.gb18030 {
					err = errInvalidGB18030
				} else {
					err = errInvalidGBK
				}
				break loop
			}
			r, size = '\ufffd', 2
			if i := int(c0-0x81)*190 + int(c1); i < len(decode) {
				r = rune(decode[i])
				if r == 0 {
					r = '\ufffd'
				}
			}

		default:
			if d.gb18030 {
				err = errInvalidGB18030
			} else {
				err = errInvalidGBK
			}
			break loop
		}

	write:
		if nDst+utf8.RuneLen(r) > len(dst) {
			err = transform.ErrShortDst
			break loop
		}
		nDst += utf8.EncodeRune(dst[nDst:], r)
	}
	if atEOF && err == transform.ErrShortSrc {
		if d.gb18030 {
			err = errInvalidGB18030
		} else {
			err = errInvalidGBK
		}
	}
	return nDst, nSrc, err
}

type gbkEncoder struct {
	transform.NopResetter
	gb18030 bool
}

func (e gbkEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
	r, r2, size := rune(0), rune(0), 0
	for ; nSrc < len(src); nSrc += size {
		r = rune(src[nSrc])

		// Decode a 1-byte rune.
		if r < utf8.RuneSelf {
			size = 1

		} else {
			// Decode a multi-byte rune.
			r, size = utf8.DecodeRune(src[nSrc:])
			if size == 1 {
				// All valid runes of size 1 (those below utf8.RuneSelf) were
				// handled above. We have invalid UTF-8 or we haven't seen the
				// full character yet.
				if !atEOF && !utf8.FullRune(src[nSrc:]) {
					err = transform.ErrShortSrc
					break
				}
			}

			// func init checks that the switch covers all tables.
			switch {
			case encode0Low <= r && r < encode0High:
				if r2 = rune(encode0[r-encode0Low]); r2 != 0 {
					goto write2
				}
			case encode1Low <= r && r < encode1High:
				// Microsoft's Code Page 936 extends GBK 1.0 to encode the euro sign U+20AC
				// as 0x80. The HTML5 specification at http://encoding.spec.whatwg.org/#gbk
				// says to treat "gbk" as Code Page 936.
				if r == '€' {
					r = 0x80
					goto write1
				}
				if r2 = rune(encode1[r-encode1Low]); r2 != 0 {
					goto write2
				}
			case encode2Low <= r && r < encode2High:
				if r2 = rune(encode2[r-encode2Low]); r2 != 0 {
					goto write2
				}
			case encode3Low <= r && r < encode3High:
				if r2 = rune(encode3[r-encode3Low]); r2 != 0 {
					goto write2
				}
			case encode4Low <= r && r < encode4High:
				if r2 = rune(encode4[r-encode4Low]); r2 != 0 {
					goto write2
				}
			}

			if e.gb18030 {
				if r < 0x10000 {
					i, j := 0, len(gb18030)
					for i < j {
						h := i + (j-i)/2
						if r >= rune(gb18030[h][1]) {
							i = h + 1
						} else {
							j = h
						}
					}
					dec := &gb18030[i-1]
					r += rune(dec[0]) - rune(dec[1])
					goto write4
				} else if r < 0x110000 {
					r += 189000 - 0x10000
					goto write4
				}
			}
			r = encoding.ASCIISub
		}

	write1:
		if nDst >= len(dst) {
			err = transform.ErrShortDst
			break
		}
		dst[nDst] = uint8(r)
		nDst++
		continue

	write2:
		if nDst+2 > len(dst) {
			err = transform.ErrShortDst
			break
		}
		dst[nDst+0] = uint8(r2 >> 8)
		dst[nDst+1] = uint8(r2)
		nDst += 2
		continue

	write4:
		if nDst+4 > len(dst) {
			err = transform.ErrShortDst
			break
		}
		dst[nDst+3] = uint8(r%10 + 0x30)
		r /= 10
		dst[nDst+2] = uint8(r%126 + 0x81)
		r /= 126
		dst[nDst+1] = uint8(r%10 + 0x30)
		r /= 10
		dst[nDst+0] = uint8(r + 0x81)
		nDst += 4
		continue
	}
	return nDst, nSrc, err
}

func init() {
	// Check that the hard-coded encode switch covers all tables.
	if numEncodeTables != 5 {
		panic("bad numEncodeTables")
	}
}
<commit_msg>auto commit<commit_after>// Copyright 2013 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 simplifiedchinese

import (
	"errors"
	"unicode/utf8"

    "github.com/weisd/golang.org-x-text/encoding"
    "github.com/weisd/golang.org-x-text/transform"
	//"golang.org/x/text/encoding"
	//"golang.org/x/text/transform"
)

var (
	// GB18030 is the GB18030 encoding.
	GB18030 encoding.Encoding = gbk{gb18030: true}
	// GBK is the GBK encoding. It encodes an extension of the GB2312 character set
	// and is also known as Code Page 936.
	GBK encoding.Encoding = gbk{gb18030: false}
)

type gbk struct {
	gb18030 bool
}

func (g gbk) NewDecoder() transform.Transformer {
	return gbkDecoder{gb18030: g.gb18030}
}

func (g gbk) NewEncoder() transform.Transformer {
	return gbkEncoder{gb18030: g.gb18030}
}

func (g gbk) String() string {
	if g.gb18030 {
		return "GB18030"
	}
	return "GBK"
}

var (
	errInvalidGB18030 = errors.New("simplifiedchinese: invalid GB18030 encoding")
	errInvalidGBK     = errors.New("simplifiedchinese: invalid GBK encoding")
)

type gbkDecoder struct {
	transform.NopResetter
	gb18030 bool
}

func (d gbkDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
	r, size := rune(0), 0
loop:
	for ; nSrc < len(src); nSrc += size {
		switch c0 := src[nSrc]; {
		case c0 < utf8.RuneSelf:
			r, size = rune(c0), 1

		// Microsoft's Code Page 936 extends GBK 1.0 to encode the euro sign U+20AC
		// as 0x80. The HTML5 specification at http://encoding.spec.whatwg.org/#gbk
		// says to treat "gbk" as Code Page 936.
		case c0 == 0x80:
			r, size = '€', 1

		case c0 < 0xff:
			if nSrc+1 >= len(src) {
				err = transform.ErrShortSrc
				break loop
			}
			c1 := src[nSrc+1]
			switch {
			case 0x40 <= c1 && c1 < 0x7f:
				c1 -= 0x40
			case 0x80 <= c1 && c1 < 0xff:
				c1 -= 0x41
			case d.gb18030 && 0x30 <= c1 && c1 < 0x40:
				if nSrc+3 >= len(src) {
					err = transform.ErrShortSrc
					break loop
				}
				c2 := src[nSrc+2]
				if c2 < 0x81 || 0xff <= c2 {
					err = errInvalidGB18030
					break loop
				}
				c3 := src[nSrc+3]
				if c3 < 0x30 || 0x3a <= c3 {
					err = errInvalidGB18030
					break loop
				}
				size = 4
				r = ((rune(c0-0x81)*10+rune(c1-0x30))*126+rune(c2-0x81))*10 + rune(c3-0x30)
				if r < 39420 {
					i, j := 0, len(gb18030)
					for i < j {
						h := i + (j-i)/2
						if r >= rune(gb18030[h][0]) {
							i = h + 1
						} else {
							j = h
						}
					}
					dec := &gb18030[i-1]
					r += rune(dec[1]) - rune(dec[0])
					goto write
				}
				r -= 189000
				if 0 <= r && r < 0x100000 {
					r += 0x10000
					goto write
				}
				err = errInvalidGB18030
				break loop
			default:
				if d.gb18030 {
					err = errInvalidGB18030
				} else {
					err = errInvalidGBK
				}
				break loop
			}
			r, size = '\ufffd', 2
			if i := int(c0-0x81)*190 + int(c1); i < len(decode) {
				r = rune(decode[i])
				if r == 0 {
					r = '\ufffd'
				}
			}

		default:
			if d.gb18030 {
				err = errInvalidGB18030
			} else {
				err = errInvalidGBK
			}
			break loop
		}

	write:
		if nDst+utf8.RuneLen(r) > len(dst) {
			err = transform.ErrShortDst
			break loop
		}
		nDst += utf8.EncodeRune(dst[nDst:], r)
	}
	if atEOF && err == transform.ErrShortSrc {
		if d.gb18030 {
			err = errInvalidGB18030
		} else {
			err = errInvalidGBK
		}
	}
	return nDst, nSrc, err
}

type gbkEncoder struct {
	transform.NopResetter
	gb18030 bool
}

func (e gbkEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
	r, r2, size := rune(0), rune(0), 0
	for ; nSrc < len(src); nSrc += size {
		r = rune(src[nSrc])

		// Decode a 1-byte rune.
		if r < utf8.RuneSelf {
			size = 1

		} else {
			// Decode a multi-byte rune.
			r, size = utf8.DecodeRune(src[nSrc:])
			if size == 1 {
				// All valid runes of size 1 (those below utf8.RuneSelf) were
				// handled above. We have invalid UTF-8 or we haven't seen the
				// full character yet.
				if !atEOF && !utf8.FullRune(src[nSrc:]) {
					err = transform.ErrShortSrc
					break
				}
			}

			// func init checks that the switch covers all tables.
			switch {
			case encode0Low <= r && r < encode0High:
				if r2 = rune(encode0[r-encode0Low]); r2 != 0 {
					goto write2
				}
			case encode1Low <= r && r < encode1High:
				// Microsoft's Code Page 936 extends GBK 1.0 to encode the euro sign U+20AC
				// as 0x80. The HTML5 specification at http://encoding.spec.whatwg.org/#gbk
				// says to treat "gbk" as Code Page 936.
				if r == '€' {
					r = 0x80
					goto write1
				}
				if r2 = rune(encode1[r-encode1Low]); r2 != 0 {
					goto write2
				}
			case encode2Low <= r && r < encode2High:
				if r2 = rune(encode2[r-encode2Low]); r2 != 0 {
					goto write2
				}
			case encode3Low <= r && r < encode3High:
				if r2 = rune(encode3[r-encode3Low]); r2 != 0 {
					goto write2
				}
			case encode4Low <= r && r < encode4High:
				if r2 = rune(encode4[r-encode4Low]); r2 != 0 {
					goto write2
				}
			}

			if e.gb18030 {
				if r < 0x10000 {
					i, j := 0, len(gb18030)
					for i < j {
						h := i + (j-i)/2
						if r >= rune(gb18030[h][1]) {
							i = h + 1
						} else {
							j = h
						}
					}
					dec := &gb18030[i-1]
					r += rune(dec[0]) - rune(dec[1])
					goto write4
				} else if r < 0x110000 {
					r += 189000 - 0x10000
					goto write4
				}
			}
			r = encoding.ASCIISub
		}

	write1:
		if nDst >= len(dst) {
			err = transform.ErrShortDst
			break
		}
		dst[nDst] = uint8(r)
		nDst++
		continue

	write2:
		if nDst+2 > len(dst) {
			err = transform.ErrShortDst
			break
		}
		dst[nDst+0] = uint8(r2 >> 8)
		dst[nDst+1] = uint8(r2)
		nDst += 2
		continue

	write4:
		if nDst+4 > len(dst) {
			err = transform.ErrShortDst
			break
		}
		dst[nDst+3] = uint8(r%10 + 0x30)
		r /= 10
		dst[nDst+2] = uint8(r%126 + 0x81)
		r /= 126
		dst[nDst+1] = uint8(r%10 + 0x30)
		r /= 10
		dst[nDst+0] = uint8(r + 0x81)
		nDst += 4
		continue
	}
	return nDst, nSrc, err
}

func init() {
	// Check that the hard-coded encode switch covers all tables.
	if numEncodeTables != 5 {
		panic("bad numEncodeTables")
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 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 simplifiedchinese

import (
	"errors"
	"unicode/utf8"

	"code.google.com/p/go.text/encoding"
	"code.google.com/p/go.text/transform"
)

// GBK is the GBK encoding. It encodes an extension of the GB2312 character set
// and is also known as Code Page 936.
var GBK encoding.Encoding = gbk{}

type gbk struct{}

func (gbk) NewDecoder() transform.Transformer {
	return gbkDecoder{}
}

func (gbk) NewEncoder() transform.Transformer {
	return gbkEncoder{}
}

func (gbk) String() string {
	return "GBK"
}

var errInvalidGBK = errors.New("simplifiedchinese: invalid GBK encoding")

type gbkDecoder struct{}

func (gbkDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
	r, size := rune(0), 0
loop:
	for ; nSrc < len(src); nSrc += size {
		switch c0 := src[nSrc]; {
		case c0 < utf8.RuneSelf:
			r, size = rune(c0), 1

		// Microsoft's Code Page 936 extends GBK 1.0 to encode the euro sign U+20AC
		// as 0x80. The HTML5 specification at http://encoding.spec.whatwg.org/#gbk
		// says to treat "gbk" as Code Page 936.
		case c0 == 0x80:
			r, size = '€', 1

		case c0 < 0xff:
			if nSrc+1 >= len(src) {
				err = transform.ErrShortSrc
				break loop
			}
			c1 := src[nSrc+1]
			switch {
			case 0x40 <= c1 && c1 < 0x7f:
				c1 -= 0x40
			case 0x80 <= c1 && c1 < 0xff:
				c1 -= 0x41
			default:
				err = errInvalidGBK
				break loop
			}
			r, size = encoding.ASCIISub, 2
			if i := int(c0-0x81)*190 + int(c1); i < len(gbkDecode) {
				r = rune(gbkDecode[i])
				if r == 0 {
					r = encoding.ASCIISub
				}
			}

		default:
			err = errInvalidGBK
			break loop
		}

		if nDst+utf8.RuneLen(r) > len(dst) {
			err = transform.ErrShortDst
			break loop
		}
		nDst += utf8.EncodeRune(dst[nDst:], r)
	}
	if atEOF && err == transform.ErrShortSrc {
		err = errInvalidGBK
	}
	return nDst, nSrc, err
}

type gbkEncoder struct{}

func (gbkEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
	r, size := rune(0), 0
	for ; nSrc < len(src); nSrc += size {
		r = rune(src[nSrc])

		// Decode a 1-byte rune.
		if r < utf8.RuneSelf {
			size = 1

		} else {
			// Decode a multi-byte rune.
			r, size = utf8.DecodeRune(src[nSrc:])
			if size == 1 {
				// All valid runes of size 1 (those below utf8.RuneSelf) were
				// handled above. We have invalid UTF-8 or we haven't seen the
				// full character yet.
				if !atEOF && !utf8.FullRune(src[nSrc:]) {
					err = transform.ErrShortSrc
					break
				}
			}
		}

		if r >= utf8.RuneSelf {
			switch {
			case gbkEncode0Low <= r && r < gbkEncode0High:
				if r = rune(gbkEncode0[r-gbkEncode0Low]); r != 0 {
					goto write2
				}
			case gbkEncode1Low <= r && r < gbkEncode1High:
				// Microsoft's Code Page 936 extends GBK 1.0 to encode the euro sign U+20AC
				// as 0x80. The HTML5 specification at http://encoding.spec.whatwg.org/#gbk
				// says to treat "gbk" as Code Page 936.
				if r == '€' {
					r = 0x80
					goto write1
				}
				if r = rune(gbkEncode1[r-gbkEncode1Low]); r != 0 {
					goto write2
				}
			case gbkEncode2Low <= r && r < gbkEncode2High:
				if r = rune(gbkEncode2[r-gbkEncode2Low]); r != 0 {
					goto write2
				}
			case gbkEncode3Low <= r && r < gbkEncode3High:
				if r = rune(gbkEncode3[r-gbkEncode3Low]); r != 0 {
					goto write2
				}
			case gbkEncode4Low <= r && r < gbkEncode4High:
				if r = rune(gbkEncode4[r-gbkEncode4Low]); r != 0 {
					goto write2
				}
			}
			r = encoding.ASCIISub
		}

	write1:
		if nDst >= len(dst) {
			err = transform.ErrShortDst
			break
		}
		dst[nDst] = uint8(r)
		nDst++
		continue

	write2:
		if nDst+2 > len(dst) {
			err = transform.ErrShortDst
			break
		}
		dst[nDst+0] = uint8(r >> 8)
		dst[nDst+1] = uint8(r)
		nDst += 2
		continue
	}
	return nDst, nSrc, err
}
<commit_msg>go.text/encoding/simplifiedchinese: remove redundant if check.<commit_after>// Copyright 2013 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 simplifiedchinese

import (
	"errors"
	"unicode/utf8"

	"code.google.com/p/go.text/encoding"
	"code.google.com/p/go.text/transform"
)

// GBK is the GBK encoding. It encodes an extension of the GB2312 character set
// and is also known as Code Page 936.
var GBK encoding.Encoding = gbk{}

type gbk struct{}

func (gbk) NewDecoder() transform.Transformer {
	return gbkDecoder{}
}

func (gbk) NewEncoder() transform.Transformer {
	return gbkEncoder{}
}

func (gbk) String() string {
	return "GBK"
}

var errInvalidGBK = errors.New("simplifiedchinese: invalid GBK encoding")

type gbkDecoder struct{}

func (gbkDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
	r, size := rune(0), 0
loop:
	for ; nSrc < len(src); nSrc += size {
		switch c0 := src[nSrc]; {
		case c0 < utf8.RuneSelf:
			r, size = rune(c0), 1

		// Microsoft's Code Page 936 extends GBK 1.0 to encode the euro sign U+20AC
		// as 0x80. The HTML5 specification at http://encoding.spec.whatwg.org/#gbk
		// says to treat "gbk" as Code Page 936.
		case c0 == 0x80:
			r, size = '€', 1

		case c0 < 0xff:
			if nSrc+1 >= len(src) {
				err = transform.ErrShortSrc
				break loop
			}
			c1 := src[nSrc+1]
			switch {
			case 0x40 <= c1 && c1 < 0x7f:
				c1 -= 0x40
			case 0x80 <= c1 && c1 < 0xff:
				c1 -= 0x41
			default:
				err = errInvalidGBK
				break loop
			}
			r, size = encoding.ASCIISub, 2
			if i := int(c0-0x81)*190 + int(c1); i < len(gbkDecode) {
				r = rune(gbkDecode[i])
				if r == 0 {
					r = encoding.ASCIISub
				}
			}

		default:
			err = errInvalidGBK
			break loop
		}

		if nDst+utf8.RuneLen(r) > len(dst) {
			err = transform.ErrShortDst
			break loop
		}
		nDst += utf8.EncodeRune(dst[nDst:], r)
	}
	if atEOF && err == transform.ErrShortSrc {
		err = errInvalidGBK
	}
	return nDst, nSrc, err
}

type gbkEncoder struct{}

func (gbkEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
	r, size := rune(0), 0
	for ; nSrc < len(src); nSrc += size {
		r = rune(src[nSrc])

		// Decode a 1-byte rune.
		if r < utf8.RuneSelf {
			size = 1

		} else {
			// Decode a multi-byte rune.
			r, size = utf8.DecodeRune(src[nSrc:])
			if size == 1 {
				// All valid runes of size 1 (those below utf8.RuneSelf) were
				// handled above. We have invalid UTF-8 or we haven't seen the
				// full character yet.
				if !atEOF && !utf8.FullRune(src[nSrc:]) {
					err = transform.ErrShortSrc
					break
				}
			}

			switch {
			case gbkEncode0Low <= r && r < gbkEncode0High:
				if r = rune(gbkEncode0[r-gbkEncode0Low]); r != 0 {
					goto write2
				}
			case gbkEncode1Low <= r && r < gbkEncode1High:
				// Microsoft's Code Page 936 extends GBK 1.0 to encode the euro sign U+20AC
				// as 0x80. The HTML5 specification at http://encoding.spec.whatwg.org/#gbk
				// says to treat "gbk" as Code Page 936.
				if r == '€' {
					r = 0x80
					goto write1
				}
				if r = rune(gbkEncode1[r-gbkEncode1Low]); r != 0 {
					goto write2
				}
			case gbkEncode2Low <= r && r < gbkEncode2High:
				if r = rune(gbkEncode2[r-gbkEncode2Low]); r != 0 {
					goto write2
				}
			case gbkEncode3Low <= r && r < gbkEncode3High:
				if r = rune(gbkEncode3[r-gbkEncode3Low]); r != 0 {
					goto write2
				}
			case gbkEncode4Low <= r && r < gbkEncode4High:
				if r = rune(gbkEncode4[r-gbkEncode4Low]); r != 0 {
					goto write2
				}
			}
			r = encoding.ASCIISub
		}

	write1:
		if nDst >= len(dst) {
			err = transform.ErrShortDst
			break
		}
		dst[nDst] = uint8(r)
		nDst++
		continue

	write2:
		if nDst+2 > len(dst) {
			err = transform.ErrShortDst
			break
		}
		dst[nDst+0] = uint8(r >> 8)
		dst[nDst+1] = uint8(r)
		nDst += 2
		continue
	}
	return nDst, nSrc, err
}
<|endoftext|>


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

import (
	"bytes"
	"fmt"
	"go/build"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
)

func ensureVendor() {
	var buf bytes.Buffer
	cmd := exec.Command("vendor")
	cmd.Stderr = &buf
	cmd.Run()
	if !strings.HasPrefix(buf.String(), "Usage: vendor") {
		fmt.Fprintln(os.Stderr, "The save/restore functionality uses 'vendor'.")
		fmt.Fprintln(os.Stderr, "To install vendor, 'go get github.com/skelterjohn/vendor'.")
		os.Exit(1)
	}
}

func save(w *workspace) {
	ensureVendor()

	var buf bytes.Buffer
	os.Setenv("GOPATH", w.gopath())
	cmd := exec.Command("go", "list", "-f", "{{range .Deps}}{{.}}\n{{end}}", "./src/...")
	cmd.Dir = w.root
	cmd.Stdout = &buf
	orExit(cmd.Run())

	goroot := runtime.GOROOT()
	build.Default.GOPATH = w.gopath()

	pkgs := map[string]string{}
	for _, pkg := range strings.Split(buf.String(), "\n") {
		if pkg == "" {
			continue
		}
		p, err := build.Import(pkg, w.root, build.FindOnly)
		if err != nil {
			continue
		}
		if strings.HasPrefix(p.Dir, goroot+"/") {
			continue
		}
		pkgs[pkg] = p.Dir
	}

	var addonArgs []string

	for pkg, dir := range pkgs {
		addonArgs = append(addonArgs, "-a", filepath.Join("src", pkg)+"="+dir)

	}
	w.shellOutToVendor(
		append([]string{"wgo", "vendor", "-s"}, addonArgs...))
}

func restore(w *workspace) {
	ensureVendor()

	w.shellOutToVendor([]string{"wgo", "vendor", "-r"})
}
<commit_msg>Fix saving when there are alternative GOPATHs, and making sure things don't get doubled.<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main

import (
	"bytes"
	"fmt"
	"go/build"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
)

func ensureVendor() {
	var buf bytes.Buffer
	cmd := exec.Command("vendor")
	cmd.Stderr = &buf
	cmd.Run()
	if !strings.HasPrefix(buf.String(), "Usage: vendor") {
		fmt.Fprintln(os.Stderr, "The save/restore functionality uses 'vendor'.")
		fmt.Fprintln(os.Stderr, "To install vendor, 'go get github.com/skelterjohn/vendor'.")
		os.Exit(1)
	}
}

func save(w *workspace) {
	ensureVendor()

	var buf bytes.Buffer
	os.Setenv("GOPATH", w.gopath())
	for _, gopath := range w.gopaths {
		cmd := exec.Command("go", "list", "-f", "{{range .Deps}}{{.}}\n{{end}}", "./"+gopath+"/...")
		cmd.Dir = w.root
		cmd.Stdout = &buf
		orExit(cmd.Run())
	}

	goroot := runtime.GOROOT()
	build.Default.GOPATH = w.gopath()

	pkgs := map[string]string{}
	for _, pkg := range strings.Split(buf.String(), "\n") {
		if pkg == "" {
			continue
		}
		p, err := build.Import(pkg, w.root, build.FindOnly)
		if err != nil {
			continue
		}
		if _, err := filepath.Rel(goroot, p.Dir); err == nil {
			continue
		}
		pkgs[pkg] = p.Dir
	}

	firstGopath := "."
	if len(w.gopaths) != 0 {
		firstGopath = w.gopaths[0]
	}

	addonMapping := map[string]string{}
	for pkg, dir := range pkgs {
		destination := filepath.Join(firstGopath, "src", pkg)
		// if it's already in here, vendor will pick it up
		if !filepath.IsAbs(dir) {
			continue
		}
		if _, err := filepath.Rel(w.root, dir); err == nil {
			continue
		}
		addonMapping[destination] = dir
	}

	var addonArgs []string
	for destination, dir := range addonMapping {
		addonArgs = append(addonArgs, "-a", destination+"="+dir)
	}
	w.shellOutToVendor(
		append([]string{"wgo", "vendor", "-s"}, addonArgs...))
}

func restore(w *workspace) {
	ensureVendor()

	w.shellOutToVendor([]string{"wgo", "vendor", "-r"})
}
<|endoftext|>


<commit_before>// Copyright (C) 2015 Thomas de Zeeuw.
//
// Licensed onder the MIT license that can be found in the LICENSE file.

package ini

import (
	"fmt"
	"math"
	"reflect"
	"strconv"
	"strings"
	"time"
)

func setReflectValue(keyValue *reflect.Value, value string) error {
	// todo: improve the detection of time.Duration and time.Time.
	if tStr := fmt.Sprintf("%v", keyValue.Type()); tStr == "time.Duration" {
		return setDuration(keyValue, value)
	} else if tStr == "time.Time" {
		return setTime(keyValue, value)
	}

	switch keyValue.Kind() {
	case reflect.String:
		keyValue.SetString(value)
	case reflect.Bool:
		return setBool(keyValue, value)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return setInt(keyValue, value)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
		reflect.Uint64, reflect.Uintptr:
		return setUint(keyValue, value)
	case reflect.Float32, reflect.Float64:
		return setFloat(keyValue, value)
	case reflect.Slice:
		return setSlice(keyValue, value)
	}

	return nil
}

// SetSlice sets a slice of reflected value.
func setSlice(f *reflect.Value, value string) error {
	if !f.IsValid() || !f.CanSet() {
		return nil
	}

	values := strings.Split(value, ",")
	for i, value := range values {
		values[i] = strings.TrimSpace(value)
	}

	// todo: clean up switch
	switch f.Type().Elem().Kind() {
	case reflect.String:
		f.Set(reflect.ValueOf(values))
	case reflect.Bool:
		var bs []bool
		for _, value := range values {
			bs = append(bs, parseBool(value))
		}
		f.Set(reflect.ValueOf(bs))
	case reflect.Int:
		var ints []int
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			}
			ints = append(ints, i)
		}
		f.Set(reflect.ValueOf(ints))
	case reflect.Int8:
		var ints []int8
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			} else if i > math.MaxInt8 || i < math.MinInt8 {
				return fmt.Errorf("ini: %d overflows %q", i, f.Type().Elem().Kind())
			}
			ints = append(ints, int8(i))
		}
		f.Set(reflect.ValueOf(ints))
	case reflect.Int16:
		var ints []int16
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			} else if i > math.MaxInt16 || i < math.MinInt16 {
				return fmt.Errorf("ini: %d overflows %q", i, f.Type().Elem().Kind())
			}
			ints = append(ints, int16(i))
		}
		f.Set(reflect.ValueOf(ints))
	case reflect.Int32:
		var ints []int32
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			} else if i > math.MaxInt32 || i < math.MinInt32 {
				return fmt.Errorf("ini: %d overflows %q", i, f.Type().Elem().Kind())
			}
			ints = append(ints, int32(i))
		}
		f.Set(reflect.ValueOf(ints))
	case reflect.Int64:
		var ints []int64
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			}
			ints = append(ints, int64(i))
		}
		f.Set(reflect.ValueOf(ints))
	case reflect.Uint:
		var is []uint
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			} else if i < 0 {
				return fmt.Errorf("ini: %d overflows %q", i, f.Type().Elem().Kind())
			}
			is = append(is, uint(i))
		}
		f.Set(reflect.ValueOf(is))
	case reflect.Uint8:
		var is []uint8
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			} else if i > math.MaxUint8 || i < 0 {
				return fmt.Errorf("ini: %d overflows %q", i, f.Type().Elem().Kind())
			}
			is = append(is, uint8(i))
		}
		f.Set(reflect.ValueOf(is))
	case reflect.Uint16:
		var is []uint16
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			} else if i > math.MaxUint16 || i < 0 {
				return fmt.Errorf("ini: %d overflows %q", i, f.Type().Elem().Kind())
			}
			is = append(is, uint16(i))
		}
		f.Set(reflect.ValueOf(is))
	case reflect.Uint32:
		var is []uint32
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			} else if i > math.MaxUint32 || i < 0 {
				return fmt.Errorf("ini: %d overflows %q", i, f.Type().Elem().Kind())
			}
			is = append(is, uint32(i))
		}
		f.Set(reflect.ValueOf(is))
	case reflect.Uint64:
		var is []uint64
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			}
			is = append(is, uint64(i))
		}
		f.Set(reflect.ValueOf(is))
	case reflect.Uintptr:
		var is []uintptr
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			} else if i > math.MaxUint8 || i < 0 {
				return fmt.Errorf("ini: %d overflows %q", i, f.Type().Elem().Kind())
			}
			is = append(is, uintptr(i))
		}
		f.Set(reflect.ValueOf(is))
	case reflect.Float32:
		var fs []float32
		for _, value := range values {
			fv, err := strconv.ParseFloat(value, 64)
			if err != nil {
				return err
			} else if fv > math.MaxFloat32 {
				return fmt.Errorf("ini: %f overflows %q", fv, f.Type().Elem().Kind())
			}
			fs = append(fs, float32(fv))
		}
		f.Set(reflect.ValueOf(fs))
	case reflect.Float64:
		var fs []float64
		for _, value := range values {
			f, err := strconv.ParseFloat(value, 64)
			if err != nil {
				return err
			}
			fs = append(fs, f)
		}
		f.Set(reflect.ValueOf(fs))
	}

	return nil
}

func setDuration(keyValue *reflect.Value, value string) error {
	duration, err := time.ParseDuration(value)
	if err != nil {
		return createConvertError(value, "time.Duration")
	}

	durationValue := reflect.ValueOf(duration)
	keyValue.Set(durationValue)
	return nil
}

func setTime(keyValue *reflect.Value, value string) error {
	for _, format := range timeFormats {
		t, err := time.Parse(format, value)
		if err == nil {
			timeValue := reflect.ValueOf(t)
			keyValue.Set(timeValue)
			return nil
		}
	}

	return createConvertError(value, "time.Time")
}

// Returns true on "1" and "true", anything returns false.
func setBool(keyValue *reflect.Value, value string) error {
	b := parseBool(value)
	keyValue.SetBool(b)
	return nil
}

func parseBool(value string) bool {
	var b bool
	value = strings.TrimSpace(value)
	if value == "1" || strings.ToLower(value) == "true" {
		b = true
	}
	return b
}

func setInt(keyValue *reflect.Value, value string) error {
	i, err := strconv.Atoi(value)
	if err != nil {
		return createConvertError(value, keyValue.Kind().String())
	}
	i64 := int64(i)

	if keyValue.OverflowInt(i64) {
		createOverflowError(value, keyValue.Kind().String())
	}

	keyValue.SetInt(i64)
	return nil
}

func setUint(keyValue *reflect.Value, value string) error {
	i, err := strconv.Atoi(value)
	if err != nil {
		return createConvertError(value, keyValue.Kind().String())
	}
	ui64 := uint64(i)

	if keyValue.OverflowUint(ui64) {
		createOverflowError(value, keyValue.Kind().String())
	}

	keyValue.SetUint(ui64)
	return nil
}

func setFloat(keyValue *reflect.Value, value string) error {
	f, err := strconv.ParseFloat(value, 64)
	if err != nil {
		return createConvertError(value, keyValue.Kind().String())
	}

	if keyValue.OverflowFloat(f) {
		createOverflowError(value, keyValue.Kind().String())
	}

	keyValue.SetFloat(f)
	return nil
}

// Rename the key to a public name of a struct, e.g.
//
//	"my key" -> "MyKey"
func renameToPublicName(name string) string {
	name = strings.Title(name)
	name = strings.Replace(name, " ", "", -1)
	return name
}

func getSectionValue(keyValue reflect.Value, sectionName string) reflect.Value {
	if sectionName == Global {
		return keyValue
	}

	sectionName = renameToPublicName(sectionName)
	return keyValue.FieldByName(sectionName)
}

func createOverflowError(value, t string) error {
	return fmt.Errorf("can't convert %q to type %s, it overflows type %s",
		value, t, t)
}

func createConvertError(value, t string) error {
	return fmt.Errorf("can't convert %q to type %s", value, t)
}
<commit_msg>Better type detection in setReflectValue<commit_after>// Copyright (C) 2015 Thomas de Zeeuw.
//
// Licensed onder the MIT license that can be found in the LICENSE file.

package ini

import (
	"fmt"
	"math"
	"reflect"
	"strconv"
	"strings"
	"time"
)

var (
	typeString   = reflect.TypeOf("")
	typeBool     = reflect.TypeOf(true)
	typeInt      = reflect.TypeOf(int(1))
	typeInt8     = reflect.TypeOf(int8(1))
	typeInt16    = reflect.TypeOf(int16(1))
	typeInt32    = reflect.TypeOf(int32(1))
	typeInt64    = reflect.TypeOf(int64(1))
	typeUint     = reflect.TypeOf(uint(1))
	typeUint8    = reflect.TypeOf(uint8(1))
	typeUint16   = reflect.TypeOf(uint16(1))
	typeUint32   = reflect.TypeOf(uint32(1))
	typeUint64   = reflect.TypeOf(uint64(1))
	typeFloat32  = reflect.TypeOf(float32(1.0))
	typeFloat64  = reflect.TypeOf(float64(1.0))
	typeDuration = reflect.TypeOf(time.Nanosecond)
	typeTime     = reflect.TypeOf(time.Time{})
)

func setReflectValue(keyValue *reflect.Value, value string) error {
	if keyValue.Kind() == reflect.Slice {
		return setSlice(keyValue, value)
	}

	// todo: (maybe) add map and struct (convert to a section maybe?).
	switch keyValue.Type() {
	case typeString:
		keyValue.SetString(value)
	case typeBool:
		return setBool(keyValue, value)
	case typeInt, typeInt8, typeInt16, typeInt32, typeInt64:
		return setInt(keyValue, value)
	case typeUint, typeUint8, typeUint16, typeUint32, typeUint64:
		return setUint(keyValue, value)
	case typeFloat32, typeFloat64:
		return setFloat(keyValue, value)
	case typeDuration:
		return setDuration(keyValue, value)
	case typeTime:
		return setTime(keyValue, value)
	}

	return nil
}

// SetSlice sets a slice of reflected value.
func setSlice(f *reflect.Value, value string) error {
	if !f.IsValid() || !f.CanSet() {
		return nil
	}

	values := strings.Split(value, ",")
	for i, value := range values {
		values[i] = strings.TrimSpace(value)
	}

	// todo: clean up switch
	switch f.Type().Elem().Kind() {
	case reflect.String:
		f.Set(reflect.ValueOf(values))
	case reflect.Bool:
		var bs []bool
		for _, value := range values {
			bs = append(bs, parseBool(value))
		}
		f.Set(reflect.ValueOf(bs))
	case reflect.Int:
		var ints []int
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			}
			ints = append(ints, i)
		}
		f.Set(reflect.ValueOf(ints))
	case reflect.Int8:
		var ints []int8
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			} else if i > math.MaxInt8 || i < math.MinInt8 {
				return fmt.Errorf("ini: %d overflows %q", i, f.Type().Elem().Kind())
			}
			ints = append(ints, int8(i))
		}
		f.Set(reflect.ValueOf(ints))
	case reflect.Int16:
		var ints []int16
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			} else if i > math.MaxInt16 || i < math.MinInt16 {
				return fmt.Errorf("ini: %d overflows %q", i, f.Type().Elem().Kind())
			}
			ints = append(ints, int16(i))
		}
		f.Set(reflect.ValueOf(ints))
	case reflect.Int32:
		var ints []int32
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			} else if i > math.MaxInt32 || i < math.MinInt32 {
				return fmt.Errorf("ini: %d overflows %q", i, f.Type().Elem().Kind())
			}
			ints = append(ints, int32(i))
		}
		f.Set(reflect.ValueOf(ints))
	case reflect.Int64:
		var ints []int64
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			}
			ints = append(ints, int64(i))
		}
		f.Set(reflect.ValueOf(ints))
	case reflect.Uint:
		var is []uint
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			} else if i < 0 {
				return fmt.Errorf("ini: %d overflows %q", i, f.Type().Elem().Kind())
			}
			is = append(is, uint(i))
		}
		f.Set(reflect.ValueOf(is))
	case reflect.Uint8:
		var is []uint8
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			} else if i > math.MaxUint8 || i < 0 {
				return fmt.Errorf("ini: %d overflows %q", i, f.Type().Elem().Kind())
			}
			is = append(is, uint8(i))
		}
		f.Set(reflect.ValueOf(is))
	case reflect.Uint16:
		var is []uint16
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			} else if i > math.MaxUint16 || i < 0 {
				return fmt.Errorf("ini: %d overflows %q", i, f.Type().Elem().Kind())
			}
			is = append(is, uint16(i))
		}
		f.Set(reflect.ValueOf(is))
	case reflect.Uint32:
		var is []uint32
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			} else if i > math.MaxUint32 || i < 0 {
				return fmt.Errorf("ini: %d overflows %q", i, f.Type().Elem().Kind())
			}
			is = append(is, uint32(i))
		}
		f.Set(reflect.ValueOf(is))
	case reflect.Uint64:
		var is []uint64
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			}
			is = append(is, uint64(i))
		}
		f.Set(reflect.ValueOf(is))
	case reflect.Uintptr:
		var is []uintptr
		for _, value := range values {
			i, err := strconv.Atoi(value)
			if err != nil {
				return err
			} else if i > math.MaxUint8 || i < 0 {
				return fmt.Errorf("ini: %d overflows %q", i, f.Type().Elem().Kind())
			}
			is = append(is, uintptr(i))
		}
		f.Set(reflect.ValueOf(is))
	case reflect.Float32:
		var fs []float32
		for _, value := range values {
			fv, err := strconv.ParseFloat(value, 64)
			if err != nil {
				return err
			} else if fv > math.MaxFloat32 {
				return fmt.Errorf("ini: %f overflows %q", fv, f.Type().Elem().Kind())
			}
			fs = append(fs, float32(fv))
		}
		f.Set(reflect.ValueOf(fs))
	case reflect.Float64:
		var fs []float64
		for _, value := range values {
			f, err := strconv.ParseFloat(value, 64)
			if err != nil {
				return err
			}
			fs = append(fs, f)
		}
		f.Set(reflect.ValueOf(fs))
	}

	return nil
}

func setDuration(keyValue *reflect.Value, value string) error {
	duration, err := time.ParseDuration(value)
	if err != nil {
		return createConvertError(value, "time.Duration")
	}

	durationValue := reflect.ValueOf(duration)
	keyValue.Set(durationValue)
	return nil
}

func setTime(keyValue *reflect.Value, value string) error {
	for _, format := range timeFormats {
		t, err := time.Parse(format, value)
		if err == nil {
			timeValue := reflect.ValueOf(t)
			keyValue.Set(timeValue)
			return nil
		}
	}

	return createConvertError(value, "time.Time")
}

// Returns true on "1" and "true", anything returns false.
func setBool(keyValue *reflect.Value, value string) error {
	b := parseBool(value)
	keyValue.SetBool(b)
	return nil
}

func parseBool(value string) bool {
	var b bool
	value = strings.TrimSpace(value)
	if value == "1" || strings.ToLower(value) == "true" {
		b = true
	}
	return b
}

func setInt(keyValue *reflect.Value, value string) error {
	i, err := strconv.Atoi(value)
	if err != nil {
		return createConvertError(value, keyValue.Kind().String())
	}
	i64 := int64(i)

	if keyValue.OverflowInt(i64) {
		createOverflowError(value, keyValue.Kind().String())
	}

	keyValue.SetInt(i64)
	return nil
}

func setUint(keyValue *reflect.Value, value string) error {
	i, err := strconv.Atoi(value)
	if err != nil {
		return createConvertError(value, keyValue.Kind().String())
	}
	ui64 := uint64(i)

	if keyValue.OverflowUint(ui64) {
		createOverflowError(value, keyValue.Kind().String())
	}

	keyValue.SetUint(ui64)
	return nil
}

func setFloat(keyValue *reflect.Value, value string) error {
	f, err := strconv.ParseFloat(value, 64)
	if err != nil {
		return createConvertError(value, keyValue.Kind().String())
	}

	if keyValue.OverflowFloat(f) {
		createOverflowError(value, keyValue.Kind().String())
	}

	keyValue.SetFloat(f)
	return nil
}

// Rename the key to a public name of a struct, e.g.
//
//	"my key" -> "MyKey"
func renameToPublicName(name string) string {
	name = strings.Title(name)
	name = strings.Replace(name, " ", "", -1)
	return name
}

func getSectionValue(keyValue reflect.Value, sectionName string) reflect.Value {
	if sectionName == Global {
		return keyValue
	}

	sectionName = renameToPublicName(sectionName)
	return keyValue.FieldByName(sectionName)
}

func createOverflowError(value, t string) error {
	return fmt.Errorf("can't convert %q to type %s, it overflows type %s",
		value, t, t)
}

func createConvertError(value, t string) error {
	return fmt.Errorf("can't convert %q to type %s", value, t)
}
<|endoftext|>


<commit_before>package sdnv

import (
	"errors"
	"reflect"
)

// Sdnv struct containing the value and
// encoded byte length information
type Sdnv struct {
	Value  uint64
	EncLen uint64
}

// NewSdnv creates and initializes
// a new sdnv struct
func NewSdnv(val uint64) *Sdnv {
	s := new(Sdnv)
	s.Value = val
	return s
}

// Marshal returns and sdnv encoded byte array
func (s Sdnv) Marshal() []byte {
	data := []byte{}
	flag := byte(0)
	done := false
	for done == false {
		newbits := byte(s.Value & 0x7f)
		s.Value = s.Value >> 7
		newbyte := byte(newbits + flag)
		data = append([]byte{newbyte}, data...)
		if flag == 0 {
			flag = 0x80
		}
		if s.Value == 0 {
			done = true
		}
	}
	return data
}

// Unmarshal unencodes a byte array into an
// sdnv structure
func (s *Sdnv) Unmarshal(data []byte) error {
	s.Value = uint64(0)
	s.EncLen = 0
	length := int(reflect.TypeOf(s.Value).Size())
	if len(data) < length {
		length = len(data)
	}
	for i := 0; i < length; i++ {
		s.Value = s.Value << 7
		s.Value = s.Value + uint64(data[i]&0x7f)
		if (data[i] >> 7) == 0 {
			s.EncLen += 1
			break
		} else if i == (length - 1) {
			return errors.New("Reached end of input without seeing end of SDNV")
		}
	}
	return nil
}
<commit_msg>complying with golint<commit_after>package sdnv

import (
	"errors"
	"reflect"
)

// Sdnv struct containing the value and
// encoded byte length information
type Sdnv struct {
	Value  uint64
	EncLen uint64
}

// NewSdnv creates and initializes
// a new sdnv struct
func NewSdnv(val uint64) *Sdnv {
	s := new(Sdnv)
	s.Value = val
	return s
}

// Marshal returns and sdnv encoded byte array
func (s Sdnv) Marshal() []byte {
	data := []byte{}
	flag := byte(0)
	done := false
	for done == false {
		newbits := byte(s.Value & 0x7f)
		s.Value = s.Value >> 7
		newbyte := byte(newbits + flag)
		data = append([]byte{newbyte}, data...)
		if flag == 0 {
			flag = 0x80
		}
		if s.Value == 0 {
			done = true
		}
	}
	return data
}

// Unmarshal unencodes a byte array into an
// sdnv structure
func (s *Sdnv) Unmarshal(data []byte) error {
	s.Value = uint64(0)
	s.EncLen = 0
	length := int(reflect.TypeOf(s.Value).Size())
	if len(data) < length {
		length = len(data)
	}
	for i := 0; i < length; i++ {
		s.Value = s.Value << 7
		s.Value = s.Value + uint64(data[i]&0x7f)
		if (data[i] >> 7) == 0 {
			s.EncLen++
			break
		} else if i == (length - 1) {
			return errors.New("Reached end of input without seeing end of SDNV")
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/log4go"
	"errors"
	"flag"
	"fmt"
	"gopkg.in/mgo.v2"
	"net/url"
	"os"
	"runtime"
	"strings"
	"time"
)

var (
	logdebug  = flag.Bool("v", false, "debug")
	logfinest = flag.Bool("vv", false, "super debug")
	logStats  = flag.Bool("stats", false, "show period mongostats")
)

var (
	source_uri = flag.String("s", "", "set the source mongo uri")
	dest_uri   = flag.String("d", "", "set the destination mongo uri")
)

var (
	// you won't need these for normal use
	from = flag.String("from", "now", "begin processing the oplog at this timestamp.  accepts timestemps in the form -from=+1 (now + 1 second), -from=now, -from=12345,123 (seconds, iterations)")
	to   = flag.String("to", "+86400", "begin processing the oplog at this timestamp.  accepts timestemps in the form -to=+1 (now + 1 second), -to=now, -to=12345,123 (seconds, iterations)")
)

var (
	initial_sync      = flag.Bool("i", false, "perform an initial sync")
	forceTableScan    = flag.Bool("forceTableScan", false, "don't sort by ids in initial sync. (sorting by _id can sometimes miss documents if _id is a non ObjectId())")
	forceIndexBuild   = flag.String("forceindex", "", "force Index builds on to either forground or background.  -forceindex foreground OR -forceindex background")
	replay_oplog      = flag.Bool("o", false, "replay the oplog from -from to -to")
	oplog_name        = flag.String("oplog", "oplog.rs", "the name of the oplog to use")
	ignore_errors     = flag.Bool("f", true, "force oplog sync, even if counts don't quite match (hope they match after oplog is sync'd)")
	allDbs            = flag.Bool("allDbs", false, "copy all the databases from the source to the destination")
	ignoreSslError    = flag.Bool("ignoreSslError", false, "ignore validation of SSL certificate")
	connectionTimeout = flag.Int("connectionTimeout", 60, "connection timeout in seconds")
)

var logger log4go.Logger

const (
	BUFFER_SIZE     = 2500
	GOPOOL_SIZE     = 1
	MAX_BUFFER_SIZE = 4e7
)

func main() {
	flag.Parse()
	logger = initLogger()

	runtime.GOMAXPROCS(runtime.NumCPU())

	if *source_uri == "" || *dest_uri == "" {
		Quit(1, errors.New("Provide both source and destination URIs"))
	}

	switch *forceIndexBuild {
	case "bg", "background":
		*forceIndexBuild = "bg"
	case "fg", "foreground":
		*forceIndexBuild = "fg"
	case "im", "immediate":
		*forceIndexBuild = "im"
	case "":
		*forceIndexBuild = ""
	default:
		Quit(1, errors.New("Please provide a valid forceindex, either foreground or background, or immediate"))
	}

	if *source_uri == *dest_uri {
		Quit(1, errors.New("Source and destination can't be the same"))
	}

	// establish connections to source and destination mongos
	srcURI, srcDB, err := adminifyURI(*source_uri)
	if err != nil {
		Quit(1, err)
	}

	srcTarget := NewMongoTarget(srcURI, srcDB)
	err = srcTarget.Dial()
	source := srcTarget.dst
	if err != nil {
		logger.Critical("Cannot dial %s\n, %v", srcURI.String(), err)
		Quit(1, err)
	}
	source.SetMode(mgo.Monotonic, false)

	dstURI, dstDB, err := adminifyURI(*dest_uri)
	if err != nil {
		Quit(1, err)
	}

	target := NewTarget(dstURI, dstDB)
	err = target.Dial()
	if err != nil {
		Quit(1, fmt.Errorf("Cannot dial %s\n, %v", *dest_uri, err))
	}

	from := NewTimestamp(*from)
	to := NewTimestamp(*to)

	if *logStats {
		// periodically dump some mongo stats to stdout
		mgo.SetStats(true)
		statsChan := time.Tick(2 * time.Second)
		go func(c <-chan time.Time) {
			for _ = range c {
				s := mgo.GetStats()
				logger.Info("Sockets in use: %d, sentOps: %d, receivedOps: %d, receivedDocs: %d", s.SocketsInUse, s.SentOps, s.ReceivedOps, s.ReceivedDocs)
			}
		}(statsChan)
	}

	if *replay_oplog {
		//from, err = CurrentOplogTimestamp(source)
		//if err != nil {
		//	Quit(1, errors.New("unable to get most recent oplog timestamp"))
		//}
		logger.Info("Oplog at: %s", from)
	}
	if *initial_sync {
		logger.Info("copying data from %s to %s.", *source_uri, *dest_uri)
		// copying all the databases
		if *allDbs {
			databaseNames, err := source.DatabaseNames()
			if err != nil {
				Quit(1, err)
			}
			for _, d := range databaseNames {
				if isSpecialDatabase(d) {
					continue
				}
				logger.Info("Copying db " + d)
				target.DB(d)

				if err := target.Sync(source, srcURI, d); err != nil {
					Quit(1, err)
				}
			}

		} else {
			// copying one database
			if err := target.Sync(source, srcURI, srcDB); err != nil {
				Quit(1, err)
			}
		}
		logger.Info("initial sync completed")
	}

	if *replay_oplog {
		player, err := newLogReplayer(source, target, from, to, srcDB, dstDB)
		if err != nil {
			logger.Critical("Could not initialize logReplayer: %s", err)
			Quit(1, err)
		}

		err = player.playLog()
		if err != nil {
			logger.Error(err)
		}
	}
	Quit(0, nil)
}

func initLogger() log4go.Logger {
	loglevel := log4go.INFO
	if *logdebug {
		loglevel = log4go.DEBUG
	}
	if *logfinest {
		loglevel = log4go.FINEST
	}

	return log4go.NewDefaultLogger(loglevel)
}

func Quit(code int, err error) {
	if err != nil {
		logger.Critical("Failed: " + err.Error())
	}
	time.Sleep(100 * time.Millisecond)
	logger.Close()
	os.Exit(code)
}

func adminifyURI(s string) (*url.URL, string, error) {
	uri, err := url.Parse(s)
	if err != nil {
		return uri, "", err
	}

	db := uri.Path
	if len(db) < 2 {
		return uri, "", fmt.Errorf("invalid database name")
	}
	if db[0] == '/' {
		db = db[1:]
	}

	uri.Path = "/admin"

	logger.Debug("Connecting to URL: %v", uri)

	return uri, db, nil
}

// is the database a special database
var specialDatabases = []string{"admin", "local", "test", "config"}

func isSpecialDatabase(item string) bool {
	item = strings.ToLower(item)
	for _, v := range specialDatabases {
		if v == item {
			return true
		}
	}
	return false
}
<commit_msg>still some - 7<commit_after>package main

import (
	"code.google.com/p/log4go"
	"errors"
	"flag"
	"fmt"
	"gopkg.in/mgo.v2"
	"net/url"
	"os"
	"runtime"
	"strings"
	"time"
)

var (
	logdebug  = flag.Bool("v", false, "debug")
	logfinest = flag.Bool("vv", false, "super debug")
	logStats  = flag.Bool("stats", false, "show period mongostats")
)

var (
	source_uri = flag.String("s", "", "set the source mongo uri")
	dest_uri   = flag.String("d", "", "set the destination mongo uri")
)

var (
	// you won't need these for normal use
	from = flag.String("from", "now", "begin processing the oplog at this timestamp.  accepts timestemps in the form -from=+1 (now + 1 second), -from=now, -from=12345,123 (seconds, iterations)")
	to   = flag.String("to", "+86400", "begin processing the oplog at this timestamp.  accepts timestemps in the form -to=+1 (now + 1 second), -to=now, -to=12345,123 (seconds, iterations)")
)

var (
	initial_sync      = flag.Bool("i", false, "perform an initial sync")
	forceTableScan    = flag.Bool("forceTableScan", false, "don't sort by ids in initial sync. (sorting by _id can sometimes miss documents if _id is a non ObjectId())")
	forceIndexBuild   = flag.String("forceindex", "", "force Index builds on to either forground or background.  -forceindex foreground OR -forceindex background")
	replay_oplog      = flag.Bool("o", false, "replay the oplog from -from to -to")
	oplog_name        = flag.String("oplog", "oplog.rs", "the name of the oplog to use")
	ignore_errors     = flag.Bool("f", true, "force oplog sync, even if counts don't quite match (hope they match after oplog is sync'd)")
	allDbs            = flag.Bool("allDbs", false, "copy all the databases from the source to the destination")
	ignoreSslError    = flag.Bool("ignoreSslError", false, "ignore validation of SSL certificate")
	connectionTimeout = flag.Int("connectionTimeout", 60, "connection timeout in seconds")
)

var logger log4go.Logger

const (
	BUFFER_SIZE     = 500
	GOPOOL_SIZE     = 16
	MAX_BUFFER_SIZE = 4e7
)

func main() {
	flag.Parse()
	logger = initLogger()

	runtime.GOMAXPROCS(runtime.NumCPU())

	if *source_uri == "" || *dest_uri == "" {
		Quit(1, errors.New("Provide both source and destination URIs"))
	}

	switch *forceIndexBuild {
	case "bg", "background":
		*forceIndexBuild = "bg"
	case "fg", "foreground":
		*forceIndexBuild = "fg"
	case "im", "immediate":
		*forceIndexBuild = "im"
	case "":
		*forceIndexBuild = ""
	default:
		Quit(1, errors.New("Please provide a valid forceindex, either foreground or background, or immediate"))
	}

	if *source_uri == *dest_uri {
		Quit(1, errors.New("Source and destination can't be the same"))
	}

	// establish connections to source and destination mongos
	srcURI, srcDB, err := adminifyURI(*source_uri)
	if err != nil {
		Quit(1, err)
	}

	srcTarget := NewMongoTarget(srcURI, srcDB)
	err = srcTarget.Dial()
	source := srcTarget.dst
	if err != nil {
		logger.Critical("Cannot dial %s\n, %v", srcURI.String(), err)
		Quit(1, err)
	}
	source.SetMode(mgo.Monotonic, false)

	dstURI, dstDB, err := adminifyURI(*dest_uri)
	if err != nil {
		Quit(1, err)
	}

	target := NewTarget(dstURI, dstDB)
	err = target.Dial()
	if err != nil {
		Quit(1, fmt.Errorf("Cannot dial %s\n, %v", *dest_uri, err))
	}

	from := NewTimestamp(*from)
	to := NewTimestamp(*to)

	if *logStats {
		// periodically dump some mongo stats to stdout
		mgo.SetStats(true)
		statsChan := time.Tick(2 * time.Second)
		go func(c <-chan time.Time) {
			for _ = range c {
				s := mgo.GetStats()
				logger.Info("Sockets in use: %d, sentOps: %d, receivedOps: %d, receivedDocs: %d", s.SocketsInUse, s.SentOps, s.ReceivedOps, s.ReceivedDocs)
			}
		}(statsChan)
	}

	if *replay_oplog {
		//from, err = CurrentOplogTimestamp(source)
		//if err != nil {
		//	Quit(1, errors.New("unable to get most recent oplog timestamp"))
		//}
		logger.Info("Oplog at: %s", from)
	}
	if *initial_sync {
		logger.Info("copying data from %s to %s.", *source_uri, *dest_uri)
		// copying all the databases
		if *allDbs {
			databaseNames, err := source.DatabaseNames()
			if err != nil {
				Quit(1, err)
			}
			for _, d := range databaseNames {
				if isSpecialDatabase(d) {
					continue
				}
				logger.Info("Copying db " + d)
				target.DB(d)

				if err := target.Sync(source, srcURI, d); err != nil {
					Quit(1, err)
				}
			}

		} else {
			// copying one database
			if err := target.Sync(source, srcURI, srcDB); err != nil {
				Quit(1, err)
			}
		}
		logger.Info("initial sync completed")
	}

	if *replay_oplog {
		player, err := newLogReplayer(source, target, from, to, srcDB, dstDB)
		if err != nil {
			logger.Critical("Could not initialize logReplayer: %s", err)
			Quit(1, err)
		}

		err = player.playLog()
		if err != nil {
			logger.Error(err)
		}
	}
	Quit(0, nil)
}

func initLogger() log4go.Logger {
	loglevel := log4go.INFO
	if *logdebug {
		loglevel = log4go.DEBUG
	}
	if *logfinest {
		loglevel = log4go.FINEST
	}

	return log4go.NewDefaultLogger(loglevel)
}

func Quit(code int, err error) {
	if err != nil {
		logger.Critical("Failed: " + err.Error())
	}
	time.Sleep(100 * time.Millisecond)
	logger.Close()
	os.Exit(code)
}

func adminifyURI(s string) (*url.URL, string, error) {
	uri, err := url.Parse(s)
	if err != nil {
		return uri, "", err
	}

	db := uri.Path
	if len(db) < 2 {
		return uri, "", fmt.Errorf("invalid database name")
	}
	if db[0] == '/' {
		db = db[1:]
	}

	uri.Path = "/admin"

	logger.Debug("Connecting to URL: %v", uri)

	return uri, db, nil
}

// is the database a special database
var specialDatabases = []string{"admin", "local", "test", "config"}

func isSpecialDatabase(item string) bool {
	item = strings.ToLower(item)
	for _, v := range specialDatabases {
		if v == item {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Datawise Systems Inc. All rights reserved.

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

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

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

// Utility to perform master election/failover using etcd.

package utils

import (
	"errors"
	"fmt"
	"runtime"
	"sync"
	"time"

	"github.com/coreos/go-etcd/etcd"
	"github.com/golang/glog"
)

const kRetrySleep time.Duration = 100 // milliseconds

// Various event types for the events channel.
type MasterEventType int

const (
	MasterAdded MasterEventType = iota // this node has the lock.
	MasterDeleted
	MasterModified
	MasterError
)

// MasterEvent represents a single event sent on the events channel.
type MasterEvent struct {
	Type   MasterEventType // event type
	Master string          // identity of the lock holder
}

// Interface used by the etcd master lock clients.
type MasterInterface interface {
	// Start the election and attempt to acquire the lock. If acquired, the
	// lock is refreshed periodically based on the ttl.
	Start()

	// Stops watching the lock. Closes the events channel.
	Stop()

	// Returns the event channel used by the etcd lock.
	EventsChan() <-chan MasterEvent

	// Method to get the current lockholder. Returns "" if free.
	GetHolder() string
}

// Internal structure to represent an etcd lock.
type etcdLock struct {
	sync.Mutex
	client        Registry         // etcd interface
	name          string           // name of the lock
	id            string           // identity of the lockholder
	ttl           uint64           // ttl of the lock
	enabled       bool             // Used to enable/disable the lock
	master        string           // Lock holder
	watchStopCh   chan bool        // To stop the watch
	eventsCh      chan MasterEvent // channel to send lock ownership updates
	stoppedCh     chan bool        // channel that waits for acquire to finish
	refreshStopCh chan bool        // channel used to stop the refresh routine
	holding       bool             // whether this node is holding the lock
	modifiedIndex uint64           // valid only when this node is holding the lock
}

// Method to create a new etcd lock.
func NewMaster(client Registry, name string, id string,
	ttl uint64) (MasterInterface, error) {
	// client is mandatory. Min ttl is 5 seconds.
	if client == nil || ttl < 5 {
		return nil, errors.New("Invalid args")
	}

	return &etcdLock{client: client, name: name, id: id, ttl: ttl,
		enabled:       false,
		master:        "",
		watchStopCh:   make(chan bool, 1),
		eventsCh:      make(chan MasterEvent, 1),
		stoppedCh:     make(chan bool, 1),
		refreshStopCh: make(chan bool, 1),
		holding:       false,
		modifiedIndex: 0}, nil
}

// Method to start the attempt to acquire the lock.
func (e *etcdLock) Start() {
	glog.Infof("Starting attempt to acquire lock %s", e.name)

	e.Lock()
	if e.enabled {
		// Already running
		glog.Warningf("Duplicate Start for lock %s", e.name)
		return
	}

	e.enabled = true
	e.Unlock()

	// Acquire in the background.
	go func() {
		// If acquire returns without error, exit. If not, acquire
		// crashed and needs to be called again.
		for {
			if err := e.acquire(); err == nil {
				break
			}
		}
	}()
}

// Method to stop the acquisition of lock and release it if holding the lock.
func (e *etcdLock) Stop() {
	glog.Infof("Stopping attempt to acquire lock %s", e.name)

	e.Lock()
	if !e.enabled {
		// Not running
		glog.Warningf("Duplicate Stop for lock %s", e.name)
		return
	}

	// Disable the lock and stop the watch.
	e.enabled = false
	e.Unlock()

	e.watchStopCh <- true

	// Wait for acquire to finish.
	<-e.stoppedCh
}

// Method to get the event channel used by the etcd lock.
func (e *etcdLock) EventsChan() <-chan MasterEvent {
	return e.eventsCh
}

// Method to get the lockholder.
func (e *etcdLock) GetHolder() string {
	e.Lock()
	defer e.Unlock()
	return e.master
}

// Method to acquire the lock. It launches another goroutine to refresh the ttl
// if successful in acquiring the lock.
func (e *etcdLock) acquire() (ret error) {
	defer func() {
		if r := recover(); r != nil {
			callers := ""
			for i := 0; true; i++ {
				_, file, line, ok := runtime.Caller(i)
				if !ok {
					break
				}
				callers = callers + fmt.Sprintf("%v:%v\n", file,
					line)
			}
			errMsg := fmt.Sprintf("Recovered from panic: %#v (%v)\n%v",
				r, r, callers)
			glog.Errorf(errMsg)
			ret = errors.New(errMsg)
		}
	}()

	var resp *etcd.Response
	// Initialize error to dummy.
	err := fmt.Errorf("Dummy error")

	for {
		// Stop was called, stop the refresh routine if needed and
		// abort the acquire routine.
		if !e.enabled {
			if e.holding {
				glog.V(2).Infof("Deleting lock %s", e.name)
				// Delete the lock so other nodes can get it sooner.
				// Otherwise, they have to wait until ttl expiry.
				if _, err = e.client.Delete(e.name, false); err != nil {
					glog.V(2).Infof("Failed to delete lock %s, "+
						"error %v", e.name, err)
				}
				e.holding = false
				e.refreshStopCh <- true
			}
			// Wont be able to track the master.
			e.Lock()
			e.master = ""
			e.Unlock()

			e.stoppedCh <- true
			break
		}

		// If there is an error (at the beginning or with watch) or if
		// the lock is deleted, try to get the lockholder/acquire the lock.
		if err != nil || resp.Node.Value == "" {
			resp, err = e.client.Get(e.name, false, false)
			if err != nil {
				if IsEtcdErrorNotFound(err) {
					// Try to acquire the lock.
					glog.V(2).Infof("Trying to acquire lock %s", e.name)
					resp, err = e.client.Create(e.name, e.id, e.ttl)
					if err != nil {
						// Failed to acquire the lock.
						continue
					}
				} else {
					glog.V(2).Infof("Failed to get lock %s, error: %v",
						e.name, err)
					time.Sleep(kRetrySleep * time.Millisecond)
					continue
				}
			}
		}

		if resp.Node.Value == e.id {
			// This node is the lock holder.
			if !e.holding {
				// If not already holding the lock, send an
				// event and start the refresh routine.
				glog.Infof("Acquired lock %s", e.name)
				e.holding = true
				e.eventsCh <- MasterEvent{Type: MasterAdded,
					Master: e.id}
				go e.refresh()
			}
		} else {
			// Some other node is the lock holder.
			if e.holding {
				// If previously holding the lock, stop the
				// refresh routine and send a deleted event.
				glog.Errorf("Lost lock %s to %s", e.name,
					resp.Node.Value)
				e.holding = false
				e.refreshStopCh <- true
				e.eventsCh <- MasterEvent{Type: MasterDeleted,
					Master: ""}
			}
			if e.master != resp.Node.Value {
				// If master changed, send a modified event.
				e.eventsCh <- MasterEvent{Type: MasterModified,
					Master: resp.Node.Value}
			}
		}

		// Record the new master and modified index.
		e.Lock()
		e.master = resp.Node.Value
		e.Unlock()
		e.modifiedIndex = resp.Node.ModifiedIndex

		var prevIndex uint64

		// Intent is to start the watch using EtcdIndex. Sometimes, etcd
		// is returning EtcdIndex lower than ModifiedIndex. In such
		// cases, use ModifiedIndex to set the watch.
		// TODO: Change this code when etcd behavior changes.
		if resp.EtcdIndex < resp.Node.ModifiedIndex {
			prevIndex = resp.Node.ModifiedIndex + 1
		} else {
			prevIndex = resp.EtcdIndex + 1
		}

		// Start watching for changes to lock.
		resp, err = e.client.Watch(e.name, prevIndex, false, nil, e.watchStopCh)
		if IsEtcdErrorWatchStoppedByUser(err) {
			glog.Infof("Watch for lock %s stopped by user", e.name)
		} else if err != nil {
			// Log only if its not too old event index error.
			if !IsEtcdErrorEventIndexCleared(err) {
				glog.Errorf("Failed to watch lock %s, error %v",
					e.name, err)
			}
		}
	}

	return nil
}

// Method to refresh the lock. It refreshes the ttl at ttl*4/10 interval.
func (e *etcdLock) refresh() {
	for {
		select {
		case <-e.refreshStopCh:
			glog.V(2).Infof("Stopping refresh for lock %s", e.name)
			// Lock released.
			return
		case <-time.After(time.Second * time.Duration(e.ttl*4/10)):
			// Uses CompareAndSwap to protect against the case where a
			// watch is received with a "delete" and refresh routine is
			// still running.
			if resp, err := e.client.CompareAndSwap(e.name, e.id, e.ttl,
				e.id, e.modifiedIndex); err != nil {
				// Failure here could mean that some other node
				// acquired the lock. Should also get a watch
				// notification if that happens and this go routine
				// is stopped there.
				glog.Errorf("Failed to set the ttl for lock %s with "+
					"error: %s", e.name, err.Error())
			} else {
				e.modifiedIndex = resp.Node.ModifiedIndex
			}
		}
	}
}
<commit_msg>Unlock mutex in start & release paths<commit_after>/*
Copyright 2014 Datawise Systems Inc. All rights reserved.

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

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

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

// Utility to perform master election/failover using etcd.

package utils

import (
	"errors"
	"fmt"
	"runtime"
	"sync"
	"time"

	"github.com/coreos/go-etcd/etcd"
	"github.com/golang/glog"
)

const kRetrySleep time.Duration = 100 // milliseconds

// Various event types for the events channel.
type MasterEventType int

const (
	MasterAdded MasterEventType = iota // this node has the lock.
	MasterDeleted
	MasterModified
	MasterError
)

// MasterEvent represents a single event sent on the events channel.
type MasterEvent struct {
	Type   MasterEventType // event type
	Master string          // identity of the lock holder
}

// Interface used by the etcd master lock clients.
type MasterInterface interface {
	// Start the election and attempt to acquire the lock. If acquired, the
	// lock is refreshed periodically based on the ttl.
	Start()

	// Stops watching the lock. Closes the events channel.
	Stop()

	// Returns the event channel used by the etcd lock.
	EventsChan() <-chan MasterEvent

	// Method to get the current lockholder. Returns "" if free.
	GetHolder() string
}

// Internal structure to represent an etcd lock.
type etcdLock struct {
	sync.Mutex
	client        Registry         // etcd interface
	name          string           // name of the lock
	id            string           // identity of the lockholder
	ttl           uint64           // ttl of the lock
	enabled       bool             // Used to enable/disable the lock
	master        string           // Lock holder
	watchStopCh   chan bool        // To stop the watch
	eventsCh      chan MasterEvent // channel to send lock ownership updates
	stoppedCh     chan bool        // channel that waits for acquire to finish
	refreshStopCh chan bool        // channel used to stop the refresh routine
	holding       bool             // whether this node is holding the lock
	modifiedIndex uint64           // valid only when this node is holding the lock
}

// Method to create a new etcd lock.
func NewMaster(client Registry, name string, id string,
	ttl uint64) (MasterInterface, error) {
	// client is mandatory. Min ttl is 5 seconds.
	if client == nil || ttl < 5 {
		return nil, errors.New("Invalid args")
	}

	return &etcdLock{client: client, name: name, id: id, ttl: ttl,
		enabled:       false,
		master:        "",
		watchStopCh:   make(chan bool, 1),
		eventsCh:      make(chan MasterEvent, 1),
		stoppedCh:     make(chan bool, 1),
		refreshStopCh: make(chan bool, 1),
		holding:       false,
		modifiedIndex: 0}, nil
}

// Method to start the attempt to acquire the lock.
func (e *etcdLock) Start() {
	glog.Infof("Starting attempt to acquire lock %s", e.name)

	e.Lock()
	if e.enabled {
		e.Unlock()
		// Already running
		glog.Warningf("Duplicate Start for lock %s", e.name)
		return
	}

	e.enabled = true
	e.Unlock()

	// Acquire in the background.
	go func() {
		// If acquire returns without error, exit. If not, acquire
		// crashed and needs to be called again.
		for {
			if err := e.acquire(); err == nil {
				break
			}
		}
	}()
}

// Method to stop the acquisition of lock and release it if holding the lock.
func (e *etcdLock) Stop() {
	glog.Infof("Stopping attempt to acquire lock %s", e.name)

	e.Lock()
	if !e.enabled {
		e.Unlock()
		// Not running
		glog.Warningf("Duplicate Stop for lock %s", e.name)
		return
	}

	// Disable the lock and stop the watch.
	e.enabled = false
	e.Unlock()

	e.watchStopCh <- true

	// Wait for acquire to finish.
	<-e.stoppedCh
}

// Method to get the event channel used by the etcd lock.
func (e *etcdLock) EventsChan() <-chan MasterEvent {
	return e.eventsCh
}

// Method to get the lockholder.
func (e *etcdLock) GetHolder() string {
	e.Lock()
	defer e.Unlock()
	return e.master
}

// Method to acquire the lock. It launches another goroutine to refresh the ttl
// if successful in acquiring the lock.
func (e *etcdLock) acquire() (ret error) {
	defer func() {
		if r := recover(); r != nil {
			callers := ""
			for i := 0; true; i++ {
				_, file, line, ok := runtime.Caller(i)
				if !ok {
					break
				}
				callers = callers + fmt.Sprintf("%v:%v\n", file,
					line)
			}
			errMsg := fmt.Sprintf("Recovered from panic: %#v (%v)\n%v",
				r, r, callers)
			glog.Errorf(errMsg)
			ret = errors.New(errMsg)
		}
	}()

	var resp *etcd.Response
	// Initialize error to dummy.
	err := fmt.Errorf("Dummy error")

	for {
		// Stop was called, stop the refresh routine if needed and
		// abort the acquire routine.
		if !e.enabled {
			if e.holding {
				glog.V(2).Infof("Deleting lock %s", e.name)
				// Delete the lock so other nodes can get it sooner.
				// Otherwise, they have to wait until ttl expiry.
				if _, err = e.client.Delete(e.name, false); err != nil {
					glog.V(2).Infof("Failed to delete lock %s, "+
						"error %v", e.name, err)
				}
				e.holding = false
				e.refreshStopCh <- true
			}
			// Wont be able to track the master.
			e.Lock()
			e.master = ""
			e.Unlock()

			e.stoppedCh <- true
			break
		}

		// If there is an error (at the beginning or with watch) or if
		// the lock is deleted, try to get the lockholder/acquire the lock.
		if err != nil || resp.Node.Value == "" {
			resp, err = e.client.Get(e.name, false, false)
			if err != nil {
				if IsEtcdErrorNotFound(err) {
					// Try to acquire the lock.
					glog.V(2).Infof("Trying to acquire lock %s", e.name)
					resp, err = e.client.Create(e.name, e.id, e.ttl)
					if err != nil {
						// Failed to acquire the lock.
						continue
					}
				} else {
					glog.V(2).Infof("Failed to get lock %s, error: %v",
						e.name, err)
					time.Sleep(kRetrySleep * time.Millisecond)
					continue
				}
			}
		}

		if resp.Node.Value == e.id {
			// This node is the lock holder.
			if !e.holding {
				// If not already holding the lock, send an
				// event and start the refresh routine.
				glog.Infof("Acquired lock %s", e.name)
				e.holding = true
				e.eventsCh <- MasterEvent{Type: MasterAdded,
					Master: e.id}
				go e.refresh()
			}
		} else {
			// Some other node is the lock holder.
			if e.holding {
				// If previously holding the lock, stop the
				// refresh routine and send a deleted event.
				glog.Errorf("Lost lock %s to %s", e.name,
					resp.Node.Value)
				e.holding = false
				e.refreshStopCh <- true
				e.eventsCh <- MasterEvent{Type: MasterDeleted,
					Master: ""}
			}
			if e.master != resp.Node.Value {
				// If master changed, send a modified event.
				e.eventsCh <- MasterEvent{Type: MasterModified,
					Master: resp.Node.Value}
			}
		}

		// Record the new master and modified index.
		e.Lock()
		e.master = resp.Node.Value
		e.Unlock()
		e.modifiedIndex = resp.Node.ModifiedIndex

		var prevIndex uint64

		// Intent is to start the watch using EtcdIndex. Sometimes, etcd
		// is returning EtcdIndex lower than ModifiedIndex. In such
		// cases, use ModifiedIndex to set the watch.
		// TODO: Change this code when etcd behavior changes.
		if resp.EtcdIndex < resp.Node.ModifiedIndex {
			prevIndex = resp.Node.ModifiedIndex + 1
		} else {
			prevIndex = resp.EtcdIndex + 1
		}

		// Start watching for changes to lock.
		resp, err = e.client.Watch(e.name, prevIndex, false, nil, e.watchStopCh)
		if IsEtcdErrorWatchStoppedByUser(err) {
			glog.Infof("Watch for lock %s stopped by user", e.name)
		} else if err != nil {
			// Log only if its not too old event index error.
			if !IsEtcdErrorEventIndexCleared(err) {
				glog.Errorf("Failed to watch lock %s, error %v",
					e.name, err)
			}
		}
	}

	return nil
}

// Method to refresh the lock. It refreshes the ttl at ttl*4/10 interval.
func (e *etcdLock) refresh() {
	for {
		select {
		case <-e.refreshStopCh:
			glog.V(2).Infof("Stopping refresh for lock %s", e.name)
			// Lock released.
			return
		case <-time.After(time.Second * time.Duration(e.ttl*4/10)):
			// Uses CompareAndSwap to protect against the case where a
			// watch is received with a "delete" and refresh routine is
			// still running.
			if resp, err := e.client.CompareAndSwap(e.name, e.id, e.ttl,
				e.id, e.modifiedIndex); err != nil {
				// Failure here could mean that some other node
				// acquired the lock. Should also get a watch
				// notification if that happens and this go routine
				// is stopped there.
				glog.Errorf("Failed to set the ttl for lock %s with "+
					"error: %s", e.name, err.Error())
			} else {
				e.modifiedIndex = resp.Node.ModifiedIndex
			}
		}
	}
}
<|endoftext|>


<commit_before>package btrfs

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"unsafe"
)

func Send(w io.Writer, parent string, subvols ...string) error {
	if len(subvols) == 0 {
		return nil
	}
	// use first send subvol to determine mount_root
	subvol, err := filepath.Abs(subvols[0])
	if err != nil {
		return err
	}
	mountRoot, err := findMountRoot(subvol)
	if err == os.ErrNotExist {
		return fmt.Errorf("cannot find a mountpoint for %s", subvol)
	} else if err != nil {
		return err
	}
	var (
		cloneSrc []objectID
		parentID objectID
	)
	if parent != "" {
		parent, err = filepath.Abs(parent)
		if err != nil {
			return err
		}
		id, err := getPathRootID(parent)
		if err != nil {
			return fmt.Errorf("cannot get parent root id: %v", err)
		}
		parentID = id
		cloneSrc = append(cloneSrc, id)
	}
	// check all subvolumes
	paths := make([]string, 0, len(subvols))
	for _, sub := range subvols {
		sub, err = filepath.Abs(sub)
		if err != nil {
			return err
		}
		paths = append(paths, sub)
		mount, err := findMountRoot(sub)
		if err != nil {
			return fmt.Errorf("cannot find mount root for %v: %v", sub, err)
		} else if mount != mountRoot {
			return fmt.Errorf("all subvolumes must be from the same filesystem (%s is not)", sub)
		}
		ok, err := IsReadOnly(sub)
		if err != nil {
			return err
		} else if !ok {
			return fmt.Errorf("subvolume %s is not read-only", sub)
		}
	}
	mfs, err := Open(mountRoot, true)
	if err != nil {
		return err
	}
	defer mfs.Close()
	full := len(cloneSrc) == 0
	for i, sub := range paths {
		var rootID objectID
		if !full && parent != "" {
			rel, err := filepath.Rel(mountRoot, sub)
			if err != nil {
				return err
			}
			si, err := subvolSearchByPath(mfs.f, rel)
			if err != nil {
				return fmt.Errorf("cannot find subvolume %s: %v", rel, err)
			}
			rootID = si.RootID
			parentID, err = findGoodParent(mfs.f, rootID, cloneSrc)
			if err != nil {
				return fmt.Errorf("cannot find good parent for %v: %v", rel, err)
			}
		}
		fs, err := Open(sub, true)
		if err != nil {
			return err
		}
		var flags uint64
		if i != 0 { // not first
			flags |= _BTRFS_SEND_FLAG_OMIT_STREAM_HEADER
		}
		if i < len(paths)-1 { // not last
			flags |= _BTRFS_SEND_FLAG_OMIT_END_CMD
		}
		err = send(w, fs.f, parentID, cloneSrc, flags)
		fs.Close()
		if err != nil {
			return fmt.Errorf("error sending %s: %v", sub, err)
		}
		if !full && parent != "" {
			cloneSrc = append(cloneSrc, rootID)
		}
	}
	return nil
}

func send(w io.Writer, subvol *os.File, parent objectID, sources []objectID, flags uint64) error {
	pr, pw, err := os.Pipe()
	if err != nil {
		return err
	}
	errc := make(chan error, 1)
	go func() {
		defer pr.Close()
		_, err := io.Copy(w, pr)
		errc <- err
	}()
	fd := pw.Fd()
	wait := func() error {
		pw.Close()
		return <-errc
	}
	args := &btrfs_ioctl_send_args{
		send_fd:     int64(fd),
		parent_root: parent,
		flags:       flags,
	}
	if len(sources) != 0 {
		args.clone_sources = &sources[0]
		args.clone_sources_count = uint64(len(sources))
	}
	if err := iocSend(subvol, args); err != nil {
		wait()
		return err
	}
	return wait()
}

// readRootItem reads a root item from the tree.
//
// TODO(dennwc): support older kernels:
// In case we detect a root item smaller then sizeof(root_item),
// we know it's an old version of the root structure and initialize all new fields to zero.
// The same happens if we detect mismatching generation numbers as then we know the root was
// once mounted with an older kernel that was not aware of the root item structure change.
func readRootItem(mnt *os.File, rootID objectID) (*rootItem, error) {
	sk := btrfs_ioctl_search_key{
		tree_id: rootTreeObjectid,
		// There may be more than one ROOT_ITEM key if there are
		// snapshots pending deletion, we have to loop through them.
		min_objectid: rootID,
		max_objectid: rootID,
		min_type:     rootItemKey,
		max_type:     rootItemKey,
		max_offset:   maxUint64,
		max_transid:  maxUint64,
		nr_items:     4096,
	}
	for ; sk.min_offset < maxUint64; sk.min_offset++ {
		results, err := treeSearchRaw(mnt, sk)
		if err != nil {
			return nil, err
		} else if len(results) == 0 {
			break
		}
		for _, r := range results {
			sk.min_objectid = r.ObjectID
			sk.min_type = r.Type
			sk.min_offset = r.Offset
			if r.ObjectID > rootID {
				break
			}
			if r.ObjectID == rootID && r.Type == rootItemKey {
				const sz = int(unsafe.Sizeof(btrfs_root_item_raw{}))
				if len(r.Data) > sz {
					return nil, fmt.Errorf("btrfs_root_item is larger than expected; kernel is newer than the library")
				} else if len(r.Data) < sz { // TODO
					return nil, fmt.Errorf("btrfs_root_item is smaller then expected; kernel version is too old")
				}
				p := asRootItem(r.Data).Decode()
				return &p, nil
			}
		}
		results = nil
		if sk.min_type != rootItemKey || sk.min_objectid != rootID {
			break
		}
	}
	return nil, ErrNotFound
}

func getParent(mnt *os.File, rootID objectID) (*SubvolInfo, error) {
	st, err := subvolSearchByRootID(mnt, rootID, "")
	if err != nil {
		return nil, err
	}
	return subvolSearchByUUID(mnt, st.ParentUUID)
}

func findGoodParent(mnt *os.File, rootID objectID, cloneSrc []objectID) (objectID, error) {
	parent, err := getParent(mnt, rootID)
	if err != nil {
		return 0, fmt.Errorf("get parent failed: %v", err)
	}
	for _, id := range cloneSrc {
		if id == parent.RootID {
			return parent.RootID, nil
		}
	}
	var (
		bestParent *SubvolInfo
		bestDiff   uint64 = maxUint64
	)
	for _, id := range cloneSrc {
		parent2, err := getParent(mnt, id)
		if err != nil {
			return 0, err
		}
		if parent2.RootID != parent.RootID {
			continue
		}
		parent2, err = subvolSearchByRootID(mnt, id, "")
		if err != nil {
			return 0, err
		}
		diff := int64(parent2.CTransID - parent.CTransID)
		if diff < 0 {
			diff = -diff
		}
		if uint64(diff) < bestDiff {
			bestParent, bestDiff = parent2, uint64(diff)
		}
	}
	if bestParent == nil {
		return 0, ErrNotFound
	}
	return bestParent.RootID, nil
}
<commit_msg>send: recursive best parent search<commit_after>package btrfs

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"unsafe"
)

func Send(w io.Writer, parent string, subvols ...string) error {
	if len(subvols) == 0 {
		return nil
	}
	// use first send subvol to determine mount_root
	subvol, err := filepath.Abs(subvols[0])
	if err != nil {
		return err
	}
	mountRoot, err := findMountRoot(subvol)
	if err == os.ErrNotExist {
		return fmt.Errorf("cannot find a mountpoint for %s", subvol)
	} else if err != nil {
		return err
	}
	var (
		cloneSrc []objectID
		parentID objectID
	)
	if parent != "" {
		parent, err = filepath.Abs(parent)
		if err != nil {
			return err
		}
		id, err := getPathRootID(parent)
		if err != nil {
			return fmt.Errorf("cannot get parent root id: %v", err)
		}
		parentID = id
		cloneSrc = append(cloneSrc, id)
	}
	// check all subvolumes
	paths := make([]string, 0, len(subvols))
	for _, sub := range subvols {
		sub, err = filepath.Abs(sub)
		if err != nil {
			return err
		}
		paths = append(paths, sub)
		mount, err := findMountRoot(sub)
		if err != nil {
			return fmt.Errorf("cannot find mount root for %v: %v", sub, err)
		} else if mount != mountRoot {
			return fmt.Errorf("all subvolumes must be from the same filesystem (%s is not)", sub)
		}
		ok, err := IsReadOnly(sub)
		if err != nil {
			return err
		} else if !ok {
			return fmt.Errorf("subvolume %s is not read-only", sub)
		}
	}
	mfs, err := Open(mountRoot, true)
	if err != nil {
		return err
	}
	defer mfs.Close()
	full := len(cloneSrc) == 0
	for i, sub := range paths {
		var rootID objectID
		if !full && parent != "" {
			rel, err := filepath.Rel(mountRoot, sub)
			if err != nil {
				return err
			}
			si, err := subvolSearchByPath(mfs.f, rel)
			if err != nil {
				return fmt.Errorf("cannot find subvolume %s: %v", rel, err)
			}
			rootID = si.RootID
			parentID, err = findGoodParent(mfs.f, rootID, cloneSrc)
			if err != nil {
				return fmt.Errorf("cannot find good parent for %v: %v", rel, err)
			}
		}
		fs, err := Open(sub, true)
		if err != nil {
			return err
		}
		var flags uint64
		if i != 0 { // not first
			flags |= _BTRFS_SEND_FLAG_OMIT_STREAM_HEADER
		}
		if i < len(paths)-1 { // not last
			flags |= _BTRFS_SEND_FLAG_OMIT_END_CMD
		}
		err = send(w, fs.f, parentID, cloneSrc, flags)
		fs.Close()
		if err != nil {
			return fmt.Errorf("error sending %s: %v", sub, err)
		}
		if !full && parent != "" {
			cloneSrc = append(cloneSrc, rootID)
		}
	}
	return nil
}

func send(w io.Writer, subvol *os.File, parent objectID, sources []objectID, flags uint64) error {
	pr, pw, err := os.Pipe()
	if err != nil {
		return err
	}
	errc := make(chan error, 1)
	go func() {
		defer pr.Close()
		_, err := io.Copy(w, pr)
		errc <- err
	}()
	fd := pw.Fd()
	wait := func() error {
		pw.Close()
		return <-errc
	}
	args := &btrfs_ioctl_send_args{
		send_fd:     int64(fd),
		parent_root: parent,
		flags:       flags,
	}
	if len(sources) != 0 {
		args.clone_sources = &sources[0]
		args.clone_sources_count = uint64(len(sources))
	}
	if err := iocSend(subvol, args); err != nil {
		wait()
		return err
	}
	return wait()
}

// readRootItem reads a root item from the tree.
//
// TODO(dennwc): support older kernels:
// In case we detect a root item smaller then sizeof(root_item),
// we know it's an old version of the root structure and initialize all new fields to zero.
// The same happens if we detect mismatching generation numbers as then we know the root was
// once mounted with an older kernel that was not aware of the root item structure change.
func readRootItem(mnt *os.File, rootID objectID) (*rootItem, error) {
	sk := btrfs_ioctl_search_key{
		tree_id: rootTreeObjectid,
		// There may be more than one ROOT_ITEM key if there are
		// snapshots pending deletion, we have to loop through them.
		min_objectid: rootID,
		max_objectid: rootID,
		min_type:     rootItemKey,
		max_type:     rootItemKey,
		max_offset:   maxUint64,
		max_transid:  maxUint64,
		nr_items:     4096,
	}
	for ; sk.min_offset < maxUint64; sk.min_offset++ {
		results, err := treeSearchRaw(mnt, sk)
		if err != nil {
			return nil, err
		} else if len(results) == 0 {
			break
		}
		for _, r := range results {
			sk.min_objectid = r.ObjectID
			sk.min_type = r.Type
			sk.min_offset = r.Offset
			if r.ObjectID > rootID {
				break
			}
			if r.ObjectID == rootID && r.Type == rootItemKey {
				const sz = int(unsafe.Sizeof(btrfs_root_item_raw{}))
				if len(r.Data) > sz {
					return nil, fmt.Errorf("btrfs_root_item is larger than expected; kernel is newer than the library")
				} else if len(r.Data) < sz { // TODO
					return nil, fmt.Errorf("btrfs_root_item is smaller then expected; kernel version is too old")
				}
				p := asRootItem(r.Data).Decode()
				return &p, nil
			}
		}
		results = nil
		if sk.min_type != rootItemKey || sk.min_objectid != rootID {
			break
		}
	}
	return nil, ErrNotFound
}

func getParent(mnt *os.File, rootID objectID) (*SubvolInfo, error) {
	st, err := subvolSearchByRootID(mnt, rootID, "")
	if err != nil {
		return nil, fmt.Errorf("cannot find subvolume %d to determine parent: %v", rootID, err)
	}
	return subvolSearchByUUID(mnt, st.ParentUUID)
}

func findGoodParent(mnt *os.File, rootID objectID, cloneSrc []objectID) (objectID, error) {
	parent, err := getParent(mnt, rootID)
	if err != nil {
		return 0, fmt.Errorf("get parent failed: %v", err)
	}
	for _, id := range cloneSrc {
		if id == parent.RootID {
			return parent.RootID, nil
		}
	}
	var (
		bestParent *SubvolInfo
		bestDiff   uint64 = maxUint64
	)
	for _, id := range cloneSrc {
		parent2, err := getParent(mnt, id)
		if err == ErrNotFound {
			continue
		} else if err != nil {
			return 0, err
		}
		if parent2.RootID != parent.RootID {
			continue
		}
		parent2, err = subvolSearchByRootID(mnt, id, "")
		if err != nil {
			return 0, err
		}
		diff := int64(parent2.CTransID - parent.CTransID)
		if diff < 0 {
			diff = -diff
		}
		if uint64(diff) < bestDiff {
			bestParent, bestDiff = parent2, uint64(diff)
		}
	}
	if bestParent != nil {
		return bestParent.RootID, nil
	}
	if !parent.ParentUUID.IsZero() {
		return findGoodParent(mnt, parent.RootID, cloneSrc)
	}
	return 0, ErrNotFound
}
<|endoftext|>


<commit_before>// The MIT License (MIT)

// Copyright (c) 2016 Maciej Borzecki

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package main

import (
	"github.com/gorilla/mux"
	"html/template"
	"log"
	"net/http"
	"path"
)

type Site struct {
	m    *MatchController
	tdir string
}

func NewSite(m *MatchController) *Site {
	return &Site{
		m:    m,
		tdir: path.Join(C.Webroot, "templates"),
	}
}

func (s *Site) SetupHandlers(r *mux.Router) {
	r.HandleFunc(uriIndex, s.siteHomeHandler).
		Methods("GET")
}

func (s *Site) siteHomeHandler(w http.ResponseWriter, req *http.Request) {

	log.Printf("site home handler")

	matches := s.m.ListMatches()
	mt := s.loadTemplates("matches.tmpl", "base.tmpl")

	err := renderTemplate(w, mt, matches)
	if err != nil {
		log.Printf("failed to execute template: %s", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

func (s *Site) loadTemplates(names ...string) *template.Template {
	paths := make([]string, 0, len(names))
	for _, n := range names {
		tpath := path.Join(s.tdir, n)
		paths = append(paths, tpath)
	}

	log.Printf("loading templates: %s", paths)

	t, err := template.ParseFiles(paths...)
	if err != nil {
		log.Printf("failed to parse templates: %s", err)
		return nil
	}
	return t
}

func renderTemplate(w http.ResponseWriter, t *template.Template,
	data interface{}) error {

	// set header?
	w.Header().Set("Content-Type", "text/html; charset=utf-8")

	return t.ExecuteTemplate(w, "base", data)
}
<commit_msg>site: pass matches as template data<commit_after>// The MIT License (MIT)

// Copyright (c) 2016 Maciej Borzecki

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package main

import (
	"github.com/bboozzoo/q3stats/models"
	"github.com/gorilla/mux"
	"html/template"
	"log"
	"net/http"
	"path"
)

type Site struct {
	m    *MatchController
	tdir string
}

func NewSite(m *MatchController) *Site {
	return &Site{
		m:    m,
		tdir: path.Join(C.Webroot, "templates"),
	}
}

func (s *Site) SetupHandlers(r *mux.Router) {
	r.HandleFunc(uriIndex, s.siteHomeHandler).
		Methods("GET")
}

func (s *Site) siteHomeHandler(w http.ResponseWriter, req *http.Request) {

	log.Printf("site home handler")

	matches := s.m.ListMatches()
	mt := s.loadTemplates("matches.tmpl", "base.tmpl")

	data := struct {
		Matches []models.Match
	}{
		matches,
	}
	err := renderTemplate(w, mt, data)
	if err != nil {
		log.Printf("failed to execute template: %s", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

func (s *Site) loadTemplates(names ...string) *template.Template {
	paths := make([]string, 0, len(names))
	for _, n := range names {
		tpath := path.Join(s.tdir, n)
		paths = append(paths, tpath)
	}

	log.Printf("loading templates: %s", paths)

	t, err := template.ParseFiles(paths...)
	if err != nil {
		log.Printf("failed to parse templates: %s", err)
		return nil
	}
	return t
}

func renderTemplate(w http.ResponseWriter, t *template.Template,
	data interface{}) error {

	// set header?
	w.Header().Set("Content-Type", "text/html; charset=utf-8")

	return t.ExecuteTemplate(w, "base", data)
}
<|endoftext|>