<commit_before>package common

import (
	. "DNA/common"
	. "DNA/common/config"
	"DNA/core/ledger"
	tx "DNA/core/transaction"
	. "DNA/net/httpjsonrpc"
	Err "DNA/net/httprestful/error"
	. "DNA/net/protocol"
	"bytes"
	"encoding/hex"
	"encoding/json"
	"io/ioutil"
	"net"
	"net/http"
	"regexp"
	"strconv"
	"time"
)

var node Noder
var pushBlockFlag bool = true

type ApiServer interface {
	Start() error
	Stop()
}

func SetNode(n Noder) {
	node = n
}
func CheckPushBlock() bool {
	return pushBlockFlag
}

//Node
func GetConnectionCount(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)
	if node != nil {
		resp["Result"] = node.GetConnectionCnt()
	}

	return resp
}

//Block
func GetBlockHeight(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)
	resp["Result"] = ledger.DefaultLedger.Blockchain.BlockHeight
	return resp
}
func getBlock(hash Uint256, getTxBytes bool) (interface{}, int64) {
	block, err := ledger.DefaultLedger.Store.GetBlock(hash)
	if err != nil {
		return "", Err.UNKNOWN_BLOCK
	}
	if getTxBytes {
		w := bytes.NewBuffer(nil)
		block.Serialize(w)
		return hex.EncodeToString(w.Bytes()), Err.SUCCESS
	}

	blockHead := &BlockHead{
		Version:          block.Blockdata.Version,
		PrevBlockHash:    ToHexString(block.Blockdata.PrevBlockHash.ToArray()),
		TransactionsRoot: ToHexString(block.Blockdata.TransactionsRoot.ToArray()),
		Timestamp:        block.Blockdata.Timestamp,
		Height:           block.Blockdata.Height,
		ConsensusData:    block.Blockdata.ConsensusData,
		NextBookKeeper:   ToHexString(block.Blockdata.NextBookKeeper.ToArray()),
		Program: ProgramInfo{
			Code:      ToHexString(block.Blockdata.Program.Code),
			Parameter: ToHexString(block.Blockdata.Program.Parameter),
		},
		Hash: ToHexString(hash.ToArray()),
	}

	trans := make([]*Transactions, len(block.Transactions))
	for i := 0; i < len(block.Transactions); i++ {
		trans[i] = TransArryByteToHexString(block.Transactions[i])
	}

	b := BlockInfo{
		Hash:         ToHexString(hash.ToArray()),
		BlockData:    blockHead,
		Transactions: trans,
	}
	return b, Err.SUCCESS
}
func GetBlockByHash(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)
	param := cmd["Hash"].(string)
	if len(param) == 0 {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	var getTxBytes bool = false
	if cmd["Raw"].(string) == "1" {
		getTxBytes = true
	}
	var hash Uint256
	hex, err := hex.DecodeString(param)
	if err != nil {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	if err := hash.Deserialize(bytes.NewReader(hex)); err != nil {
		resp["Error"] = Err.INVALID_TRANSACTION
		return resp
	}

	resp["Result"], resp["Error"] = getBlock(hash, getTxBytes)

	return resp
}
func GetBlockByHeight(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)

	param := cmd["Height"].(string)
	if len(param) == 0 {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	var getTxBytes bool = false
	if cmd["Raw"].(string) == "1" {
		getTxBytes = true
	}
	height, err := strconv.ParseInt(param, 10, 64)
	if err != nil {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	index := uint32(height)
	hash, err := ledger.DefaultLedger.Store.GetBlockHash(index)
	if err != nil {
		resp["Error"] = Err.UNKNOWN_BLOCK
		return resp
	}
	resp["Result"], resp["Error"] = getBlock(hash, getTxBytes)
	return resp
}

//Asset
func GetAssetByHash(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)

	str := cmd["Hash"].(string)
	hex, err := hex.DecodeString(str)
	if err != nil {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	var hash Uint256
	err = hash.Deserialize(bytes.NewReader(hex))
	if err != nil {
		resp["Error"] = Err.INVALID_TRANSACTION
		return resp
	}
	asset, err := ledger.DefaultLedger.Store.GetAsset(hash)
	if err != nil {
		resp["Error"] = Err.UNKNOWN_TRANSACTION
		return resp
	}
	resp["Result"] = asset
	return resp
}

//Transaction
func GetTransactionByHash(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)

	str := cmd["Hash"].(string)
	bys, err := hex.DecodeString(str)
	if err != nil {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	var hash Uint256
	err = hash.Deserialize(bytes.NewReader(bys))
	if err != nil {
		resp["Error"] = Err.INVALID_TRANSACTION
		return resp
	}
	tx, err := ledger.DefaultLedger.Store.GetTransaction(hash)
	if err != nil {
		resp["Error"] = Err.UNKNOWN_TRANSACTION
		return resp
	}
	if cmd["Raw"].(string) == "1" {
		w := bytes.NewBuffer(nil)
		tx.Serialize(w)
		resp["Result"] = hex.EncodeToString(w.Bytes())
		return resp
	}
	tran := TransArryByteToHexString(tx)
	resp["Result"] = tran
	return resp
}
func SendRawTransaction(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)

	str, ok := cmd["Data"].(string)
	if !ok {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	hex, err := hex.DecodeString(str)
	if err != nil {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	var txn tx.Transaction
	if err := txn.Deserialize(bytes.NewReader(hex)); err != nil {
		resp["Error"] = Err.INVALID_TRANSACTION
		return resp
	}
	var hash Uint256
	hash = txn.Hash()
	if err := VerifyAndSendTx(&txn); err != nil {
		resp["Error"] = Err.INTERNAL_ERROR
		return resp
	}
	resp["Result"] = ToHexString(hash.ToArray())
	return resp
}

//config
func GetOauthServerAddr(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)
	resp["Result"] = Parameters.OauthServerAddr
	return resp
}
func SetOauthServerAddr(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)

	addr, ok := cmd["Addr"].(string)
	if !ok {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	if len(addr) > 0 {
		var reg *regexp.Regexp
		pattern := `((http|https)://)(([a-zA-Z0-9\._-]+\.[a-zA-Z]{2,6})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,4})*(/[a-zA-Z0-9\&%_\./-~-]*)?`
		reg = regexp.MustCompile(pattern)
		if !reg.Match([]byte(addr)) {
			resp["Error"] = Err.INVALID_PARAMS
			return resp
		}
	}
	Parameters.OauthServerAddr = addr
	resp["Result"] = Parameters.OauthServerAddr
	return resp
}
func GetNoticeServerAddr(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)
	resp["Result"] = Parameters.NoticeServerAddr
	return resp
}

func SetPushBlockFlag(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)
	start, ok := cmd["Open"].(bool)
	if !ok {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	if start {
		pushBlockFlag = true
	} else {
		pushBlockFlag = false
	}
	resp["Result"] = pushBlockFlag
	return resp
}
func SetNoticeServerAddr(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)

	addr, ok := cmd["Addr"].(string)
	if !ok || len(addr) == 0 {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	var reg *regexp.Regexp
	pattern := `((http|https)://)(([a-zA-Z0-9\._-]+\.[a-zA-Z]{2,6})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,4})*(/[a-zA-Z0-9\&%_\./-~-]*)?`
	reg = regexp.MustCompile(pattern)
	if !reg.Match([]byte(addr)) {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	Parameters.NoticeServerAddr = addr
	resp["Result"] = Parameters.NoticeServerAddr
	return resp
}
func ResponsePack(errCode int64) map[string]interface{} {
	resp := map[string]interface{}{
		"Action":  "",
		"Result":  "",
		"Error":   errCode,
		"Desc":    "",
		"Version": "1.0.0",
	}
	return resp
}

func PostRequest(cmd map[string]interface{}, addr string) (map[string]interface{}, error) {

	var repMsg = make(map[string]interface{})

	data, err := json.Marshal(cmd)
	if err != nil {
		return repMsg, err
	}
	reqData := bytes.NewBuffer(data)
	transport := http.Transport{
		Dial: func(netw, addr string) (net.Conn, error) {
			conn, err := net.DialTimeout(netw, addr, time.Second*2)
			if err != nil {
				return nil, err
			}
			conn.SetDeadline(time.Now().Add(time.Second * 5))
			return conn, nil
		},
		DisableKeepAlives: true,
	}
	client := &http.Client{Transport: &transport}
	request, err := http.NewRequest("POST", addr, reqData)
	if err != nil {
		return repMsg, err
	}
	request.Header.Set("Content-type", "application/json")

	response, err := client.Do(request)
	if err != nil {
		return repMsg, err
	}
	defer response.Body.Close()

	if response.StatusCode == 200 {
		body, _ := ioutil.ReadAll(response.Body)
		if err := json.Unmarshal(body, &repMsg); err == nil {
			return repMsg, err
		}
	}
	return repMsg, err
}
<commit_msg>Avoid resp["Raw"] is nil or other type error<commit_after>package common

import (
	. "DNA/common"
	. "DNA/common/config"
	"DNA/core/ledger"
	tx "DNA/core/transaction"
	. "DNA/net/httpjsonrpc"
	Err "DNA/net/httprestful/error"
	. "DNA/net/protocol"
	"bytes"
	"encoding/hex"
	"encoding/json"
	"io/ioutil"
	"net"
	"net/http"
	"regexp"
	"strconv"
	"time"
)

var node Noder
var pushBlockFlag bool = true

type ApiServer interface {
	Start() error
	Stop()
}

func SetNode(n Noder) {
	node = n
}
func CheckPushBlock() bool {
	return pushBlockFlag
}

//Node
func GetConnectionCount(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)
	if node != nil {
		resp["Result"] = node.GetConnectionCnt()
	}

	return resp
}

//Block
func GetBlockHeight(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)
	resp["Result"] = ledger.DefaultLedger.Blockchain.BlockHeight
	return resp
}
func getBlock(hash Uint256, getTxBytes bool) (interface{}, int64) {
	block, err := ledger.DefaultLedger.Store.GetBlock(hash)
	if err != nil {
		return "", Err.UNKNOWN_BLOCK
	}
	if getTxBytes {
		w := bytes.NewBuffer(nil)
		block.Serialize(w)
		return hex.EncodeToString(w.Bytes()), Err.SUCCESS
	}

	blockHead := &BlockHead{
		Version:          block.Blockdata.Version,
		PrevBlockHash:    ToHexString(block.Blockdata.PrevBlockHash.ToArray()),
		TransactionsRoot: ToHexString(block.Blockdata.TransactionsRoot.ToArray()),
		Timestamp:        block.Blockdata.Timestamp,
		Height:           block.Blockdata.Height,
		ConsensusData:    block.Blockdata.ConsensusData,
		NextBookKeeper:   ToHexString(block.Blockdata.NextBookKeeper.ToArray()),
		Program: ProgramInfo{
			Code:      ToHexString(block.Blockdata.Program.Code),
			Parameter: ToHexString(block.Blockdata.Program.Parameter),
		},
		Hash: ToHexString(hash.ToArray()),
	}

	trans := make([]*Transactions, len(block.Transactions))
	for i := 0; i < len(block.Transactions); i++ {
		trans[i] = TransArryByteToHexString(block.Transactions[i])
	}

	b := BlockInfo{
		Hash:         ToHexString(hash.ToArray()),
		BlockData:    blockHead,
		Transactions: trans,
	}
	return b, Err.SUCCESS
}
func GetBlockByHash(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)
	param := cmd["Hash"].(string)
	if len(param) == 0 {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	var getTxBytes bool = false
	if raw, ok := cmd["Raw"].(string); ok && raw == "1" {
		getTxBytes = true
	}
	var hash Uint256
	hex, err := hex.DecodeString(param)
	if err != nil {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	if err := hash.Deserialize(bytes.NewReader(hex)); err != nil {
		resp["Error"] = Err.INVALID_TRANSACTION
		return resp
	}

	resp["Result"], resp["Error"] = getBlock(hash, getTxBytes)

	return resp
}
func GetBlockByHeight(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)

	param := cmd["Height"].(string)
	if len(param) == 0 {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	var getTxBytes bool = false
	if raw, ok := cmd["Raw"].(string); ok && raw == "1" {
		getTxBytes = true
	}
	height, err := strconv.ParseInt(param, 10, 64)
	if err != nil {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	index := uint32(height)
	hash, err := ledger.DefaultLedger.Store.GetBlockHash(index)
	if err != nil {
		resp["Error"] = Err.UNKNOWN_BLOCK
		return resp
	}
	resp["Result"], resp["Error"] = getBlock(hash, getTxBytes)
	return resp
}

//Asset
func GetAssetByHash(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)

	str := cmd["Hash"].(string)
	hex, err := hex.DecodeString(str)
	if err != nil {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	var hash Uint256
	err = hash.Deserialize(bytes.NewReader(hex))
	if err != nil {
		resp["Error"] = Err.INVALID_TRANSACTION
		return resp
	}
	asset, err := ledger.DefaultLedger.Store.GetAsset(hash)
	if err != nil {
		resp["Error"] = Err.UNKNOWN_TRANSACTION
		return resp
	}
	resp["Result"] = asset
	return resp
}

//Transaction
func GetTransactionByHash(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)

	str := cmd["Hash"].(string)
	bys, err := hex.DecodeString(str)
	if err != nil {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	var hash Uint256
	err = hash.Deserialize(bytes.NewReader(bys))
	if err != nil {
		resp["Error"] = Err.INVALID_TRANSACTION
		return resp
	}
	tx, err := ledger.DefaultLedger.Store.GetTransaction(hash)
	if err != nil {
		resp["Error"] = Err.UNKNOWN_TRANSACTION
		return resp
	}
	if raw, ok := cmd["Raw"].(string); ok && raw == "1" {
		w := bytes.NewBuffer(nil)
		tx.Serialize(w)
		resp["Result"] = hex.EncodeToString(w.Bytes())
		return resp
	}
	tran := TransArryByteToHexString(tx)
	resp["Result"] = tran
	return resp
}
func SendRawTransaction(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)

	str, ok := cmd["Data"].(string)
	if !ok {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	hex, err := hex.DecodeString(str)
	if err != nil {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	var txn tx.Transaction
	if err := txn.Deserialize(bytes.NewReader(hex)); err != nil {
		resp["Error"] = Err.INVALID_TRANSACTION
		return resp
	}
	var hash Uint256
	hash = txn.Hash()
	if err := VerifyAndSendTx(&txn); err != nil {
		resp["Error"] = Err.INTERNAL_ERROR
		return resp
	}
	resp["Result"] = ToHexString(hash.ToArray())
	return resp
}

//config
func GetOauthServerAddr(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)
	resp["Result"] = Parameters.OauthServerAddr
	return resp
}
func SetOauthServerAddr(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)

	addr, ok := cmd["Addr"].(string)
	if !ok {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	if len(addr) > 0 {
		var reg *regexp.Regexp
		pattern := `((http|https)://)(([a-zA-Z0-9\._-]+\.[a-zA-Z]{2,6})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,4})*(/[a-zA-Z0-9\&%_\./-~-]*)?`
		reg = regexp.MustCompile(pattern)
		if !reg.Match([]byte(addr)) {
			resp["Error"] = Err.INVALID_PARAMS
			return resp
		}
	}
	Parameters.OauthServerAddr = addr
	resp["Result"] = Parameters.OauthServerAddr
	return resp
}
func GetNoticeServerAddr(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)
	resp["Result"] = Parameters.NoticeServerAddr
	return resp
}

func SetPushBlockFlag(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)
	start, ok := cmd["Open"].(bool)
	if !ok {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	if start {
		pushBlockFlag = true
	} else {
		pushBlockFlag = false
	}
	resp["Result"] = pushBlockFlag
	return resp
}
func SetNoticeServerAddr(cmd map[string]interface{}) map[string]interface{} {
	resp := ResponsePack(Err.SUCCESS)

	addr, ok := cmd["Addr"].(string)
	if !ok || len(addr) == 0 {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	var reg *regexp.Regexp
	pattern := `((http|https)://)(([a-zA-Z0-9\._-]+\.[a-zA-Z]{2,6})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,4})*(/[a-zA-Z0-9\&%_\./-~-]*)?`
	reg = regexp.MustCompile(pattern)
	if !reg.Match([]byte(addr)) {
		resp["Error"] = Err.INVALID_PARAMS
		return resp
	}
	Parameters.NoticeServerAddr = addr
	resp["Result"] = Parameters.NoticeServerAddr
	return resp
}
func ResponsePack(errCode int64) map[string]interface{} {
	resp := map[string]interface{}{
		"Action":  "",
		"Result":  "",
		"Error":   errCode,
		"Desc":    "",
		"Version": "1.0.0",
	}
	return resp
}

func PostRequest(cmd map[string]interface{}, addr string) (map[string]interface{}, error) {

	var repMsg = make(map[string]interface{})

	data, err := json.Marshal(cmd)
	if err != nil {
		return repMsg, err
	}
	reqData := bytes.NewBuffer(data)
	transport := http.Transport{
		Dial: func(netw, addr string) (net.Conn, error) {
			conn, err := net.DialTimeout(netw, addr, time.Second*2)
			if err != nil {
				return nil, err
			}
			conn.SetDeadline(time.Now().Add(time.Second * 5))
			return conn, nil
		},
		DisableKeepAlives: true,
	}
	client := &http.Client{Transport: &transport}
	request, err := http.NewRequest("POST", addr, reqData)
	if err != nil {
		return repMsg, err
	}
	request.Header.Set("Content-type", "application/json")

	response, err := client.Do(request)
	if err != nil {
		return repMsg, err
	}
	defer response.Body.Close()

	if response.StatusCode == 200 {
		body, _ := ioutil.ReadAll(response.Body)
		if err := json.Unmarshal(body, &repMsg); err == nil {
			return repMsg, err
		}
	}
	return repMsg, err
}
<|endoftext|>


<commit_before>package imageutil

import (
	"fmt"
	"image"
	"image/color"
	"strings"

	"golang.org/x/image/draw"
)

// AddBackgroundColor adds a background of `color.Color` to an image.
// It is is useful when the image has a transparent background.
func AddBackgroundColor(imgSrc image.Image, clr color.Color) image.Image {
	imgNew := image.NewRGBA(imgSrc.Bounds())
	draw.Draw(imgNew, imgNew.Bounds(), &image.Uniform{clr}, image.Point{}, draw.Src)
	draw.Draw(imgNew, imgNew.Bounds(), imgSrc, imgSrc.Bounds().Min, draw.Over)
	return imgNew
}

// AddBackgroundWhite adds a white background which is usable
// when the image has a transparent background.
func AddBackgroundWhite(imgSrc image.Image) image.Image {
	return AddBackgroundColor(imgSrc, color.White)
}

// Resize scales an image to the provided size units. Use a 0
// to scale the aspect ratio. See gitub.com/nfnt/resize for Lanczos3, etc.
// https://github.com/nfnt/resize .
func Resize(width, height uint, src image.Image, scale draw.Scaler) image.Image {
	if width == 0 && height != 0 {
		width = uint(ImageAspect(src) * float64(height))
	} else if height == 0 && width != 0 {
		height = uint(float64(width) / ImageAspect(src))
	}
	rect := image.Rect(0, 0, int(width), int(height))
	dst := image.NewRGBA(rect)
	scale.Scale(dst, rect, src, src.Bounds(), draw.Over, nil)
	return dst
}

func ResizeMaxDimension(maxSide uint, src image.Image, scale draw.Scaler) image.Image {
	width := src.Bounds().Dx()
	height := src.Bounds().Dy()
	if width > height {
		if width == int(maxSide) {
			return src
		}
		return Resize(maxSide, 0, src, scale)
	}
	if height == int(maxSide) {
		return src
	}
	return Resize(0, maxSide, src, scale)
}

func Square(src image.Image) image.Image {
	width := src.Bounds().Dx()
	height := src.Bounds().Dy()
	if width == height {
		return src
	}
	if height > width {
		// https://www.golangprograms.com/how-to-add-watermark-or-merge-two-image.html
		newSq := image.NewRGBA(image.Rect(0, 0, height, height))
		offset := image.Point{
			X: int((float64(height) - float64(width)) / 2),
			Y: 0}
		draw.Draw(newSq, src.Bounds().Add(offset), src, image.Point{}, draw.Over)
		return newSq
	}
	newSq := image.NewRGBA(image.Rect(0, 0, width, width))
	offset := image.Point{
		X: 0,
		Y: int((float64(width) - float64(height)) / 2)}
	draw.Draw(newSq, src.Bounds().Add(offset), src, image.Point{}, draw.Over)
	return newSq
}

// Scale will resize the image to the provided rectangle using the
// provided interpolation function.
func Scale(src image.Image, rect image.Rectangle, scale draw.Scaler) image.Image {
	dst := image.NewRGBA(rect)
	scale.Scale(dst, rect, src, src.Bounds(), draw.Over, nil)
	return dst
}

func ResizeSameY(img1, img2 image.Image, larger bool) (image.Image, image.Image) {
	y1 := img1.Bounds().Dy()
	y2 := img2.Bounds().Dy()
	if y1 == y2 {
		return img1, img2
	} else if y1 > y2 {
		if larger {
			img2 = Resize(0, uint(y1), img2, ScalerBest())
		} else {
			img1 = Resize(0, uint(y2), img1, ScalerBest())
		}
	} else {
		if larger {
			img1 = Resize(0, uint(y2), img1, ScalerBest())
		} else {
			img2 = Resize(0, uint(y1), img2, ScalerBest())
		}
	}
	return img1, img2
}

func ResizeSameX(img1, img2 image.Image, larger bool) (image.Image, image.Image) {
	x1 := img1.Bounds().Dx()
	x2 := img2.Bounds().Dx()
	if x1 == x2 {
		return img1, img2
	} else if x1 > x2 {
		if larger {
			img2 = Resize(uint(x1), 0, img2, ScalerBest())
		} else {
			img1 = Resize(uint(x2), 0, img1, ScalerBest())
		}
	} else {
		if larger {
			img1 = Resize(uint(x2), 0, img1, ScalerBest())
		} else {
			img2 = Resize(uint(x1), 0, img2, ScalerBest())
		}
	}
	return img1, img2
}

// ScalerDefault returns a general best results interpolation
// algorithm. See more here https://blog.codinghorror.com/better-image-resizing/ ,
// https://support.esri.com/en/technical-article/000005606 ,
// https://stackoverflow.com/questions/384991/what-is-the-best-image-downscaling-algorithm-quality-wise/6171860 .

func ScalerDefault() draw.Scaler { return draw.BiLinear }

func ScalerBest() draw.Scaler { return draw.CatmullRom }

func ParseScaler(rawInterpolation string) (draw.Scaler, error) {
	rawInterpolation = strings.ToLower(strings.TrimSpace(rawInterpolation))
	switch rawInterpolation {
	case "nearestneighbor":
		return draw.NearestNeighbor, nil
	case "approxbilinear":
		return draw.ApproxBiLinear, nil
	case "bilinear":
		return draw.BiLinear, nil
	case "catmullrom":
		return draw.CatmullRom, nil
	}
	return draw.NearestNeighbor, fmt.Errorf("Cannot parse Scalar [%s]", rawInterpolation)
}

func PaintColorRGBA(img *image.RGBA, clr color.RGBA) {
	if img == nil {
		return
	}
	PaintColorRGBARectangle(img, clr, img.Bounds())
}

func PaintColorRGBARectangle(img *image.RGBA, clr color.RGBA, rectNew image.Rectangle) {
	if img == nil {
		return
	}
	rectImg := img.Bounds()

	for x := rectNew.Min.X; x < rectNew.Max.X; x++ {
		if x < rectImg.Min.X || x > rectImg.Max.X {
			continue
		}
		for y := rectNew.Min.Y; y < rectNew.Max.Y; y++ {
			if y < rectImg.Min.Y || y > rectImg.Max.Y {
				continue
			}
			img.Set(x, y, clr)
		}
	}
}

// Information on rotation:
//
// https://www.golangprograms.com/how-to-rotate-an-image-in-golang.html
// https://code.google.com/archive/p/graphics-go/
// https://github.com/BurntSushi/graphics-go
// graphics.Rotate(dstImage, srcImage, &graphics.RotateOptions{math.Pi / 2.0}
<commit_msg>feat: imageutil: add SliceXY(); refactor ResizeSameX() to image slice vs. 2 images<commit_after>package imageutil

import (
	"fmt"
	"image"
	"image/color"
	"strings"

	"golang.org/x/image/draw"
)

// AddBackgroundColor adds a background of `color.Color` to an image.
// It is is useful when the image has a transparent background.
func AddBackgroundColor(imgSrc image.Image, clr color.Color) image.Image {
	imgNew := image.NewRGBA(imgSrc.Bounds())
	draw.Draw(imgNew, imgNew.Bounds(), &image.Uniform{clr}, image.Point{}, draw.Src)
	draw.Draw(imgNew, imgNew.Bounds(), imgSrc, imgSrc.Bounds().Min, draw.Over)
	return imgNew
}

// AddBackgroundWhite adds a white background which is usable
// when the image has a transparent background.
func AddBackgroundWhite(imgSrc image.Image) image.Image {
	return AddBackgroundColor(imgSrc, color.White)
}

// Resize scales an image to the provided size units. Use a 0
// to scale the aspect ratio. See gitub.com/nfnt/resize for Lanczos3, etc.
// https://github.com/nfnt/resize .
func Resize(width, height uint, src image.Image, scale draw.Scaler) image.Image {
	if width == 0 && height != 0 {
		width = uint(ImageAspect(src) * float64(height))
	} else if height == 0 && width != 0 {
		height = uint(float64(width) / ImageAspect(src))
	}
	rect := image.Rect(0, 0, int(width), int(height))
	dst := image.NewRGBA(rect)
	scale.Scale(dst, rect, src, src.Bounds(), draw.Over, nil)
	return dst
}

func ResizeMaxDimension(maxSide uint, src image.Image, scale draw.Scaler) image.Image {
	width := src.Bounds().Dx()
	height := src.Bounds().Dy()
	if width > height {
		if width == int(maxSide) {
			return src
		}
		return Resize(maxSide, 0, src, scale)
	}
	if height == int(maxSide) {
		return src
	}
	return Resize(0, maxSide, src, scale)
}

func Square(src image.Image) image.Image {
	width := src.Bounds().Dx()
	height := src.Bounds().Dy()
	if width == height {
		return src
	}
	if height > width {
		// https://www.golangprograms.com/how-to-add-watermark-or-merge-two-image.html
		newSq := image.NewRGBA(image.Rect(0, 0, height, height))
		offset := image.Point{
			X: int((float64(height) - float64(width)) / 2),
			Y: 0}
		draw.Draw(newSq, src.Bounds().Add(offset), src, image.Point{}, draw.Over)
		return newSq
	}
	newSq := image.NewRGBA(image.Rect(0, 0, width, width))
	offset := image.Point{
		X: 0,
		Y: int((float64(width) - float64(height)) / 2)}
	draw.Draw(newSq, src.Bounds().Add(offset), src, image.Point{}, draw.Over)
	return newSq
}

// Scale will resize the image to the provided rectangle using the
// provided interpolation function.
func Scale(src image.Image, rect image.Rectangle, scale draw.Scaler) image.Image {
	dst := image.NewRGBA(rect)
	scale.Scale(dst, rect, src, src.Bounds(), draw.Over, nil)
	return dst
}

func SliceXY(images []image.Image, maxIdx int) (minX, maxX, minY, maxY, sumX, sumY int) {
	for i, img := range images {
		if maxIdx >= 0 && i > maxIdx {
			break
		}
		sumX += img.Bounds().Dx()
		sumY += img.Bounds().Dy()
		if i == 0 {
			minX = img.Bounds().Dx()
			maxX = img.Bounds().Dx()
			minY = img.Bounds().Dy()
			maxY = img.Bounds().Dy()
			continue
		}
		if minX > img.Bounds().Dx() {
			minX = img.Bounds().Dx()
		}
		if maxX < img.Bounds().Dx() {
			maxX = img.Bounds().Dx()
		}
		if minY > img.Bounds().Dy() {
			minY = img.Bounds().Dy()
		}
		if maxY < img.Bounds().Dy() {
			maxY = img.Bounds().Dy()
		}
	}
	return
}

func ResizeSameX(images []image.Image, larger bool) []image.Image {
	minX, maxX, _, _, _, _ := SliceXY(images, -1)
	for i, img := range images {
		if larger && img.Bounds().Dx() != maxX {
			images[i] = Resize(uint(maxX), 0, img, ScalerBest())
		} else if !larger && img.Bounds().Dx() != minX {
			images[i] = Resize(uint(minX), 0, img, ScalerBest())
		}
	}
	return images
}

func ResizeSameY(images []image.Image, larger bool) []image.Image {
	_, _, minY, maxY, _, _ := SliceXY(images, -1)
	for i, img := range images {
		if larger && img.Bounds().Dy() != maxY {
			images[i] = Resize(uint(maxY), 0, img, ScalerBest())
		} else if !larger && img.Bounds().Dy() != minY {
			images[i] = Resize(uint(minY), 0, img, ScalerBest())
		}
	}
	return images
}

func ResizeSameYTwo(img1, img2 image.Image, larger bool) (image.Image, image.Image) {
	y1 := img1.Bounds().Dy()
	y2 := img2.Bounds().Dy()
	if y1 == y2 {
		return img1, img2
	} else if y1 > y2 {
		if larger {
			img2 = Resize(0, uint(y1), img2, ScalerBest())
		} else {
			img1 = Resize(0, uint(y2), img1, ScalerBest())
		}
	} else {
		if larger {
			img1 = Resize(0, uint(y2), img1, ScalerBest())
		} else {
			img2 = Resize(0, uint(y1), img2, ScalerBest())
		}
	}
	return img1, img2
}

func ResizeSameXTwo(img1, img2 image.Image, larger bool) (image.Image, image.Image) {
	x1 := img1.Bounds().Dx()
	x2 := img2.Bounds().Dx()
	if x1 == x2 {
		return img1, img2
	} else if x1 > x2 {
		if larger {
			img2 = Resize(uint(x1), 0, img2, ScalerBest())
		} else {
			img1 = Resize(uint(x2), 0, img1, ScalerBest())
		}
	} else {
		if larger {
			img1 = Resize(uint(x2), 0, img1, ScalerBest())
		} else {
			img2 = Resize(uint(x1), 0, img2, ScalerBest())
		}
	}
	return img1, img2
}

// ScalerDefault returns a general best results interpolation
// algorithm. See more here https://blog.codinghorror.com/better-image-resizing/ ,
// https://support.esri.com/en/technical-article/000005606 ,
// https://stackoverflow.com/questions/384991/what-is-the-best-image-downscaling-algorithm-quality-wise/6171860 .

func ScalerDefault() draw.Scaler { return draw.BiLinear }

func ScalerBest() draw.Scaler { return draw.CatmullRom }

func ParseScaler(rawInterpolation string) (draw.Scaler, error) {
	rawInterpolation = strings.ToLower(strings.TrimSpace(rawInterpolation))
	switch rawInterpolation {
	case "nearestneighbor":
		return draw.NearestNeighbor, nil
	case "approxbilinear":
		return draw.ApproxBiLinear, nil
	case "bilinear":
		return draw.BiLinear, nil
	case "catmullrom":
		return draw.CatmullRom, nil
	}
	return draw.NearestNeighbor, fmt.Errorf("Cannot parse Scalar [%s]", rawInterpolation)
}

func PaintColorRGBA(img *image.RGBA, clr color.RGBA) {
	if img == nil {
		return
	}
	PaintColorRGBARectangle(img, clr, img.Bounds())
}

func PaintColorRGBARectangle(img *image.RGBA, clr color.RGBA, rectNew image.Rectangle) {
	if img == nil {
		return
	}
	rectImg := img.Bounds()

	for x := rectNew.Min.X; x < rectNew.Max.X; x++ {
		if x < rectImg.Min.X || x > rectImg.Max.X {
			continue
		}
		for y := rectNew.Min.Y; y < rectNew.Max.Y; y++ {
			if y < rectImg.Min.Y || y > rectImg.Max.Y {
				continue
			}
			img.Set(x, y, clr)
		}
	}
}

// Information on rotation:
//
// https://www.golangprograms.com/how-to-rotate-an-image-in-golang.html
// https://code.google.com/archive/p/graphics-go/
// https://github.com/BurntSushi/graphics-go
// graphics.Rotate(dstImage, srcImage, &graphics.RotateOptions{math.Pi / 2.0}
<|endoftext|>


<commit_before>package filesystem

import (
	"bufio"
	"bytes"
	"io/ioutil"
	"os"
	"path/filepath"
	"subuk/vmango/compute"
	"subuk/vmango/util"

	"golang.org/x/crypto/ssh"

	"github.com/rs/zerolog"
)

const KEY_UPLOAD_FILENAME = "vmango_authorized_keys"

type KeyRepository struct {
	filename string
	logger   zerolog.Logger
}

func NewKeyRepository(filename string, logger zerolog.Logger) (*KeyRepository, error) {
	dirname := filepath.Dir(filename)
	if err := os.MkdirAll(dirname, 0755); err != nil {
		return nil, util.NewError(err, "cannot create base directory")
	}
	repo := &KeyRepository{filename: filename, logger: logger}
	return repo, nil
}

func (repo *KeyRepository) parseKey(input []byte) (*compute.Key, error) {
	pubkey, comment, options, _, err := ssh.ParseAuthorizedKey(input)
	if err != nil {
		return nil, err
	}
	key := &compute.Key{
		Type:        pubkey.Type(),
		Value:       input,
		Comment:     comment,
		Options:     options,
		Fingerprint: ssh.FingerprintLegacyMD5(pubkey),
	}
	return key, nil
}

func (repo *KeyRepository) List() ([]*compute.Key, error) {
	file, err := os.Open(repo.filename)
	if err != nil {
		return nil, util.NewError(err, "cannot open key file")
	}
	defer file.Close()

	keys := []*compute.Key{}
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		key, err := repo.parseKey(scanner.Bytes())
		if err != nil {
			repo.logger.Warn().Err(err).Msg("ignoring invalid key line")
			continue
		}
		keys = append(keys, key)
	}
	return keys, nil
}

func (repo *KeyRepository) Get(fingerprint string) (*compute.Key, error) {
	keys, err := repo.List()
	if err != nil {
		return nil, util.NewError(err, "cannot load keys")
	}
	for _, key := range keys {
		if key.Fingerprint == fingerprint {
			return key, nil
		}
	}
	return nil, compute.ErrKeyNotFound
}

func (repo *KeyRepository) Add(inputStr string) error {
	input := bytes.TrimSpace([]byte(inputStr))

	newKey, err := repo.parseKey(input)
	if err != nil {
		return util.NewError(err, "cannot parse provided key")
	}
	existingKey, err := repo.Get(newKey.Fingerprint)
	if err != nil && err != compute.ErrKeyNotFound {
		return util.NewError(err, "cannot check if key already exists")
	}
	if existingKey != nil {
		return compute.ErrKeyAlreadyExists
	}
	file, err := os.OpenFile(repo.filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return util.NewError(err, "cannot open key file")
	}
	defer file.Close()

	input = append(input, '\n')
	if _, err := file.Write(input); err != nil {
		return util.NewError(err, "cannot write key")
	}

	return nil
}

func (repo *KeyRepository) Delete(fingerprint string) error {
	file, err := os.Open(repo.filename)
	if err != nil {
		return util.NewError(err, "cannot open key file")
	}
	defer file.Close()

	keyFound := false
	contentWithoutKey := []byte{}
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := bytes.TrimSpace(scanner.Bytes())
		if bytes.Equal(line, []byte("")) {
			continue
		}
		key, err := repo.parseKey(line)
		line = append(line, '\n')
		if err != nil {
			contentWithoutKey = append(contentWithoutKey, line...)
			continue
		}
		if key.Fingerprint != fingerprint {
			contentWithoutKey = append(contentWithoutKey, line...)
			continue
		}
		keyFound = true
	}
	if !keyFound {
		return compute.ErrKeyNotFound
	}
	if err := ioutil.WriteFile(file.Name(), contentWithoutKey, 0644); err != nil {
		return util.NewError(err, "cannot rewrite key file")
	}
	return nil
}
<commit_msg>Create key_file if not exists<commit_after>package filesystem

import (
	"bufio"
	"bytes"
	"io/ioutil"
	"os"
	"path/filepath"
	"subuk/vmango/compute"
	"subuk/vmango/util"

	"golang.org/x/crypto/ssh"

	"github.com/rs/zerolog"
)

const KEY_UPLOAD_FILENAME = "vmango_authorized_keys"

type KeyRepository struct {
	filename string
	logger   zerolog.Logger
}

func NewKeyRepository(filename string, logger zerolog.Logger) (*KeyRepository, error) {
	dirname := filepath.Dir(filename)
	if err := os.MkdirAll(dirname, 0755); err != nil {
		return nil, util.NewError(err, "cannot create base directory")
	}
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		file, err := os.Create(filename)
		if err != nil {
			return nil, util.NewError(err, "key_file doesn't exist and cannot be created")
		}
		file.Close()
	}
	repo := &KeyRepository{filename: filename, logger: logger}
	return repo, nil
}

func (repo *KeyRepository) parseKey(input []byte) (*compute.Key, error) {
	pubkey, comment, options, _, err := ssh.ParseAuthorizedKey(input)
	if err != nil {
		return nil, err
	}
	key := &compute.Key{
		Type:        pubkey.Type(),
		Value:       input,
		Comment:     comment,
		Options:     options,
		Fingerprint: ssh.FingerprintLegacyMD5(pubkey),
	}
	return key, nil
}

func (repo *KeyRepository) List() ([]*compute.Key, error) {
	file, err := os.Open(repo.filename)
	if err != nil {
		return nil, util.NewError(err, "cannot open key file")
	}
	defer file.Close()

	keys := []*compute.Key{}
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		key, err := repo.parseKey(scanner.Bytes())
		if err != nil {
			repo.logger.Warn().Err(err).Msg("ignoring invalid key line")
			continue
		}
		keys = append(keys, key)
	}
	return keys, nil
}

func (repo *KeyRepository) Get(fingerprint string) (*compute.Key, error) {
	keys, err := repo.List()
	if err != nil {
		return nil, util.NewError(err, "cannot load keys")
	}
	for _, key := range keys {
		if key.Fingerprint == fingerprint {
			return key, nil
		}
	}
	return nil, compute.ErrKeyNotFound
}

func (repo *KeyRepository) Add(inputStr string) error {
	input := bytes.TrimSpace([]byte(inputStr))

	newKey, err := repo.parseKey(input)
	if err != nil {
		return util.NewError(err, "cannot parse provided key")
	}
	existingKey, err := repo.Get(newKey.Fingerprint)
	if err != nil && err != compute.ErrKeyNotFound {
		return util.NewError(err, "cannot check if key already exists")
	}
	if existingKey != nil {
		return compute.ErrKeyAlreadyExists
	}
	file, err := os.OpenFile(repo.filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return util.NewError(err, "cannot open key file")
	}
	defer file.Close()

	input = append(input, '\n')
	if _, err := file.Write(input); err != nil {
		return util.NewError(err, "cannot write key")
	}

	return nil
}

func (repo *KeyRepository) Delete(fingerprint string) error {
	file, err := os.Open(repo.filename)
	if err != nil {
		return util.NewError(err, "cannot open key file")
	}
	defer file.Close()

	keyFound := false
	contentWithoutKey := []byte{}
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := bytes.TrimSpace(scanner.Bytes())
		if bytes.Equal(line, []byte("")) {
			continue
		}
		key, err := repo.parseKey(line)
		line = append(line, '\n')
		if err != nil {
			contentWithoutKey = append(contentWithoutKey, line...)
			continue
		}
		if key.Fingerprint != fingerprint {
			contentWithoutKey = append(contentWithoutKey, line...)
			continue
		}
		keyFound = true
	}
	if !keyFound {
		return compute.ErrKeyNotFound
	}
	if err := ioutil.WriteFile(file.Name(), contentWithoutKey, 0644); err != nil {
		return util.NewError(err, "cannot rewrite key file")
	}
	return nil
}
<|endoftext|>


<commit_before>package base

import (
	"../../gotcp"
	"../common"
	"encoding/binary"
	"fmt"
	"io"
)

func HandleHeartBeatRequestBw(task *gotcp.Task) {
	cmd := Cmd.NewT_ClientNullCmd()
	task.SendCmd(cmd)
}
func HandleHeartBeatReturnBw(task *gotcp.Task) {
	HandleWriteFunBw(task, []byte("return tick"))
}
func HandleReadFunBw(task *gotcp.Task) ([]gotcp.ReadData, error) {
	var l uint32
	if err := binary.Read(task.Conn, binary.LittleEndian, &l); err != nil {
		return nil, err
	}
	data := make([]gotcp.ReadData, 1)
	data[0].Length = int(l)
	data[0].Data = make([]byte, l)
	_, err := io.ReadFull(task.Conn, data[0].Data)
	return data, err
}
func HandleWriteFunBw(task *gotcp.Task, data []byte) error {
	err := binary.Write(task.Conn, binary.LittleEndian, uint16(len(data)))
	if err != nil {
		return err
	}
	err = binary.Write(task.Conn, binary.LittleEndian, uint16(0))
	if err != nil {
		return err
	}
	_, err = task.Conn.Write(data)
	return err
}
func HandleParseBw(task *gotcp.Task, data []byte) bool {
	fmt.Println(data[0], data[1])
	switch byCmd := data[0]; byCmd {
	case Cmd.CMD_NULL:
		switch byParam := data[1]; byParam {
		case Cmd.SERVER_PARA_NULL:
			HandleWriteFunBw(task, data)
			task.HeartBeatReturn = true
		case Cmd.CLIENT_PARA_NULL:
			task.HeartBeatReturn = true
		}
	default:
		return false
	}
	return true
}
<commit_msg>error comple<commit_after>package base

import (
	"github.com/navy1125/gotcp/gotcp"
	"github.com/navy1125/gotcp/bw/common"
	"encoding/binary"
	"fmt"
	"io"
)

func HandleHeartBeatRequestBw(task *gotcp.Task) {
	cmd := Cmd.NewT_ClientNullCmd()
	task.SendCmd(cmd)
}
func HandleHeartBeatReturnBw(task *gotcp.Task) {
	HandleWriteFunBw(task, []byte("return tick"))
}
func HandleReadFunBw(task *gotcp.Task) ([]gotcp.ReadData, error) {
	var l uint32
	if err := binary.Read(task.Conn, binary.LittleEndian, &l); err != nil {
		return nil, err
	}
	data := make([]gotcp.ReadData, 1)
	data[0].Length = int(l)
	data[0].Data = make([]byte, l)
	_, err := io.ReadFull(task.Conn, data[0].Data)
	return data, err
}
func HandleWriteFunBw(task *gotcp.Task, data []byte) error {
	err := binary.Write(task.Conn, binary.LittleEndian, uint16(len(data)))
	if err != nil {
		return err
	}
	err = binary.Write(task.Conn, binary.LittleEndian, uint16(0))
	if err != nil {
		return err
	}
	_, err = task.Conn.Write(data)
	return err
}
func HandleParseBw(task *gotcp.Task, data []byte) bool {
	fmt.Println(data[0], data[1])
	switch byCmd := data[0]; byCmd {
	case Cmd.CMD_NULL:
		switch byParam := data[1]; byParam {
		case Cmd.SERVER_PARA_NULL:
			HandleWriteFunBw(task, data)
			task.HeartBeatReturn = true
		case Cmd.CLIENT_PARA_NULL:
			task.HeartBeatReturn = true
		}
	default:
		return false
	}
	return true
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Uber Technologies, Inc.
//
// 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 dispatcher

import (
	"fmt"
	"time"

	"go.uber.org/yarpc"
	"go.uber.org/yarpc/api/transport"
	"go.uber.org/yarpc/encoding/raw"
	"go.uber.org/yarpc/internal/crossdock/client/params"
	"go.uber.org/yarpc/transport/http"
	"go.uber.org/yarpc/transport/tchannel"
	"go.uber.org/yarpc/transport/x/cherami"
	"go.uber.org/yarpc/transport/x/redis"

	"github.com/crossdock/crossdock-go"
	cherami_client "github.com/uber/cherami-client-go/client/cherami"
)

// Create creates an RPC from the given parameters or fails the whole behavior.
func Create(t crossdock.T) *yarpc.Dispatcher {
	fatals := crossdock.Fatals(t)

	server := t.Param(params.Server)
	fatals.NotEmpty(server, "server is required")

	var unaryOutbound transport.UnaryOutbound
	trans := t.Param(params.Transport)
	switch trans {
	case "http":
		httpTransport := http.NewTransport()
		unaryOutbound = httpTransport.NewSingleOutbound(fmt.Sprintf("http://%s:8081", server))
	case "tchannel":
		tchannelTransport, err := tchannel.NewChannelTransport(tchannel.ServiceName("client"))
		fatals.NoError(err, "Failed to build ChannelTransport")

		unaryOutbound = tchannelTransport.NewSingleOutbound(server + ":8082")
	default:
		fatals.Fail("", "unknown transport %q", trans)
	}

	return yarpc.NewDispatcher(yarpc.Config{
		Name: "client",
		Outbounds: yarpc.Outbounds{
			"yarpc-test": {
				Unary: unaryOutbound,
			},
		},
	})
}

// CreateOnewayDispatcher returns a started dispatcher and returns the address the
// server should call back to (ie this host)
func CreateOnewayDispatcher(t crossdock.T, handler raw.OnewayHandler) (*yarpc.Dispatcher, string) {
	fatals := crossdock.Fatals(t)

	server := t.Param("server_oneway")
	fatals.NotEmpty(server, "oneway server is required")

	httpTransport := http.NewTransport()
	var outbound transport.OnewayOutbound

	trans := t.Param("transport_oneway")
	switch trans {
	case "http":
		outbound = httpTransport.NewSingleOutbound(fmt.Sprintf("http://%s:8084", server))
	case "redis":
		outbound = redis.NewOnewayOutbound(
			redis.NewRedis5Client("redis:6379"),
			"yarpc/oneway",
		)
	case "cherami":
		cheramiClient, err := cherami_client.NewClient(`example`, `cherami`, 4922, &cherami_client.ClientOptions{
			Timeout: 5 * time.Second,
			ReconfigurationPollingInterval: 1 * time.Second,
		})
		fatals.NoError(err, "couldn't create cherami client")

		transport := cherami.NewTransport(cheramiClient)
		err = transport.Start()
		fatals.NoError(err, "couldn't start cherami transport")

		outbound = transport.NewOutbound(cherami.OutboundConfig{
			Destination: `/test/dest`})
	default:
		fatals.Fail("", "unknown transport %q", trans)
	}

	callBackInbound := httpTransport.NewInbound(":0")
	dispatcher := yarpc.NewDispatcher(yarpc.Config{
		Name: "oneway-client",
		Outbounds: yarpc.Outbounds{
			"oneway-server": {Oneway: outbound},
		},
		Inbounds: yarpc.Inbounds{callBackInbound},
	})

	// register procedure for server to call us back on
	dispatcher.Register(raw.OnewayProcedure("call-back", raw.OnewayHandler(handler)))
	fatals.NoError(dispatcher.Start(), "could not start oneway Dispatcher")

	return dispatcher, callBackInbound.Addr().String()
}
<commit_msg>crossdock/oneway: Fix the inbound url for oneway<commit_after>// Copyright (c) 2017 Uber Technologies, Inc.
//
// 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 dispatcher

import (
	"fmt"
	"time"

	"go.uber.org/yarpc"
	"go.uber.org/yarpc/api/transport"
	"go.uber.org/yarpc/encoding/raw"
	"go.uber.org/yarpc/internal/crossdock/client/params"
	"go.uber.org/yarpc/transport/http"
	"go.uber.org/yarpc/transport/tchannel"
	"go.uber.org/yarpc/transport/x/cherami"
	"go.uber.org/yarpc/transport/x/redis"

	"github.com/crossdock/crossdock-go"
	cherami_client "github.com/uber/cherami-client-go/client/cherami"
)

// Create creates an RPC from the given parameters or fails the whole behavior.
func Create(t crossdock.T) *yarpc.Dispatcher {
	fatals := crossdock.Fatals(t)

	server := t.Param(params.Server)
	fatals.NotEmpty(server, "server is required")

	var unaryOutbound transport.UnaryOutbound
	trans := t.Param(params.Transport)
	switch trans {
	case "http":
		httpTransport := http.NewTransport()
		unaryOutbound = httpTransport.NewSingleOutbound(fmt.Sprintf("http://%s:8081", server))
	case "tchannel":
		tchannelTransport, err := tchannel.NewChannelTransport(tchannel.ServiceName("client"))
		fatals.NoError(err, "Failed to build ChannelTransport")

		unaryOutbound = tchannelTransport.NewSingleOutbound(server + ":8082")
	default:
		fatals.Fail("", "unknown transport %q", trans)
	}

	return yarpc.NewDispatcher(yarpc.Config{
		Name: "client",
		Outbounds: yarpc.Outbounds{
			"yarpc-test": {
				Unary: unaryOutbound,
			},
		},
	})
}

// CreateOnewayDispatcher returns a started dispatcher and returns the address the
// server should call back to (ie this host)
func CreateOnewayDispatcher(t crossdock.T, handler raw.OnewayHandler) (*yarpc.Dispatcher, string) {
	fatals := crossdock.Fatals(t)

	server := t.Param("server_oneway")
	fatals.NotEmpty(server, "oneway server is required")

	httpTransport := http.NewTransport()
	var outbound transport.OnewayOutbound

	trans := t.Param("transport_oneway")
	switch trans {
	case "http":
		outbound = httpTransport.NewSingleOutbound(fmt.Sprintf("http://%s:8084", server))
	case "redis":
		outbound = redis.NewOnewayOutbound(
			redis.NewRedis5Client("redis:6379"),
			"yarpc/oneway",
		)
	case "cherami":
		cheramiClient, err := cherami_client.NewClient(`example`, `cherami`, 4922, &cherami_client.ClientOptions{
			Timeout: 5 * time.Second,
			ReconfigurationPollingInterval: 1 * time.Second,
		})
		fatals.NoError(err, "couldn't create cherami client")

		transport := cherami.NewTransport(cheramiClient)
		err = transport.Start()
		fatals.NoError(err, "couldn't start cherami transport")

		outbound = transport.NewOutbound(cherami.OutboundConfig{
			Destination: `/test/dest`})
	default:
		fatals.Fail("", "unknown transport %q", trans)
	}

	client := t.Param("client_oneway")
	callBackInbound := httpTransport.NewInbound(fmt.Sprintf("%s:0", client))
	dispatcher := yarpc.NewDispatcher(yarpc.Config{
		Name: "oneway-client",
		Outbounds: yarpc.Outbounds{
			"oneway-server": {Oneway: outbound},
		},
		Inbounds: yarpc.Inbounds{callBackInbound},
	})

	// register procedure for server to call us back on
	dispatcher.Register(raw.OnewayProcedure("call-back", raw.OnewayHandler(handler)))
	fatals.NoError(dispatcher.Start(), "could not start oneway Dispatcher")

	return dispatcher, callBackInbound.Addr().String()
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package gsharedmemory

import (
	"context"
	"sync"

	"github.com/ava-labs/avalanchego/chains/atomic"
	"github.com/ava-labs/avalanchego/chains/atomic/gsharedmemory/gsharedmemoryproto"
	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/ids"
)

var (
	_ gsharedmemoryproto.SharedMemoryServer = &Server{}
)

// Server is shared memory that is managed over RPC.
type Server struct {
	sm atomic.SharedMemory
	db database.Database

	putsLock sync.Mutex
	puts     map[int64]*putRequest

	getsLock sync.Mutex
	gets     map[int64]*getRequest

	indexedLock sync.Mutex
	indexed     map[int64]*indexedRequest

	removesLock sync.Mutex
	removes     map[int64]*removeRequest
}

// NewServer returns shared memory connected to remote shared memory
func NewServer(sm atomic.SharedMemory, db database.Database) *Server {
	return &Server{
		sm:      sm,
		db:      db,
		puts:    make(map[int64]*putRequest),
		gets:    make(map[int64]*getRequest),
		indexed: make(map[int64]*indexedRequest),
		removes: make(map[int64]*removeRequest),
	}
}

type putRequest struct {
	peerChainID ids.ID
	elems       []*atomic.Element
	batches     map[int64]database.Batch
}

func (s *Server) Put(
	_ context.Context,
	req *gsharedmemoryproto.PutRequest,
) (*gsharedmemoryproto.PutResponse, error) {
	s.putsLock.Lock()
	defer s.putsLock.Unlock()

	put, exists := s.puts[req.Id]
	if !exists {
		peerChainID, err := ids.ToID(req.PeerChainID)
		if err != nil {
			return nil, err
		}

		put = &putRequest{
			peerChainID: peerChainID,
			elems:       make([]*atomic.Element, 0, len(req.Elems)),
			batches:     make(map[int64]database.Batch),
		}
	}

	for _, elem := range req.Elems {
		put.elems = append(put.elems, &atomic.Element{
			Key:    elem.Key,
			Value:  elem.Value,
			Traits: elem.Traits,
		})
	}

	if err := s.parseBatches(put.batches, req.Batches); err != nil {
		delete(s.puts, req.Id)
		return nil, err
	}

	if req.Continues {
		s.puts[req.Id] = put
		return &gsharedmemoryproto.PutResponse{}, nil
	}

	delete(s.puts, req.Id)

	batches := make([]database.Batch, len(put.batches))
	i := 0
	for _, batch := range put.batches {
		batches[i] = batch
		i++
	}
	return &gsharedmemoryproto.PutResponse{}, s.sm.Put(put.peerChainID, put.elems, batches...)
}

type getRequest struct {
	peerChainID ids.ID
	keys        [][]byte

	executed        bool
	remainingValues [][]byte
}

func (s *Server) Get(
	_ context.Context,
	req *gsharedmemoryproto.GetRequest,
) (*gsharedmemoryproto.GetResponse, error) {
	s.getsLock.Lock()
	defer s.getsLock.Unlock()

	get, exists := s.gets[req.Id]
	if !exists {
		peerChainID, err := ids.ToID(req.PeerChainID)
		if err != nil {
			return nil, err
		}

		get = &getRequest{
			peerChainID: peerChainID,
			keys:        make([][]byte, 0, len(req.Keys)),
		}
	}

	get.keys = append(get.keys, req.Keys...)

	if req.Continues {
		s.gets[req.Id] = get
		return &gsharedmemoryproto.GetResponse{}, nil
	}

	if !get.executed {
		values, err := s.sm.Get(get.peerChainID, get.keys)
		if err != nil {
			delete(s.gets, req.Id)
			return nil, err
		}

		get.executed = true
		get.remainingValues = values
	}

	currentSize := 0
	resp := &gsharedmemoryproto.GetResponse{
		Values: make([][]byte, 0, len(get.remainingValues)),
	}
	for i, value := range get.remainingValues {
		sizeChange := baseElementSize + len(value)
		if newSize := currentSize + sizeChange; newSize > maxBatchSize && i > 0 {
			break
		}
		currentSize += sizeChange

		resp.Values = append(resp.Values, value)
	}

	get.remainingValues = get.remainingValues[len(resp.Values):]
	resp.Continues = len(get.remainingValues) > 0

	if resp.Continues {
		s.gets[req.Id] = get
	} else {
		delete(s.gets, req.Id)
	}
	return resp, nil
}

type indexedRequest struct {
	peerChainID ids.ID
	traits      [][]byte
	startTrait  []byte
	startKey    []byte
	limit       int

	executed        bool
	remainingValues [][]byte
	lastTrait       []byte
	lastKey         []byte
}

func (s *Server) Indexed(
	_ context.Context,
	req *gsharedmemoryproto.IndexedRequest,
) (*gsharedmemoryproto.IndexedResponse, error) {
	s.indexedLock.Lock()
	defer s.indexedLock.Unlock()

	indexed, exists := s.indexed[req.Id]
	if !exists {
		peerChainID, err := ids.ToID(req.PeerChainID)
		if err != nil {
			return nil, err
		}

		indexed = &indexedRequest{
			peerChainID: peerChainID,
			traits:      make([][]byte, 0, len(req.Traits)),
			startTrait:  req.StartTrait,
			startKey:    req.StartKey,
			limit:       int(req.Limit),
		}
	}

	indexed.traits = append(indexed.traits, req.Traits...)

	if req.Continues {
		s.indexed[req.Id] = indexed
		return &gsharedmemoryproto.IndexedResponse{}, nil
	}

	if !indexed.executed {
		values, lastTrait, lastKey, err := s.sm.Indexed(
			indexed.peerChainID,
			indexed.traits,
			indexed.startTrait,
			indexed.startKey,
			indexed.limit,
		)
		if err != nil {
			delete(s.indexed, req.Id)
			return nil, err
		}

		indexed.executed = true
		indexed.remainingValues = values
		indexed.lastTrait = lastTrait
		indexed.lastKey = lastKey
	}

	currentSize := 0
	resp := &gsharedmemoryproto.IndexedResponse{
		Values:    make([][]byte, 0, len(indexed.remainingValues)),
		LastTrait: indexed.lastTrait,
		LastKey:   indexed.lastKey,
	}
	for i, value := range indexed.remainingValues {
		sizeChange := baseElementSize + len(value)
		if newSize := currentSize + sizeChange; newSize > maxBatchSize && i > 0 {
			break
		}
		currentSize += sizeChange

		resp.Values = append(resp.Values, value)
	}

	indexed.remainingValues = indexed.remainingValues[len(resp.Values):]
	resp.Continues = len(indexed.remainingValues) > 0

	if resp.Continues {
		indexed.lastTrait = nil
		indexed.lastKey = nil
		s.indexed[req.Id] = indexed
	} else {
		delete(s.indexed, req.Id)
	}
	return resp, nil
}

type removeRequest struct {
	peerChainID ids.ID
	keys        [][]byte
	batches     map[int64]database.Batch
}

func (s *Server) Remove(
	_ context.Context,
	req *gsharedmemoryproto.RemoveRequest,
) (*gsharedmemoryproto.RemoveResponse, error) {
	s.removesLock.Lock()
	defer s.removesLock.Unlock()

	remove, exists := s.removes[req.Id]
	if !exists {
		peerChainID, err := ids.ToID(req.PeerChainID)
		if err != nil {
			return nil, err
		}

		remove = &removeRequest{
			peerChainID: peerChainID,
			batches:     make(map[int64]database.Batch),
		}
	}

	remove.keys = append(remove.keys, req.Keys...)
	if err := s.parseBatches(remove.batches, req.Batches); err != nil {
		delete(s.removes, req.Id)
		return nil, err
	}

	if req.Continues {
		s.removes[req.Id] = remove
		return &gsharedmemoryproto.RemoveResponse{}, nil
	}

	delete(s.removes, req.Id)

	batches := make([]database.Batch, len(remove.batches))
	i := 0
	for _, batch := range remove.batches {
		batches[i] = batch
		i++
	}
	return &gsharedmemoryproto.RemoveResponse{}, s.sm.Remove(remove.peerChainID, remove.keys, batches...)
}

func (s *Server) parseBatches(
	batches map[int64]database.Batch,
	rawBatches []*gsharedmemoryproto.Batch,
) error {
	for _, reqBatch := range rawBatches {
		batch, exists := batches[reqBatch.Id]
		if !exists {
			batch = s.db.NewBatch()
			batches[reqBatch.Id] = batch
		}

		for _, putReq := range reqBatch.Puts {
			if err := batch.Put(putReq.Key, putReq.Value); err != nil {
				return err
			}
		}

		for _, deleteReq := range reqBatch.Deletes {
			if err := batch.Delete(deleteReq.Key); err != nil {
				return err
			}
		}
	}
	return nil
}
<commit_msg>removed aggressive pre-allocations<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package gsharedmemory

import (
	"context"
	"sync"

	"github.com/ava-labs/avalanchego/chains/atomic"
	"github.com/ava-labs/avalanchego/chains/atomic/gsharedmemory/gsharedmemoryproto"
	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/ids"
)

var (
	_ gsharedmemoryproto.SharedMemoryServer = &Server{}
)

// Server is shared memory that is managed over RPC.
type Server struct {
	sm atomic.SharedMemory
	db database.Database

	putsLock sync.Mutex
	puts     map[int64]*putRequest

	getsLock sync.Mutex
	gets     map[int64]*getRequest

	indexedLock sync.Mutex
	indexed     map[int64]*indexedRequest

	removesLock sync.Mutex
	removes     map[int64]*removeRequest
}

// NewServer returns shared memory connected to remote shared memory
func NewServer(sm atomic.SharedMemory, db database.Database) *Server {
	return &Server{
		sm:      sm,
		db:      db,
		puts:    make(map[int64]*putRequest),
		gets:    make(map[int64]*getRequest),
		indexed: make(map[int64]*indexedRequest),
		removes: make(map[int64]*removeRequest),
	}
}

type putRequest struct {
	peerChainID ids.ID
	elems       []*atomic.Element
	batches     map[int64]database.Batch
}

func (s *Server) Put(
	_ context.Context,
	req *gsharedmemoryproto.PutRequest,
) (*gsharedmemoryproto.PutResponse, error) {
	s.putsLock.Lock()
	defer s.putsLock.Unlock()

	put, exists := s.puts[req.Id]
	if !exists {
		peerChainID, err := ids.ToID(req.PeerChainID)
		if err != nil {
			return nil, err
		}

		put = &putRequest{
			peerChainID: peerChainID,
			elems:       make([]*atomic.Element, 0, len(req.Elems)),
			batches:     make(map[int64]database.Batch),
		}
	}

	for _, elem := range req.Elems {
		put.elems = append(put.elems, &atomic.Element{
			Key:    elem.Key,
			Value:  elem.Value,
			Traits: elem.Traits,
		})
	}

	if err := s.parseBatches(put.batches, req.Batches); err != nil {
		delete(s.puts, req.Id)
		return nil, err
	}

	if req.Continues {
		s.puts[req.Id] = put
		return &gsharedmemoryproto.PutResponse{}, nil
	}

	delete(s.puts, req.Id)

	batches := make([]database.Batch, len(put.batches))
	i := 0
	for _, batch := range put.batches {
		batches[i] = batch
		i++
	}
	return &gsharedmemoryproto.PutResponse{}, s.sm.Put(put.peerChainID, put.elems, batches...)
}

type getRequest struct {
	peerChainID ids.ID
	keys        [][]byte

	executed        bool
	remainingValues [][]byte
}

func (s *Server) Get(
	_ context.Context,
	req *gsharedmemoryproto.GetRequest,
) (*gsharedmemoryproto.GetResponse, error) {
	s.getsLock.Lock()
	defer s.getsLock.Unlock()

	get, exists := s.gets[req.Id]
	if !exists {
		peerChainID, err := ids.ToID(req.PeerChainID)
		if err != nil {
			return nil, err
		}

		get = &getRequest{
			peerChainID: peerChainID,
			keys:        make([][]byte, 0, len(req.Keys)),
		}
	}

	get.keys = append(get.keys, req.Keys...)

	if req.Continues {
		s.gets[req.Id] = get
		return &gsharedmemoryproto.GetResponse{}, nil
	}

	if !get.executed {
		values, err := s.sm.Get(get.peerChainID, get.keys)
		if err != nil {
			delete(s.gets, req.Id)
			return nil, err
		}

		get.executed = true
		get.remainingValues = values
	}

	currentSize := 0
	resp := &gsharedmemoryproto.GetResponse{}
	for i, value := range get.remainingValues {
		sizeChange := baseElementSize + len(value)
		if newSize := currentSize + sizeChange; newSize > maxBatchSize && i > 0 {
			break
		}
		currentSize += sizeChange

		resp.Values = append(resp.Values, value)
	}

	get.remainingValues = get.remainingValues[len(resp.Values):]
	resp.Continues = len(get.remainingValues) > 0

	if resp.Continues {
		s.gets[req.Id] = get
	} else {
		delete(s.gets, req.Id)
	}
	return resp, nil
}

type indexedRequest struct {
	peerChainID ids.ID
	traits      [][]byte
	startTrait  []byte
	startKey    []byte
	limit       int

	executed        bool
	remainingValues [][]byte
	lastTrait       []byte
	lastKey         []byte
}

func (s *Server) Indexed(
	_ context.Context,
	req *gsharedmemoryproto.IndexedRequest,
) (*gsharedmemoryproto.IndexedResponse, error) {
	s.indexedLock.Lock()
	defer s.indexedLock.Unlock()

	indexed, exists := s.indexed[req.Id]
	if !exists {
		peerChainID, err := ids.ToID(req.PeerChainID)
		if err != nil {
			return nil, err
		}

		indexed = &indexedRequest{
			peerChainID: peerChainID,
			traits:      make([][]byte, 0, len(req.Traits)),
			startTrait:  req.StartTrait,
			startKey:    req.StartKey,
			limit:       int(req.Limit),
		}
	}

	indexed.traits = append(indexed.traits, req.Traits...)

	if req.Continues {
		s.indexed[req.Id] = indexed
		return &gsharedmemoryproto.IndexedResponse{}, nil
	}

	if !indexed.executed {
		values, lastTrait, lastKey, err := s.sm.Indexed(
			indexed.peerChainID,
			indexed.traits,
			indexed.startTrait,
			indexed.startKey,
			indexed.limit,
		)
		if err != nil {
			delete(s.indexed, req.Id)
			return nil, err
		}

		indexed.executed = true
		indexed.remainingValues = values
		indexed.lastTrait = lastTrait
		indexed.lastKey = lastKey
	}

	currentSize := 0
	resp := &gsharedmemoryproto.IndexedResponse{
		LastTrait: indexed.lastTrait,
		LastKey:   indexed.lastKey,
	}
	for i, value := range indexed.remainingValues {
		sizeChange := baseElementSize + len(value)
		if newSize := currentSize + sizeChange; newSize > maxBatchSize && i > 0 {
			break
		}
		currentSize += sizeChange

		resp.Values = append(resp.Values, value)
	}

	indexed.remainingValues = indexed.remainingValues[len(resp.Values):]
	resp.Continues = len(indexed.remainingValues) > 0

	if resp.Continues {
		indexed.lastTrait = nil
		indexed.lastKey = nil
		s.indexed[req.Id] = indexed
	} else {
		delete(s.indexed, req.Id)
	}
	return resp, nil
}

type removeRequest struct {
	peerChainID ids.ID
	keys        [][]byte
	batches     map[int64]database.Batch
}

func (s *Server) Remove(
	_ context.Context,
	req *gsharedmemoryproto.RemoveRequest,
) (*gsharedmemoryproto.RemoveResponse, error) {
	s.removesLock.Lock()
	defer s.removesLock.Unlock()

	remove, exists := s.removes[req.Id]
	if !exists {
		peerChainID, err := ids.ToID(req.PeerChainID)
		if err != nil {
			return nil, err
		}

		remove = &removeRequest{
			peerChainID: peerChainID,
			batches:     make(map[int64]database.Batch),
		}
	}

	remove.keys = append(remove.keys, req.Keys...)
	if err := s.parseBatches(remove.batches, req.Batches); err != nil {
		delete(s.removes, req.Id)
		return nil, err
	}

	if req.Continues {
		s.removes[req.Id] = remove
		return &gsharedmemoryproto.RemoveResponse{}, nil
	}

	delete(s.removes, req.Id)

	batches := make([]database.Batch, len(remove.batches))
	i := 0
	for _, batch := range remove.batches {
		batches[i] = batch
		i++
	}
	return &gsharedmemoryproto.RemoveResponse{}, s.sm.Remove(remove.peerChainID, remove.keys, batches...)
}

func (s *Server) parseBatches(
	batches map[int64]database.Batch,
	rawBatches []*gsharedmemoryproto.Batch,
) error {
	for _, reqBatch := range rawBatches {
		batch, exists := batches[reqBatch.Id]
		if !exists {
			batch = s.db.NewBatch()
			batches[reqBatch.Id] = batch
		}

		for _, putReq := range reqBatch.Puts {
			if err := batch.Put(putReq.Key, putReq.Value); err != nil {
				return err
			}
		}

		for _, deleteReq := range reqBatch.Deletes {
			if err := batch.Delete(deleteReq.Key); err != nil {
				return err
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package v1alpha1

import (
	"fmt"
)

const (
	// GarbdListenPort is the port at which Galera Arbitrator Daemon (garbd) listen
	GarbdListenPort = 4444

	// GarbdXtrabackupSSTMethod is the name of the method or script that is
	// used during a State Snapshot Transfer to Galera Arbitrator Daemon (garbd).
	GarbdXtrabackupSSTMethod = "xtrabackup-v2"

	// GarbdXtrabackupSSTRequestSuffix denotes the suffix of sst request string for xtrabackup
	// used by Galera Arbitrator Daemon (garbd)
	GarbdXtrabackupSSTRequestSuffix = "/xtrabackup_sst//1"
	// GarbdLogFile is the name log file at which Galera Arbitrator Daemon (garbd) puts logs
	GarbdLogFile = "/tmp/garb.log"

	// GaleraParamsGarbdListenAddr defines an arbitrary listen socket address
	// that Galera Arbitrator Daemon (garbd) opens to communicate with the cluster
	// https://galeracluster.com/library/documentation/backup-cluster.html
	GaleraParamsGarbdListenAddr = "gmcast.listen_addr=tcp://0.0.0.0:" + string(GarbdListenPort)

	// SOCAT is needed after completing sst by Galera Arbitrator (garbd)
	// SOCATOptionTCPLISTEN is the SOCAT tcp listen option
	SOCATOptionTCPLISTEN = "TCP-LISTEN:" + string(GarbdListenPort)
	// SOCATOptionReUseAddr is the SOCAT reuseaddr option
	SOCATOptionReUseAddr = "reuseaddr"
	// SOCATOptionRetry is the default retry value for `socat` binary
	SOCATOptionRetry = 30
)

// ClusterAddressWithListenOption method returns the galera cluster address with
// the listening option (address at which Galera Cluster listens to connections from
// other nodes) for `--address` option in `garbd`
func (g *GaleraArbitratorConfiguration) ClusterAddressWithListenOption() string {
	if g == nil {
		return ""
	}

	return fmt.Sprintf("%s?%s", g.Address, GaleraParamsGarbdListenAddr)
}

// SSTRequestString method form the sst request string
// for `--sst` option in `garbd`
func (g *GaleraArbitratorConfiguration) SSTRequestString(host string) string {
	if g == nil {
		return ""
	}

	return fmt.Sprintf("%s:%s:%d%s", g.SSTMethod, host, GarbdListenPort, GarbdXtrabackupSSTRequestSuffix)
}

// SOCATOption returns the option string used for `SOCAT` in the
// percona xtradb backup process
func SOCATOption(retry int32) string {
	return fmt.Sprintf("%s,%s,retry=%d", SOCATOptionTCPLISTEN, SOCATOptionReUseAddr, retry)
}
<commit_msg>Update galera arbitrator helper methods and constants (#438)<commit_after>package v1alpha1

import (
	"fmt"
)

const (
	// GarbdListenPort is the port at which Galera Arbitrator Daemon (garbd) listen
	GarbdListenPort = 4444

	// GarbdXtrabackupSSTMethod is the name of the method or script that is
	// used during a State Snapshot Transfer to Galera Arbitrator Daemon (garbd).
	GarbdXtrabackupSSTMethod = "xtrabackup-v2"

	// GarbdXtrabackupSSTRequestSuffix denotes the suffix of sst request string for xtrabackup
	// used by Galera Arbitrator Daemon (garbd)
	GarbdXtrabackupSSTRequestSuffix = "/xtrabackup_sst//1"
	// GarbdLogFile is the name log file at which Galera Arbitrator Daemon (garbd) puts logs
	GarbdLogFile = "/tmp/garb.log"

	// SOCAT is needed after completing sst by Galera Arbitrator Daemon (garbd)
	// SOCATOptionReUseAddr is the SOCAT reuseaddr option
	SOCATOptionReUseAddr = "reuseaddr"
	// SOCATOptionRetry is the default retry value for `socat` binary
	SOCATOptionRetry = 30
)

// ClusterAddressWithListenOption method returns the galera cluster address with
// the listening option (address at which Galera Cluster listens to connections from
// other nodes) for `--address` option in `garbd`
// Here, ‘?gmcast.listen_addr=tcp://0.0.0.0:4444‘ is an arbitrary listen socket address
// that Galera Arbitrator opens to communicate with the cluster.
// https://galeracluster.com/library/documentation/backup-cluster.html
func (g *GaleraArbitratorConfiguration) ClusterAddressWithListenOption() string {
	if g == nil {
		return ""
	}

	return fmt.Sprintf("%s?gmcast.listen_addr=tcp://0.0.0.0:%d", g.Address, GarbdListenPort)
}

// SSTRequestString method form the sst request string
// for `--sst` option in `garbd`
func (g *GaleraArbitratorConfiguration) SSTRequestString(host string) string {
	if g == nil {
		return ""
	}

	return fmt.Sprintf("%s:%s:%d%s", g.SSTMethod, host, GarbdListenPort, GarbdXtrabackupSSTRequestSuffix)
}

// SOCATOption returns the option string used for `SOCAT` in the
// percona xtradb backup process
func SOCATOption(retry int32) string {
	return fmt.Sprintf("TCP-LISTEN:%d,%s,retry=%d", GarbdListenPort, SOCATOptionReUseAddr, retry)
}
<|endoftext|>


<commit_before>// Copyright (C) 2016  Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.

// Package client provides helper functions for OpenConfig CLI tools.
package client

import (
	"io"
	"strings"
	"sync"

	"github.com/aristanetworks/glog"
	"github.com/aristanetworks/goarista/openconfig"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

// Run creates a new gRPC client, sends subscriptions, and consumes responses.
// The given publish function is used to publish SubscribeResponses received
// for the given subscriptions, when connected to the given host, with the
// given user/pass pair, or the client-side cert specified in the gRPC opts.
// This function does not normally return so it should probably be run in its
// own goroutine.  When this function returns, the given WaitGroup is marked
// as done.
func Run(publish func(*openconfig.SubscribeResponse), wg *sync.WaitGroup,
	username, password, addr string, subscriptions []string,
	opts []grpc.DialOption) {

	defer wg.Done()
	conn, err := grpc.Dial(addr, opts...)
	if err != nil {
		glog.Fatalf("fail to dial: %s", err)
	}
	glog.Infof("Connected to %s", addr)
	defer conn.Close()
	client := openconfig.NewOpenConfigClient(conn)

	ctx := context.Background()
	if username != "" {
		ctx = metadata.NewContext(ctx, metadata.Pairs(
			"username", username,
			"password", password))
	}

	stream, err := client.Subscribe(ctx)
	if err != nil {
		glog.Fatalf("Subscribe failed: %s", err)
	}
	defer stream.CloseSend()

	for _, path := range subscriptions {
		sub := &openconfig.SubscribeRequest{
			Request: &openconfig.SubscribeRequest_Subscribe{
				Subscribe: &openconfig.SubscriptionList{
					Subscription: []*openconfig.Subscription{
						&openconfig.Subscription{
							Path: &openconfig.Path{Element: strings.Split(path, "/")},
						},
					},
				},
			},
		}

		err = stream.Send(sub)
		if err != nil {
			glog.Fatalf("Failed to subscribe: %s", err)
		}
	}

	for {
		resp, err := stream.Recv()
		if err != nil {
			if err != io.EOF {
				glog.Fatalf("Error received from the server: %s", err)
			}
			return
		}
		publish(resp)
	}
}
<commit_msg>openconfig/client: log subscribe requests<commit_after>// Copyright (C) 2016  Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.

// Package client provides helper functions for OpenConfig CLI tools.
package client

import (
	"io"
	"strings"
	"sync"

	"github.com/aristanetworks/glog"
	"github.com/aristanetworks/goarista/openconfig"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

// Run creates a new gRPC client, sends subscriptions, and consumes responses.
// The given publish function is used to publish SubscribeResponses received
// for the given subscriptions, when connected to the given host, with the
// given user/pass pair, or the client-side cert specified in the gRPC opts.
// This function does not normally return so it should probably be run in its
// own goroutine.  When this function returns, the given WaitGroup is marked
// as done.
func Run(publish func(*openconfig.SubscribeResponse), wg *sync.WaitGroup,
	username, password, addr string, subscriptions []string,
	opts []grpc.DialOption) {

	defer wg.Done()
	conn, err := grpc.Dial(addr, opts...)
	if err != nil {
		glog.Fatalf("fail to dial: %s", err)
	}
	glog.Infof("Connected to %s", addr)
	defer conn.Close()
	client := openconfig.NewOpenConfigClient(conn)

	ctx := context.Background()
	if username != "" {
		ctx = metadata.NewContext(ctx, metadata.Pairs(
			"username", username,
			"password", password))
	}

	stream, err := client.Subscribe(ctx)
	if err != nil {
		glog.Fatalf("Subscribe failed: %s", err)
	}
	defer stream.CloseSend()

	for _, path := range subscriptions {
		sub := &openconfig.SubscribeRequest{
			Request: &openconfig.SubscribeRequest_Subscribe{
				Subscribe: &openconfig.SubscriptionList{
					Subscription: []*openconfig.Subscription{
						&openconfig.Subscription{
							Path: &openconfig.Path{Element: strings.Split(path, "/")},
						},
					},
				},
			},
		}

		glog.Infof("Sending subscribe request: %s", sub)
		err = stream.Send(sub)
		if err != nil {
			glog.Fatalf("Failed to subscribe: %s", err)
		}
	}

	for {
		resp, err := stream.Recv()
		if err != nil {
			if err != io.EOF {
				glog.Fatalf("Error received from the server: %s", err)
			}
			return
		}
		publish(resp)
	}
}
<|endoftext|>


<commit_before>// Copyright 2009 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.

// This package provides basic interfaces to I/O primitives.
// Its primary job is to wrap existing implementations of such primitives,
// such as those in package os, into shared public interfaces that
// abstract the functionality, plus some other related primitives.
package io

import "os"

// Error represents an unexpected I/O behavior.
type Error struct {
	os.ErrorString
}

// ErrShortWrite means that a write accepted fewer bytes than requested
// but failed to return an explicit error.
var ErrShortWrite os.Error = &Error{"short write"}

// ErrShortBuffer means that a read required a longer buffer than was provided.
var ErrShortBuffer os.Error = &Error{"short buffer"}

// ErrUnexpectedEOF means that os.EOF was encountered in the
// middle of reading a fixed-size block or data structure.
var ErrUnexpectedEOF os.Error = &Error{"unexpected EOF"}

// Reader is the interface that wraps the basic Read method.
//
// Read reads up to len(p) bytes into p.  It returns the number of bytes
// read (0 <= n <= len(p)) and any error encountered.
// Even if Read returns n < len(p),
// it may use all of p as scratch space during the call.
// If some data is available but not len(p) bytes, Read conventionally
// returns what is available rather than block waiting for more.
//
// At the end of the input stream, Read returns 0, os.EOF.
// Read may return a non-zero number of bytes with a non-nil err.
// In particular, a Read that exhausts the input may return n > 0, os.EOF.
type Reader interface {
	Read(p []byte) (n int, err os.Error)
}

// Writer is the interface that wraps the basic Write method.
//
// Write writes len(p) bytes from p to the underlying data stream.
// It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// Write must return a non-nil error if it returns n < len(p).
type Writer interface {
	Write(p []byte) (n int, err os.Error)
}

// Closer is the interface that wraps the basic Close method.
type Closer interface {
	Close() os.Error
}

// Seeker is the interface that wraps the basic Seek method.
//
// Seek sets the offset for the next Read or Write to offset,
// interpreted according to whence: 0 means relative to the origin of
// the file, 1 means relative to the current offset, and 2 means
// relative to the end.  Seek returns the new offset and an Error, if
// any.
type Seeker interface {
	Seek(offset int64, whence int) (ret int64, err os.Error)
}

// ReadWriter is the interface that groups the basic Read and Write methods.
type ReadWriter interface {
	Reader
	Writer
}

// ReadCloser is the interface that groups the basic Read and Close methods.
type ReadCloser interface {
	Reader
	Closer
}

// WriteCloser is the interface that groups the basic Write and Close methods.
type WriteCloser interface {
	Writer
	Closer
}

// ReadWriteCloser is the interface that groups the basic Read, Write and Close methods.
type ReadWriteCloser interface {
	Reader
	Writer
	Closer
}

// ReadSeeker is the interface that groups the basic Read and Seek methods.
type ReadSeeker interface {
	Reader
	Seeker
}

// WriteSeeker is the interface that groups the basic Write and Seek methods.
type WriteSeeker interface {
	Writer
	Seeker
}

// ReadWriteSeeker is the interface that groups the basic Read, Write and Seek methods.
type ReadWriteSeeker interface {
	Reader
	Writer
	Seeker
}

// ReaderFrom is the interface that wraps the ReadFrom method.
type ReaderFrom interface {
	ReadFrom(r Reader) (n int64, err os.Error)
}

// WriterTo is the interface that wraps the WriteTo method.
type WriterTo interface {
	WriteTo(w Writer) (n int64, err os.Error)
}

// ReaderAt is the interface that wraps the basic ReadAt method.
//
// ReadAt reads len(p) bytes into p starting at offset off in the
// underlying data stream.  It returns the number of bytes
// read (0 <= n <= len(p)) and any error encountered.
//
// Even if ReadAt returns n < len(p),
// it may use all of p as scratch space during the call.
// If some data is available but not len(p) bytes, ReadAt blocks
// until either all the data is available or an error occurs.
//
// At the end of the input stream, ReadAt returns 0, os.EOF.
// ReadAt may return a non-zero number of bytes with a non-nil err.
// In particular, a ReadAt that exhausts the input may return n > 0, os.EOF.
type ReaderAt interface {
	ReadAt(p []byte, off int64) (n int, err os.Error)
}

// WriterAt is the interface that wraps the basic WriteAt method.
//
// WriteAt writes len(p) bytes from p to the underlying data stream
// at offset off.  It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// WriteAt must return a non-nil error if it returns n < len(p).
type WriterAt interface {
	WriteAt(p []byte, off int64) (n int, err os.Error)
}

// ReadByter is the interface that wraps the ReadByte method.
//
// ReadByte reads and returns the next byte from the input.
// If no byte is available, err will be set.
type ReadByter interface {
	ReadByte() (c byte, err os.Error)
}

// WriteString writes the contents of the string s to w, which accepts an array of bytes.
func WriteString(w Writer, s string) (n int, err os.Error) {
	return w.Write([]byte(s))
}

// ReadAtLeast reads from r into buf until it has read at least min bytes.
// It returns the number of bytes copied and an error if fewer bytes were read.
// The error is os.EOF only if no bytes were read.
// If an EOF happens after reading fewer than min bytes,
// ReadAtLeast returns ErrUnexpectedEOF.
// If min is greater than the length of buf, ReadAtLeast returns ErrShortBuffer.
func ReadAtLeast(r Reader, buf []byte, min int) (n int, err os.Error) {
	if len(buf) < min {
		return 0, ErrShortBuffer
	}
	for n < min {
		nn, e := r.Read(buf[n:])
		if nn > 0 {
			n += nn
		}
		if e != nil {
			if e == os.EOF && n > 0 {
				e = ErrUnexpectedEOF
			}
			return n, e
		}
	}
	return
}

// ReadFull reads exactly len(buf) bytes from r into buf.
// It returns the number of bytes copied and an error if fewer bytes were read.
// The error is os.EOF only if no bytes were read.
// If an EOF happens after reading some but not all the bytes,
// ReadFull returns ErrUnexpectedEOF.
func ReadFull(r Reader, buf []byte) (n int, err os.Error) {
	return ReadAtLeast(r, buf, len(buf))
}

// Copyn copies n bytes (or until an error) from src to dst.
// It returns the number of bytes copied and the error, if any.
//
// If dst implements the ReaderFrom interface,
// the copy is implemented by calling dst.ReadFrom(src).
func Copyn(dst Writer, src Reader, n int64) (written int64, err os.Error) {
	// If the writer has a ReadFrom method, use it to to do the copy.
	// Avoids a buffer allocation and a copy.
	if rt, ok := dst.(ReaderFrom); ok {
		return rt.ReadFrom(LimitReader(src, n))
	}
	buf := make([]byte, 32*1024)
	for written < n {
		l := len(buf)
		if d := n - written; d < int64(l) {
			l = int(d)
		}
		nr, er := src.Read(buf[0:l])
		if nr > 0 {
			nw, ew := dst.Write(buf[0:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = ErrShortWrite
				break
			}
		}
		if er != nil {
			err = er
			break
		}
	}
	return written, err
}

// Copy copies from src to dst until either EOF is reached
// on src or an error occurs.  It returns the number of bytes
// copied and the error, if any.
//
// If dst implements the ReaderFrom interface,
// the copy is implemented by calling dst.ReadFrom(src).
// Otherwise, if src implements the WriterTo interface,
// the copy is implemented by calling src.WriteTo(dst).
func Copy(dst Writer, src Reader) (written int64, err os.Error) {
	// If the writer has a ReadFrom method, use it to to do the copy.
	// Avoids an allocation and a copy.
	if rt, ok := dst.(ReaderFrom); ok {
		return rt.ReadFrom(src)
	}
	// Similarly, if the reader has a WriteTo method, use it to to do the copy.
	if wt, ok := src.(WriterTo); ok {
		return wt.WriteTo(dst)
	}
	buf := make([]byte, 32*1024)
	for {
		nr, er := src.Read(buf)
		if nr > 0 {
			nw, ew := dst.Write(buf[0:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = ErrShortWrite
				break
			}
		}
		if er == os.EOF {
			break
		}
		if er != nil {
			err = er
			break
		}
	}
	return written, err
}

// LimitReader returns a Reader that reads from r
// but stops with os.EOF after n bytes.
func LimitReader(r Reader, n int64) Reader { return &limitedReader{r, n} }

type limitedReader struct {
	r Reader
	n int64
}

func (l *limitedReader) Read(p []byte) (n int, err os.Error) {
	if l.n <= 0 {
		return 0, os.EOF
	}
	if int64(len(p)) > l.n {
		p = p[0:l.n]
	}
	n, err = l.r.Read(p)
	l.n -= int64(n)
	return
}

// NewSectionReader returns a SectionReader that reads from r
// starting at offset off and stops with os.EOF after n bytes.
func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader {
	return &SectionReader{r, off, off, off + n}
}

// SectionReader implements Read, Seek, and ReadAt on a section
// of an underlying ReaderAt.
type SectionReader struct {
	r     ReaderAt
	base  int64
	off   int64
	limit int64
}

func (s *SectionReader) Read(p []byte) (n int, err os.Error) {
	if s.off >= s.limit {
		return 0, os.EOF
	}
	if max := s.limit - s.off; int64(len(p)) > max {
		p = p[0:max]
	}
	n, err = s.r.ReadAt(p, s.off)
	s.off += int64(n)
	return
}

func (s *SectionReader) Seek(offset int64, whence int) (ret int64, err os.Error) {
	switch whence {
	default:
		return 0, os.EINVAL
	case 0:
		offset += s.base
	case 1:
		offset += s.off
	case 2:
		offset += s.limit
	}
	if offset < s.base || offset > s.limit {
		return 0, os.EINVAL
	}
	s.off = offset
	return offset - s.base, nil
}

func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err os.Error) {
	if off < 0 || off >= s.limit-s.base {
		return 0, os.EOF
	}
	off += s.base
	if max := s.limit - off; int64(len(p)) > max {
		p = p[0:max]
	}
	return s.r.ReadAt(p, off)
}

// Size returns the size of the section in bytes.
func (s *SectionReader) Size() int64 { return s.limit - s.base }
<commit_msg>Fix documentation typo. 	This is really insignificant, but it might as well be fixed.<commit_after>// Copyright 2009 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.

// This package provides basic interfaces to I/O primitives.
// Its primary job is to wrap existing implementations of such primitives,
// such as those in package os, into shared public interfaces that
// abstract the functionality, plus some other related primitives.
package io

import "os"

// Error represents an unexpected I/O behavior.
type Error struct {
	os.ErrorString
}

// ErrShortWrite means that a write accepted fewer bytes than requested
// but failed to return an explicit error.
var ErrShortWrite os.Error = &Error{"short write"}

// ErrShortBuffer means that a read required a longer buffer than was provided.
var ErrShortBuffer os.Error = &Error{"short buffer"}

// ErrUnexpectedEOF means that os.EOF was encountered in the
// middle of reading a fixed-size block or data structure.
var ErrUnexpectedEOF os.Error = &Error{"unexpected EOF"}

// Reader is the interface that wraps the basic Read method.
//
// Read reads up to len(p) bytes into p.  It returns the number of bytes
// read (0 <= n <= len(p)) and any error encountered.
// Even if Read returns n < len(p),
// it may use all of p as scratch space during the call.
// If some data is available but not len(p) bytes, Read conventionally
// returns what is available rather than block waiting for more.
//
// At the end of the input stream, Read returns 0, os.EOF.
// Read may return a non-zero number of bytes with a non-nil err.
// In particular, a Read that exhausts the input may return n > 0, os.EOF.
type Reader interface {
	Read(p []byte) (n int, err os.Error)
}

// Writer is the interface that wraps the basic Write method.
//
// Write writes len(p) bytes from p to the underlying data stream.
// It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// Write must return a non-nil error if it returns n < len(p).
type Writer interface {
	Write(p []byte) (n int, err os.Error)
}

// Closer is the interface that wraps the basic Close method.
type Closer interface {
	Close() os.Error
}

// Seeker is the interface that wraps the basic Seek method.
//
// Seek sets the offset for the next Read or Write to offset,
// interpreted according to whence: 0 means relative to the origin of
// the file, 1 means relative to the current offset, and 2 means
// relative to the end.  Seek returns the new offset and an Error, if
// any.
type Seeker interface {
	Seek(offset int64, whence int) (ret int64, err os.Error)
}

// ReadWriter is the interface that groups the basic Read and Write methods.
type ReadWriter interface {
	Reader
	Writer
}

// ReadCloser is the interface that groups the basic Read and Close methods.
type ReadCloser interface {
	Reader
	Closer
}

// WriteCloser is the interface that groups the basic Write and Close methods.
type WriteCloser interface {
	Writer
	Closer
}

// ReadWriteCloser is the interface that groups the basic Read, Write and Close methods.
type ReadWriteCloser interface {
	Reader
	Writer
	Closer
}

// ReadSeeker is the interface that groups the basic Read and Seek methods.
type ReadSeeker interface {
	Reader
	Seeker
}

// WriteSeeker is the interface that groups the basic Write and Seek methods.
type WriteSeeker interface {
	Writer
	Seeker
}

// ReadWriteSeeker is the interface that groups the basic Read, Write and Seek methods.
type ReadWriteSeeker interface {
	Reader
	Writer
	Seeker
}

// ReaderFrom is the interface that wraps the ReadFrom method.
type ReaderFrom interface {
	ReadFrom(r Reader) (n int64, err os.Error)
}

// WriterTo is the interface that wraps the WriteTo method.
type WriterTo interface {
	WriteTo(w Writer) (n int64, err os.Error)
}

// ReaderAt is the interface that wraps the basic ReadAt method.
//
// ReadAt reads len(p) bytes into p starting at offset off in the
// underlying data stream.  It returns the number of bytes
// read (0 <= n <= len(p)) and any error encountered.
//
// Even if ReadAt returns n < len(p),
// it may use all of p as scratch space during the call.
// If some data is available but not len(p) bytes, ReadAt blocks
// until either all the data is available or an error occurs.
//
// At the end of the input stream, ReadAt returns 0, os.EOF.
// ReadAt may return a non-zero number of bytes with a non-nil err.
// In particular, a ReadAt that exhausts the input may return n > 0, os.EOF.
type ReaderAt interface {
	ReadAt(p []byte, off int64) (n int, err os.Error)
}

// WriterAt is the interface that wraps the basic WriteAt method.
//
// WriteAt writes len(p) bytes from p to the underlying data stream
// at offset off.  It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// WriteAt must return a non-nil error if it returns n < len(p).
type WriterAt interface {
	WriteAt(p []byte, off int64) (n int, err os.Error)
}

// ReadByter is the interface that wraps the ReadByte method.
//
// ReadByte reads and returns the next byte from the input.
// If no byte is available, err will be set.
type ReadByter interface {
	ReadByte() (c byte, err os.Error)
}

// WriteString writes the contents of the string s to w, which accepts an array of bytes.
func WriteString(w Writer, s string) (n int, err os.Error) {
	return w.Write([]byte(s))
}

// ReadAtLeast reads from r into buf until it has read at least min bytes.
// It returns the number of bytes copied and an error if fewer bytes were read.
// The error is os.EOF only if no bytes were read.
// If an EOF happens after reading fewer than min bytes,
// ReadAtLeast returns ErrUnexpectedEOF.
// If min is greater than the length of buf, ReadAtLeast returns ErrShortBuffer.
func ReadAtLeast(r Reader, buf []byte, min int) (n int, err os.Error) {
	if len(buf) < min {
		return 0, ErrShortBuffer
	}
	for n < min {
		nn, e := r.Read(buf[n:])
		if nn > 0 {
			n += nn
		}
		if e != nil {
			if e == os.EOF && n > 0 {
				e = ErrUnexpectedEOF
			}
			return n, e
		}
	}
	return
}

// ReadFull reads exactly len(buf) bytes from r into buf.
// It returns the number of bytes copied and an error if fewer bytes were read.
// The error is os.EOF only if no bytes were read.
// If an EOF happens after reading some but not all the bytes,
// ReadFull returns ErrUnexpectedEOF.
func ReadFull(r Reader, buf []byte) (n int, err os.Error) {
	return ReadAtLeast(r, buf, len(buf))
}

// Copyn copies n bytes (or until an error) from src to dst.
// It returns the number of bytes copied and the error, if any.
//
// If dst implements the ReaderFrom interface,
// the copy is implemented by calling dst.ReadFrom(src).
func Copyn(dst Writer, src Reader, n int64) (written int64, err os.Error) {
	// If the writer has a ReadFrom method, use it to do the copy.
	// Avoids a buffer allocation and a copy.
	if rt, ok := dst.(ReaderFrom); ok {
		return rt.ReadFrom(LimitReader(src, n))
	}
	buf := make([]byte, 32*1024)
	for written < n {
		l := len(buf)
		if d := n - written; d < int64(l) {
			l = int(d)
		}
		nr, er := src.Read(buf[0:l])
		if nr > 0 {
			nw, ew := dst.Write(buf[0:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = ErrShortWrite
				break
			}
		}
		if er != nil {
			err = er
			break
		}
	}
	return written, err
}

// Copy copies from src to dst until either EOF is reached
// on src or an error occurs.  It returns the number of bytes
// copied and the error, if any.
//
// If dst implements the ReaderFrom interface,
// the copy is implemented by calling dst.ReadFrom(src).
// Otherwise, if src implements the WriterTo interface,
// the copy is implemented by calling src.WriteTo(dst).
func Copy(dst Writer, src Reader) (written int64, err os.Error) {
	// If the writer has a ReadFrom method, use it to do the copy.
	// Avoids an allocation and a copy.
	if rt, ok := dst.(ReaderFrom); ok {
		return rt.ReadFrom(src)
	}
	// Similarly, if the reader has a WriteTo method, use it to do the copy.
	if wt, ok := src.(WriterTo); ok {
		return wt.WriteTo(dst)
	}
	buf := make([]byte, 32*1024)
	for {
		nr, er := src.Read(buf)
		if nr > 0 {
			nw, ew := dst.Write(buf[0:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = ErrShortWrite
				break
			}
		}
		if er == os.EOF {
			break
		}
		if er != nil {
			err = er
			break
		}
	}
	return written, err
}

// LimitReader returns a Reader that reads from r
// but stops with os.EOF after n bytes.
func LimitReader(r Reader, n int64) Reader { return &limitedReader{r, n} }

type limitedReader struct {
	r Reader
	n int64
}

func (l *limitedReader) Read(p []byte) (n int, err os.Error) {
	if l.n <= 0 {
		return 0, os.EOF
	}
	if int64(len(p)) > l.n {
		p = p[0:l.n]
	}
	n, err = l.r.Read(p)
	l.n -= int64(n)
	return
}

// NewSectionReader returns a SectionReader that reads from r
// starting at offset off and stops with os.EOF after n bytes.
func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader {
	return &SectionReader{r, off, off, off + n}
}

// SectionReader implements Read, Seek, and ReadAt on a section
// of an underlying ReaderAt.
type SectionReader struct {
	r     ReaderAt
	base  int64
	off   int64
	limit int64
}

func (s *SectionReader) Read(p []byte) (n int, err os.Error) {
	if s.off >= s.limit {
		return 0, os.EOF
	}
	if max := s.limit - s.off; int64(len(p)) > max {
		p = p[0:max]
	}
	n, err = s.r.ReadAt(p, s.off)
	s.off += int64(n)
	return
}

func (s *SectionReader) Seek(offset int64, whence int) (ret int64, err os.Error) {
	switch whence {
	default:
		return 0, os.EINVAL
	case 0:
		offset += s.base
	case 1:
		offset += s.off
	case 2:
		offset += s.limit
	}
	if offset < s.base || offset > s.limit {
		return 0, os.EINVAL
	}
	s.off = offset
	return offset - s.base, nil
}

func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err os.Error) {
	if off < 0 || off >= s.limit-s.base {
		return 0, os.EOF
	}
	off += s.base
	if max := s.limit - off; int64(len(p)) > max {
		p = p[0:max]
	}
	return s.r.ReadAt(p, off)
}

// Size returns the size of the section in bytes.
func (s *SectionReader) Size() int64 { return s.limit - s.base }
<|endoftext|>