<commit_before>package main

import (
	"errors"
	"fmt"
	"image"
	"image/color"
	"image/jpeg"
	_ "image/png"
	"math"
	"os"
	"time"
)

var (
	aspect                  = 0
	cropWidth               = 0.0
	cropHeight              = 0.0
	detailWeight            = 0.2
	skinColor               = [3]float64{0.78, 0.57, 0.44}
	skinBias                = 0.01
	skinBrightnessMin       = 0.2
	skinBrightnessMax       = 1.0
	skinThreshold           = 0.8
	skinWeight              = 1.8
	saturationBrightnessMin = 0.25
	saturationBrightnessMax = 0.9
	saturationThreshold     = 0.4
	saturationBias          = 0.2
	saturationWeight        = 0.3
	// step * minscale rounded down to the next power of two should be good
	scoreDownSample   = 8
	invDownSample     = 1.0 / float64(scoreDownSample)
	step              = 8
	scaleStep         = 0.1
	minScale          = 0.9
	maxScale          = 1.0
	edgeRadius        = 0.4
	edgeWeight        = -20.0
	outsideImportance = -0.5
	ruleOfThirds      = true
	prescale          = true
	debug             = false
)

type Score struct {
	Detail     float64
	Saturation float64
	Skin       float64
	Total      float64
}

type Crop struct {
	X      int
	Y      int
	Width  int
	Height int
	Score  Score
}

type SubImager interface {
	SubImage(r image.Rectangle) image.Image
}

func thirds(x float64) float64 {
	x1 := int(x - (1.0 / 3.0) + 1.0)
	res := (float64(x1%2.0) * 0.5) - 0.5
	return res * 16.0
}

func importance(crop *Crop, x, y int) float64 {
	if crop.X > x || x >= crop.X+crop.Width || crop.Y > y || y >= crop.Y+crop.Height {
		return outsideImportance
	}

	xf := float64(x-crop.X) / float64(crop.Width)
	yf := float64(y-crop.Y) / float64(crop.Height)

	px := math.Abs(0.5-xf) * 2.0
	py := math.Abs(0.5-yf) * 2.0

	dx := math.Max(px-1.0+edgeRadius, 0.0)
	dy := math.Max(py-1.0+edgeRadius, 0.0)
	d := (math.Pow(dx, 2) + math.Pow(dy, 2)) * edgeWeight

	s := 1.41 - math.Sqrt(math.Pow(px, 2)+math.Pow(py, 2))
	if ruleOfThirds {
		s += (math.Max(0.0, s+d+0.5) * 1.2) * (thirds(px) + thirds(py))
	}

	return s + d
}

func score(output *image.Image, crop *Crop) Score {
	height := (*output).Bounds().Size().Y
	width := (*output).Bounds().Size().X
	score := Score{}

	for y := 0; y < height*scoreDownSample; y += scoreDownSample {
		for x := 0; x < width*scoreDownSample; x += scoreDownSample {
			//			now := time.Now()
			imp := importance(crop, x, y)
			//			fmt.Println("Time elapsed single-imp:", time.Since(now))

			ny := int(float64(y) * invDownSample)
			nx := int(float64(x) * invDownSample)

			r, g, b, _ := (*output).At(nx, ny).RGBA()
			r8 := float64(r >> 8)
			g8 := float64(g >> 8)
			b8 := float64(b >> 8)

			score.Skin += (r8 / 255.0) * (g8/255.0 + skinBias) * imp
			score.Detail += (g8 / 255.0) * imp
			score.Saturation += (b8 / 255.0) * (g8/255.0 + saturationBias) * imp
		}
	}

	score.Total = (score.Detail*detailWeight + score.Skin*skinWeight + score.Saturation*saturationWeight) / float64(crop.Width) / float64(crop.Height)
	return score
}

func writeImage(img *image.Image, name string) {
	fso, err := os.Create(name)
	if err != nil {
		panic(err)
	}
	defer fso.Close()

	jpeg.Encode(fso, (*img), &jpeg.Options{Quality: 90})
	fso.Close()
}

func analyse(img image.Image) Crop {
	o := image.Image(image.NewRGBA(img.Bounds()))

	now := time.Now()
	edgeDetect(&img, &o)
	fmt.Println("Time elapsed edge:", time.Since(now))
	writeImage(&o, "/tmp/foo_step1.jpg")

	now = time.Now()
	skinDetect(&img, &o)
	fmt.Println("Time elapsed skin:", time.Since(now))
	writeImage(&o, "/tmp/foo_step2.jpg")

	now = time.Now()
	saturationDetect(&img, &o)
	fmt.Println("Time elapsed sat:", time.Since(now))
	writeImage(&o, "/tmp/foo_step3.jpg")

	now = time.Now()
	var topCrop Crop
	topScore := -1.0
	cs := crops(&o)
	fmt.Println("Time elapsed crops:", time.Since(now), len(cs))

	now = time.Now()
	for _, crop := range cs {
		//		nowIn := time.Now()
		crop.Score = score(&o, &crop)
		//		fmt.Println("Time elapsed single-score:", time.Since(nowIn))
		if crop.Score.Total > topScore {
			topCrop = crop
			topScore = crop.Score.Total
		}
	}
	fmt.Printf("Top crop: %+v\n", topCrop)
	fmt.Println("Time elapsed score:", time.Since(now))

	cropImage := img.(SubImager).SubImage(image.Rect(topCrop.X, topCrop.Y, topCrop.Width+topCrop.X, topCrop.Height+topCrop.Y))
	writeImage(&cropImage, "/tmp/foo_topcrop.jpg")

	return topCrop
}

func crop(img image.Image, width, height int) error {
	if width == 0 && height == 0 {
		return errors.New("Expect either a height or width")
	}

	scale := math.Min(float64(img.Bounds().Size().X)/float64(width), float64(img.Bounds().Size().Y)/float64(height))
	cropWidth, cropHeight = math.Floor(float64(width)*scale), math.Floor(float64(height)*scale)
	minScale = math.Min(maxScale, math.Max(1.0/scale, minScale))

	fmt.Printf("original resolution: %dx%d\n", img.Bounds().Size().X, img.Bounds().Size().Y)
	fmt.Printf("scale: %f, cropw: %f, croph: %f, minscale: %f\n", scale, cropWidth, cropHeight, minScale)

	now := time.Now()
	analyse(img)
	fmt.Println("Time elapsed:", time.Since(now))

	return nil
}

func saturation(c color.Color) float64 {
	r, g, b, _ := c.RGBA()
	r8 := float64(r >> 8)
	g8 := float64(g >> 8)
	b8 := float64(b >> 8)

	maximum := math.Max(math.Max(r8/255.0, g8/255.0), b8/255.0)
	minimum := math.Min(math.Min(r8/255.0, g8/255.0), b8/255.0)

	if maximum == minimum {
		return 0
	}

	l := (maximum + minimum) / 2.0
	d := maximum - minimum

	if l > 0.5 {
		return d / (2.0 - maximum - minimum)
	} else {
		return d / (maximum + minimum)
	}
}

func cie(c color.Color) float64 {
	r, g, b, _ := c.RGBA()
	r8 := float64(r >> 8)
	g8 := float64(g >> 8)
	b8 := float64(b >> 8)

	return 0.5126*b8 + 0.7152*g8 + 0.0722*r8
}

func skinCol(c color.Color) float64 {
	r, g, b, _ := c.RGBA()
	r8 := float64(r >> 8)
	g8 := float64(g >> 8)
	b8 := float64(b >> 8)

	mag := math.Sqrt(math.Pow(r8, 2) + math.Pow(g8, 2) + math.Pow(b8, 2))
	rd := r8/mag - skinColor[0]
	gd := g8/mag - skinColor[1]
	bd := b8/mag - skinColor[2]

	d := math.Sqrt(math.Pow(rd, 2) + math.Pow(gd, 2) + math.Pow(bd, 2))
	return 1.0 - d
}

func edgeDetect(i *image.Image, o *image.Image) {
	w := (*i).Bounds().Size().X
	h := (*i).Bounds().Size().Y

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			var lightness float64

			if x == 0 || x >= w-1 || y == 0 || y >= h-1 {
				lightness = cie((*i).At(x, y))
			} else {
				lightness = cie((*i).At(x, y))*4.0 -
					cie((*i).At(x, y-1)) -
					cie((*i).At(x-1, y)) -
					cie((*i).At(x+1, y)) -
					cie((*i).At(x, y+1))
			}

			if lightness < 0 {
				continue
			}

			nc := color.RGBA{uint8(lightness), uint8(lightness), uint8(lightness), 255}
			(*o).(*image.RGBA).Set(x, y, nc)
		}
	}
}

func skinDetect(i *image.Image, o *image.Image) {
	w := (*i).Bounds().Size().X
	h := (*i).Bounds().Size().Y

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			lightness := cie((*i).At(x, y)) / 255.0
			skin := skinCol((*i).At(x, y))

			if skin > skinThreshold && lightness >= skinBrightnessMin && lightness <= skinBrightnessMax {
				r := (skin - skinThreshold) * (255.0 / (1.0 - skinThreshold))
				_, g, b, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{uint8(r), uint8(g >> 8), uint8(b >> 8), 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			} else {
				_, g, b, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{0, uint8(g >> 8), uint8(b >> 8), 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			}
		}
	}
}

func saturationDetect(i *image.Image, o *image.Image) {
	w := (*i).Bounds().Size().X
	h := (*i).Bounds().Size().Y

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			lightness := cie((*i).At(x, y)) / 255.0
			saturation := saturation((*i).At(x, y))

			if saturation > saturationThreshold && lightness >= saturationBrightnessMin && lightness <= saturationBrightnessMax {
				b := (saturation - saturationThreshold) * (255.0 / (1.0 - saturationThreshold))
				r, g, _, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b), 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			} else {
				r, g, _, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{uint8(r >> 8), uint8(g >> 8), 0, 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			}
		}
	}
}

func crops(i *image.Image) []Crop {
	res := []Crop{}
	width := (*i).Bounds().Size().X
	height := (*i).Bounds().Size().Y

	//minDimension := math.Min(float64(width), float64(height))
	cropW := cropWidth  //|| minDimension
	cropH := cropHeight //|| minDimension

	for scale := maxScale; scale >= minScale; scale -= scaleStep {
		for y := 0; float64(y)+cropH*scale <= float64(height); y += step {
			for x := 0; float64(x)+cropW*scale <= float64(width); x += step {
				res = append(res, Crop{
					X:      x,
					Y:      y,
					Width:  int(cropW * scale),
					Height: int(cropH * scale),
				})
			}
		}
	}

	return res
}

func main() {
	fi, _ := os.Open("/tmp/foo.png")
	defer fi.Close()

	img, _, err := image.Decode(fi)
	if err != nil {
		panic(err)
		return
	}

	crop(img, 250, 250)
}
<commit_msg>* Optimize score performance.<commit_after>package main

import (
	"errors"
	"fmt"
	"image"
	"image/color"
	"image/jpeg"
	_ "image/png"
	"math"
	"os"
	"time"
)

var (
	aspect                  = 0
	cropWidth               = 0.0
	cropHeight              = 0.0
	detailWeight            = 0.2
	skinColor               = [3]float64{0.78, 0.57, 0.44}
	skinBias                = 0.01
	skinBrightnessMin       = 0.2
	skinBrightnessMax       = 1.0
	skinThreshold           = 0.8
	skinWeight              = 1.8
	saturationBrightnessMin = 0.25
	saturationBrightnessMax = 0.9
	saturationThreshold     = 0.4
	saturationBias          = 0.2
	saturationWeight        = 0.3
	// step * minscale rounded down to the next power of two should be good
	scoreDownSample   = 8
	invDownSample     = 1.0 / float64(scoreDownSample)
	step              = 8
	scaleStep         = 0.1
	minScale          = 0.9
	maxScale          = 1.0
	edgeRadius        = 0.4
	edgeWeight        = -20.0
	outsideImportance = -0.5
	ruleOfThirds      = true
	prescale          = true
	debug             = false
)

type Score struct {
	Detail     float64
	Saturation float64
	Skin       float64
	Total      float64
}

type Crop struct {
	X      int
	Y      int
	Width  int
	Height int
	Score  Score
}

type SubImager interface {
	SubImage(r image.Rectangle) image.Image
}

func thirds(x float64) float64 {
	x1 := int(x - (1.0 / 3.0) + 1.0)
	res := (float64(x1%2.0) * 0.5) - 0.5
	return res * 16.0
}

func importance(crop *Crop, x, y int) float64 {
	if crop.X > x || x >= crop.X+crop.Width || crop.Y > y || y >= crop.Y+crop.Height {
		return outsideImportance
	}

	xf := float64(x-crop.X) / float64(crop.Width)
	yf := float64(y-crop.Y) / float64(crop.Height)

	px := math.Abs(0.5-xf) * 2.0
	py := math.Abs(0.5-yf) * 2.0

	dx := math.Max(px-1.0+edgeRadius, 0.0)
	dy := math.Max(py-1.0+edgeRadius, 0.0)
	d := (math.Pow(dx, 2) + math.Pow(dy, 2)) * edgeWeight

	s := 1.41 - math.Sqrt(math.Pow(px, 2)+math.Pow(py, 2))
	if ruleOfThirds {
		s += (math.Max(0.0, s+d+0.5) * 1.2) * (thirds(px) + thirds(py))
	}

	return s + d
}

func score(output *image.Image, crop *Crop) Score {
	o := (*output).(*image.RGBA)
	height := (*output).Bounds().Size().Y
	width := (*output).Bounds().Size().X
	score := Score{}

	for y := 0; y < height*scoreDownSample; y += scoreDownSample {
		yoffset := int(math.Floor(float64(y)*invDownSample)) * width
		for x := 0; x < width*scoreDownSample; x += scoreDownSample {
			//			now := time.Now()
			imp := importance(crop, x, y)
			//			fmt.Println("Time elapsed single-imp:", time.Since(now))

			p := yoffset + int(math.Floor(float64(x)*invDownSample)) * 4

			r8 := float64(o.Pix[p]) / 255.0
			g8 := float64(o.Pix[p+1]) / 255.0
			b8 := float64(o.Pix[p+2]) / 255.0

			score.Skin += r8 * (g8 + skinBias) * imp
			score.Detail += g8 * imp
			score.Saturation += b8 * (g8 + saturationBias) * imp
		}
	}

	score.Total = (score.Detail*detailWeight + score.Skin*skinWeight + score.Saturation*saturationWeight) / float64(crop.Width) / float64(crop.Height)
	return score
}

func writeImage(img *image.Image, name string) {
	fso, err := os.Create(name)
	if err != nil {
		panic(err)
	}
	defer fso.Close()

	jpeg.Encode(fso, (*img), &jpeg.Options{Quality: 90})
	fso.Close()
}

func analyse(img image.Image) Crop {
	o := image.Image(image.NewRGBA(img.Bounds()))

	now := time.Now()
	edgeDetect(&img, &o)
	fmt.Println("Time elapsed edge:", time.Since(now))
	writeImage(&o, "/tmp/foo_step1.jpg")

	now = time.Now()
	skinDetect(&img, &o)
	fmt.Println("Time elapsed skin:", time.Since(now))
	writeImage(&o, "/tmp/foo_step2.jpg")

	now = time.Now()
	saturationDetect(&img, &o)
	fmt.Println("Time elapsed sat:", time.Since(now))
	writeImage(&o, "/tmp/foo_step3.jpg")

	now = time.Now()
	var topCrop Crop
	topScore := -1.0
	cs := crops(&o)
	fmt.Println("Time elapsed crops:", time.Since(now), len(cs))

	now = time.Now()
	for _, crop := range cs {
		//		nowIn := time.Now()
		crop.Score = score(&o, &crop)
		//		fmt.Println("Time elapsed single-score:", time.Since(nowIn))
		if crop.Score.Total > topScore {
			topCrop = crop
			topScore = crop.Score.Total
		}
	}
	fmt.Printf("Top crop: %+v\n", topCrop)
	fmt.Println("Time elapsed score:", time.Since(now))

	cropImage := img.(SubImager).SubImage(image.Rect(topCrop.X, topCrop.Y, topCrop.Width+topCrop.X, topCrop.Height+topCrop.Y))
	writeImage(&cropImage, "/tmp/foo_topcrop.jpg")

	return topCrop
}

func crop(img image.Image, width, height int) error {
	if width == 0 && height == 0 {
		return errors.New("Expect either a height or width")
	}

	scale := math.Min(float64(img.Bounds().Size().X)/float64(width), float64(img.Bounds().Size().Y)/float64(height))
	cropWidth, cropHeight = math.Floor(float64(width)*scale), math.Floor(float64(height)*scale)
	minScale = math.Min(maxScale, math.Max(1.0/scale, minScale))

	fmt.Printf("original resolution: %dx%d\n", img.Bounds().Size().X, img.Bounds().Size().Y)
	fmt.Printf("scale: %f, cropw: %f, croph: %f, minscale: %f\n", scale, cropWidth, cropHeight, minScale)

	now := time.Now()
	analyse(img)
	fmt.Println("Time elapsed:", time.Since(now))

	return nil
}

func saturation(c color.Color) float64 {
	r, g, b, _ := c.RGBA()
	r8 := float64(r >> 8)
	g8 := float64(g >> 8)
	b8 := float64(b >> 8)

	maximum := math.Max(math.Max(r8/255.0, g8/255.0), b8/255.0)
	minimum := math.Min(math.Min(r8/255.0, g8/255.0), b8/255.0)

	if maximum == minimum {
		return 0
	}

	l := (maximum + minimum) / 2.0
	d := maximum - minimum

	if l > 0.5 {
		return d / (2.0 - maximum - minimum)
	} else {
		return d / (maximum + minimum)
	}
}

func cie(c color.Color) float64 {
	r, g, b, _ := c.RGBA()
	r8 := float64(r >> 8)
	g8 := float64(g >> 8)
	b8 := float64(b >> 8)

	return 0.5126*b8 + 0.7152*g8 + 0.0722*r8
}

func skinCol(c color.Color) float64 {
	r, g, b, _ := c.RGBA()
	r8 := float64(r >> 8)
	g8 := float64(g >> 8)
	b8 := float64(b >> 8)

	mag := math.Sqrt(math.Pow(r8, 2) + math.Pow(g8, 2) + math.Pow(b8, 2))
	rd := r8/mag - skinColor[0]
	gd := g8/mag - skinColor[1]
	bd := b8/mag - skinColor[2]

	d := math.Sqrt(math.Pow(rd, 2) + math.Pow(gd, 2) + math.Pow(bd, 2))
	return 1.0 - d
}

func edgeDetect(i *image.Image, o *image.Image) {
	w := (*i).Bounds().Size().X
	h := (*i).Bounds().Size().Y

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			var lightness float64

			if x == 0 || x >= w-1 || y == 0 || y >= h-1 {
				lightness = cie((*i).At(x, y))
			} else {
				lightness = cie((*i).At(x, y))*4.0 -
					cie((*i).At(x, y-1)) -
					cie((*i).At(x-1, y)) -
					cie((*i).At(x+1, y)) -
					cie((*i).At(x, y+1))
			}

			if lightness < 0 {
				continue
			}

			nc := color.RGBA{uint8(lightness), uint8(lightness), uint8(lightness), 255}
			(*o).(*image.RGBA).Set(x, y, nc)
		}
	}
}

func skinDetect(i *image.Image, o *image.Image) {
	w := (*i).Bounds().Size().X
	h := (*i).Bounds().Size().Y

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			lightness := cie((*i).At(x, y)) / 255.0
			skin := skinCol((*i).At(x, y))

			if skin > skinThreshold && lightness >= skinBrightnessMin && lightness <= skinBrightnessMax {
				r := (skin - skinThreshold) * (255.0 / (1.0 - skinThreshold))
				_, g, b, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{uint8(r), uint8(g >> 8), uint8(b >> 8), 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			} else {
				_, g, b, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{0, uint8(g >> 8), uint8(b >> 8), 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			}
		}
	}
}

func saturationDetect(i *image.Image, o *image.Image) {
	w := (*i).Bounds().Size().X
	h := (*i).Bounds().Size().Y

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			lightness := cie((*i).At(x, y)) / 255.0
			saturation := saturation((*i).At(x, y))

			if saturation > saturationThreshold && lightness >= saturationBrightnessMin && lightness <= saturationBrightnessMax {
				b := (saturation - saturationThreshold) * (255.0 / (1.0 - saturationThreshold))
				r, g, _, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b), 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			} else {
				r, g, _, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{uint8(r >> 8), uint8(g >> 8), 0, 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			}
		}
	}
}

func crops(i *image.Image) []Crop {
	res := []Crop{}
	width := (*i).Bounds().Size().X
	height := (*i).Bounds().Size().Y

	//minDimension := math.Min(float64(width), float64(height))
	cropW := cropWidth  //|| minDimension
	cropH := cropHeight //|| minDimension

	for scale := maxScale; scale >= minScale; scale -= scaleStep {
		for y := 0; float64(y)+cropH*scale <= float64(height); y += step {
			for x := 0; float64(x)+cropW*scale <= float64(width); x += step {
				res = append(res, Crop{
					X:      x,
					Y:      y,
					Width:  int(cropW * scale),
					Height: int(cropH * scale),
				})
			}
		}
	}

	return res
}

func main() {
	fi, _ := os.Open("/tmp/foo.png")
	defer fi.Close()

	img, _, err := image.Decode(fi)
	if err != nil {
		panic(err)
		return
	}

	crop(img, 250, 250)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"

	"github.com/Sirupsen/logrus"
)

type Instance struct {
	time *InstanceTime
	name map[ClientID]ClientName

	g *Game
	r InstanceInput
	w InstanceOutputWriter
}

// NewInstance returns a Instance
func NewInstance(r InstanceInput, w InstanceOutputWriter) *Instance {
	time := new(InstanceTime)
	return &Instance{
		time: time,
		name: make(map[ClientID]ClientName),

		g: NewGame(time, w),
		r: r,
		w: w,
	}
}

// Run starts the instance routine
func (i *Instance) Run() {
	t := time.Tick(RealGameTick)
	for {
		select {
		case <-t:
			*i.time = i.time.Add(GameTick)
			if i.time.IsRegenerationTick() {
				i.g.PerformRegenerationTick()
			}
			if i.time.IsPeriodicalTick() {
				i.g.PerformPeriodicalTick()
			}
			i.g.PerformGameTick()
		case input, ok := <-i.r:
			if !ok {
				log.Fatal("Cannot read from the input channel")
			}
			id := input.ClientID
			switch input := input.Input.(type) {
			case InputConnect:
				i.connect(id, input)
			case InputDisconnect:
				i.disconnect(id, input)
			case InputChat:
				i.chat(id, input)
			case InputStage:
			case InputJoin:
				i.join(id, input)
			case InputLeave:
				i.leave(id, input)
			case InputAbility:
				i.ability(id, input)
			case InputInterrupt:
			default:
				log.Fatal("Unknown input type")
			}
		}
	}
}

// connect
func (i *Instance) connect(id ClientID, input InputConnect) {
	i.name[id] = input.ClientName

	i.w.BindClientID(id).Write(OutputMessage{
		Message: "Welcome to the Crescent!",
	})
	i.w.Write(OutputMessage{
		Message: fmt.Sprintf("%s has connected.", input.ClientName),
	})
}

// disconnect
func (i *Instance) disconnect(id ClientID, input InputDisconnect) {
	name := i.name[id]
	delete(i.name, id)

	i.w.Write(OutputMessage{
		Message: fmt.Sprintf("%s has disconnected.", name),
	})
}

// chat
func (i *Instance) chat(id ClientID, input InputChat) {
	name := i.name[id]
	message := input.Message

	i.w.Write(OutputChat{
		ClientName: i.name[id],
		Message:    input.Message,
	})
	log.WithFields(logrus.Fields{
		"type":    "chat",
		"name":    name,
		"message": message,
	}).Infof("%s: %s", name, message)
}

// join
func (i *Instance) join(id ClientID, input InputJoin) {
	// TODO WIP
	class := NewClassHealer()
	u, err := i.g.units.Join(0, UnitName(i.name[id]), class)
	if err != nil {
		return
	}
	i.w.Write(OutputUnitJoin{
		UnitID:       u.ID(),
		UnitGroup:    u.Group(),
		UnitPosition: u.Position(),
		UnitName:     u.Name(),
		ClassName:    u.ClassName(),
		Health:       u.Health(),
		HealthMax:    u.HealthMax(),
		Mana:         u.Mana(),
		ManaMax:      u.ManaMax(),
	})
}

// leave
func (i *Instance) leave(id ClientID, input InputLeave) {
	// TODO WIP
	i.g.units.Leave(0)
	i.w.Write(OutputUnitLeave{
		UnitID: 0,
	})
}

// ability
func (i *Instance) ability(id ClientID, input InputAbility) {
	// TODO WIP
	u := i.g.units.Find(0)
	if input.ObjectUnitID == nil {
		i.g.Activating(u, nil, u.Ability(input.AbilityName))
	} else {
		i.g.Activating(u, i.g.units.Find(*input.ObjectUnitID), u.Ability(input.AbilityName))
	}
}
<commit_msg>:fire:<commit_after>package main

import (
	"fmt"
	"time"

	"github.com/Sirupsen/logrus"
)

type Instance struct {
	time *InstanceTime
	name map[ClientID]ClientName

	g *Game
	r InstanceInput
	w InstanceOutputWriter
}

// NewInstance returns a Instance
func NewInstance(r InstanceInput, w InstanceOutputWriter) *Instance {
	time := new(InstanceTime)
	return &Instance{
		time: time,
		name: make(map[ClientID]ClientName),

		g: NewGame(time, w),
		r: r,
		w: w,
	}
}

// Run starts the instance routine
func (i *Instance) Run() {
	t := time.Tick(RealGameTick)
	for {
		select {
		case <-t:
			*i.time = i.time.Add(GameTick)
			if i.time.IsRegenerationTick() {
				i.g.PerformRegenerationTick()
			}
			if i.time.IsPeriodicalTick() {
				i.g.PerformPeriodicalTick()
			}
			i.g.PerformGameTick()
		case input, ok := <-i.r:
			if !ok {
				log.Fatal("Cannot read from the input channel")
			}
			id := input.ClientID
			switch input := input.Input.(type) {
			case InputConnect:
				i.connect(id, input)
			case InputDisconnect:
				i.disconnect(id, input)
			case InputChat:
				i.chat(id, input)
			case InputStage:
			case InputJoin:
				i.join(id, input)
			case InputLeave:
				i.leave(id, input)
			case InputAbility:
				i.ability(id, input)
			case InputInterrupt:
			default:
				log.Fatal("Unknown input type")
			}
		}
	}
}

// connect
func (i *Instance) connect(id ClientID, input InputConnect) {
	i.name[id] = input.ClientName

	i.w.BindClientID(id).Write(OutputMessage{
		Message: "Welcome to Crescent!",
	})
	i.w.Write(OutputMessage{
		Message: fmt.Sprintf("%s has connected.", input.ClientName),
	})
}

// disconnect
func (i *Instance) disconnect(id ClientID, input InputDisconnect) {
	name := i.name[id]
	delete(i.name, id)

	i.w.Write(OutputMessage{
		Message: fmt.Sprintf("%s has disconnected.", name),
	})
}

// chat
func (i *Instance) chat(id ClientID, input InputChat) {
	name := i.name[id]
	message := input.Message

	i.w.Write(OutputChat{
		ClientName: i.name[id],
		Message:    input.Message,
	})
	log.WithFields(logrus.Fields{
		"type":    "chat",
		"name":    name,
		"message": message,
	}).Infof("%s: %s", name, message)
}

// join
func (i *Instance) join(id ClientID, input InputJoin) {
	// TODO WIP
	class := NewClassHealer()
	u, err := i.g.units.Join(0, UnitName(i.name[id]), class)
	if err != nil {
		return
	}
	i.w.Write(OutputUnitJoin{
		UnitID:       u.ID(),
		UnitGroup:    u.Group(),
		UnitPosition: u.Position(),
		UnitName:     u.Name(),
		ClassName:    u.ClassName(),
		Health:       u.Health(),
		HealthMax:    u.HealthMax(),
		Mana:         u.Mana(),
		ManaMax:      u.ManaMax(),
	})
}

// leave
func (i *Instance) leave(id ClientID, input InputLeave) {
	// TODO WIP
	i.g.units.Leave(0)
	i.w.Write(OutputUnitLeave{
		UnitID: 0,
	})
}

// ability
func (i *Instance) ability(id ClientID, input InputAbility) {
	// TODO WIP
	u := i.g.units.Find(0)
	if input.ObjectUnitID == nil {
		i.g.Activating(u, nil, u.Ability(input.AbilityName))
	} else {
		i.g.Activating(u, i.g.units.Find(*input.ObjectUnitID), u.Ability(input.AbilityName))
	}
}
<|endoftext|>


<commit_before>package speed

// Instances defines a valid collection of instance name and values
type Instances map[string]interface{}

// pcpInstance wraps a PCP compatible Instance
type pcpInstance struct {
	name   string
	id     uint32
	offset int
}

// newpcpInstance generates a new Instance type based on the passed parameters
// the id is passed explicitly as it is assumed that this will be constructed
// after initializing the InstanceDomain
// this is not a part of the public API as this is not supposed to be used directly,
// but instead added using the AddInstance method of InstanceDomain
func newpcpInstance(name string) *pcpInstance {
	return &pcpInstance{
		name, hash(name, 0), 0,
	}
}
<commit_msg>instance: add a Keys method to Instances<commit_after>package speed

// Instances defines a valid collection of instance name and values
type Instances map[string]interface{}

// Keys collects and returns all the keys in all instance values
func (i Instances) Keys() []string {
	s := make([]string, 0, len(i))
	for k := range i {
		s = append(s, k)
	}
	return s
}

// pcpInstance wraps a PCP compatible Instance
type pcpInstance struct {
	name   string
	id     uint32
	offset int
}

// newpcpInstance generates a new Instance type based on the passed parameters
// the id is passed explicitly as it is assumed that this will be constructed
// after initializing the InstanceDomain
// this is not a part of the public API as this is not supposed to be used directly,
// but instead added using the AddInstance method of InstanceDomain
func newpcpInstance(name string) *pcpInstance {
	return &pcpInstance{
		name, hash(name, 0), 0,
	}
}
<|endoftext|>


<commit_before>package gopcap

//-------------------------------------------------------------------------------------------
// UnknownINet
//-------------------------------------------------------------------------------------------

// UnknownINet represents the data for an internet-layer packet that gopcap doesn't understand.
// It simply provides uninterpreted data representing the entire internet-layer packet.
type UnknownINet struct {
	data TransportLayer
}

func (u *UnknownINet) InternetData() TransportLayer {
	return u.data
}

func (u *UnknownINet) FromBytes(data []byte) error {
	u.data = new(UnknownTransport)
	u.data.FromBytes(data)
	return nil
}

//-------------------------------------------------------------------------------------------
// IPv4
//-------------------------------------------------------------------------------------------

// IPv4Packet represents an unpacked IPv4 packet. This method of storing the IPv4 packet data
// is less efficient than the byte-packed form used on the wire.
type IPv4Packet struct {
	IHL            uint8
	DSCP           uint8
	ECN            uint8
	TotalLength    uint16
	ID             uint16
	DontFragment   bool
	MoreFragments  bool
	FragmentOffset uint16
	TTL            uint8
	Protocol       IPProtocol
	Checksum       uint16
	SourceAddress  []byte
	DestAddress    []byte
	Options        []byte
	data           TransportLayer
}

func (p *IPv4Packet) InternetData() TransportLayer {
	return p.data
}

func (p *IPv4Packet) FromBytes(data []byte) error {
	// The IPv4 header is full of crazy non-aligned fields that I've expanded in the structure.
	// This makes this function a total nightmare. My apologies in advance.

	// Check that we have enough data for the header, at the very least.
	if len(data) < 20 {
		return InsufficientLength
	}

	// Check that this actually is an IPv4 packet.
	if ((uint8(data[0]) & 0xF0) >> 4) != uint8(4) {
		return IncorrectPacket
	}

	// The header length is the low four bits of the first byte.
	p.IHL = uint8(data[0]) & 0x0F

	// The DSCP is the high six(!) bits of the second byte.
	p.DSCP = (uint8(data[1]) & 0xFC) >> 2

	// Congestion notification is the low two bits of the second byte.
	p.ECN = uint8(data[1]) & 0x03

	// Total length is saner: 16-bit integer.
	p.TotalLength = getUint16(data[2:4], false)

	// Same with the ID.
	p.ID = getUint16(data[4:6], false)

	// Back to the crazy with the flags: the top three bits of the 7th byte. We only care
	// about bits two and three. It hurt me to write that sentence.
	if (uint8(data[6]) & 0x40) != 0 {
		p.DontFragment = true
	} else if (uint8(data[6]) & 0x20) != 0 {
		p.MoreFragments = true
	}

	// Following from the flag crazy, the fragment offset is the low 13 bits of the 7th
	// and 8th bytes.
	p.FragmentOffset = getUint16(data[6:8], false) & 0x1FFF

	// The remaining fields are fairly sensible. TTL is the 9th byte.
	p.TTL = uint8(data[8])

	// Protocol is the tenth.
	p.Protocol = IPProtocol(data[9])

	// Header checksum is eleven and twelve.
	p.Checksum = getUint16(data[10:12], false)

	// Then the source IP and destination IP.
	p.SourceAddress = data[12:16]
	p.DestAddress = data[16:20]

	// If IHL is more than 5, we have (IHL - 5) * 4 bytes of options.
	if p.IHL > 5 {
		optionLength := uint16(p.IHL-5) * 4
		p.Options = data[20 : 20+optionLength]

		// Reslice data so that the actual packet contents still start at offset 20.
		data = data[optionLength:]
	}

	// The data length is the total length, minus the headers. The headers are, for no good
	// reason, measured in 32-bit words, so the data length is actually:
	dataLen := p.TotalLength - (uint16(p.IHL) * 4)
	if len(data[20:]) != int(dataLen) {
		return IncorrectPacket
	}

	// Build the transport layer data.
	p.buildTransportLayer(data[20:])

	return nil
}

func (p *IPv4Packet) buildTransportLayer(data []byte) {
	switch p.Protocol {
	case IPP_TCP:
		p.data = new(TCPSegment)
	case IPP_UDP:
		p.data = new(UDPDatagram)
	default:
		p.data = new(UnknownTransport)
	}
	p.data.FromBytes(data)
}

//-------------------------------------------------------------------------------------------
// IPv6
//-------------------------------------------------------------------------------------------

type IPv6Packet struct {
	TrafficClass       uint8
	FlowLabel          uint32 // This is a huge waste of space for a 20-bit field. Rethink?
	Length             uint16
	NextHeader         uint8
	HopLimit           uint8
	SourceAddress      []byte
	DestinationAddress []byte
	data               TransportLayer
}

func (p *IPv6Packet) InternetData() TransportLayer {
	return p.data
}

func (p *IPv6Packet) FromBytes(data []byte) error {
	// Confirm that we have enough data for the smallest possible header.
	if len(data) < 40 {
		return InsufficientLength
	}

	// Check that this actually is an IPv6 packet.
	if ((uint8(data[0]) & 0xF0) >> 4) != uint8(6) {
		return IncorrectPacket
	}

	// The traffic class is the octet following the version.
	p.TrafficClass = (uint8(data[0]) & 0x0F) << 4
	p.TrafficClass += (uint8(data[1]) * 0xF0) >> 4

	// The flow label is the next 20 bits.
	p.FlowLabel = (uint32(data[1]) & 0x0F) << 16
	p.FlowLabel += uint32(data[2]) << 8
	p.FlowLabel += uint32(data[3])

	// The remaining fields are simply aligned.
	p.Length = getUint16(data[4:6], false)
	p.NextHeader = uint8(data[6])
	p.HopLimit = uint8(data[7])

	p.SourceAddress = data[8:24]
	p.DestinationAddress = data[24:40]

	// The data is everything else.
	// NOTE: This is untrue, there are potential subsequent headers in the packet. Currently I'm ignoring
	// them to go all "minimim-viable-product" on this library. We'll swing back to it.
	p.data = new(UnknownTransport)
	p.data.FromBytes(data[40:])

	return nil
}
<commit_msg>Decode transport layer for IPv6 message<commit_after>package gopcap

//-------------------------------------------------------------------------------------------
// UnknownINet
//-------------------------------------------------------------------------------------------

// UnknownINet represents the data for an internet-layer packet that gopcap doesn't understand.
// It simply provides uninterpreted data representing the entire internet-layer packet.
type UnknownINet struct {
	data TransportLayer
}

func (u *UnknownINet) InternetData() TransportLayer {
	return u.data
}

func (u *UnknownINet) FromBytes(data []byte) error {
	u.data = new(UnknownTransport)
	u.data.FromBytes(data)
	return nil
}

//-------------------------------------------------------------------------------------------
// IPv4
//-------------------------------------------------------------------------------------------

// IPv4Packet represents an unpacked IPv4 packet. This method of storing the IPv4 packet data
// is less efficient than the byte-packed form used on the wire.
type IPv4Packet struct {
	IHL            uint8
	DSCP           uint8
	ECN            uint8
	TotalLength    uint16
	ID             uint16
	DontFragment   bool
	MoreFragments  bool
	FragmentOffset uint16
	TTL            uint8
	Protocol       IPProtocol
	Checksum       uint16
	SourceAddress  []byte
	DestAddress    []byte
	Options        []byte
	data           TransportLayer
}

func (p *IPv4Packet) InternetData() TransportLayer {
	return p.data
}

func (p *IPv4Packet) FromBytes(data []byte) error {
	// The IPv4 header is full of crazy non-aligned fields that I've expanded in the structure.
	// This makes this function a total nightmare. My apologies in advance.

	// Check that we have enough data for the header, at the very least.
	if len(data) < 20 {
		return InsufficientLength
	}

	// Check that this actually is an IPv4 packet.
	if ((uint8(data[0]) & 0xF0) >> 4) != uint8(4) {
		return IncorrectPacket
	}

	// The header length is the low four bits of the first byte.
	p.IHL = uint8(data[0]) & 0x0F

	// The DSCP is the high six(!) bits of the second byte.
	p.DSCP = (uint8(data[1]) & 0xFC) >> 2

	// Congestion notification is the low two bits of the second byte.
	p.ECN = uint8(data[1]) & 0x03

	// Total length is saner: 16-bit integer.
	p.TotalLength = getUint16(data[2:4], false)

	// Same with the ID.
	p.ID = getUint16(data[4:6], false)

	// Back to the crazy with the flags: the top three bits of the 7th byte. We only care
	// about bits two and three. It hurt me to write that sentence.
	if (uint8(data[6]) & 0x40) != 0 {
		p.DontFragment = true
	} else if (uint8(data[6]) & 0x20) != 0 {
		p.MoreFragments = true
	}

	// Following from the flag crazy, the fragment offset is the low 13 bits of the 7th
	// and 8th bytes.
	p.FragmentOffset = getUint16(data[6:8], false) & 0x1FFF

	// The remaining fields are fairly sensible. TTL is the 9th byte.
	p.TTL = uint8(data[8])

	// Protocol is the tenth.
	p.Protocol = IPProtocol(data[9])

	// Header checksum is eleven and twelve.
	p.Checksum = getUint16(data[10:12], false)

	// Then the source IP and destination IP.
	p.SourceAddress = data[12:16]
	p.DestAddress = data[16:20]

	// If IHL is more than 5, we have (IHL - 5) * 4 bytes of options.
	if p.IHL > 5 {
		optionLength := uint16(p.IHL-5) * 4
		p.Options = data[20 : 20+optionLength]

		// Reslice data so that the actual packet contents still start at offset 20.
		data = data[optionLength:]
	}

	// The data length is the total length, minus the headers. The headers are, for no good
	// reason, measured in 32-bit words, so the data length is actually:
	dataLen := p.TotalLength - (uint16(p.IHL) * 4)
	if len(data[20:]) != int(dataLen) {
		return IncorrectPacket
	}

	// Build the transport layer data.
	p.buildTransportLayer(data[20:])

	return nil
}

func (p *IPv4Packet) buildTransportLayer(data []byte) {
	switch p.Protocol {
	case IPP_TCP:
		p.data = new(TCPSegment)
	case IPP_UDP:
		p.data = new(UDPDatagram)
	default:
		p.data = new(UnknownTransport)
	}
	p.data.FromBytes(data)
}

//-------------------------------------------------------------------------------------------
// IPv6
//-------------------------------------------------------------------------------------------

type IPv6Packet struct {
	TrafficClass       uint8
	FlowLabel          uint32 // This is a huge waste of space for a 20-bit field. Rethink?
	Length             uint16
	NextHeader         IPProtocol
	HopLimit           uint8
	SourceAddress      []byte
	DestinationAddress []byte
	data               TransportLayer
}

func (p *IPv6Packet) InternetData() TransportLayer {
	return p.data
}

func (p *IPv6Packet) FromBytes(data []byte) error {
	// Confirm that we have enough data for the smallest possible header.
	if len(data) < 40 {
		return InsufficientLength
	}

	// Check that this actually is an IPv6 packet.
	if ((uint8(data[0]) & 0xF0) >> 4) != uint8(6) {
		return IncorrectPacket
	}

	// The traffic class is the octet following the version.
	p.TrafficClass = (uint8(data[0]) & 0x0F) << 4
	p.TrafficClass += (uint8(data[1]) * 0xF0) >> 4

	// The flow label is the next 20 bits.
	p.FlowLabel = (uint32(data[1]) & 0x0F) << 16
	p.FlowLabel += uint32(data[2]) << 8
	p.FlowLabel += uint32(data[3])

	// The remaining fields are simply aligned.
	p.Length = getUint16(data[4:6], false)
	p.NextHeader = IPProtocol(data[6])
	p.HopLimit = uint8(data[7])

	p.SourceAddress = data[8:24]
	p.DestinationAddress = data[24:40]

	// Following the fixed headers are a sequence of extension headers
	// terminating in the transport data.
	p.parseRemainingHeaders(data[40:])

	return nil
}

func (p *IPv6Packet) parseRemainingHeaders(data []byte) {
	// Currently we don't support any extension headers so if the next header
	// isn't the transport data then give up and interpret it as an unknown
	// transport type.
	switch p.NextHeader {
	case IPP_TCP:
		p.data = new(TCPSegment)
	case IPP_UDP:
		p.data = new(UDPDatagram)
	default:
		p.data = new(UnknownTransport)
	}
	p.data.FromBytes(data)
}
<|endoftext|>


<commit_before>package invoices

import (
	"bytes"
	"fmt"
	"math"
	"os"
	"strings"
	"unicode"
	"unsafe"
)

var (
	trim = strings.TrimSpace
	sf   = fmt.Sprintf
)

// math ---------------------------------------------------------

// imax returns the maximum value
func imax(a, b int) int {
	if a > b {
		return a
	}
	return b
}

// imax returns the minimum value
func imin(a, b int) int {
	if a < b {
		return a
	}
	return b
}

// isum returns the summation
func isum(vals ...int) int {
	sum := 0
	for _, v := range vals {
		sum += v
	}
	return sum
}

// string ---------------------------------------------------------

// rpad adds padding to the right of a string.
func rpad(s string, padding int) string {
	template := fmt.Sprintf("%%-%ds", padding)
	return fmt.Sprintf(template, s)
}

// BytesSizeToString convert bytes to a human-readable size
func BytesSizeToString(byteCount int) string {
	suf := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"} //Longs run out around EB
	if byteCount == 0 {
		return "0" + suf[0]
	}
	bytes := math.Abs(float64(byteCount))
	place := int32(math.Floor(math.Log2(bytes) / 10))
	num := bytes / math.Pow(1024.0, float64(place))
	var strnum string
	if place == 0 {
		strnum = fmt.Sprintf("%.0f", num) + suf[place]
	} else {
		strnum = fmt.Sprintf("%.1f", num) + suf[place]
	}
	return strnum
}

// ConvertBytesToString convert []byte to string
func ConvertBytesToString(bs []byte) string {
	return *(*string)(unsafe.Pointer(&bs))
}

// GetColStr return string use in field
func GetColStr(s string, size int, isleft bool) string {
	_, _, n := CountChars(s)
	spaces := strings.Repeat(" ", size-n)
	// size := nc*2 + ne // s 實際佔位數
	var tab string
	if isleft {
		tab = fmt.Sprintf("%[1]s%[2]s", s, spaces)
	} else {
		tab = fmt.Sprintf("%[2]s%[1]s", s, spaces)
	}
	return " " + tab
}

// CountChars returns the number of each other of chinses and english characters
func CountChars(str string) (nc, ne, n int) {
	for _, r := range str {
		lchar := len(string(r))
		// n += lchar
		if lchar > 1 {
			nc++
		} else {
			ne++
		}
	}
	n = 2*nc + ne
	return nc, ne, n
}

// IsChineseChar judges whether the chinese character exists ?
func IsChineseChar(str string) bool {
	// n := 0
	for _, r := range str {
		// io.Pf("%q ", r)
		if unicode.Is(unicode.Scripts["Han"], r) {
			// n++
			return true
		}
	}
	return false
}

// ArgsTable prints a nice table with input arguments
//  Input:
//   title -- title of table; e.g. INPUT ARGUMENTS
//   data  -- sets of THREE items in the following order:
//                 description, key, value, ...
//                 description, key, value, ...
//                      ...
//                 description, key, value, ...
func ArgsTable(title string, data ...interface{}) string {
	heads := []string{"description", "key", "value"}
	return ArgsTableN(title, 0, heads, data...)
}

// ArgsTableN prints a nice table with input arguments
//  Input:
//   title -- title of table; e.g. INPUT ARGUMENTS
//	 heads -- heads of table; e.g. []string{ col1,  col2, ... }
//	 nledsp -- length of leading spaces in every row
//   data  -- sets of THREE items in the following order:
//                 column1, column2, column3, ...
//                 column1, column2, column3, ...
//                      ...
//                 column1, column2, column3, ...
func ArgsTableN(title string, nledsp int, heads []string, data ...interface{}) string {
	Sf := fmt.Sprintf
	nf := len(heads)
	ndat := len(data)
	if ndat < nf {
		return ""
	}
	if nledsp < 0 {
		nledsp = 0
	}
	lspaces := StrSpaces(nledsp)
	nlines := ndat / nf
	sizes := make([]int, nf)
	for i := 0; i < nf; i++ {
		_, _, sizes[i] = CountChars(heads[i])
	}
	for i := 0; i < nlines; i++ {
		if i*nf+(nf-1) >= ndat {
			return Sf("ArgsTable: input arguments are not a multiple of %d\n", nf)
		}
		for j := 0; j < nf; j++ {
			str := Sf("%v", data[i*nf+j])
			_, _, nmix := CountChars(str)
			sizes[j] = imax(sizes[j], nmix)
		}
	}
	// strfmt := Sf("%%v  %%v  %%v\n")
	n := isum(sizes...) + nf + (nf-1)*2 + 1 // sizes[0] + sizes[1] + sizes[2] + 3 + 4
	_, _, l := CountChars(title)
	m := (n - l) / 2
	//
	var b bytes.Buffer
	bw := b.WriteString
	//
	bw(StrSpaces(m+nledsp) + title + "\n")
	bw(lspaces + StrThickLine(n))
	isleft := true
	sfields := make([]string, nf)
	for i := 0; i < nf; i++ {
		sfields[i] = GetColStr(heads[i], sizes[i], isleft)
		switch i {
		case 0:
			bw(Sf("%v", lspaces+sfields[i]))
		default:
			bw(Sf("  %v", sfields[i]))
		}
	}
	bw("\n")
	bw(lspaces + StrThinLine(n))
	for i := 0; i < nlines; i++ {
		for j := 0; j < nf; j++ {
			sfields[j] = GetColStr(Sf("%v", data[i*nf+j]), sizes[j], isleft)
			switch j {
			case 0:
				bw(Sf("%v", lspaces+sfields[j]))
			default:
				bw(Sf("  %v", sfields[j]))
			}
		}
		bw("\n")
	}
	bw(lspaces + StrThickLine(n))
	return b.String()
}

// StrThickLine returns a thick line (using '=')
func StrThickLine(n int) (l string) {
	l = strings.Repeat("=", n)
	return l + "\n"
}

// StrThinLine returns a thin line (using '-')
func StrThinLine(n int) (l string) {
	l = strings.Repeat("-", n)
	return l + "\n"
}

// StrSpaces returns a line with spaces
func StrSpaces(n int) (l string) {
	l = strings.Repeat(" ", n)
	return
}

// file ---------------------------------------------------------

// isFileExist checks whether a file exist
func isFileExist(filename string) bool {
	path := os.ExpandEnv(filename)
	isExist := true
	if _, err := os.Stat(path); os.IsNotExist(err) {
		isExist = false
	}
	return isExist
}

// print ---------------------------------------------------------
<commit_msg>Add new var Verbose, ColorsOn<commit_after>package invoices

import (
	"bytes"
	"fmt"
	"math"
	"os"
	"strings"
	"unicode"
	"unsafe"
)

var (
	// Verbose activates display of messages on console
	Verbose = true

	// ColorsOn activates use of colors on console
	ColorsOn = true
)

// math ---------------------------------------------------------

// imax returns the maximum value
func imax(a, b int) int {
	if a > b {
		return a
	}
	return b
}

// imax returns the minimum value
func imin(a, b int) int {
	if a < b {
		return a
	}
	return b
}

// isum returns the summation
func isum(vals ...int) int {
	sum := 0
	for _, v := range vals {
		sum += v
	}
	return sum
}

// string ---------------------------------------------------------

// rpad adds padding to the right of a string.
func rpad(s string, padding int) string {
	template := fmt.Sprintf("%%-%ds", padding)
	return fmt.Sprintf(template, s)
}

// BytesSizeToString convert bytes to a human-readable size
func BytesSizeToString(byteCount int) string {
	suf := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"} //Longs run out around EB
	if byteCount == 0 {
		return "0" + suf[0]
	}
	bytes := math.Abs(float64(byteCount))
	place := int32(math.Floor(math.Log2(bytes) / 10))
	num := bytes / math.Pow(1024.0, float64(place))
	var strnum string
	if place == 0 {
		strnum = fmt.Sprintf("%.0f", num) + suf[place]
	} else {
		strnum = fmt.Sprintf("%.1f", num) + suf[place]
	}
	return strnum
}

// ConvertBytesToString convert []byte to string
func ConvertBytesToString(bs []byte) string {
	return *(*string)(unsafe.Pointer(&bs))
}

// GetColStr return string use in field
func GetColStr(s string, size int, isleft bool) string {
	_, _, n := CountChars(s)
	spaces := strings.Repeat(" ", size-n)
	// size := nc*2 + ne // s 實際佔位數
	var tab string
	if isleft {
		tab = fmt.Sprintf("%[1]s%[2]s", s, spaces)
	} else {
		tab = fmt.Sprintf("%[2]s%[1]s", s, spaces)
	}
	return " " + tab
}

// CountChars returns the number of each other of chinses and english characters
func CountChars(str string) (nc, ne, n int) {
	for _, r := range str {
		lchar := len(string(r))
		// n += lchar
		if lchar > 1 {
			nc++
		} else {
			ne++
		}
	}
	n = 2*nc + ne
	return nc, ne, n
}

// IsChineseChar judges whether the chinese character exists ?
func IsChineseChar(str string) bool {
	// n := 0
	for _, r := range str {
		// io.Pf("%q ", r)
		if unicode.Is(unicode.Scripts["Han"], r) {
			// n++
			return true
		}
	}
	return false
}

// ArgsTable prints a nice table with input arguments
//  Input:
//   title -- title of table; e.g. INPUT ARGUMENTS
//   data  -- sets of THREE items in the following order:
//                 description, key, value, ...
//                 description, key, value, ...
//                      ...
//                 description, key, value, ...
func ArgsTable(title string, data ...interface{}) string {
	heads := []string{"description", "key", "value"}
	return ArgsTableN(title, 0, heads, data...)
}

// ArgsTableN prints a nice table with input arguments
//  Input:
//   title -- title of table; e.g. INPUT ARGUMENTS
//	 heads -- heads of table; e.g. []string{ col1,  col2, ... }
//	 nledsp -- length of leading spaces in every row
//   data  -- sets of THREE items in the following order:
//                 column1, column2, column3, ...
//                 column1, column2, column3, ...
//                      ...
//                 column1, column2, column3, ...
func ArgsTableN(title string, nledsp int, heads []string, data ...interface{}) string {
	Sf := fmt.Sprintf
	nf := len(heads)
	ndat := len(data)
	if ndat < nf {
		return ""
	}
	if nledsp < 0 {
		nledsp = 0
	}
	lspaces := StrSpaces(nledsp)
	nlines := ndat / nf
	sizes := make([]int, nf)
	for i := 0; i < nf; i++ {
		_, _, sizes[i] = CountChars(heads[i])
	}
	for i := 0; i < nlines; i++ {
		if i*nf+(nf-1) >= ndat {
			return Sf("ArgsTable: input arguments are not a multiple of %d\n", nf)
		}
		for j := 0; j < nf; j++ {
			str := Sf("%v", data[i*nf+j])
			_, _, nmix := CountChars(str)
			sizes[j] = imax(sizes[j], nmix)
		}
	}
	// strfmt := Sf("%%v  %%v  %%v\n")
	n := isum(sizes...) + nf + (nf-1)*2 + 1 // sizes[0] + sizes[1] + sizes[2] + 3 + 4
	_, _, l := CountChars(title)
	m := (n - l) / 2
	//
	var b bytes.Buffer
	bw := b.WriteString
	//
	bw(StrSpaces(m+nledsp) + title + "\n")
	bw(lspaces + StrThickLine(n))
	isleft := true
	sfields := make([]string, nf)
	for i := 0; i < nf; i++ {
		sfields[i] = GetColStr(heads[i], sizes[i], isleft)
		switch i {
		case 0:
			bw(Sf("%v", lspaces+sfields[i]))
		default:
			bw(Sf("  %v", sfields[i]))
		}
	}
	bw("\n")
	bw(lspaces + StrThinLine(n))
	for i := 0; i < nlines; i++ {
		for j := 0; j < nf; j++ {
			sfields[j] = GetColStr(Sf("%v", data[i*nf+j]), sizes[j], isleft)
			switch j {
			case 0:
				bw(Sf("%v", lspaces+sfields[j]))
			default:
				bw(Sf("  %v", sfields[j]))
			}
		}
		bw("\n")
	}
	bw(lspaces + StrThickLine(n))
	return b.String()
}

// StrThickLine returns a thick line (using '=')
func StrThickLine(n int) (l string) {
	l = strings.Repeat("=", n)
	return l + "\n"
}

// StrThinLine returns a thin line (using '-')
func StrThinLine(n int) (l string) {
	l = strings.Repeat("-", n)
	return l + "\n"
}

// StrSpaces returns a line with spaces
func StrSpaces(n int) (l string) {
	l = strings.Repeat(" ", n)
	return
}

// file ---------------------------------------------------------

// isFileExist checks whether a file exist
func isFileExist(filename string) bool {
	path := os.ExpandEnv(filename)
	isExist := true
	if _, err := os.Stat(path); os.IsNotExist(err) {
		isExist = false
	}
	return isExist
}

// print ---------------------------------------------------------
<|endoftext|>


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

// Package ipv6 implements IP-level socket options for the Internet
// Protocol version 6.
//
// The package provides IP-level socket options that allow
// manipulation of IPv6 facilities.
//
// The IPv6 protocol is defined in RFC 8200.
// Socket interface extensions are defined in RFC 3493, RFC 3542 and
// RFC 3678.
// MLDv1 and MLDv2 are defined in RFC 2710 and RFC 3810.
// Source-specific multicast is defined in RFC 4607.
//
// On Darwin, this package requires OS X Mavericks version 10.9 or
// above, or equivalent.
//
//
// Unicasting
//
// The options for unicasting are available for net.TCPConn,
// net.UDPConn and net.IPConn which are created as network connections
// that use the IPv6 transport. When a single TCP connection carrying
// a data flow of multiple packets needs to indicate the flow is
// important, Conn is used to set the traffic class field on the IPv6
// header for each packet.
//
//	ln, err := net.Listen("tcp6", "[::]:1024")
//	if err != nil {
//		// error handling
//	}
//	defer ln.Close()
//	for {
//		c, err := ln.Accept()
//		if err != nil {
//			// error handling
//		}
//		go func(c net.Conn) {
//			defer c.Close()
//
// The outgoing packets will be labeled DiffServ assured forwarding
// class 1 low drop precedence, known as AF11 packets.
//
//			if err := ipv6.NewConn(c).SetTrafficClass(0x28); err != nil {
//				// error handling
//			}
//			if _, err := c.Write(data); err != nil {
//				// error handling
//			}
//		}(c)
//	}
//
//
// Multicasting
//
// The options for multicasting are available for net.UDPConn and
// net.IPconn which are created as network connections that use the
// IPv6 transport. A few network facilities must be prepared before
// you begin multicasting, at a minimum joining network interfaces and
// multicast groups.
//
//	en0, err := net.InterfaceByName("en0")
//	if err != nil {
//		// error handling
//	}
//	en1, err := net.InterfaceByIndex(911)
//	if err != nil {
//		// error handling
//	}
//	group := net.ParseIP("ff02::114")
//
// First, an application listens to an appropriate address with an
// appropriate service port.
//
//	c, err := net.ListenPacket("udp6", "[::]:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c.Close()
//
// Second, the application joins multicast groups, starts listening to
// the groups on the specified network interfaces. Note that the
// service port for transport layer protocol does not matter with this
// operation as joining groups affects only network and link layer
// protocols, such as IPv6 and Ethernet.
//
//	p := ipv6.NewPacketConn(c)
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: group}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en1, &net.UDPAddr{IP: group}); err != nil {
//		// error handling
//	}
//
// The application might set per packet control message transmissions
// between the protocol stack within the kernel. When the application
// needs a destination address on an incoming packet,
// SetControlMessage of PacketConn is used to enable control message
// transmissions.
//
//	if err := p.SetControlMessage(ipv6.FlagDst, true); err != nil {
//		// error handling
//	}
//
// The application could identify whether the received packets are
// of interest by using the control message that contains the
// destination address of the received packet.
//
//	b := make([]byte, 1500)
//	for {
//		n, rcm, src, err := p.ReadFrom(b)
//		if err != nil {
//			// error handling
//		}
//		if rcm.Dst.IsMulticast() {
//			if rcm.Dst.Equal(group) {
//				// joined group, do something
//			} else {
//				// unknown group, discard
//				continue
//			}
//		}
//
// The application can also send both unicast and multicast packets.
//
//		p.SetTrafficClass(0x0)
//		p.SetHopLimit(16)
//		if _, err := p.WriteTo(data[:n], nil, src); err != nil {
//			// error handling
//		}
//		dst := &net.UDPAddr{IP: group, Port: 1024}
//		wcm := ipv6.ControlMessage{TrafficClass: 0xe0, HopLimit: 1}
//		for _, ifi := range []*net.Interface{en0, en1} {
//			wcm.IfIndex = ifi.Index
//			if _, err := p.WriteTo(data[:n], &wcm, dst); err != nil {
//				// error handling
//			}
//		}
//	}
//
//
// More multicasting
//
// An application that uses PacketConn may join multiple multicast
// groups. For example, a UDP listener with port 1024 might join two
// different groups across over two different network interfaces by
// using:
//
//	c, err := net.ListenPacket("udp6", "[::]:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c.Close()
//	p := ipv6.NewPacketConn(c)
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::1:114")}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en1, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}); err != nil {
//		// error handling
//	}
//
// It is possible for multiple UDP listeners that listen on the same
// UDP port to join the same multicast group. The net package will
// provide a socket that listens to a wildcard address with reusable
// UDP port when an appropriate multicast address prefix is passed to
// the net.ListenPacket or net.ListenUDP.
//
//	c1, err := net.ListenPacket("udp6", "[ff02::]:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c1.Close()
//	c2, err := net.ListenPacket("udp6", "[ff02::]:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c2.Close()
//	p1 := ipv6.NewPacketConn(c1)
//	if err := p1.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
//		// error handling
//	}
//	p2 := ipv6.NewPacketConn(c2)
//	if err := p2.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
//		// error handling
//	}
//
// Also it is possible for the application to leave or rejoin a
// multicast group on the network interface.
//
//	if err := p.LeaveGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff01::114")}); err != nil {
//		// error handling
//	}
//
//
// Source-specific multicasting
//
// An application that uses PacketConn on MLDv2 supported platform is
// able to join source-specific multicast groups.
// The application may use JoinSourceSpecificGroup and
// LeaveSourceSpecificGroup for the operation known as "include" mode,
//
//	ssmgroup := net.UDPAddr{IP: net.ParseIP("ff32::8000:9")}
//	ssmsource := net.UDPAddr{IP: net.ParseIP("fe80::cafe")}
//	if err := p.JoinSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
//		// error handling
//	}
//	if err := p.LeaveSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
//		// error handling
//	}
//
// or JoinGroup, ExcludeSourceSpecificGroup,
// IncludeSourceSpecificGroup and LeaveGroup for the operation known
// as "exclude" mode.
//
//	exclsource := net.UDPAddr{IP: net.ParseIP("fe80::dead")}
//	if err := p.JoinGroup(en0, &ssmgroup); err != nil {
//		// error handling
//	}
//	if err := p.ExcludeSourceSpecificGroup(en0, &ssmgroup, &exclsource); err != nil {
//		// error handling
//	}
//	if err := p.LeaveGroup(en0, &ssmgroup); err != nil {
//		// error handling
//	}
//
// Note that it depends on each platform implementation what happens
// when an application which runs on MLDv2 unsupported platform uses
// JoinSourceSpecificGroup and LeaveSourceSpecificGroup.
// In general the platform tries to fall back to conversations using
// MLDv1 and starts to listen to multicast traffic.
// In the fallback case, ExcludeSourceSpecificGroup and
// IncludeSourceSpecificGroup may return an error.
package ipv6 // import "golang.org/x/net/ipv6"

// BUG(mikio): This package is not implemented on JS, NaCl and Plan 9.
<commit_msg>ipv6: fix a typo<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package ipv6 implements IP-level socket options for the Internet
// Protocol version 6.
//
// The package provides IP-level socket options that allow
// manipulation of IPv6 facilities.
//
// The IPv6 protocol is defined in RFC 8200.
// Socket interface extensions are defined in RFC 3493, RFC 3542 and
// RFC 3678.
// MLDv1 and MLDv2 are defined in RFC 2710 and RFC 3810.
// Source-specific multicast is defined in RFC 4607.
//
// On Darwin, this package requires OS X Mavericks version 10.9 or
// above, or equivalent.
//
//
// Unicasting
//
// The options for unicasting are available for net.TCPConn,
// net.UDPConn and net.IPConn which are created as network connections
// that use the IPv6 transport. When a single TCP connection carrying
// a data flow of multiple packets needs to indicate the flow is
// important, Conn is used to set the traffic class field on the IPv6
// header for each packet.
//
//	ln, err := net.Listen("tcp6", "[::]:1024")
//	if err != nil {
//		// error handling
//	}
//	defer ln.Close()
//	for {
//		c, err := ln.Accept()
//		if err != nil {
//			// error handling
//		}
//		go func(c net.Conn) {
//			defer c.Close()
//
// The outgoing packets will be labeled DiffServ assured forwarding
// class 1 low drop precedence, known as AF11 packets.
//
//			if err := ipv6.NewConn(c).SetTrafficClass(0x28); err != nil {
//				// error handling
//			}
//			if _, err := c.Write(data); err != nil {
//				// error handling
//			}
//		}(c)
//	}
//
//
// Multicasting
//
// The options for multicasting are available for net.UDPConn and
// net.IPConn which are created as network connections that use the
// IPv6 transport. A few network facilities must be prepared before
// you begin multicasting, at a minimum joining network interfaces and
// multicast groups.
//
//	en0, err := net.InterfaceByName("en0")
//	if err != nil {
//		// error handling
//	}
//	en1, err := net.InterfaceByIndex(911)
//	if err != nil {
//		// error handling
//	}
//	group := net.ParseIP("ff02::114")
//
// First, an application listens to an appropriate address with an
// appropriate service port.
//
//	c, err := net.ListenPacket("udp6", "[::]:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c.Close()
//
// Second, the application joins multicast groups, starts listening to
// the groups on the specified network interfaces. Note that the
// service port for transport layer protocol does not matter with this
// operation as joining groups affects only network and link layer
// protocols, such as IPv6 and Ethernet.
//
//	p := ipv6.NewPacketConn(c)
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: group}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en1, &net.UDPAddr{IP: group}); err != nil {
//		// error handling
//	}
//
// The application might set per packet control message transmissions
// between the protocol stack within the kernel. When the application
// needs a destination address on an incoming packet,
// SetControlMessage of PacketConn is used to enable control message
// transmissions.
//
//	if err := p.SetControlMessage(ipv6.FlagDst, true); err != nil {
//		// error handling
//	}
//
// The application could identify whether the received packets are
// of interest by using the control message that contains the
// destination address of the received packet.
//
//	b := make([]byte, 1500)
//	for {
//		n, rcm, src, err := p.ReadFrom(b)
//		if err != nil {
//			// error handling
//		}
//		if rcm.Dst.IsMulticast() {
//			if rcm.Dst.Equal(group) {
//				// joined group, do something
//			} else {
//				// unknown group, discard
//				continue
//			}
//		}
//
// The application can also send both unicast and multicast packets.
//
//		p.SetTrafficClass(0x0)
//		p.SetHopLimit(16)
//		if _, err := p.WriteTo(data[:n], nil, src); err != nil {
//			// error handling
//		}
//		dst := &net.UDPAddr{IP: group, Port: 1024}
//		wcm := ipv6.ControlMessage{TrafficClass: 0xe0, HopLimit: 1}
//		for _, ifi := range []*net.Interface{en0, en1} {
//			wcm.IfIndex = ifi.Index
//			if _, err := p.WriteTo(data[:n], &wcm, dst); err != nil {
//				// error handling
//			}
//		}
//	}
//
//
// More multicasting
//
// An application that uses PacketConn may join multiple multicast
// groups. For example, a UDP listener with port 1024 might join two
// different groups across over two different network interfaces by
// using:
//
//	c, err := net.ListenPacket("udp6", "[::]:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c.Close()
//	p := ipv6.NewPacketConn(c)
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::1:114")}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en1, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}); err != nil {
//		// error handling
//	}
//
// It is possible for multiple UDP listeners that listen on the same
// UDP port to join the same multicast group. The net package will
// provide a socket that listens to a wildcard address with reusable
// UDP port when an appropriate multicast address prefix is passed to
// the net.ListenPacket or net.ListenUDP.
//
//	c1, err := net.ListenPacket("udp6", "[ff02::]:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c1.Close()
//	c2, err := net.ListenPacket("udp6", "[ff02::]:1024")
//	if err != nil {
//		// error handling
//	}
//	defer c2.Close()
//	p1 := ipv6.NewPacketConn(c1)
//	if err := p1.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
//		// error handling
//	}
//	p2 := ipv6.NewPacketConn(c2)
//	if err := p2.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
//		// error handling
//	}
//
// Also it is possible for the application to leave or rejoin a
// multicast group on the network interface.
//
//	if err := p.LeaveGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
//		// error handling
//	}
//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff01::114")}); err != nil {
//		// error handling
//	}
//
//
// Source-specific multicasting
//
// An application that uses PacketConn on MLDv2 supported platform is
// able to join source-specific multicast groups.
// The application may use JoinSourceSpecificGroup and
// LeaveSourceSpecificGroup for the operation known as "include" mode,
//
//	ssmgroup := net.UDPAddr{IP: net.ParseIP("ff32::8000:9")}
//	ssmsource := net.UDPAddr{IP: net.ParseIP("fe80::cafe")}
//	if err := p.JoinSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
//		// error handling
//	}
//	if err := p.LeaveSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil {
//		// error handling
//	}
//
// or JoinGroup, ExcludeSourceSpecificGroup,
// IncludeSourceSpecificGroup and LeaveGroup for the operation known
// as "exclude" mode.
//
//	exclsource := net.UDPAddr{IP: net.ParseIP("fe80::dead")}
//	if err := p.JoinGroup(en0, &ssmgroup); err != nil {
//		// error handling
//	}
//	if err := p.ExcludeSourceSpecificGroup(en0, &ssmgroup, &exclsource); err != nil {
//		// error handling
//	}
//	if err := p.LeaveGroup(en0, &ssmgroup); err != nil {
//		// error handling
//	}
//
// Note that it depends on each platform implementation what happens
// when an application which runs on MLDv2 unsupported platform uses
// JoinSourceSpecificGroup and LeaveSourceSpecificGroup.
// In general the platform tries to fall back to conversations using
// MLDv1 and starts to listen to multicast traffic.
// In the fallback case, ExcludeSourceSpecificGroup and
// IncludeSourceSpecificGroup may return an error.
package ipv6 // import "golang.org/x/net/ipv6"

// BUG(mikio): This package is not implemented on JS, NaCl and Plan 9.
<|endoftext|>


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

// +build js

package opengl

import (
	"errors"
	"fmt"
	"github.com/gopherjs/gopherjs/js"
	"github.com/gopherjs/webgl"
)

type Texture struct {
	*js.Object
}

type Framebuffer struct {
	*js.Object
}

var ZeroFramebuffer Framebuffer

type Shader struct {
	*js.Object
}

type Program struct {
	*js.Object
}

type Buffer struct {
	*js.Object
}

type uniformLocation struct {
	*js.Object
}

type attribLocation int

type programID int

func (p Program) id() programID {
	return programID(p.Get("__ebiten_programId").Int())
}

type context struct {
	gl              *webgl.Context
	lastFramebuffer Framebuffer
	lastProgramID   programID
}

func NewContext() (*Context, error) {
	var gl *webgl.Context

	if js.Global.Get("require") == js.Undefined {
		// TODO: Define id?
		canvas := js.Global.Get("document").Call("querySelector", "canvas")
		var err error
		gl, err = webgl.NewContext(canvas, &webgl.ContextAttributes{
			Alpha:              true,
			PremultipliedAlpha: true,
		})
		if err != nil {
			return nil, err
		}
	} else {
		// TODO: Now Ebiten with headless-gl doesn't work well (#141).
		// Use headless-gl for testing.
		options := map[string]bool{
			"alpha":              true,
			"premultipliedAlpha": true,
		}
		webglContext := js.Global.Call("require", "gl").Invoke(16, 16, options)
		gl = &webgl.Context{Object: webglContext}
	}

	c := &Context{
		Nearest:            Filter(gl.NEAREST),
		Linear:             Filter(gl.LINEAR),
		VertexShader:       ShaderType(gl.VERTEX_SHADER),
		FragmentShader:     ShaderType(gl.FRAGMENT_SHADER),
		ArrayBuffer:        BufferType(gl.ARRAY_BUFFER),
		ElementArrayBuffer: BufferType(gl.ELEMENT_ARRAY_BUFFER),
		DynamicDraw:        BufferUsage(gl.DYNAMIC_DRAW),
		StaticDraw:         BufferUsage(gl.STATIC_DRAW),
		Triangles:          Mode(gl.TRIANGLES),
		Lines:              Mode(gl.LINES),
		zero:               operation(gl.ZERO),
		one:                operation(gl.ONE),
		srcAlpha:           operation(gl.SRC_ALPHA),
		dstAlpha:           operation(gl.DST_ALPHA),
		oneMinusSrcAlpha:   operation(gl.ONE_MINUS_SRC_ALPHA),
		oneMinusDstAlpha:   operation(gl.ONE_MINUS_DST_ALPHA),
		locationCache:      newLocationCache(),
		lastCompositeMode:  CompositeModeUnknown,
	}
	c.gl = gl
	c.init()
	return c, nil
}

func (c *Context) init() {
	gl := c.gl
	// Textures' pixel formats are alpha premultiplied.
	gl.Enable(gl.BLEND)
	//gl.BlendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)
	c.BlendFunc(CompositeModeSourceOver)
}

func (c *Context) BlendFunc(mode CompositeMode) {
	if c.lastCompositeMode == mode {
		return
	}
	c.lastCompositeMode = mode
	s, d := c.operations(mode)
	gl := c.gl
	gl.BlendFunc(int(s), int(d))
}

func (c *Context) NewTexture(width, height int, pixels []uint8, filter Filter) (Texture, error) {
	gl := c.gl
	t := gl.CreateTexture()
	if t == nil {
		return Texture{nil}, errors.New("opengl: glGenTexture failed")
	}
	gl.PixelStorei(gl.UNPACK_ALIGNMENT, 4)
	gl.BindTexture(gl.TEXTURE_2D, t)

	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, int(filter))
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, int(filter))

	// TODO: Can we use glTexSubImage2D with linear filtering?

	// void texImage2D(GLenum target, GLint level, GLenum internalformat,
	//     GLsizei width, GLsizei height, GLint border, GLenum format,
	//     GLenum type, ArrayBufferView? pixels);
	var p interface{}
	if pixels != nil {
		p = pixels
	}
	gl.Call("texImage2D", gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, p)

	return Texture{t}, nil
}

func (c *Context) bindFramebuffer(f Framebuffer) {
	gl := c.gl
	if c.lastFramebuffer != f {
		gl.BindFramebuffer(gl.FRAMEBUFFER, f.Object)
		c.lastFramebuffer = f
	}
}

func (c *Context) FramebufferPixels(f Framebuffer, width, height int) ([]uint8, error) {
	gl := c.gl

	c.bindFramebuffer(f)

	pixels := js.Global.Get("Uint8Array").New(4 * width * height)
	gl.ReadPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels)
	if e := gl.GetError(); e != gl.NO_ERROR {
		return nil, errors.New(fmt.Sprintf("opengl: error: %d", e))
	}
	return pixels.Interface().([]uint8), nil
}

func (c *Context) BindTexture(t Texture) {
	gl := c.gl
	gl.BindTexture(gl.TEXTURE_2D, t.Object)
}

func (c *Context) DeleteTexture(t Texture) {
	gl := c.gl
	gl.DeleteTexture(t.Object)
}

func (c *Context) TexSubImage2D(p []uint8, width, height int) {
	gl := c.gl
	// void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
	//                    GLsizei width, GLsizei height,
	//                    GLenum format, GLenum type, ArrayBufferView? pixels);
	gl.Call("texSubImage2D", gl.TEXTURE_2D, 0, 0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, p)
}

func (c *Context) NewFramebuffer(t Texture) (Framebuffer, error) {
	gl := c.gl
	f := gl.CreateFramebuffer()
	c.bindFramebuffer(Framebuffer{f})

	gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, t.Object, 0)
	s := gl.CheckFramebufferStatus(gl.FRAMEBUFFER)
	if s != gl.FRAMEBUFFER_COMPLETE {
		return Framebuffer{nil}, errors.New(fmt.Sprintf("opengl: creating framebuffer failed: %d", s))
	}

	return Framebuffer{f}, nil
}

func (c *Context) SetViewport(f Framebuffer, width, height int) error {
	// TODO: Not sure if Flush is needed here.
	if f.Object == nil {
		c.bindFramebuffer(f)
		gl := c.gl
		gl.Viewport(0, 0, width, height)
		return nil
	}
	c.bindFramebuffer(f)
	gl := c.gl
	gl.Viewport(0, 0, width, height)
	return nil
}

func (c *Context) FillFramebuffer(r, g, b, a float64) error {
	// TODO: Use f?
	gl := c.gl
	gl.ClearColor(float32(r), float32(g), float32(b), float32(a))
	gl.Clear(gl.COLOR_BUFFER_BIT)
	return nil
}

func (c *Context) DeleteFramebuffer(f Framebuffer) {
	gl := c.gl
	gl.DeleteFramebuffer(f.Object)
}

func (c *Context) NewShader(shaderType ShaderType, source string) (Shader, error) {
	gl := c.gl
	s := gl.CreateShader(int(shaderType))
	if s == nil {
		return Shader{nil}, errors.New("opengl: glCreateShader failed")
	}

	gl.ShaderSource(s, source)
	gl.CompileShader(s)

	if !gl.GetShaderParameterb(s, gl.COMPILE_STATUS) {
		log := gl.GetShaderInfoLog(s)
		return Shader{nil}, errors.New(fmt.Sprintf("opengl: shader compile failed: %s", log))
	}
	return Shader{s}, nil
}

func (c *Context) DeleteShader(s Shader) {
	gl := c.gl
	gl.DeleteShader(s.Object)
}

func (c *Context) GlslHighpSupported() bool {
	gl := c.gl
	// headless-gl library may not define getShaderPrecisionFormat.
	if gl.Get("getShaderPrecisionFormat") == js.Undefined {
		return false
	}
	return gl.Call("getShaderPrecisionFormat", gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).Get("precision").Int() != 0
}

func (c *Context) NewProgram(shaders []Shader) (Program, error) {
	gl := c.gl
	p := gl.CreateProgram()
	if p == nil {
		return Program{nil}, errors.New("opengl: glCreateProgram failed")
	}
	p.Set("__ebiten_programId", c.lastProgramID)
	c.lastProgramID++

	for _, shader := range shaders {
		gl.AttachShader(p, shader.Object)
	}
	gl.LinkProgram(p)
	if !gl.GetProgramParameterb(p, gl.LINK_STATUS) {
		return Program{nil}, errors.New("opengl: program error")
	}
	return Program{p}, nil
}

func (c *Context) UseProgram(p Program) {
	gl := c.gl
	gl.UseProgram(p.Object)
}

func (c *Context) getUniformLocation(p Program, location string) uniformLocation {
	gl := c.gl
	return uniformLocation{gl.GetUniformLocation(p.Object, location)}
}

func (c *Context) UniformInt(p Program, location string, v int) {
	gl := c.gl
	l := c.locationCache.GetUniformLocation(c, p, location)
	gl.Uniform1i(l.Object, v)
}

func (c *Context) UniformFloats(p Program, location string, v []float32) {
	gl := c.gl
	l := c.locationCache.GetUniformLocation(c, p, location)
	switch len(v) {
	case 4:
		gl.Call("uniform4fv", l.Object, v)
	case 16:
		gl.UniformMatrix4fv(l.Object, false, v)
	default:
		panic("not reach")
	}
}

func (c *Context) getAttribLocation(p Program, location string) attribLocation {
	gl := c.gl
	return attribLocation(gl.GetAttribLocation(p.Object, location))
}

func (c *Context) VertexAttribPointer(p Program, location string, normalize bool, stride int, size int, v int) {
	gl := c.gl
	l := c.locationCache.GetAttribLocation(c, p, location)
	gl.VertexAttribPointer(int(l), size, gl.SHORT, normalize, stride, v)
}

func (c *Context) EnableVertexAttribArray(p Program, location string) {
	gl := c.gl
	l := c.locationCache.GetAttribLocation(c, p, location)
	gl.EnableVertexAttribArray(int(l))
}

func (c *Context) DisableVertexAttribArray(p Program, location string) {
	gl := c.gl
	l := c.locationCache.GetAttribLocation(c, p, location)
	gl.DisableVertexAttribArray(int(l))
}

func (c *Context) DeleteProgram(p Program) {
	gl := c.gl
	gl.DeleteProgram(p.Object)
}

func (c *Context) NewBuffer(bufferType BufferType, v interface{}, bufferUsage BufferUsage) Buffer {
	gl := c.gl
	b := gl.CreateBuffer()
	gl.BindBuffer(int(bufferType), b)
	gl.BufferData(int(bufferType), v, int(bufferUsage))
	return Buffer{b}
}

func (c *Context) BindElementArrayBuffer(b Buffer) {
	gl := c.gl
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, b.Object)
}

func (c *Context) BufferSubData(bufferType BufferType, data []int16) {
	gl := c.gl
	gl.BufferSubData(int(bufferType), 0, data)
}

func (c *Context) DeleteBuffer(b Buffer) {
	gl := c.gl
	gl.DeleteBuffer(b.Object)
}

func (c *Context) DrawElements(mode Mode, len int) {
	gl := c.gl
	gl.DrawElements(int(mode), len, gl.UNSIGNED_SHORT, 0)
}
<commit_msg>opengl: Refactoring: Remove unneeded code<commit_after>// Copyright 2014 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build js

package opengl

import (
	"errors"
	"fmt"
	"github.com/gopherjs/gopherjs/js"
	"github.com/gopherjs/webgl"
)

type Texture struct {
	*js.Object
}

type Framebuffer struct {
	*js.Object
}

var ZeroFramebuffer Framebuffer

type Shader struct {
	*js.Object
}

type Program struct {
	*js.Object
}

type Buffer struct {
	*js.Object
}

type uniformLocation struct {
	*js.Object
}

type attribLocation int

type programID int

func (p Program) id() programID {
	return programID(p.Get("__ebiten_programId").Int())
}

type context struct {
	gl              *webgl.Context
	lastFramebuffer Framebuffer
	lastProgramID   programID
}

func NewContext() (*Context, error) {
	var gl *webgl.Context

	if js.Global.Get("require") == js.Undefined {
		// TODO: Define id?
		canvas := js.Global.Get("document").Call("querySelector", "canvas")
		var err error
		gl, err = webgl.NewContext(canvas, &webgl.ContextAttributes{
			Alpha:              true,
			PremultipliedAlpha: true,
		})
		if err != nil {
			return nil, err
		}
	} else {
		// TODO: Now Ebiten with headless-gl doesn't work well (#141).
		// Use headless-gl for testing.
		options := map[string]bool{
			"alpha":              true,
			"premultipliedAlpha": true,
		}
		webglContext := js.Global.Call("require", "gl").Invoke(16, 16, options)
		gl = &webgl.Context{Object: webglContext}
	}

	c := &Context{
		Nearest:            Filter(gl.NEAREST),
		Linear:             Filter(gl.LINEAR),
		VertexShader:       ShaderType(gl.VERTEX_SHADER),
		FragmentShader:     ShaderType(gl.FRAGMENT_SHADER),
		ArrayBuffer:        BufferType(gl.ARRAY_BUFFER),
		ElementArrayBuffer: BufferType(gl.ELEMENT_ARRAY_BUFFER),
		DynamicDraw:        BufferUsage(gl.DYNAMIC_DRAW),
		StaticDraw:         BufferUsage(gl.STATIC_DRAW),
		Triangles:          Mode(gl.TRIANGLES),
		Lines:              Mode(gl.LINES),
		zero:               operation(gl.ZERO),
		one:                operation(gl.ONE),
		srcAlpha:           operation(gl.SRC_ALPHA),
		dstAlpha:           operation(gl.DST_ALPHA),
		oneMinusSrcAlpha:   operation(gl.ONE_MINUS_SRC_ALPHA),
		oneMinusDstAlpha:   operation(gl.ONE_MINUS_DST_ALPHA),
		locationCache:      newLocationCache(),
		lastCompositeMode:  CompositeModeUnknown,
	}
	c.gl = gl
	c.init()
	return c, nil
}

func (c *Context) init() {
	gl := c.gl
	// Textures' pixel formats are alpha premultiplied.
	gl.Enable(gl.BLEND)
	//gl.BlendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)
	c.BlendFunc(CompositeModeSourceOver)
}

func (c *Context) BlendFunc(mode CompositeMode) {
	if c.lastCompositeMode == mode {
		return
	}
	c.lastCompositeMode = mode
	s, d := c.operations(mode)
	gl := c.gl
	gl.BlendFunc(int(s), int(d))
}

func (c *Context) NewTexture(width, height int, pixels []uint8, filter Filter) (Texture, error) {
	gl := c.gl
	t := gl.CreateTexture()
	if t == nil {
		return Texture{nil}, errors.New("opengl: glGenTexture failed")
	}
	gl.PixelStorei(gl.UNPACK_ALIGNMENT, 4)
	gl.BindTexture(gl.TEXTURE_2D, t)

	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, int(filter))
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, int(filter))

	// TODO: Can we use glTexSubImage2D with linear filtering?

	// void texImage2D(GLenum target, GLint level, GLenum internalformat,
	//     GLsizei width, GLsizei height, GLint border, GLenum format,
	//     GLenum type, ArrayBufferView? pixels);
	var p interface{}
	if pixels != nil {
		p = pixels
	}
	gl.Call("texImage2D", gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, p)

	return Texture{t}, nil
}

func (c *Context) bindFramebuffer(f Framebuffer) {
	gl := c.gl
	if c.lastFramebuffer != f {
		gl.BindFramebuffer(gl.FRAMEBUFFER, f.Object)
		c.lastFramebuffer = f
	}
}

func (c *Context) FramebufferPixels(f Framebuffer, width, height int) ([]uint8, error) {
	gl := c.gl

	c.bindFramebuffer(f)

	pixels := js.Global.Get("Uint8Array").New(4 * width * height)
	gl.ReadPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels)
	if e := gl.GetError(); e != gl.NO_ERROR {
		return nil, errors.New(fmt.Sprintf("opengl: error: %d", e))
	}
	return pixels.Interface().([]uint8), nil
}

func (c *Context) BindTexture(t Texture) {
	gl := c.gl
	gl.BindTexture(gl.TEXTURE_2D, t.Object)
}

func (c *Context) DeleteTexture(t Texture) {
	gl := c.gl
	gl.DeleteTexture(t.Object)
}

func (c *Context) TexSubImage2D(p []uint8, width, height int) {
	gl := c.gl
	// void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
	//                    GLsizei width, GLsizei height,
	//                    GLenum format, GLenum type, ArrayBufferView? pixels);
	gl.Call("texSubImage2D", gl.TEXTURE_2D, 0, 0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, p)
}

func (c *Context) NewFramebuffer(t Texture) (Framebuffer, error) {
	gl := c.gl
	f := gl.CreateFramebuffer()
	c.bindFramebuffer(Framebuffer{f})

	gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, t.Object, 0)
	s := gl.CheckFramebufferStatus(gl.FRAMEBUFFER)
	if s != gl.FRAMEBUFFER_COMPLETE {
		return Framebuffer{nil}, errors.New(fmt.Sprintf("opengl: creating framebuffer failed: %d", s))
	}

	return Framebuffer{f}, nil
}

func (c *Context) SetViewport(f Framebuffer, width, height int) error {
	c.bindFramebuffer(f)
	gl := c.gl
	gl.Viewport(0, 0, width, height)
	return nil
}

func (c *Context) FillFramebuffer(r, g, b, a float64) error {
	// TODO: Use f?
	gl := c.gl
	gl.ClearColor(float32(r), float32(g), float32(b), float32(a))
	gl.Clear(gl.COLOR_BUFFER_BIT)
	return nil
}

func (c *Context) DeleteFramebuffer(f Framebuffer) {
	gl := c.gl
	gl.DeleteFramebuffer(f.Object)
}

func (c *Context) NewShader(shaderType ShaderType, source string) (Shader, error) {
	gl := c.gl
	s := gl.CreateShader(int(shaderType))
	if s == nil {
		return Shader{nil}, errors.New("opengl: glCreateShader failed")
	}

	gl.ShaderSource(s, source)
	gl.CompileShader(s)

	if !gl.GetShaderParameterb(s, gl.COMPILE_STATUS) {
		log := gl.GetShaderInfoLog(s)
		return Shader{nil}, errors.New(fmt.Sprintf("opengl: shader compile failed: %s", log))
	}
	return Shader{s}, nil
}

func (c *Context) DeleteShader(s Shader) {
	gl := c.gl
	gl.DeleteShader(s.Object)
}

func (c *Context) GlslHighpSupported() bool {
	gl := c.gl
	// headless-gl library may not define getShaderPrecisionFormat.
	if gl.Get("getShaderPrecisionFormat") == js.Undefined {
		return false
	}
	return gl.Call("getShaderPrecisionFormat", gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).Get("precision").Int() != 0
}

func (c *Context) NewProgram(shaders []Shader) (Program, error) {
	gl := c.gl
	p := gl.CreateProgram()
	if p == nil {
		return Program{nil}, errors.New("opengl: glCreateProgram failed")
	}
	p.Set("__ebiten_programId", c.lastProgramID)
	c.lastProgramID++

	for _, shader := range shaders {
		gl.AttachShader(p, shader.Object)
	}
	gl.LinkProgram(p)
	if !gl.GetProgramParameterb(p, gl.LINK_STATUS) {
		return Program{nil}, errors.New("opengl: program error")
	}
	return Program{p}, nil
}

func (c *Context) UseProgram(p Program) {
	gl := c.gl
	gl.UseProgram(p.Object)
}

func (c *Context) getUniformLocation(p Program, location string) uniformLocation {
	gl := c.gl
	return uniformLocation{gl.GetUniformLocation(p.Object, location)}
}

func (c *Context) UniformInt(p Program, location string, v int) {
	gl := c.gl
	l := c.locationCache.GetUniformLocation(c, p, location)
	gl.Uniform1i(l.Object, v)
}

func (c *Context) UniformFloats(p Program, location string, v []float32) {
	gl := c.gl
	l := c.locationCache.GetUniformLocation(c, p, location)
	switch len(v) {
	case 4:
		gl.Call("uniform4fv", l.Object, v)
	case 16:
		gl.UniformMatrix4fv(l.Object, false, v)
	default:
		panic("not reach")
	}
}

func (c *Context) getAttribLocation(p Program, location string) attribLocation {
	gl := c.gl
	return attribLocation(gl.GetAttribLocation(p.Object, location))
}

func (c *Context) VertexAttribPointer(p Program, location string, normalize bool, stride int, size int, v int) {
	gl := c.gl
	l := c.locationCache.GetAttribLocation(c, p, location)
	gl.VertexAttribPointer(int(l), size, gl.SHORT, normalize, stride, v)
}

func (c *Context) EnableVertexAttribArray(p Program, location string) {
	gl := c.gl
	l := c.locationCache.GetAttribLocation(c, p, location)
	gl.EnableVertexAttribArray(int(l))
}

func (c *Context) DisableVertexAttribArray(p Program, location string) {
	gl := c.gl
	l := c.locationCache.GetAttribLocation(c, p, location)
	gl.DisableVertexAttribArray(int(l))
}

func (c *Context) DeleteProgram(p Program) {
	gl := c.gl
	gl.DeleteProgram(p.Object)
}

func (c *Context) NewBuffer(bufferType BufferType, v interface{}, bufferUsage BufferUsage) Buffer {
	gl := c.gl
	b := gl.CreateBuffer()
	gl.BindBuffer(int(bufferType), b)
	gl.BufferData(int(bufferType), v, int(bufferUsage))
	return Buffer{b}
}

func (c *Context) BindElementArrayBuffer(b Buffer) {
	gl := c.gl
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, b.Object)
}

func (c *Context) BufferSubData(bufferType BufferType, data []int16) {
	gl := c.gl
	gl.BufferSubData(int(bufferType), 0, data)
}

func (c *Context) DeleteBuffer(b Buffer) {
	gl := c.gl
	gl.DeleteBuffer(b.Object)
}

func (c *Context) DrawElements(mode Mode, len int) {
	gl := c.gl
	gl.DrawElements(int(mode), len, gl.UNSIGNED_SHORT, 0)
}
<|endoftext|>


<commit_before>// An example FTP server build on top of graval. graval handles the details
// of the FTP protocol, we just provide a persistence driver for rackspace
// cloud files.
//
// If you're looking to create a custom graval driver, this example is a
// reasonable starting point. I suggest copying this file and changing the
// function bodies as required.
//
// USAGE:
//
//    go get github.com/yob/graval
//    go install github.com/yob/graval/graval-swift
//    export UserName=myusername
//    export ApiKey=myapikey
//    export AuthUrl="https://auth.api.rackspacecloud.com/v1.0"
//    export Container=my-container
//    ./bin/graval-swift
//
package main

import (
	"errors"
	"github.com/ncw/swift"
	"github.com/yob/graval"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// A minimal driver for graval that stores all data on rackspace cloudfiles. The
// authentication are ignored, any username and password will work.
//
// This really just exists as a minimal demonstration of the interface graval
// drivers are required to implement.
type SwiftDriver struct{
	connection *swift.Connection
	container  string
	user       string
}

func (driver *SwiftDriver) Authenticate(user string, pass string) bool {
	log.Printf("Authenticate: %s %s", user, pass)
	driver.user = user
	return true
}
func (driver *SwiftDriver) Bytes(path string) (bytes int) {
	path = scoped_path(driver.user, path)
	log.Printf("Bytes: %s", path)
	object, _, err := driver.connection.Object(driver.container, path)
	if err != nil {
		return -1
	}
	return int(object.Bytes)
}
func (driver *SwiftDriver) ModifiedTime(path string) (time.Time, error) {
	path = scoped_path(driver.user, path)
	log.Printf("ModifiedTime: %s", path)
	object, _, err := driver.connection.Object(driver.container, path)
	if err != nil {
		return time.Now(), err
	}
	return object.LastModified, nil
}
func (driver *SwiftDriver) ChangeDir(path string) bool {
	path = scoped_path(driver.user, path)
	if path == ("/"+driver.user) {
		return true
	}
	log.Printf("ChangeDir: %s", path)
	object, _, err := driver.connection.Object(driver.container, path)
	if err != nil {
		return false
	}
	return object.ContentType == "application/directory"
}
func (driver *SwiftDriver) DirContents(path string) (files []os.FileInfo) {
	path = scoped_path_with_trailing_slash(driver.user, path)
	log.Printf("DirContents: %s", path)
	opts    := &swift.ObjectsOpts{Prefix:path, Delimiter:'/'}
	objects, err := driver.connection.ObjectsAll(driver.container, opts)
	if err != nil {
		return // error connecting to cloud files
	}
	for _, object := range objects {
		tail     := strings.Replace(object.Name, path, "", 1)
        basename := strings.Split(tail, "/")[0]
		if object.ContentType == "application/directory" && object.SubDir == "" {
			files = append(files, graval.NewDirItem(basename))
		} else if object.ContentType != "application/directory"  {
			files = append(files, graval.NewFileItem(basename, int(object.Bytes)))
		}
	}
	return
}

func (driver *SwiftDriver) DeleteDir(path string) bool {
	path = scoped_path(driver.user, path)
	log.Printf("DeleteDir: %s", path)
	err := driver.connection.ObjectDelete(driver.container, path)
	if err != nil {
		return false
	}
	return true
}
func (driver *SwiftDriver) DeleteFile(path string) bool {
	path = scoped_path(driver.user, path)
	log.Printf("DeleteFile: %s", path)
	err := driver.connection.ObjectDelete(driver.container, path)
	if err != nil {
		return false
	}
	return true
}
func (driver *SwiftDriver) Rename(fromPath string, toPath string) bool {
	fromPath = scoped_path(driver.user, fromPath)
	toPath   = scoped_path(driver.user, toPath)
	log.Printf("Rename: %s %s", fromPath, toPath)
	return false
}
func (driver *SwiftDriver) MakeDir(path string) bool {
	path = scoped_path(driver.user, path)
	log.Printf("MakeDir: %s", path)
	opts    := &swift.ObjectsOpts{Prefix:path}
	objects, err := driver.connection.ObjectNames(driver.container, opts)
	if err != nil {
		return false // error connection to cloud files
	}
	if len(objects) > 0 {
		return false // the dir already exists
	}
	driver.connection.ObjectPutString(driver.container, path, "", "application/directory")
	return true
}
func (driver *SwiftDriver) GetFile(path string) (data string, err error) {
	path = scoped_path(driver.user, path)
	log.Printf("GetFile: %s", path)
	data, err = driver.connection.ObjectGetString(driver.container, path)
	if err != nil {
		return "", errors.New("foo")
	}
	return
}
func (driver *SwiftDriver) PutFile(destPath string, data io.Reader) bool {
	destPath = scoped_path(driver.user, destPath)
	log.Printf("PutFile: %s", destPath)
	contents, err := ioutil.ReadAll(data)
	if err != nil {
		return false
	}
	err = driver.connection.ObjectPutBytes(driver.container, destPath, contents, "application/octet-stream")
	if err != nil {
		return false
	}
	return true
}

func scoped_path_with_trailing_slash(user string, path string) string {
	path = scoped_path(user, path)
	if !strings.HasSuffix(path, "/") {
		path += "/"
	}
	if path == "/" {
		return ""
	}
	return path
}

func scoped_path(user string, path string) string {
	if path == "/" {
		path = ""
	}
	return filepath.Join("/", user, path)
}

// graval requires a factory that will create a new driver instance for each
// client connection. Generally the factory will be fairly minimal. This is
// a good place to read any required config for your driver.
type SwiftDriverFactory struct{}

func (factory *SwiftDriverFactory) NewDriver() (graval.FTPDriver, error) {
	driver := &SwiftDriver{}
	driver.container  = os.Getenv("Container")
	if driver.container == "" {
		return nil, errors.New("Container env variable not set")
	}
	driver.connection = &swift.Connection{
		UserName: os.Getenv("UserName"),
		ApiKey:   os.Getenv("ApiKey"),
		AuthUrl:  os.Getenv("AuthUrl"),
	}
	err := driver.connection.Authenticate()
	if err != nil {
		return nil, err
	}
	return driver, nil
}

// it's alive!
func main() {
	factory := &SwiftDriverFactory{}
	ftpServer := graval.NewFTPServer(&graval.FTPServerOpts{ Factory: factory })
	err := ftpServer.ListenAndServe()
	if err != nil {
		log.Fatal("Error starting server!")
		log.Fatal(err)
	}
}
<commit_msg>remove swift example<commit_after><|endoftext|>