<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.

package ebiten

import (
	"math"
	"sync/atomic"

	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/opengl"
	"github.com/hajimehoshi/ebiten/internal/restorable"
	"github.com/hajimehoshi/ebiten/internal/ui"
)

func newGraphicsContext(f func(*Image) error) *graphicsContext {
	return &graphicsContext{
		f: f,
	}
}

type graphicsContext struct {
	f           func(*Image) error
	offscreen   *Image
	offscreen2  *Image // TODO: better name
	screen      *Image
	screenScale float64
	initialized int32
	invalidated bool // browser only
}

func (c *graphicsContext) GLContext() *opengl.Context {
	if atomic.LoadInt32(&c.initialized) == 0 {
		return nil
	}
	return ui.GLContext()
}

func (c *graphicsContext) Invalidate() {
	// Note that this is called only on browsers so far.
	// TODO: On mobiles, this function is not called and instead IsTexture is called
	// to detect if the context is lost. This is simple but might not work on some platforms.
	// Should Invalidate be called explicitly?
	c.invalidated = true
}

func (c *graphicsContext) SetSize(screenWidth, screenHeight int, screenScale float64) error {
	if c.screen != nil {
		if err := c.screen.Dispose(); err != nil {
			return err
		}
	}
	if c.offscreen != nil {
		if err := c.offscreen.Dispose(); err != nil {
			return err
		}
	}
	if c.offscreen2 != nil {
		if err := c.offscreen2.Dispose(); err != nil {
			return err
		}
	}
	offscreen, err := newVolatileImage(screenWidth, screenHeight, FilterNearest)
	if err != nil {
		return err
	}

	intScreenScale := int(math.Ceil(screenScale))
	w := screenWidth * intScreenScale
	h := screenHeight * intScreenScale
	offscreen2, err := newVolatileImage(w, h, FilterLinear)
	if err != nil {
		return err
	}

	w = int(float64(screenWidth) * screenScale)
	h = int(float64(screenHeight) * screenScale)
	c.screen, err = newImageWithScreenFramebuffer(w, h)
	if err != nil {
		return err
	}
	if err := c.screen.Clear(); err != nil {
		return err
	}

	c.offscreen = offscreen
	c.offscreen2 = offscreen2
	c.screenScale = screenScale
	return nil
}

func (c *graphicsContext) initializeIfNeeded(context *opengl.Context) error {
	if atomic.LoadInt32(&c.initialized) == 0 {
		if err := graphics.Reset(context); err != nil {
			return err
		}
		atomic.StoreInt32(&c.initialized, 1)
	}
	r, err := c.needsRestoring(context)
	if err != nil {
		return err
	}
	if !r {
		return nil
	}
	if err := c.restore(context); err != nil {
		return err
	}
	return nil
}

func drawWithFittingScale(dst *Image, src *Image) error {
	wd, hd := dst.Size()
	ws, hs := src.Size()
	sw := float64(wd) / float64(ws)
	sh := float64(hd) / float64(hs)
	op := &DrawImageOptions{}
	op.GeoM.Scale(sw, sh)
	if err := dst.DrawImage(src, op); err != nil {
		return err
	}
	return nil
}

func (c *graphicsContext) drawToDefaultRenderTarget(context *opengl.Context) error {
	if err := c.screen.Clear(); err != nil {
		return err
	}
	if err := drawWithFittingScale(c.screen, c.offscreen2); err != nil {
		return err
	}
	if err := graphics.FlushCommands(context); err != nil {
		return err
	}
	return nil
}

func (c *graphicsContext) UpdateAndDraw(context *opengl.Context, updateCount int) error {
	if err := c.initializeIfNeeded(context); err != nil {
		return err
	}
	if err := restorable.ResolveStalePixels(context); err != nil {
		return err
	}
	for i := 0; i < updateCount; i++ {
		restorable.ClearVolatileImages()
		setRunningSlowly(i < updateCount-1)
		if err := c.f(c.offscreen); err != nil {
			return err
		}
	}
	if 0 < updateCount {
		if err := drawWithFittingScale(c.offscreen2, c.offscreen); err != nil {
			return err
		}
	}
	if err := c.drawToDefaultRenderTarget(context); err != nil {
		return err
	}
	return nil
}

func (c *graphicsContext) restore(context *opengl.Context) error {
	if err := graphics.Reset(context); err != nil {
		return err
	}
	if err := restorable.Restore(context); err != nil {
		return err
	}
	c.invalidated = false
	return nil
}
<commit_msg>graphics: Refactoring: no needed returning values<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.

package ebiten

import (
	"math"
	"sync/atomic"

	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/opengl"
	"github.com/hajimehoshi/ebiten/internal/restorable"
	"github.com/hajimehoshi/ebiten/internal/ui"
)

func newGraphicsContext(f func(*Image) error) *graphicsContext {
	return &graphicsContext{
		f: f,
	}
}

type graphicsContext struct {
	f           func(*Image) error
	offscreen   *Image
	offscreen2  *Image // TODO: better name
	screen      *Image
	screenScale float64
	initialized int32
	invalidated bool // browser only
}

func (c *graphicsContext) GLContext() *opengl.Context {
	if atomic.LoadInt32(&c.initialized) == 0 {
		return nil
	}
	return ui.GLContext()
}

func (c *graphicsContext) Invalidate() {
	// Note that this is called only on browsers so far.
	// TODO: On mobiles, this function is not called and instead IsTexture is called
	// to detect if the context is lost. This is simple but might not work on some platforms.
	// Should Invalidate be called explicitly?
	c.invalidated = true
}

func (c *graphicsContext) SetSize(screenWidth, screenHeight int, screenScale float64) error {
	if c.screen != nil {
		if err := c.screen.Dispose(); err != nil {
			return err
		}
	}
	if c.offscreen != nil {
		if err := c.offscreen.Dispose(); err != nil {
			return err
		}
	}
	if c.offscreen2 != nil {
		if err := c.offscreen2.Dispose(); err != nil {
			return err
		}
	}
	offscreen, err := newVolatileImage(screenWidth, screenHeight, FilterNearest)
	if err != nil {
		return err
	}

	intScreenScale := int(math.Ceil(screenScale))
	w := screenWidth * intScreenScale
	h := screenHeight * intScreenScale
	offscreen2, err := newVolatileImage(w, h, FilterLinear)
	if err != nil {
		return err
	}

	w = int(float64(screenWidth) * screenScale)
	h = int(float64(screenHeight) * screenScale)
	c.screen, err = newImageWithScreenFramebuffer(w, h)
	if err != nil {
		return err
	}
	if err := c.screen.Clear(); err != nil {
		return err
	}

	c.offscreen = offscreen
	c.offscreen2 = offscreen2
	c.screenScale = screenScale
	return nil
}

func (c *graphicsContext) initializeIfNeeded(context *opengl.Context) error {
	if atomic.LoadInt32(&c.initialized) == 0 {
		if err := graphics.Reset(context); err != nil {
			return err
		}
		atomic.StoreInt32(&c.initialized, 1)
	}
	r, err := c.needsRestoring(context)
	if err != nil {
		return err
	}
	if !r {
		return nil
	}
	if err := c.restore(context); err != nil {
		return err
	}
	return nil
}

func drawWithFittingScale(dst *Image, src *Image) {
	wd, hd := dst.Size()
	ws, hs := src.Size()
	sw := float64(wd) / float64(ws)
	sh := float64(hd) / float64(hs)
	op := &DrawImageOptions{}
	op.GeoM.Scale(sw, sh)
	_ = dst.DrawImage(src, op)
}

func (c *graphicsContext) drawToDefaultRenderTarget(context *opengl.Context) error {
	_ = c.screen.Clear()
	drawWithFittingScale(c.screen, c.offscreen2)
	if err := graphics.FlushCommands(context); err != nil {
		return err
	}
	return nil
}

func (c *graphicsContext) UpdateAndDraw(context *opengl.Context, updateCount int) error {
	if err := c.initializeIfNeeded(context); err != nil {
		return err
	}
	// TODO: Is it OK to restore images here? The images can be in 'stale' state after c.f().
	if err := restorable.ResolveStalePixels(context); err != nil {
		return err
	}
	for i := 0; i < updateCount; i++ {
		restorable.ClearVolatileImages()
		setRunningSlowly(i < updateCount-1)
		if err := c.f(c.offscreen); err != nil {
			return err
		}
	}
	if 0 < updateCount {
		drawWithFittingScale(c.offscreen2, c.offscreen)
	}
	if err := c.drawToDefaultRenderTarget(context); err != nil {
		return err
	}
	return nil
}

func (c *graphicsContext) restore(context *opengl.Context) error {
	if err := graphics.Reset(context); err != nil {
		return err
	}
	if err := restorable.Restore(context); err != nil {
		return err
	}
	c.invalidated = false
	return nil
}
<|endoftext|>


<commit_before>package node

import (
	"fmt"
	"math"
	"strings"

	"github.com/philharnish/forge/src/data/graph/bloom/mask"
	"github.com/philharnish/forge/src/data/graph/bloom/weight"
)

type Node struct {
	// Non-zero when this node is a match.
	MatchWeight weight.Weight
	// Maximum weight for outgoing edges.
	MaxWeight weight.Weight
	// BitMask for outgoing edges.
	ProvideMask mask.Mask
	// BitMask for edges which lead to matching Nodes.
	RequireMask mask.Mask
	// BitMask for distances matching Nodes.
	LengthsMask mask.Mask
}

type NodeIterator interface {
	Items(acceptor NodeAcceptor) NodeItems
	Root() *Node
	String() string
}

type NodeItems interface {
	HasNext() bool
	Next() (string, NodeIterator)
}

type NodeMetadataProvider interface {
	Metadata(path string) []weight.WeightedString
}

func NewNode(matchWeight ...weight.Weight) *Node {
	result := &Node{
		RequireMask: mask.UNSET,
	}
	if len(matchWeight) == 1 {
		result.Match(matchWeight[0])
	}
	return result
}

func (node *Node) Copy() *Node {
	return &Node{
		MatchWeight: node.MatchWeight,
		MaxWeight:   node.MaxWeight,
		ProvideMask: node.ProvideMask,
		RequireMask: node.RequireMask,
		LengthsMask: node.LengthsMask,
	}
}

func (node *Node) Matches() bool {
	return node.LengthsMask&mask.Mask(0b1) == 1
}

func (node *Node) Match(weight weight.Weight) {
	if node.MatchWeight != 0.0 {
		panic(fmt.Errorf("duplicate attempts to set match weight (%f and %f)",
			node.MatchWeight, weight))
	}
	node.MatchWeight = weight
	node.LengthsMask |= 0b1 // Match at current position
	node.Weight(weight)
}

func (node *Node) MaskEdgeMask(edgeMask mask.Mask) {
	// Provide anything the edge provides.
	node.ProvideMask |= edgeMask
	// Require anything the edge provides.
	node.RequireMask &= edgeMask
}

func (node *Node) MaskEdgeMaskToChild(edgeMask mask.Mask, child *Node) {
	oneBitRemoved := edgeMask & (edgeMask - 1)
	if oneBitRemoved == 0 {
		// The path to child has only one option which implies path is required.
		node.maskMaskDistanceToChild(edgeMask, 1, child)
	} else {
		// Inherit requirements from child.
		node.MaskDistanceToChild(1, child)
		node.ProvideMask |= edgeMask
		// If node's RequireMask is still unset...
		if node.RequireMask == mask.UNSET {
			// Clear it because multiple runes implies path to child is not required.
			node.RequireMask = mask.NONE
		}
	}
}

func (node *Node) MaskDistanceToChild(distance int, child *Node) {
	if distance == 0 {
		// Optimized path for zero-length paths.
		node.Union(child)
		return
	}
	// Inherit maxWeight.
	node.Weight(child.MaxWeight)
	// Provide anything ANY children provides.
	node.ProvideMask |= mask.Mask(child.ProvideMask)
	// Inherit matching lengths.
	node.LengthsMask |= mask.ShiftLength(child.LengthsMask, distance)
	if child.RequireMask == mask.UNSET {
		// Ignore the child's require mask if it is UNSET.
	} else if child.Matches() {
		// Since the child is a match no requirements are inherited.
	} else {
		// Require anything ALL children requires.
		node.RequireMask &= child.RequireMask
	}
}

func (node *Node) MaskPath(path string) error {
	edgeMask, runeLength, err := mask.EdgeMaskAndLength(path)
	if err != nil {
		return err
	}
	node.MaskEdgeMask(edgeMask)
	// Set match at the end of path.
	node.LengthsMask |= 1 << runeLength
	return nil
}

func (node *Node) MaskPathToChild(path string, child *Node) error {
	edgeMask, runeLength, err := mask.EdgeMaskAndLength(path)
	if err != nil {
		return err
	}
	return node.maskMaskDistanceToChild(edgeMask, runeLength, child)
}

func (node *Node) MaskPrependChild(child *Node) {
	// Provide anything the child provides.
	node.ProvideMask |= child.ProvideMask
	if node.Matches() {
		// If the (old) end-point was a match then the prepend requirements
		// are the only requirements which matter.
		node.RequireMask = child.RequireMask
	} else if node.RequireMask == mask.UNSET {
		node.RequireMask = child.RequireMask
	} else {
		// Require anything the child requires.
		node.RequireMask |= child.RequireMask
	}
	node.LengthsMask = mask.ConcatLengths(child.LengthsMask, node.LengthsMask)
	if !node.Matches() {
		node.MatchWeight = 0
	}
}

func (node *Node) maskMaskDistanceToChild(edgeMask mask.Mask, distance int, child *Node) error {
	// Inherit maxWeight.
	node.Weight(child.MaxWeight)
	if distance == 0 {
		// Optimized path for zero-length paths.
		node.Union(child)
	} else {
		// Provide anything ANY children provides (including the edge itself).
		node.ProvideMask |= edgeMask | child.ProvideMask
		// Inherit matching lengths.
		node.LengthsMask |= mask.ShiftLength(child.LengthsMask, distance)
		if child.RequireMask == mask.UNSET {
			// Ignore the child's require mask if it is UNSET.
			node.RequireMask &= edgeMask
		} else if child.Matches() {
			// Since the child is a match only the edge is required.
			node.RequireMask &= edgeMask
		} else {
			// Require anything ALL children requires (including the edge itself).
			node.RequireMask &= edgeMask | child.RequireMask
		}
	}
	return nil
}

func (node *Node) RepeatLengthMask(interval int) {
	if interval < 0 {
		node.LengthsMask = mask.ConcatInfinitely(node.LengthsMask)
	} else {
		node.LengthsMask = mask.RepeatLengths(node.LengthsMask, interval)
	}
}

func (node *Node) Intersection(other *Node) *Node {
	// Copy weights using MIN operation.
	node.MatchWeight = math.Min(node.MatchWeight, other.MatchWeight)
	node.MaxWeight = math.Min(node.MaxWeight, other.MaxWeight)
	node.ProvideMask &= other.ProvideMask // Only provide what everyone can.
	// Require whatever anyone requires.
	node.RequireMask |= other.RequireMask
	if node.RequireMask == mask.UNSET {
		// Exit blocked; only keep lowest bit on LengthsMask.
		node.LengthsMask &= other.LengthsMask & mask.Mask(0b1)
	} else if node.RequireMask == node.RequireMask&node.ProvideMask {
		// Only consider aligned matches.
		node.LengthsMask &= other.LengthsMask
	} else {
		// Unsatisfiable requirements
		node.LengthsMask = mask.Mask(0)
	}
	return node
}

func (node *Node) Union(other *Node) *Node {
	// Copy weights using MAX operation.
	node.MatchWeight = math.Max(node.MatchWeight, other.MatchWeight)
	node.MaxWeight = math.Max(node.MaxWeight, other.MaxWeight)
	node.ProvideMask |= other.ProvideMask // Provide anything anyone can.
	node.RequireMask &= other.RequireMask // Only require whatever everyone requires.
	node.LengthsMask |= other.LengthsMask // Consider either matches.
	return node
}

func (node *Node) Weight(weight weight.Weight) {
	node.MaxWeight = math.Max(node.MaxWeight, weight)
}

func (node *Node) String() string {
	return Format("Node", node)
}

func (node *Node) Root() *Node {
	return node
}

func (node *Node) Items(acceptor NodeAcceptor) NodeItems {
	return node
}

func (node *Node) HasNext() bool {
	return false
}

func (node *Node) Next() (string, NodeIterator) {
	panic("Node has no children")
}

// Evaluate the `Weight` for a `node` at `path`.
// Typically, when the result is non-zero the caller should immediately
// return Cursor{node, path}
type NodeAcceptor = func(path string, node *Node) weight.Weight

func NodeAcceptAll(path string, node *Node) weight.Weight {
	return 1.0
}

func NodeAcceptNone(path string, node *Node) weight.Weight {
	return 0.0
}

func Format(name string, node *Node) string {
	parts := []string{}
	if node.Matches() {
		parts = append(parts, weight.String(node.MatchWeight))
	}
	acc := mask.MaskString(node.ProvideMask, node.RequireMask)
	if len(acc) > 0 {
		parts = append(parts, acc)
	}
	acc = mask.LengthString(node.LengthsMask)
	if len(acc) > 0 {
		parts = append(parts, acc)
	}
	acc = strings.Join(parts, " ")
	if len(acc) > 0 {
		return name + ": " + acc
	}
	return name
}
<commit_msg>Add NodeMetadata type.<commit_after>package node

import (
	"fmt"
	"math"
	"strings"

	"github.com/philharnish/forge/src/data/graph/bloom/mask"
	"github.com/philharnish/forge/src/data/graph/bloom/weight"
)

type Node struct {
	// Non-zero when this node is a match.
	MatchWeight weight.Weight
	// Maximum weight for outgoing edges.
	MaxWeight weight.Weight
	// BitMask for outgoing edges.
	ProvideMask mask.Mask
	// BitMask for edges which lead to matching Nodes.
	RequireMask mask.Mask
	// BitMask for distances matching Nodes.
	LengthsMask mask.Mask
}

type NodeIterator interface {
	Items(acceptor NodeAcceptor) NodeItems
	Root() *Node
	String() string
}

type NodeItems interface {
	HasNext() bool
	Next() (string, NodeIterator)
}

type NodeMetadata = []*weight.WeightedString

type NodeMetadataProvider interface {
	Metadata(paths []string, items []NodeItems) NodeMetadata
}

func NewNode(matchWeight ...weight.Weight) *Node {
	result := &Node{
		RequireMask: mask.UNSET,
	}
	if len(matchWeight) == 1 {
		result.Match(matchWeight[0])
	}
	return result
}

func (node *Node) Copy() *Node {
	return &Node{
		MatchWeight: node.MatchWeight,
		MaxWeight:   node.MaxWeight,
		ProvideMask: node.ProvideMask,
		RequireMask: node.RequireMask,
		LengthsMask: node.LengthsMask,
	}
}

func (node *Node) Matches() bool {
	return node.LengthsMask&mask.Mask(0b1) == 1
}

func (node *Node) Match(weight weight.Weight) {
	if node.MatchWeight != 0.0 {
		panic(fmt.Errorf("duplicate attempts to set match weight (%f and %f)",
			node.MatchWeight, weight))
	}
	node.MatchWeight = weight
	node.LengthsMask |= 0b1 // Match at current position
	node.Weight(weight)
}

func (node *Node) MaskEdgeMask(edgeMask mask.Mask) {
	// Provide anything the edge provides.
	node.ProvideMask |= edgeMask
	// Require anything the edge provides.
	node.RequireMask &= edgeMask
}

func (node *Node) MaskEdgeMaskToChild(edgeMask mask.Mask, child *Node) {
	oneBitRemoved := edgeMask & (edgeMask - 1)
	if oneBitRemoved == 0 {
		// The path to child has only one option which implies path is required.
		node.maskMaskDistanceToChild(edgeMask, 1, child)
	} else {
		// Inherit requirements from child.
		node.MaskDistanceToChild(1, child)
		node.ProvideMask |= edgeMask
		// If node's RequireMask is still unset...
		if node.RequireMask == mask.UNSET {
			// Clear it because multiple runes implies path to child is not required.
			node.RequireMask = mask.NONE
		}
	}
}

func (node *Node) MaskDistanceToChild(distance int, child *Node) {
	if distance == 0 {
		// Optimized path for zero-length paths.
		node.Union(child)
		return
	}
	// Inherit maxWeight.
	node.Weight(child.MaxWeight)
	// Provide anything ANY children provides.
	node.ProvideMask |= mask.Mask(child.ProvideMask)
	// Inherit matching lengths.
	node.LengthsMask |= mask.ShiftLength(child.LengthsMask, distance)
	if child.RequireMask == mask.UNSET {
		// Ignore the child's require mask if it is UNSET.
	} else if child.Matches() {
		// Since the child is a match no requirements are inherited.
	} else {
		// Require anything ALL children requires.
		node.RequireMask &= child.RequireMask
	}
}

func (node *Node) MaskPath(path string) error {
	edgeMask, runeLength, err := mask.EdgeMaskAndLength(path)
	if err != nil {
		return err
	}
	node.MaskEdgeMask(edgeMask)
	// Set match at the end of path.
	node.LengthsMask |= 1 << runeLength
	return nil
}

func (node *Node) MaskPathToChild(path string, child *Node) error {
	edgeMask, runeLength, err := mask.EdgeMaskAndLength(path)
	if err != nil {
		return err
	}
	return node.maskMaskDistanceToChild(edgeMask, runeLength, child)
}

func (node *Node) MaskPrependChild(child *Node) {
	// Provide anything the child provides.
	node.ProvideMask |= child.ProvideMask
	if node.Matches() {
		// If the (old) end-point was a match then the prepend requirements
		// are the only requirements which matter.
		node.RequireMask = child.RequireMask
	} else if node.RequireMask == mask.UNSET {
		node.RequireMask = child.RequireMask
	} else {
		// Require anything the child requires.
		node.RequireMask |= child.RequireMask
	}
	node.LengthsMask = mask.ConcatLengths(child.LengthsMask, node.LengthsMask)
	if !node.Matches() {
		node.MatchWeight = 0
	}
}

func (node *Node) maskMaskDistanceToChild(edgeMask mask.Mask, distance int, child *Node) error {
	// Inherit maxWeight.
	node.Weight(child.MaxWeight)
	if distance == 0 {
		// Optimized path for zero-length paths.
		node.Union(child)
	} else {
		// Provide anything ANY children provides (including the edge itself).
		node.ProvideMask |= edgeMask | child.ProvideMask
		// Inherit matching lengths.
		node.LengthsMask |= mask.ShiftLength(child.LengthsMask, distance)
		if child.RequireMask == mask.UNSET {
			// Ignore the child's require mask if it is UNSET.
			node.RequireMask &= edgeMask
		} else if child.Matches() {
			// Since the child is a match only the edge is required.
			node.RequireMask &= edgeMask
		} else {
			// Require anything ALL children requires (including the edge itself).
			node.RequireMask &= edgeMask | child.RequireMask
		}
	}
	return nil
}

func (node *Node) RepeatLengthMask(interval int) {
	if interval < 0 {
		node.LengthsMask = mask.ConcatInfinitely(node.LengthsMask)
	} else {
		node.LengthsMask = mask.RepeatLengths(node.LengthsMask, interval)
	}
}

func (node *Node) Intersection(other *Node) *Node {
	// Copy weights using MIN operation.
	node.MatchWeight = math.Min(node.MatchWeight, other.MatchWeight)
	node.MaxWeight = math.Min(node.MaxWeight, other.MaxWeight)
	node.ProvideMask &= other.ProvideMask // Only provide what everyone can.
	// Require whatever anyone requires.
	node.RequireMask |= other.RequireMask
	if node.RequireMask == mask.UNSET {
		// Exit blocked; only keep lowest bit on LengthsMask.
		node.LengthsMask &= other.LengthsMask & mask.Mask(0b1)
	} else if node.RequireMask == node.RequireMask&node.ProvideMask {
		// Only consider aligned matches.
		node.LengthsMask &= other.LengthsMask
	} else {
		// Unsatisfiable requirements
		node.LengthsMask = mask.Mask(0)
	}
	return node
}

func (node *Node) Union(other *Node) *Node {
	// Copy weights using MAX operation.
	node.MatchWeight = math.Max(node.MatchWeight, other.MatchWeight)
	node.MaxWeight = math.Max(node.MaxWeight, other.MaxWeight)
	node.ProvideMask |= other.ProvideMask // Provide anything anyone can.
	node.RequireMask &= other.RequireMask // Only require whatever everyone requires.
	node.LengthsMask |= other.LengthsMask // Consider either matches.
	return node
}

func (node *Node) Weight(weight weight.Weight) {
	node.MaxWeight = math.Max(node.MaxWeight, weight)
}

func (node *Node) String() string {
	return Format("Node", node)
}

func (node *Node) Root() *Node {
	return node
}

func (node *Node) Items(acceptor NodeAcceptor) NodeItems {
	return node
}

func (node *Node) HasNext() bool {
	return false
}

func (node *Node) Next() (string, NodeIterator) {
	panic("Node has no children")
}

// Evaluate the `Weight` for a `node` at `path`.
// Typically, when the result is non-zero the caller should immediately
// return Cursor{node, path}
type NodeAcceptor = func(path string, node *Node) weight.Weight

func NodeAcceptAll(path string, node *Node) weight.Weight {
	return 1.0
}

func NodeAcceptNone(path string, node *Node) weight.Weight {
	return 0.0
}

func Format(name string, node *Node) string {
	parts := []string{}
	if node.Matches() {
		parts = append(parts, weight.String(node.MatchWeight))
	}
	acc := mask.MaskString(node.ProvideMask, node.RequireMask)
	if len(acc) > 0 {
		parts = append(parts, acc)
	}
	acc = mask.LengthString(node.LengthsMask)
	if len(acc) > 0 {
		parts = append(parts, acc)
	}
	acc = strings.Join(parts, " ")
	if len(acc) > 0 {
		return name + ": " + acc
	}
	return name
}
<|endoftext|>


<commit_before>package cluster

import (
	"bytes"
	"encoding/json"
	"net/http"
	"time"

	"github.com/pkg/errors"
	"github.com/rancher/norman/api/access"
	"github.com/rancher/norman/httperror"
	"github.com/rancher/norman/types"
	v3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	v3client "github.com/rancher/rancher/pkg/client/generated/management/v3"
	mgmtv3 "github.com/rancher/rancher/pkg/generated/norman/management.cattle.io/v3"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func (a ActionHandler) RotateEncryptionKey(actionName string, action *types.Action, apiContext *types.APIContext) error {
	response := map[string]interface{}{
		"type": v3client.RotateEncryptionKeyOutputType,
		v3client.RotateEncryptionKeyOutputFieldMessage: "starting rotate encryption key",
	}

	var mgmtCluster mgmtv3.Cluster
	if err := access.ByID(apiContext, apiContext.Version, apiContext.Type, apiContext.ID, &mgmtCluster); err != nil {
		response[v3client.RotateEncryptionKeyOutputFieldMessage] = "cluster does not exist"
		apiContext.WriteResponse(http.StatusBadRequest, response)
		return errors.Wrapf(err, "failed to get cluster by ID %s", apiContext.ID)
	}

	cluster, err := a.ClusterClient.Get(apiContext.ID, v1.GetOptions{})
	if err != nil {
		response[v3client.RotateEncryptionKeyOutputFieldMessage] = "cluster does not exist"
		apiContext.WriteResponse(http.StatusBadRequest, response)
		return errors.Wrapf(err, "failed to get cluster by ID %s", apiContext.ID)
	}

	if err := checkEncryptionConfig(cluster); err != nil {
		return httperror.NewAPIError(httperror.InvalidAction, err.Error())
	}

	cluster.Spec.RancherKubernetesEngineConfig.RotateEncryptionKey = true
	if _, err := a.ClusterClient.Update(cluster); err != nil {
		response[v3client.RotateEncryptionKeyOutputFieldMessage] = "failed to update cluster object"
		apiContext.WriteResponse(http.StatusInternalServerError, response)
		return errors.Wrapf(err, "unable to update cluster %s", cluster.Name)
	}

	res, err := json.Marshal(response)
	if err != nil {
		return err
	}

	apiContext.Response.Header().Set("Content-Type", "application/json")
	http.ServeContent(apiContext.Response, apiContext.Request, v3.ClusterActionRotateEncryptionKey, time.Now(), bytes.NewReader(res))
	return nil
}

// checkEncryptionConfig validates that the secrets encryption is both enabled and not custom.
func checkEncryptionConfig(c *v3.Cluster) error {
	if c.Spec.RancherKubernetesEngineConfig.Services.KubeAPI.SecretsEncryptionConfig == nil {
		return errors.New("secrets encryption configuration is not defined")
	}
	if !c.Spec.RancherKubernetesEngineConfig.Services.KubeAPI.SecretsEncryptionConfig.Enabled {
		return errors.New("secrets encryption is disabled")
	}
	if c.Spec.RancherKubernetesEngineConfig.Services.KubeAPI.SecretsEncryptionConfig.CustomConfig != nil {
		return errors.New("custom encryption configuration is not supported for key rotation action")
	}
	return nil
}
<commit_msg>guard against cluster updating in rotateEncryptionKey action handler<commit_after>package cluster

import (
	"bytes"
	"encoding/json"
	"net/http"
	"time"

	"github.com/pkg/errors"
	"github.com/rancher/norman/api/access"
	"github.com/rancher/norman/httperror"
	"github.com/rancher/norman/types"
	v3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	v3client "github.com/rancher/rancher/pkg/client/generated/management/v3"
	mgmtv3 "github.com/rancher/rancher/pkg/generated/norman/management.cattle.io/v3"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func (a ActionHandler) RotateEncryptionKey(actionName string, action *types.Action, apiContext *types.APIContext) error {
	response := map[string]interface{}{
		"type": v3client.RotateEncryptionKeyOutputType,
		v3client.RotateEncryptionKeyOutputFieldMessage: "starting rotate encryption key",
	}

	var mgmtCluster mgmtv3.Cluster
	if err := access.ByID(apiContext, apiContext.Version, apiContext.Type, apiContext.ID, &mgmtCluster); err != nil {
		response[v3client.RotateEncryptionKeyOutputFieldMessage] = "cluster does not exist"
		apiContext.WriteResponse(http.StatusBadRequest, response)
		return errors.Wrapf(err, "failed to get cluster by ID %s", apiContext.ID)
	}

	cluster, err := a.ClusterClient.Get(apiContext.ID, v1.GetOptions{})
	if err != nil {
		response[v3client.RotateEncryptionKeyOutputFieldMessage] = "cluster does not exist"
		apiContext.WriteResponse(http.StatusBadRequest, response)
		return errors.Wrapf(err, "failed to get cluster by ID %s", apiContext.ID)
	}

	if err := checkEncryptionConfig(cluster); err != nil {
		return httperror.NewAPIError(httperror.InvalidAction, err.Error())
	}

	if !v3.ClusterConditionUpdated.IsTrue(cluster) {
		return httperror.NewAPIError(httperror.InvalidAction, "cluster is in updating state")
	}

	cluster.Spec.RancherKubernetesEngineConfig.RotateEncryptionKey = true
	if _, err := a.ClusterClient.Update(cluster); err != nil {
		response[v3client.RotateEncryptionKeyOutputFieldMessage] = "failed to update cluster object"
		apiContext.WriteResponse(http.StatusInternalServerError, response)
		return errors.Wrapf(err, "unable to update cluster %s", cluster.Name)
	}

	res, err := json.Marshal(response)
	if err != nil {
		return err
	}

	apiContext.Response.Header().Set("Content-Type", "application/json")
	http.ServeContent(apiContext.Response, apiContext.Request, v3.ClusterActionRotateEncryptionKey, time.Now(), bytes.NewReader(res))
	return nil
}

// checkEncryptionConfig validates that the secrets encryption is both enabled and not custom.
func checkEncryptionConfig(c *v3.Cluster) error {
	if c.Spec.RancherKubernetesEngineConfig.Services.KubeAPI.SecretsEncryptionConfig == nil {
		return errors.New("secrets encryption configuration is not defined")
	}
	if !c.Spec.RancherKubernetesEngineConfig.Services.KubeAPI.SecretsEncryptionConfig.Enabled {
		return errors.New("secrets encryption is disabled")
	}
	if c.Spec.RancherKubernetesEngineConfig.Services.KubeAPI.SecretsEncryptionConfig.CustomConfig != nil {
		return errors.New("custom encryption configuration is not supported for key rotation action")
	}
	return nil
}
<|endoftext|>


<commit_before>// This example shows how to use USART as serial console.
// It uses PA2, PA3 pins as Tx, Rx that are by default connected to ST-LINK
// Virtual Com Port.
package main

import (
	"delay"
	"fmt"
	"rtos"

	"stm32/f4/gpio"
	"stm32/f4/irqs"
	"stm32/f4/periph"
	"stm32/f4/setup"
	"stm32/f4/usarts"
	"stm32/serial"
	"stm32/usart"
)

const (
	Green = 5
)

var (
	leds = gpio.A
	udev = usarts.USART2
	s    = serial.New(udev, 80, 8)
)

func init() {
	setup.Performance84(8)

	periph.AHB1ClockEnable(periph.GPIOA)
	periph.AHB1Reset(periph.GPIOA)

	leds.SetMode(Green, gpio.Out)

	periph.APB1ClockEnable(periph.USART2)
	periph.APB1Reset(periph.USART2)

	port, tx, rx := gpio.A, uint(2), uint(3)

	port.SetMode(tx, gpio.Alt)
	port.SetOutType(tx, gpio.PushPull)
	port.SetPull(tx, gpio.PullUp)
	port.SetOutSpeed(tx, gpio.Fast)
	port.SetAltFunc(tx, gpio.USART2)
	port.SetMode(rx, gpio.Alt)
	port.SetAltFunc(rx, gpio.USART2)

	udev.SetBaudRate(115200, setup.APB1Clk)
	udev.SetWordLen(usart.Bits8)
	udev.SetParity(usart.None)
	udev.SetStopBits(usart.Stop1b)
	udev.SetMode(usart.Tx | usart.Rx)
	udev.EnableIRQs(usart.RxNotEmptyIRQ)
	udev.Enable()

	rtos.IRQ(irqs.USART2).UseHandler(sirq)
	rtos.IRQ(irqs.USART2).Enable()

	s.SetUnix(true)
}

func blink(c uint, d int) {
	leds.SetBit(c)
	if d > 0 {
		delay.Millisec(d)
	} else {
		delay.Loop(-1e4 * d)
	}
	leds.ClearBit(c)
}

func sirq() {
	s.IRQ()
}

func checkErr(err error) {
	if err != nil {
		s.WriteString("\nError: ")
		s.WriteString(err.Error())
		s.WriteByte('\n')
	}
}

func main() {
	i := -10
	u := uint(12)
	fmt.Fprint(s, "i = ", i, " u = ", u, "\n")
	sli := []int{1, 2, 3}
	b := true
	k, _ := fmt.Fprint(s, "b = ", b, " nil = ", nil, "\n")
	i = sli[k]
	fmt.Fprint(s, "sli[2] = ", i, "\n")
}
<commit_msg>examples: FormatFloat...<commit_after>// This example shows how to use USART as serial console.
// It uses PA2, PA3 pins as Tx, Rx that are by default connected to ST-LINK
// Virtual Com Port.
package main

import (
	"delay"
	"fmt"
	"rtos"
	"strconv"

	"stm32/f4/gpio"
	"stm32/f4/irqs"
	"stm32/f4/periph"
	"stm32/f4/setup"
	"stm32/f4/usarts"
	"stm32/serial"
	"stm32/usart"
)

const (
	Green = 5
)

var (
	leds = gpio.A
	udev = usarts.USART2
	s    = serial.New(udev, 80, 8)
)

func init() {
	setup.Performance84(8)

	periph.AHB1ClockEnable(periph.GPIOA)
	periph.AHB1Reset(periph.GPIOA)

	leds.SetMode(Green, gpio.Out)

	periph.APB1ClockEnable(periph.USART2)
	periph.APB1Reset(periph.USART2)

	port, tx, rx := gpio.A, uint(2), uint(3)

	port.SetMode(tx, gpio.Alt)
	port.SetOutType(tx, gpio.PushPull)
	port.SetPull(tx, gpio.PullUp)
	port.SetOutSpeed(tx, gpio.Fast)
	port.SetAltFunc(tx, gpio.USART2)
	port.SetMode(rx, gpio.Alt)
	port.SetAltFunc(rx, gpio.USART2)

	udev.SetBaudRate(115200, setup.APB1Clk)
	udev.SetWordLen(usart.Bits8)
	udev.SetParity(usart.None)
	udev.SetStopBits(usart.Stop1b)
	udev.SetMode(usart.Tx | usart.Rx)
	udev.EnableIRQs(usart.RxNotEmptyIRQ)
	udev.Enable()

	rtos.IRQ(irqs.USART2).UseHandler(sirq)
	rtos.IRQ(irqs.USART2).Enable()

	s.SetUnix(true)
}

func blink(c uint, d int) {
	leds.SetBit(c)
	if d > 0 {
		delay.Millisec(d)
	} else {
		delay.Loop(-1e4 * d)
	}
	leds.ClearBit(c)
}

func sirq() {
	s.IRQ()
}

func checkErr(err error) {
	if err != nil {
		s.WriteString("\nError: ")
		s.WriteString(err.Error())
		s.WriteByte('\n')
	}
}

func main() {
	const (
		SmallestNormal         = 2.2250738585072014e-308 * 4
		SmallestNonzeroFloat64 = 4.940656458412465441765687928682213723651e-324
	)

	lf, le, uf, ue := strconv.Show(SmallestNormal)

	fmt.Fprint(s, "low = ", lf, "p", le, "\n")
	fmt.Fprint(s, "up  = ", uf, "p", ue, "\n")
}
<|endoftext|>


<commit_before>package the_platinum_searcher

import "sync"

var newLine = []byte("\n")

type grep struct {
	in      chan string
	done    chan struct{}
	grepper grepper
	printer printer
	opts    Option
}

func newGrep(pattern pattern, in chan string, done chan struct{}, opts Option, printer printer) grep {
	return grep{
		in:   in,
		done: done,
		grepper: newGrepper(
			pattern,
			printer,
			opts,
		),
		printer: printer,
		opts:    opts,
	}
}

func (g grep) start() {
	sem := make(chan struct{}, 208)
	wg := &sync.WaitGroup{}

	for path := range g.in {
		sem <- struct{}{}
		wg.Add(1)
		go func(path string) {
			defer wg.Done()
			defer func() { <-sem }()
			g.grepper.grep(path)
		}(path)
	}
	wg.Wait()
	close(g.printer.in)
	g.done <- <-g.printer.done
}

type grepper interface {
	grep(path string)
}

func newGrepper(pattern pattern, printer printer, opts Option) grepper {
	if opts.SearchOption.EnableFilesWithRegexp {
		return passthroughGrep{
			printer: printer,
		}
	} else if opts.SearchOption.Regexp {
		return extendedGrep{
			pattern:  pattern,
			lineGrep: newLineGrep(printer, opts),
		}
	} else {
		return fixedGrep{
			pattern:  pattern,
			lineGrep: newLineGrep(printer, opts),
		}
	}
}
<commit_msg>To stabilize resource utilization, reuse goroutine for grep.<commit_after>package the_platinum_searcher

import (
	"math"
	"runtime"
	"sync"
)

var newLine = []byte("\n")

type grep struct {
	in      chan string
	done    chan struct{}
	grepper grepper
	printer printer
	opts    Option
}

func newGrep(pattern pattern, in chan string, done chan struct{}, opts Option, printer printer) grep {
	return grep{
		in:   in,
		done: done,
		grepper: newGrepper(
			pattern,
			printer,
			opts,
		),
		printer: printer,
		opts:    opts,
	}
}

func (g grep) start() {
	wg := &sync.WaitGroup{}
	worker := func() {
		defer wg.Done()
		for path := range g.in {
			g.grepper.grep(path)
		}
	}
	num := int(math.Max(float64(runtime.NumCPU()), 2.0))
	for i := 0; i < num; i++ {
		wg.Add(1)
		go worker()
	}

	wg.Wait()
	close(g.printer.in)
	g.done <- <-g.printer.done
}

type grepper interface {
	grep(path string)
}

func newGrepper(pattern pattern, printer printer, opts Option) grepper {
	if opts.SearchOption.EnableFilesWithRegexp {
		return passthroughGrep{
			printer: printer,
		}
	} else if opts.SearchOption.Regexp {
		return extendedGrep{
			pattern:  pattern,
			lineGrep: newLineGrep(printer, opts),
		}
	} else {
		return fixedGrep{
			pattern:  pattern,
			lineGrep: newLineGrep(printer, opts),
		}
	}
}
<|endoftext|>


<commit_before>// 25 february 2014
package ui

import (
	"fmt"
	"sync"
)

// A Grid arranges Controls in a two-dimensional grid.
// The height of each row and the width of each column is the maximum preferred height and width (respectively) of all the controls in that row or column (respectively).
// Controls are aligned to the top left corner of each cell.
// All Controls in a Grid maintain their preferred sizes by default; if a Control is marked as being "filling", it will be sized to fill its cell.
// Even if a Control is marked as filling, its preferred size is used to calculate cell sizes.
// One Control can be marked as "stretchy": when the Window containing the Grid is resized, the cell containing that Control resizes to take any remaining space; its row and column are adjusted accordingly (so other filling controls in the same row and column will fill to the new height and width, respectively).
// A stretchy Control implicitly fills its cell.
// All cooridnates in a Grid are given in (row,column) form with (0,0) being the top-left cell.
// Unlike other UI toolkit Grids, this Grid does not (yet? TODO) allow Controls to span multiple rows or columns.
// TODO differnet row/column control alignment
type Grid struct {
	lock					sync.Mutex
	created				bool
	controls				[][]Control
	filling				[][]bool
	stretchyrow, stretchycol	int
	widths, heights			[][]int		// caches to avoid reallocating each time
	rowheights, colwidths	[]int
}

// NewGrid creates a new Grid with the given Controls.
// NewGrid needs to know the number of Controls in a row (alternatively, the number of columns); it will determine the number in a column from the number of Controls given.
// NewGrid panics if not given a full grid of Controls.
// Example:
// 	grid := NewGrid(3,
// 		control00, control01, control02,
// 		control10, control11, control12,
// 		control20, control21, control22)
func NewGrid(nPerRow int, controls ...Control) *Grid {
	if len(controls) % nPerRow != 0 {
		panic(fmt.Errorf("incomplete grid given to NewGrid() (not enough controls to evenly divide %d controls into rows of %d controls each)", len(controls), nPerRow))
	}
	nRows := len(controls) / nPerRow
	cc := make([][]Control, nRows)
	cf := make([][]bool, nRows)
	cw := make([][]int, nRows)
	ch := make([][]int, nRows)
	i := 0
	for row := 0; row < nRows; row++ {
		cc[row] = make([]Control, nPerRow)
		cf[row] = make([]bool, nPerRow)
		cw[row] = make([]int, nPerRow)
		ch[row] = make([]int, nPerRow)
		for x := 0; x < nPerRow; x++ {
			cc[row][x] = controls[i]
			i++
		}
	}
	return &Grid{
		controls:		cc,
		filling:		cf,
		stretchyrow:	-1,
		stretchycol:	-1,
		widths:		cw,
		heights:		ch,
		rowheights:	make([]int, nRows),
		colwidths:		make([]int, nPerRow),
	}
}

// SetFilling marks the given Control of the Grid as filling its cell instead of staying at its preferred size.
// This function cannot be called after the Window that contains the Grid has been created.
// It panics if the given coordinate is invalid.
func (g *Grid) SetFilling(row int, column int) {
	g.lock.Lock()
	defer g.lock.Unlock()

	if g.created {
		panic(fmt.Errorf("Grid.SetFilling() called after window create"))
	}
	if row < 0 || column < 0 || row > len(g,filling) || column > len(g.filling[row]) {
		panic(fmt.Errorf("coordinate (%d,%d) out of range passed to Grid.SetFilling()", row, column)
	}
	g.filling[row][column] = true
}

// SetStretchy marks the given Control of the Grid as stretchy.
// Stretchy implies filling.
// Only one control can be stretchy per Grid; calling SetStretchy multiple times merely changes which control is stretchy.
// This function cannot be called after the Window that contains the Grid has been created.
// It panics if the given coordinate is invalid.
func (g *Grid) SetStretchy(row int, column int) {
	g.lock.Lock()
	defer g.lock.Unlock()

	if g.created {
		panic(fmt.Errorf("Grid.SetFilling() called after window create"))
	}
	if row < 0 || column < 0 || row > len(g,filling) || column > len(g.filling[row]) {
		panic(fmt.Errorf("coordinate (%d,%d) out of range passed to Grid.SetStretchy()", row, column)
	}
	g.stretchyrow = row
	g.stretchycol = column
	g.filling[row][column] = true
}

func (g *Grid) make(window *sysData) error {
	g.lock.Lock()
	defer g.lock.Unlock()

	for row, xcol := range g.controls {
		for col, c := range xcol {
			err := c.make(window)
			if err != nil {
				return fmt.Errorf("error adding control (%d,%d) to Grid: %v", row, col, err)
			}
		}
	}
	g.created = true
	return nil
}

func (g *Grid) setRect(x int, y int, width int, height int, winheight int) error {
	g.lock.Lock()
	defer g.lock.Unlock()

	max := func(a int, b int) int {
		if a > b {
			return a
		}
		return b
	}

	// 1) clear data structures
	for i := range g.rowheights {
		g.rowheights[i] = 0
	}
	for i := range g.colwidths {
		g.colwidths[i] = 0
	}
	// 2) get preferred sizes; compute row/column sizes
	for row, xcol := range g.controls {
		for col, c := range xcol {
			w, h := c.preferredSize()
			g.widths[row][col] = w
			g.heights[row][col] = h
			g.rowheights[row] = max(g.rowheights[row], h)
			g.colwidths[col] = max(g.colwidths[col], w)
		}
	}
	// 3) handle the stretchy control
	if g.stretchyrow != -1 && g.stretchycol != -1 {
		for i, w := range g.colwidths {
			if i != g.stretchycol {
				width -= w
			}
		}
		for i, h := range g.rowheights {
			if i != g.stretchyrow {
				height -= h
			}
		}
		g.colwidths[g.stretchycol] = width
		g.rowheights[g.stretchyrow] = height
	}
	// TODO add a sanity check for g.stretchyrow xor g.stretchycol == -1?
	// 4) draw
	startx := x
	for row, xcol := range g.controls {
		for col, c := range xcol {
			w := g.widths[row][col]
			h := g.heights[row][col]
			if g.filling[row][col] {
				w = g.colwidths[col]
				h = g.rowheights[row]
			}
			err := c.setRect(x, y, w, h, winheight)
			if err != nil {
				return fmt.Errorf("error setting size of control (%d,%d) in Grid.setRect(): %v", row, col, err)
			}
			x += g.colwidths[col]
		}
		x = startx
		y += g.rowheights[row]
	}
	return nil
}

// filling and stretchy are ignored for preferred size calculation
func (g *Grid) preferredSize() (width int, height int) {
	g.lock.Lock()
	defer g.lock.Unlock()

	max := func(a int, b int) int {
		if a > b {
			return a
		}
		return b
	}

	// 1) clear data structures
	for i := range g.rowheights {
		g.rowheights[i] = 0
	}
	for i := range g.colwidths {
		g.colwidths[i] = 0
	}
	// 2) get preferred sizes; compute row/column sizes
	for row, xcol := range g.controls {
		for col, c := range xcol {
			w, h := c.preferredSize()
			g.widths[row][col] = w
			g.heights[row][col] = h
			g.rowheights[row] = max(g.rowheights[row], h)
			g.colwidths[col] = max(g.colwidths[col], w)
		}
	}
	// 3) now compute
	for _, w := range g.colwidths {
		width += w
	}
	for _, h := range g.rowheights {
		height += h
	}
	return width, height
}
<commit_msg>Fixed compiler errors in the past few commits.<commit_after>// 25 february 2014
package ui

import (
	"fmt"
	"sync"
)

// A Grid arranges Controls in a two-dimensional grid.
// The height of each row and the width of each column is the maximum preferred height and width (respectively) of all the controls in that row or column (respectively).
// Controls are aligned to the top left corner of each cell.
// All Controls in a Grid maintain their preferred sizes by default; if a Control is marked as being "filling", it will be sized to fill its cell.
// Even if a Control is marked as filling, its preferred size is used to calculate cell sizes.
// One Control can be marked as "stretchy": when the Window containing the Grid is resized, the cell containing that Control resizes to take any remaining space; its row and column are adjusted accordingly (so other filling controls in the same row and column will fill to the new height and width, respectively).
// A stretchy Control implicitly fills its cell.
// All cooridnates in a Grid are given in (row,column) form with (0,0) being the top-left cell.
// Unlike other UI toolkit Grids, this Grid does not (yet? TODO) allow Controls to span multiple rows or columns.
// TODO differnet row/column control alignment
type Grid struct {
	lock					sync.Mutex
	created				bool
	controls				[][]Control
	filling				[][]bool
	stretchyrow, stretchycol	int
	widths, heights			[][]int		// caches to avoid reallocating each time
	rowheights, colwidths	[]int
}

// NewGrid creates a new Grid with the given Controls.
// NewGrid needs to know the number of Controls in a row (alternatively, the number of columns); it will determine the number in a column from the number of Controls given.
// NewGrid panics if not given a full grid of Controls.
// Example:
// 	grid := NewGrid(3,
// 		control00, control01, control02,
// 		control10, control11, control12,
// 		control20, control21, control22)
func NewGrid(nPerRow int, controls ...Control) *Grid {
	if len(controls) % nPerRow != 0 {
		panic(fmt.Errorf("incomplete grid given to NewGrid() (not enough controls to evenly divide %d controls into rows of %d controls each)", len(controls), nPerRow))
	}
	nRows := len(controls) / nPerRow
	cc := make([][]Control, nRows)
	cf := make([][]bool, nRows)
	cw := make([][]int, nRows)
	ch := make([][]int, nRows)
	i := 0
	for row := 0; row < nRows; row++ {
		cc[row] = make([]Control, nPerRow)
		cf[row] = make([]bool, nPerRow)
		cw[row] = make([]int, nPerRow)
		ch[row] = make([]int, nPerRow)
		for x := 0; x < nPerRow; x++ {
			cc[row][x] = controls[i]
			i++
		}
	}
	return &Grid{
		controls:		cc,
		filling:		cf,
		stretchyrow:	-1,
		stretchycol:	-1,
		widths:		cw,
		heights:		ch,
		rowheights:	make([]int, nRows),
		colwidths:		make([]int, nPerRow),
	}
}

// SetFilling marks the given Control of the Grid as filling its cell instead of staying at its preferred size.
// This function cannot be called after the Window that contains the Grid has been created.
// It panics if the given coordinate is invalid.
func (g *Grid) SetFilling(row int, column int) {
	g.lock.Lock()
	defer g.lock.Unlock()

	if g.created {
		panic(fmt.Errorf("Grid.SetFilling() called after window create"))
	}
	if row < 0 || column < 0 || row > len(g,filling) || column > len(g.filling[row]) {
		panic(fmt.Errorf("coordinate (%d,%d) out of range passed to Grid.SetFilling()", row, column))
	}
	g.filling[row][column] = true
}

// SetStretchy marks the given Control of the Grid as stretchy.
// Stretchy implies filling.
// Only one control can be stretchy per Grid; calling SetStretchy multiple times merely changes which control is stretchy.
// This function cannot be called after the Window that contains the Grid has been created.
// It panics if the given coordinate is invalid.
func (g *Grid) SetStretchy(row int, column int) {
	g.lock.Lock()
	defer g.lock.Unlock()

	if g.created {
		panic(fmt.Errorf("Grid.SetFilling() called after window create"))
	}
	if row < 0 || column < 0 || row > len(g,filling) || column > len(g.filling[row]) {
		panic(fmt.Errorf("coordinate (%d,%d) out of range passed to Grid.SetStretchy()", row, column))
	}
	g.stretchyrow = row
	g.stretchycol = column
	g.filling[row][column] = true
}

func (g *Grid) make(window *sysData) error {
	g.lock.Lock()
	defer g.lock.Unlock()

	for row, xcol := range g.controls {
		for col, c := range xcol {
			err := c.make(window)
			if err != nil {
				return fmt.Errorf("error adding control (%d,%d) to Grid: %v", row, col, err)
			}
		}
	}
	g.created = true
	return nil
}

func (g *Grid) setRect(x int, y int, width int, height int, winheight int) error {
	g.lock.Lock()
	defer g.lock.Unlock()

	max := func(a int, b int) int {
		if a > b {
			return a
		}
		return b
	}

	// 1) clear data structures
	for i := range g.rowheights {
		g.rowheights[i] = 0
	}
	for i := range g.colwidths {
		g.colwidths[i] = 0
	}
	// 2) get preferred sizes; compute row/column sizes
	for row, xcol := range g.controls {
		for col, c := range xcol {
			w, h := c.preferredSize()
			g.widths[row][col] = w
			g.heights[row][col] = h
			g.rowheights[row] = max(g.rowheights[row], h)
			g.colwidths[col] = max(g.colwidths[col], w)
		}
	}
	// 3) handle the stretchy control
	if g.stretchyrow != -1 && g.stretchycol != -1 {
		for i, w := range g.colwidths {
			if i != g.stretchycol {
				width -= w
			}
		}
		for i, h := range g.rowheights {
			if i != g.stretchyrow {
				height -= h
			}
		}
		g.colwidths[g.stretchycol] = width
		g.rowheights[g.stretchyrow] = height
	}
	// TODO add a sanity check for g.stretchyrow xor g.stretchycol == -1?
	// 4) draw
	startx := x
	for row, xcol := range g.controls {
		for col, c := range xcol {
			w := g.widths[row][col]
			h := g.heights[row][col]
			if g.filling[row][col] {
				w = g.colwidths[col]
				h = g.rowheights[row]
			}
			err := c.setRect(x, y, w, h, winheight)
			if err != nil {
				return fmt.Errorf("error setting size of control (%d,%d) in Grid.setRect(): %v", row, col, err)
			}
			x += g.colwidths[col]
		}
		x = startx
		y += g.rowheights[row]
	}
	return nil
}

// filling and stretchy are ignored for preferred size calculation
func (g *Grid) preferredSize() (width int, height int) {
	g.lock.Lock()
	defer g.lock.Unlock()

	max := func(a int, b int) int {
		if a > b {
			return a
		}
		return b
	}

	// 1) clear data structures
	for i := range g.rowheights {
		g.rowheights[i] = 0
	}
	for i := range g.colwidths {
		g.colwidths[i] = 0
	}
	// 2) get preferred sizes; compute row/column sizes
	for row, xcol := range g.controls {
		for col, c := range xcol {
			w, h := c.preferredSize()
			g.widths[row][col] = w
			g.heights[row][col] = h
			g.rowheights[row] = max(g.rowheights[row], h)
			g.colwidths[col] = max(g.colwidths[col], w)
		}
	}
	// 3) now compute
	for _, w := range g.colwidths {
		width += w
	}
	for _, h := range g.rowheights {
		height += h
	}
	return width, height
}
<|endoftext|>


<commit_before>// Tool receives raw events from dcp-client.
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/couchbase/cbauth"
	"github.com/couchbase/indexing/secondary/common"
	"github.com/couchbase/indexing/secondary/dcp"
	"github.com/couchbase/indexing/secondary/logging"
)

import (
	mcd "github.com/couchbase/indexing/secondary/dcp/transport"
	mc "github.com/couchbase/indexing/secondary/dcp/transport/client"
)

var options struct {
	buckets        []string // buckets to connect with
	kvAddress      []string
	stats          int // periodic timeout(ms) to print stats, 0 will disable
	printFLogs     bool
	auth           string
	info           bool
	debug          bool
	trace          bool
	numMessages    int
	outputFile     string
	numConnections int
}

var rch = make(chan []interface{}, 10000)

func argParse() string {
	var buckets string
	var kvAddress string

	flag.StringVar(&buckets, "buckets", "default",
		"buckets to listen")
	flag.StringVar(&kvAddress, "kvaddrs", "",
		"list of kv-nodes to connect")
	flag.IntVar(&options.stats, "stats", 1000,
		"periodic timeout in mS, to print statistics, `0` will disable stats")
	flag.BoolVar(&options.printFLogs, "flogs", false,
		"display failover logs")
	flag.StringVar(&options.auth, "auth", "",
		"Auth user and password")
	flag.BoolVar(&options.info, "info", false,
		"display informational logs")
	flag.BoolVar(&options.debug, "debug", false,
		"display debug logs")
	flag.BoolVar(&options.trace, "trace", false,
		"display trace logs")
	flag.IntVar(&options.numMessages, "nummessages", 1000000,
		"number of DCP messages to wait for")
	flag.StringVar(&options.outputFile, "outputfile", "/root/dcpstatsfile",
		"file to save dcp stats output")
	flag.IntVar(&options.numConnections, "numconnections", 4,
		"number of DCP messages to wait for")

	flag.Parse()

	options.buckets = strings.Split(buckets, ",")
	if options.debug {
		logging.SetLogLevel(logging.Debug)
	} else if options.trace {
		logging.SetLogLevel(logging.Trace)
	} else {
		logging.SetLogLevel(logging.Info)
	}
	if kvAddress == "" {
		logging.Fatalf("Please provide -kvaddrs")
	}
	options.kvAddress = strings.Split(kvAddress, ",")

	args := flag.Args()
	if len(args) < 1 {
		usage()
		os.Exit(1)
	}
	return args[0]
}

func usage() {
	fmt.Fprintf(os.Stderr, "Usage : %s [OPTIONS] <cluster-addr> \n", os.Args[0])
	flag.PrintDefaults()
}

func main() {
	cluster := argParse()

	// setup cbauth
	if options.auth != "" {
		up := strings.Split(options.auth, ":")
		if _, err := cbauth.InternalRetryDefaultInit(cluster, up[0], up[1]); err != nil {
			logging.Fatalf("Failed to initialize cbauth: %s", err)
		}
	}

	var wg sync.WaitGroup
	wg.Add(2)
	for _, bucket := range options.buckets {
		go startBucket(cluster, bucket, &wg)
	}
	go receive()
	wg.Wait()
	logging.Infof("Test Completed\n")
}

func startBucket(cluster, bucketn string, wg *sync.WaitGroup) int {
	logging.Infof("Connecting with %q\n", bucketn)
	b, err := common.ConnectBucket(cluster, "default", bucketn)
	mf(err, "bucket")

	dcpConfig := map[string]interface{}{
		"genChanSize":    10000,
		"dataChanSize":   10000,
		"numConnections": options.numConnections,
		"activeVbOnly":   true,
	}
	dcpFeed, err := b.StartDcpFeedOver(
		couchbase.NewDcpFeedName("rawupr"),
		uint32(0), options.kvAddress, 0xABCD, dcpConfig)
	mf(err, "- upr")

	vbnos := listOfVbnos()

	flogs, err := b.GetFailoverLogs(0xABCD, vbnos, dcpConfig)
	mf(err, "- dcp failoverlogs")

	if options.printFLogs {
		printFlogs(vbnos, flogs)
	}

	logging.Infof("options.messages = %d\n", options.numMessages)

	t0 := time.Now()
	go startDcp(dcpFeed, flogs, wg)

	cnt := 0
	for {
		e, ok := <-dcpFeed.C
		if ok == false {
			logging.Infof("Closing for bucket %q %d\n", b.Name, e.Cas)
			break
		}
		cnt++
		if cnt >= options.numMessages {
			break
		}
		rch <- []interface{}{b.Name, e}
	}
	t1 := time.Now()

	timeTaken := t1.Sub(t0)
	throughput := float64(options.numMessages) / timeTaken.Seconds()
	if cnt < options.numMessages {
		throughput = float64(0)
	}
	writeStatsFile(timeTaken, throughput)
	logging.Infof("Done startBucket\n")

	b.Close()

	defer wg.Done()
	return 0
}

func startDcp(dcpFeed *couchbase.DcpFeed, flogs couchbase.FailoverLog, wg *sync.WaitGroup) {
	start, end := uint64(0), uint64(0xFFFFFFFFFFFFFFFF)
	snapStart, snapEnd := uint64(0), uint64(0)
	for vbno, flog := range flogs {
		x := flog[len(flog)-1] // map[uint16][][2]uint64
		opaque, flags, vbuuid := uint16(vbno), uint32(0), x[0]
		err := dcpFeed.DcpRequestStream(
			vbno, opaque, flags, vbuuid, start, end, snapStart, snapEnd)
		mf(err, fmt.Sprintf("stream-req for %v failed", vbno))
	}
	logging.Infof("Done startDCP\n")
	defer wg.Done()
}

func mf(err error, msg string) {
	if err != nil {
		logging.Fatalf("%v: %v", msg, err)
	}
}

func receive() {
	logging.Infof("receive() Beginning")
	// bucket -> Opcode -> #count
	counts := make(map[string]map[mcd.CommandCode]int)

	var tick <-chan time.Time
	if options.stats > 0 {
		tick = time.Tick(time.Millisecond * time.Duration(options.stats))
	}

loop:
	for {
		select {
		case msg, ok := <-rch:
			if ok == false {
				break loop
			}
			bucket, e := msg[0].(string), msg[1].(*mc.DcpEvent)
			if _, ok := counts[bucket]; !ok {
				counts[bucket] = make(map[mcd.CommandCode]int)
			}
			if _, ok := counts[bucket][e.Opcode]; !ok {
				counts[bucket][e.Opcode] = 0
			}
			counts[bucket][e.Opcode]++

		case <-tick:
			for bucket, m := range counts {
				logging.Infof("%q %s\n", bucket, sprintCounts(m))
			}
			logging.Infof("\n")
		}
	}
}

func sprintCounts(counts map[mcd.CommandCode]int) string {
	line := ""
	for i := 0; i < 256; i++ {
		opcode := mcd.CommandCode(i)
		if n, ok := counts[opcode]; ok {
			line += fmt.Sprintf("%s:%v ", mcd.CommandNames[opcode], n)
		}
	}
	return strings.TrimRight(line, " ")
}

func listOfVbnos() []uint16 {
	// list of vbuckets
	vbnos := make([]uint16, 0, 1024)
	for i := 0; i < 1024; i++ {
		vbnos = append(vbnos, uint16(i))
	}
	return vbnos
}

func printFlogs(vbnos []uint16, flogs couchbase.FailoverLog) {
	for i, vbno := range vbnos {
		logging.Infof("Failover log for vbucket %v\n", vbno)
		logging.Infof("   %#v\n", flogs[uint16(i)])
	}
	logging.Infof("\n")
}

func writeStatsFile(timeTaken time.Duration, throughput float64) {
	str := fmt.Sprintf("The call took %v seconds to run.\nThroughput = %f\n", timeTaken.Seconds(), throughput)
	// write the whole body at once
	err := ioutil.WriteFile(options.outputFile, []byte(str), 0777)
	if err != nil {
		panic(err)
	}
}
<commit_msg>Remove empty log message<commit_after>// Tool receives raw events from dcp-client.
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/couchbase/cbauth"
	"github.com/couchbase/indexing/secondary/common"
	"github.com/couchbase/indexing/secondary/dcp"
	"github.com/couchbase/indexing/secondary/logging"
)

import (
	mcd "github.com/couchbase/indexing/secondary/dcp/transport"
	mc "github.com/couchbase/indexing/secondary/dcp/transport/client"
)

var options struct {
	buckets        []string // buckets to connect with
	kvAddress      []string
	stats          int // periodic timeout(ms) to print stats, 0 will disable
	printFLogs     bool
	auth           string
	info           bool
	debug          bool
	trace          bool
	numMessages    int
	outputFile     string
	numConnections int
}

var rch = make(chan []interface{}, 10000)

func argParse() string {
	var buckets string
	var kvAddress string

	flag.StringVar(&buckets, "buckets", "default",
		"buckets to listen")
	flag.StringVar(&kvAddress, "kvaddrs", "",
		"list of kv-nodes to connect")
	flag.IntVar(&options.stats, "stats", 1000,
		"periodic timeout in mS, to print statistics, `0` will disable stats")
	flag.BoolVar(&options.printFLogs, "flogs", false,
		"display failover logs")
	flag.StringVar(&options.auth, "auth", "",
		"Auth user and password")
	flag.BoolVar(&options.info, "info", false,
		"display informational logs")
	flag.BoolVar(&options.debug, "debug", false,
		"display debug logs")
	flag.BoolVar(&options.trace, "trace", false,
		"display trace logs")
	flag.IntVar(&options.numMessages, "nummessages", 1000000,
		"number of DCP messages to wait for")
	flag.StringVar(&options.outputFile, "outputfile", "/root/dcpstatsfile",
		"file to save dcp stats output")
	flag.IntVar(&options.numConnections, "numconnections", 4,
		"number of DCP messages to wait for")

	flag.Parse()

	options.buckets = strings.Split(buckets, ",")
	if options.debug {
		logging.SetLogLevel(logging.Debug)
	} else if options.trace {
		logging.SetLogLevel(logging.Trace)
	} else {
		logging.SetLogLevel(logging.Info)
	}
	if kvAddress == "" {
		logging.Fatalf("Please provide -kvaddrs")
	}
	options.kvAddress = strings.Split(kvAddress, ",")

	args := flag.Args()
	if len(args) < 1 {
		usage()
		os.Exit(1)
	}
	return args[0]
}

func usage() {
	fmt.Fprintf(os.Stderr, "Usage : %s [OPTIONS] <cluster-addr> \n", os.Args[0])
	flag.PrintDefaults()
}

func main() {
	cluster := argParse()

	// setup cbauth
	if options.auth != "" {
		up := strings.Split(options.auth, ":")
		if _, err := cbauth.InternalRetryDefaultInit(cluster, up[0], up[1]); err != nil {
			logging.Fatalf("Failed to initialize cbauth: %s", err)
		}
	}

	var wg sync.WaitGroup
	wg.Add(2)
	for _, bucket := range options.buckets {
		go startBucket(cluster, bucket, &wg)
	}
	go receive()
	wg.Wait()
	logging.Infof("Test Completed\n")
}

func startBucket(cluster, bucketn string, wg *sync.WaitGroup) int {
	logging.Infof("Connecting with %q\n", bucketn)
	b, err := common.ConnectBucket(cluster, "default", bucketn)
	mf(err, "bucket")

	dcpConfig := map[string]interface{}{
		"genChanSize":    10000,
		"dataChanSize":   10000,
		"numConnections": options.numConnections,
		"activeVbOnly":   true,
	}
	dcpFeed, err := b.StartDcpFeedOver(
		couchbase.NewDcpFeedName("rawupr"),
		uint32(0), options.kvAddress, 0xABCD, dcpConfig)
	mf(err, "- upr")

	vbnos := listOfVbnos()

	flogs, err := b.GetFailoverLogs(0xABCD, vbnos, dcpConfig)
	mf(err, "- dcp failoverlogs")

	if options.printFLogs {
		printFlogs(vbnos, flogs)
	}

	logging.Infof("options.messages = %d\n", options.numMessages)

	t0 := time.Now()
	go startDcp(dcpFeed, flogs, wg)

	cnt := 0
	for {
		e, ok := <-dcpFeed.C
		if ok == false {
			logging.Infof("Closing for bucket %q %d\n", b.Name, e.Cas)
			break
		}
		cnt++
		if cnt >= options.numMessages {
			break
		}
		rch <- []interface{}{b.Name, e}
	}
	t1 := time.Now()

	timeTaken := t1.Sub(t0)
	throughput := float64(options.numMessages) / timeTaken.Seconds()
	if cnt < options.numMessages {
		throughput = float64(0)
	}
	writeStatsFile(timeTaken, throughput)
	logging.Infof("Done startBucket\n")

	b.Close()

	defer wg.Done()
	return 0
}

func startDcp(dcpFeed *couchbase.DcpFeed, flogs couchbase.FailoverLog, wg *sync.WaitGroup) {
	start, end := uint64(0), uint64(0xFFFFFFFFFFFFFFFF)
	snapStart, snapEnd := uint64(0), uint64(0)
	for vbno, flog := range flogs {
		x := flog[len(flog)-1] // map[uint16][][2]uint64
		opaque, flags, vbuuid := uint16(vbno), uint32(0), x[0]
		err := dcpFeed.DcpRequestStream(
			vbno, opaque, flags, vbuuid, start, end, snapStart, snapEnd)
		mf(err, fmt.Sprintf("stream-req for %v failed", vbno))
	}
	logging.Infof("Done startDCP\n")
	defer wg.Done()
}

func mf(err error, msg string) {
	if err != nil {
		logging.Fatalf("%v: %v", msg, err)
	}
}

func receive() {
	logging.Infof("receive() Beginning")
	// bucket -> Opcode -> #count
	counts := make(map[string]map[mcd.CommandCode]int)

	var tick <-chan time.Time
	if options.stats > 0 {
		tick = time.Tick(time.Millisecond * time.Duration(options.stats))
	}

loop:
	for {
		select {
		case msg, ok := <-rch:
			if ok == false {
				break loop
			}
			bucket, e := msg[0].(string), msg[1].(*mc.DcpEvent)
			if _, ok := counts[bucket]; !ok {
				counts[bucket] = make(map[mcd.CommandCode]int)
			}
			if _, ok := counts[bucket][e.Opcode]; !ok {
				counts[bucket][e.Opcode] = 0
			}
			counts[bucket][e.Opcode]++

		case <-tick:
			for bucket, m := range counts {
				logging.Infof("%q %s\n", bucket, sprintCounts(m))
			}
		}
	}
}

func sprintCounts(counts map[mcd.CommandCode]int) string {
	line := ""
	for i := 0; i < 256; i++ {
		opcode := mcd.CommandCode(i)
		if n, ok := counts[opcode]; ok {
			line += fmt.Sprintf("%s:%v ", mcd.CommandNames[opcode], n)
		}
	}
	return strings.TrimRight(line, " ")
}

func listOfVbnos() []uint16 {
	// list of vbuckets
	vbnos := make([]uint16, 0, 1024)
	for i := 0; i < 1024; i++ {
		vbnos = append(vbnos, uint16(i))
	}
	return vbnos
}

func printFlogs(vbnos []uint16, flogs couchbase.FailoverLog) {
	for i, vbno := range vbnos {
		logging.Infof("Failover log for vbucket %v\n", vbno)
		logging.Infof("   %#v\n", flogs[uint16(i)])
	}
	logging.Infof("\n")
}

func writeStatsFile(timeTaken time.Duration, throughput float64) {
	str := fmt.Sprintf("The call took %v seconds to run.\nThroughput = %f\n", timeTaken.Seconds(), throughput)
	// write the whole body at once
	err := ioutil.WriteFile(options.outputFile, []byte(str), 0777)
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"time"
)

const lineBufferSize = 10000
const maxLogs = 9

var every = flag.Duration("every", 1*time.Minute, "How often to rotate")
var outputFile = flag.String("to", "output.log", "Output file to write to")

func main() {
	log.SetPrefix("grot")

	flag.Parse()
	lines := setupInputChannel(os.Stdin)
	handleOutput(*outputFile, *every, lines)
}

func mustCloseFile(fileH *os.File, panicMsg string) {
	err := fileH.Close()
	if err != nil {
		log.Panicf("%s: %v", panicMsg, err)
	}
}

func handleOutput(filename string, every time.Duration, input <-chan string) {
	fileH, err := rotate(filename, maxLogs)
	if err != nil {
		log.Panicf("could not perform initial rotation: %v", err)
	}

	writer := bufio.NewWriter(fileH)
	timer := time.NewTicker(every)
	for {
		select {
		case line, ok := <-input:
			if !ok {
				mustCloseFile(fileH, fmt.Sprintf("Could not close file"))
				return
			}
			_, err := writer.Write([]byte(line))
			if err != nil {
				log.Panicf("An error occured while writing to %v: %v", filename, err)
			}
		case <-timer.C:
			err := writer.Flush()
			if err != nil {
				log.Panicf("Could not flush contents to file on rotation: %v", err)
			}

			mustCloseFile(fileH, fmt.Sprintf("Could not close file on rotation: %v", err))

			fileH, err = rotate(filename, maxLogs)
			if err != nil {
				log.Panicf("error while performing rotation: %v", err)
			}
			writer.Reset(fileH)
		}
	}
}

func setupInputChannel(input io.Reader) <-chan string {
	reader := bufio.NewReader(input)
	lines := make(chan string, lineBufferSize)
	go func() {
		for {
			line, err := reader.ReadString('\n')
			if err != nil {
				break
			}
			lines <- line
		}
		close(lines)
	}()

	return lines
}

func rotate(filename string, keep int) (*os.File, error) {

	// make sure filename is clean before we work with it
	filename = filepath.Clean(filename)

	// read directory
	workingDir := filepath.Dir(filename)
	files, err := ioutil.ReadDir(workingDir)
	if err != nil {
		return nil, fmt.Errorf("could not open directory %v: %v", workingDir, err)
	}

	// load files in a "set"
	existingFiles := make(map[string]bool)
	for _, file := range files {
		existingFiles[file.Name()] = true
	}

	// perform rotation
	for logNumber := keep; logNumber > 0; logNumber-- {

		logName := fmt.Sprintf("%v.%v", filename, logNumber)
		logAbsPath := filepath.Join(workingDir, logName)

		// log doesn't exist yet. skip
		if !existingFiles[logName] {
			continue
		}

		// log is last to keep, delete
		if logNumber == keep {
			err := os.Remove(logAbsPath)
			if err != nil {
				return nil, fmt.Errorf("could not delete last log file: %v", err)
			}
			continue
		}

		newLogName := filepath.Join(workingDir, fmt.Sprintf("%v.%v", filename, logNumber+1))
		err := os.Rename(logAbsPath, newLogName)
		if err != nil {
			return nil, fmt.Errorf("could not rename %v into %v", logAbsPath, newLogName)
		}
	}

	// If main file exists, rotate it
	newLogName := filepath.Join(workingDir, fmt.Sprintf("%v.1", filename))
	err = os.Rename(filename, newLogName)
	if err != nil && !os.IsNotExist(err) {
		return nil, fmt.Errorf("could not rename %v into %v: %v", filename, newLogName, err)

	}

	fileH, err := os.Create(filename)
	if err != nil {
		return nil, fmt.Errorf("could not create base file %v: %v", filename, err)
	}

	return fileH, nil
}
<commit_msg>extracted ls logic to a function<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"time"
)

const lineBufferSize = 10000
const maxLogs = 9

var every = flag.Duration("every", 1*time.Minute, "How often to rotate")
var outputFile = flag.String("to", "output.log", "Output file to write to")

func main() {
	log.SetPrefix("grot")

	flag.Parse()
	lines := setupInputChannel(os.Stdin)
	handleOutput(*outputFile, *every, lines)
}

func mustCloseFile(fileH *os.File, panicMsg string) {
	err := fileH.Close()
	if err != nil {
		log.Panicf("%s: %v", panicMsg, err)
	}
}

func handleOutput(filename string, every time.Duration, input <-chan string) {
	fileH, err := rotate(filename, maxLogs)
	if err != nil {
		log.Panicf("could not perform initial rotation: %v", err)
	}

	writer := bufio.NewWriter(fileH)
	timer := time.NewTicker(every)
	for {
		select {
		case line, ok := <-input:
			if !ok {
				mustCloseFile(fileH, fmt.Sprintf("Could not close file"))
				return
			}
			_, err := writer.Write([]byte(line))
			if err != nil {
				log.Panicf("An error occured while writing to %v: %v", filename, err)
			}
		case <-timer.C:
			err := writer.Flush()
			if err != nil {
				log.Panicf("Could not flush contents to file on rotation: %v", err)
			}

			mustCloseFile(fileH, fmt.Sprintf("Could not close file on rotation: %v", err))

			fileH, err = rotate(filename, maxLogs)
			if err != nil {
				log.Panicf("error while performing rotation: %v", err)
			}
			writer.Reset(fileH)
		}
	}
}

func setupInputChannel(input io.Reader) <-chan string {
	reader := bufio.NewReader(input)
	lines := make(chan string, lineBufferSize)
	go func() {
		for {
			line, err := reader.ReadString('\n')
			if err != nil {
				break
			}
			lines <- line
		}
		close(lines)
	}()

	return lines
}

func listDirectory(dirPath string) (map[string]*os.FileInfo, error) {

	workingDir := filepath.Clean(dirPath)
	files, err := ioutil.ReadDir(workingDir)
	if err != nil {
		return nil, fmt.Errorf("could not open directory %v: %v", workingDir, err)
	}

	// load files in a "set"
	fileSet := make(map[string]*os.FileInfo)
	for i := range files {
		fileSet[files[i].Name()] = &files[i]
	}

	return fileSet, nil
}

func rotate(filename string, keep int) (*os.File, error) {

	// make sure filename is clean before we work with it
	filename = filepath.Clean(filename)

	workingDir := filepath.Dir(filename)
	existingFiles, err := listDirectory(workingDir)
	if err != nil {
		log.Panicf("Could not list list files: %v", err)
	}

	// perform rotation
	for logNumber := keep; logNumber > 0; logNumber-- {

		logName := fmt.Sprintf("%v.%v", filename, logNumber)
		logAbsPath := filepath.Join(workingDir, logName)

		// log doesn't exist yet. skip
		if _, ok := existingFiles[logName]; !ok {
			continue
		}

		// log is last to keep, delete
		if logNumber == keep {
			err := os.Remove(logAbsPath)
			if err != nil {
				return nil, fmt.Errorf("could not delete last log file: %v", err)
			}
			continue
		}

		newLogName := filepath.Join(workingDir, fmt.Sprintf("%v.%v", filename, logNumber+1))
		err := os.Rename(logAbsPath, newLogName)
		if err != nil {
			return nil, fmt.Errorf("could not rename %v into %v", logAbsPath, newLogName)
		}
	}

	// If main file exists, rotate it
	newLogName := filepath.Join(workingDir, fmt.Sprintf("%v.1", filename))
	err = os.Rename(filename, newLogName)
	if err != nil && !os.IsNotExist(err) {
		return nil, fmt.Errorf("could not rename %v into %v: %v", filename, newLogName, err)

	}

	fileH, err := os.Create(filename)
	if err != nil {
		return nil, fmt.Errorf("could not create base file %v: %v", filename, err)
	}

	return fileH, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Vitess Authors.

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

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

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

package hook

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path"
	"strings"
	"syscall"

	vtenv "vitess.io/vitess/go/vt/env"
	"vitess.io/vitess/go/vt/log"
)

// Hook is the input structure for this library.
type Hook struct {
	Name       string
	Parameters []string
	ExtraEnv   map[string]string
}

// HookResult is returned by the Execute method.
type HookResult struct {
	ExitStatus int // HOOK_SUCCESS if it succeeded
	Stdout     string
	Stderr     string
}

// The hook will return a value between 0 and 255. 0 if it succeeds.
// So we have these additional values here for more information.
const (
	// HOOK_SUCCESS is returned when the hook worked.
	HOOK_SUCCESS = 0

	// HOOK_DOES_NOT_EXIST is returned when the hook cannot be found.
	HOOK_DOES_NOT_EXIST = -1

	// HOOK_STAT_FAILED is returned when the hook exists, but stat
	// on it fails.
	HOOK_STAT_FAILED = -2

	// HOOK_CANNOT_GET_EXIT_STATUS is returned when after
	// execution, we fail to get the exit code for the hook.
	HOOK_CANNOT_GET_EXIT_STATUS = -3

	// HOOK_INVALID_NAME is returned if a hook has an invalid name.
	HOOK_INVALID_NAME = -4

	// HOOK_VTROOT_ERROR is returned if VTROOT is not set properly.
	HOOK_VTROOT_ERROR = -5

	// HOOK_GENERIC_ERROR is returned for unknown errors.
	HOOK_GENERIC_ERROR = -6
)

// WaitFunc is a return type for the Pipe methods.
// It returns the process stderr and an error, if any.
type WaitFunc func() (string, error)

// NewHook returns a Hook object with the provided name and params.
func NewHook(name string, params []string) *Hook {
	return &Hook{Name: name, Parameters: params}
}

// NewSimpleHook returns a Hook object with just a name.
func NewSimpleHook(name string) *Hook {
	return &Hook{Name: name}
}

// NewHookWithEnv returns a Hook object with the provided name, params and ExtraEnv.
func NewHookWithEnv(name string, params []string, env map[string]string) *Hook {
	return &Hook{Name: name, Parameters: params, ExtraEnv: env}
}

// findHook tries to locate the hook, and returns the exec.Cmd for it.
func (hook *Hook) findHook(ctx context.Context) (*exec.Cmd, int, error) {
	// Check the hook path.
	if strings.Contains(hook.Name, "/") {
		return nil, HOOK_INVALID_NAME, fmt.Errorf("hook cannot contain '/'")
	}

	// Find our root.
	root, err := vtenv.VtRoot()
	if err != nil {
		return nil, HOOK_VTROOT_ERROR, fmt.Errorf("cannot get VTROOT: %v", err)
	}

	// See if the hook exists.
	vthook := path.Join(root, "vthook", hook.Name)
	_, err = os.Stat(vthook)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, HOOK_DOES_NOT_EXIST, fmt.Errorf("missing hook %v", vthook)
		}

		return nil, HOOK_STAT_FAILED, fmt.Errorf("cannot stat hook %v: %v", vthook, err)
	}

	// Configure the command.
	log.Infof("hook: executing hook: %v %v", vthook, strings.Join(hook.Parameters, " "))
	cmd := exec.CommandContext(ctx, vthook, hook.Parameters...)
	if len(hook.ExtraEnv) > 0 {
		cmd.Env = os.Environ()
		for key, value := range hook.ExtraEnv {
			cmd.Env = append(cmd.Env, key+"="+value)
		}
	}

	return cmd, HOOK_SUCCESS, nil
}

// ExecuteContext tries to execute the Hook with the given context and returns a HookResult.
func (hook *Hook) ExecuteContext(ctx context.Context) (result *HookResult) {
	result = &HookResult{}

	// Find the hook.
	cmd, status, err := hook.findHook(ctx)
	if err != nil {
		result.ExitStatus = status
		result.Stderr = err.Error() + "\n"
		return result
	}

	// Run it.
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err = cmd.Run()
	result.Stdout = stdout.String()
	result.Stderr = stderr.String()
	if err == nil {
		result.ExitStatus = HOOK_SUCCESS
	} else {
		if cmd.ProcessState != nil && cmd.ProcessState.Sys() != nil {
			result.ExitStatus = cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
		} else {
			result.ExitStatus = HOOK_CANNOT_GET_EXIT_STATUS
		}
		result.Stderr += "ERROR: " + err.Error() + "\n"
	}

	log.Infof("hook: result is %v", result.String())

	return result
}

// Execute tries to execute the Hook and returns a HookResult.
func (hook *Hook) Execute() (result *HookResult) {
	return hook.ExecuteContext(context.Background())
}

// ExecuteOptional executes an optional hook, logs if it doesn't
// exist, and returns a printable error.
func (hook *Hook) ExecuteOptional() error {
	hr := hook.Execute()
	switch hr.ExitStatus {
	case HOOK_DOES_NOT_EXIST:
		log.Infof("%v hook doesn't exist", hook.Name)
	case HOOK_VTROOT_ERROR:
		log.Infof("VTROOT not set, so %v hook doesn't exist", hook.Name)
	case HOOK_SUCCESS:
		// nothing to do here
	default:
		return fmt.Errorf("%v hook failed(%v): %v", hook.Name, hr.ExitStatus, hr.Stderr)
	}
	return nil
}

// ExecuteAsWritePipe will execute the hook as in a Unix pipe,
// directing output to the provided writer. It will return:
// - an io.WriteCloser to write data to.
// - a WaitFunc method to call to wait for the process to exit,
// that returns stderr and the cmd.Wait() error.
// - an error code and an error if anything fails.
func (hook *Hook) ExecuteAsWritePipe(out io.Writer) (io.WriteCloser, WaitFunc, int, error) {
	// Find the hook.
	cmd, status, err := hook.findHook(context.Background())
	if err != nil {
		return nil, nil, status, err
	}

	// Configure the process's stdin, stdout, and stderr.
	in, err := cmd.StdinPipe()
	if err != nil {
		return nil, nil, HOOK_GENERIC_ERROR, fmt.Errorf("failed to configure stdin: %v", err)
	}
	cmd.Stdout = out
	var stderr bytes.Buffer
	cmd.Stderr = &stderr

	// Start the process.
	err = cmd.Start()
	if err != nil {
		status = HOOK_CANNOT_GET_EXIT_STATUS
		if cmd.ProcessState != nil && cmd.ProcessState.Sys() != nil {
			status = cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
		}
		return nil, nil, status, err
	}

	// And return
	return in, func() (string, error) {
		err := cmd.Wait()
		return stderr.String(), err
	}, HOOK_SUCCESS, nil
}

// ExecuteAsReadPipe will execute the hook as in a Unix pipe, reading
// from the provided reader. It will return:
// - an io.Reader to read piped data from.
// - a WaitFunc method to call to wait for the process to exit, that
// returns stderr and the Wait() error.
// - an error code and an error if anything fails.
func (hook *Hook) ExecuteAsReadPipe(in io.Reader) (io.Reader, WaitFunc, int, error) {
	// Find the hook.
	cmd, status, err := hook.findHook(context.Background())
	if err != nil {
		return nil, nil, status, err
	}

	// Configure the process's stdin, stdout, and stderr.
	out, err := cmd.StdoutPipe()
	if err != nil {
		return nil, nil, HOOK_GENERIC_ERROR, fmt.Errorf("failed to configure stdout: %v", err)
	}
	cmd.Stdin = in
	var stderr bytes.Buffer
	cmd.Stderr = &stderr

	// Start the process.
	err = cmd.Start()
	if err != nil {
		status = HOOK_CANNOT_GET_EXIT_STATUS
		if cmd.ProcessState != nil && cmd.ProcessState.Sys() != nil {
			status = cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
		}
		return nil, nil, status, err
	}

	// And return
	return out, func() (string, error) {
		err := cmd.Wait()
		return stderr.String(), err
	}, HOOK_SUCCESS, nil
}

// String returns a printable version of the HookResult
func (hr *HookResult) String() string {
	result := "result: "
	switch hr.ExitStatus {
	case HOOK_SUCCESS:
		result += "HOOK_SUCCESS"
	case HOOK_DOES_NOT_EXIST:
		result += "HOOK_DOES_NOT_EXIST"
	case HOOK_STAT_FAILED:
		result += "HOOK_STAT_FAILED"
	case HOOK_CANNOT_GET_EXIT_STATUS:
		result += "HOOK_CANNOT_GET_EXIT_STATUS"
	case HOOK_INVALID_NAME:
		result += "HOOK_INVALID_NAME"
	case HOOK_VTROOT_ERROR:
		result += "HOOK_VTROOT_ERROR"
	default:
		result += fmt.Sprintf("exit(%v)", hr.ExitStatus)
	}
	if hr.Stdout != "" {
		result += "\nstdout:\n" + hr.Stdout
	}
	if hr.Stderr != "" {
		result += "\nstderr:\n" + hr.Stderr
	}
	return result
}
<commit_msg>Track duration, correctly handle error cases for timeout-killed processes<commit_after>/*
Copyright 2019 The Vitess Authors.

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

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

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

package hook

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path"
	"strings"
	"syscall"
	"time"

	vtenv "vitess.io/vitess/go/vt/env"
	"vitess.io/vitess/go/vt/log"
)

// Hook is the input structure for this library.
type Hook struct {
	Name       string
	Parameters []string
	ExtraEnv   map[string]string
}

// HookResult is returned by the Execute method.
type HookResult struct {
	ExitStatus int // HOOK_SUCCESS if it succeeded
	Stdout     string
	Stderr     string
}

// The hook will return a value between 0 and 255. 0 if it succeeds.
// So we have these additional values here for more information.
const (
	// HOOK_SUCCESS is returned when the hook worked.
	HOOK_SUCCESS = 0

	// HOOK_DOES_NOT_EXIST is returned when the hook cannot be found.
	HOOK_DOES_NOT_EXIST = -1

	// HOOK_STAT_FAILED is returned when the hook exists, but stat
	// on it fails.
	HOOK_STAT_FAILED = -2

	// HOOK_CANNOT_GET_EXIT_STATUS is returned when after
	// execution, we fail to get the exit code for the hook.
	HOOK_CANNOT_GET_EXIT_STATUS = -3

	// HOOK_INVALID_NAME is returned if a hook has an invalid name.
	HOOK_INVALID_NAME = -4

	// HOOK_VTROOT_ERROR is returned if VTROOT is not set properly.
	HOOK_VTROOT_ERROR = -5

	// HOOK_GENERIC_ERROR is returned for unknown errors.
	HOOK_GENERIC_ERROR = -6

	// HOOK_TIMEOUT_ERROR is returned when a CommandContext has its context
	// become done before the command terminates.
	HOOK_TIMEOUT_ERROR = -7
)

// WaitFunc is a return type for the Pipe methods.
// It returns the process stderr and an error, if any.
type WaitFunc func() (string, error)

// NewHook returns a Hook object with the provided name and params.
func NewHook(name string, params []string) *Hook {
	return &Hook{Name: name, Parameters: params}
}

// NewSimpleHook returns a Hook object with just a name.
func NewSimpleHook(name string) *Hook {
	return &Hook{Name: name}
}

// NewHookWithEnv returns a Hook object with the provided name, params and ExtraEnv.
func NewHookWithEnv(name string, params []string, env map[string]string) *Hook {
	return &Hook{Name: name, Parameters: params, ExtraEnv: env}
}

// findHook tries to locate the hook, and returns the exec.Cmd for it.
func (hook *Hook) findHook(ctx context.Context) (*exec.Cmd, int, error) {
	// Check the hook path.
	if strings.Contains(hook.Name, "/") {
		return nil, HOOK_INVALID_NAME, fmt.Errorf("hook cannot contain '/'")
	}

	// Find our root.
	root, err := vtenv.VtRoot()
	if err != nil {
		return nil, HOOK_VTROOT_ERROR, fmt.Errorf("cannot get VTROOT: %v", err)
	}

	// See if the hook exists.
	vthook := path.Join(root, "vthook", hook.Name)
	_, err = os.Stat(vthook)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, HOOK_DOES_NOT_EXIST, fmt.Errorf("missing hook %v", vthook)
		}

		return nil, HOOK_STAT_FAILED, fmt.Errorf("cannot stat hook %v: %v", vthook, err)
	}

	// Configure the command.
	log.Infof("hook: executing hook: %v %v", vthook, strings.Join(hook.Parameters, " "))
	cmd := exec.CommandContext(ctx, vthook, hook.Parameters...)
	if len(hook.ExtraEnv) > 0 {
		cmd.Env = os.Environ()
		for key, value := range hook.ExtraEnv {
			cmd.Env = append(cmd.Env, key+"="+value)
		}
	}

	return cmd, HOOK_SUCCESS, nil
}

// ExecuteContext tries to execute the Hook with the given context and returns a HookResult.
func (hook *Hook) ExecuteContext(ctx context.Context) (result *HookResult) {
	result = &HookResult{}

	// Find the hook.
	cmd, status, err := hook.findHook(ctx)
	if err != nil {
		result.ExitStatus = status
		result.Stderr = err.Error() + "\n"
		return result
	}

	// Run it.
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	start := time.Now()
	err = cmd.Run()
	duration := time.Since(start)

	result.Stdout = stdout.String()
	result.Stderr = stderr.String()

	defer func() {
		log.Infof("hook: result is %v", result.String())
	}()

	if err == nil {
		result.ExitStatus = HOOK_SUCCESS
		return result
	}

	if ctx.Err() != nil && errors.Is(ctx.Err(), context.DeadlineExceeded) {
		// When (exec.Cmd).Run hits a context cancelled, the process is killed via SIGTERM.
		// This means:
		// 	1. cmd.ProcessState.Exited() is false.
		//	2. cmd.ProcessState.ExitCode() is -1.
		// [ref]: https://golang.org/pkg/os/#ProcessState.ExitCode
		//
		// Therefore, we need to catch this error specifically, and set result.ExitStatus to
		// HOOK_TIMEOUT_ERROR, because just using ExitStatus will result in HOOK_DOES_NOT_EXIST,
		// which would be wrong. Since we're already doing some custom handling, we'll also include
		// the amount of time the command was running in the error string, in case that is helpful.
		result.ExitStatus = HOOK_TIMEOUT_ERROR
		result.Stderr += fmt.Sprintf("ERROR: (after %s) %s\n", duration, err)
		return result
	}

	if cmd.ProcessState != nil && cmd.ProcessState.Sys() != nil {
		result.ExitStatus = cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
	} else {
		result.ExitStatus = HOOK_CANNOT_GET_EXIT_STATUS
	}
	result.Stderr += "ERROR: " + err.Error() + "\n"

	return result
}

// Execute tries to execute the Hook and returns a HookResult.
func (hook *Hook) Execute() (result *HookResult) {
	return hook.ExecuteContext(context.Background())
}

// ExecuteOptional executes an optional hook, logs if it doesn't
// exist, and returns a printable error.
func (hook *Hook) ExecuteOptional() error {
	hr := hook.Execute()
	switch hr.ExitStatus {
	case HOOK_DOES_NOT_EXIST:
		log.Infof("%v hook doesn't exist", hook.Name)
	case HOOK_VTROOT_ERROR:
		log.Infof("VTROOT not set, so %v hook doesn't exist", hook.Name)
	case HOOK_SUCCESS:
		// nothing to do here
	default:
		return fmt.Errorf("%v hook failed(%v): %v", hook.Name, hr.ExitStatus, hr.Stderr)
	}
	return nil
}

// ExecuteAsWritePipe will execute the hook as in a Unix pipe,
// directing output to the provided writer. It will return:
// - an io.WriteCloser to write data to.
// - a WaitFunc method to call to wait for the process to exit,
// that returns stderr and the cmd.Wait() error.
// - an error code and an error if anything fails.
func (hook *Hook) ExecuteAsWritePipe(out io.Writer) (io.WriteCloser, WaitFunc, int, error) {
	// Find the hook.
	cmd, status, err := hook.findHook(context.Background())
	if err != nil {
		return nil, nil, status, err
	}

	// Configure the process's stdin, stdout, and stderr.
	in, err := cmd.StdinPipe()
	if err != nil {
		return nil, nil, HOOK_GENERIC_ERROR, fmt.Errorf("failed to configure stdin: %v", err)
	}
	cmd.Stdout = out
	var stderr bytes.Buffer
	cmd.Stderr = &stderr

	// Start the process.
	err = cmd.Start()
	if err != nil {
		status = HOOK_CANNOT_GET_EXIT_STATUS
		if cmd.ProcessState != nil && cmd.ProcessState.Sys() != nil {
			status = cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
		}
		return nil, nil, status, err
	}

	// And return
	return in, func() (string, error) {
		err := cmd.Wait()
		return stderr.String(), err
	}, HOOK_SUCCESS, nil
}

// ExecuteAsReadPipe will execute the hook as in a Unix pipe, reading
// from the provided reader. It will return:
// - an io.Reader to read piped data from.
// - a WaitFunc method to call to wait for the process to exit, that
// returns stderr and the Wait() error.
// - an error code and an error if anything fails.
func (hook *Hook) ExecuteAsReadPipe(in io.Reader) (io.Reader, WaitFunc, int, error) {
	// Find the hook.
	cmd, status, err := hook.findHook(context.Background())
	if err != nil {
		return nil, nil, status, err
	}

	// Configure the process's stdin, stdout, and stderr.
	out, err := cmd.StdoutPipe()
	if err != nil {
		return nil, nil, HOOK_GENERIC_ERROR, fmt.Errorf("failed to configure stdout: %v", err)
	}
	cmd.Stdin = in
	var stderr bytes.Buffer
	cmd.Stderr = &stderr

	// Start the process.
	err = cmd.Start()
	if err != nil {
		status = HOOK_CANNOT_GET_EXIT_STATUS
		if cmd.ProcessState != nil && cmd.ProcessState.Sys() != nil {
			status = cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
		}
		return nil, nil, status, err
	}

	// And return
	return out, func() (string, error) {
		err := cmd.Wait()
		return stderr.String(), err
	}, HOOK_SUCCESS, nil
}

// String returns a printable version of the HookResult
func (hr *HookResult) String() string {
	result := "result: "
	switch hr.ExitStatus {
	case HOOK_SUCCESS:
		result += "HOOK_SUCCESS"
	case HOOK_DOES_NOT_EXIST:
		result += "HOOK_DOES_NOT_EXIST"
	case HOOK_STAT_FAILED:
		result += "HOOK_STAT_FAILED"
	case HOOK_CANNOT_GET_EXIT_STATUS:
		result += "HOOK_CANNOT_GET_EXIT_STATUS"
	case HOOK_INVALID_NAME:
		result += "HOOK_INVALID_NAME"
	case HOOK_VTROOT_ERROR:
		result += "HOOK_VTROOT_ERROR"
	default:
		result += fmt.Sprintf("exit(%v)", hr.ExitStatus)
	}
	if hr.Stdout != "" {
		result += "\nstdout:\n" + hr.Stdout
	}
	if hr.Stderr != "" {
		result += "\nstderr:\n" + hr.Stderr
	}
	return result
}
<|endoftext|>


<commit_before>package db

import "skia.googlesource.com/buildbot.git/go/database"

const (
	// Default database parameters.
	PROD_DB_HOST = "173.194.104.24"
	PROD_DB_PORT = 3306
	PROD_DB_NAME = "skiacorrectness"
)

// MigrationSteps returns the migration (up and down) for the database.
func MigrationSteps() []database.MigrationStep {
	return migrationSteps
}

// migrationSteps define the steps it takes to migrate the db between versions.
// Note: Only add to this list, once a step has landed in version control it
// must not be changed.
var migrationSteps = []database.MigrationStep{
	// version 1
	{
		MySQLUp: []string{
			`CREATE TABLE expectations (
				id            INT        NOT NULL AUTO_INCREMENT PRIMARY KEY,
				userid        TEXT       NOT NULL,
				ts            BIGINT     NOT NULL,
				expectations  MEDIUMTEXT NOT NULL
			)`,
		},
		MySQLDown: []string{
			`DROP TABLE expectations`,
		},
	},

	// version 2
	{
		MySQLUp: []string{
			`CREATE TABLE ignorerule (
				id            INT        NOT NULL AUTO_INCREMENT PRIMARY KEY,
				userid        TEXT       NOT NULL,
				expires       BIGINT     NOT NULL,
				query         TEXT       NOT NULL,
				note          TEXT       NOT NULL,
				INDEX expires_idx(expires)
			)`,
		},
		MySQLDown: []string{
			`DROP TABLE ignorerule`,
		},
	},

	// Use this is a template for more migration steps.
	// version x
	// {
	// 	MySQLUp: ,
	// 	MySQLDown: ,
	// },
}
<commit_msg>This is the DB change only of the recently reverted CL<commit_after>package db

import "skia.googlesource.com/buildbot.git/go/database"

const (
	// Default database parameters.
	PROD_DB_HOST = "173.194.104.24"
	PROD_DB_PORT = 3306
	PROD_DB_NAME = "skiacorrectness"
)

// MigrationSteps returns the migration (up and down) for the database.
func MigrationSteps() []database.MigrationStep {
	return migrationSteps
}

// migrationSteps define the steps it takes to migrate the db between versions.
// Note: Only add to this list, once a step has landed in version control it
// must not be changed.
var migrationSteps = []database.MigrationStep{
	// version 1
	{
		MySQLUp: []string{
			`CREATE TABLE expectations (
				id            INT        NOT NULL AUTO_INCREMENT PRIMARY KEY,
				userid        TEXT       NOT NULL,
				ts            BIGINT     NOT NULL,
				expectations  MEDIUMTEXT NOT NULL
			)`,
		},
		MySQLDown: []string{
			`DROP TABLE expectations`,
		},
	},

	// version 2
	{
		MySQLUp: []string{
			`CREATE TABLE ignorerule (
				id            INT        NOT NULL AUTO_INCREMENT PRIMARY KEY,
				userid        TEXT       NOT NULL,
				expires       BIGINT     NOT NULL,
				query         TEXT       NOT NULL,
				note          TEXT       NOT NULL,
				INDEX expires_idx(expires)
			)`,
		},
		MySQLDown: []string{
			`DROP TABLE ignorerule`,
		},
	},

	// Use this is a template for more migration steps.
	// version 3
	{
		MySQLUp: []string{
			`CREATE TABLE exp_change (
				id            INT           NOT NULL AUTO_INCREMENT PRIMARY KEY,
				userid        VARCHAR(255)  NOT NULL,
				ts            BIGINT        NOT NULL,
				INDEX userid_idx(userid),
				INDEX ts_idx(ts)
			)`,
			`CREATE TABLE exp_test_change (
				changeid      INT           NOT NULL,
				name          VARCHAR(255)  NOT NULL,
				digest        VARCHAR(255)  NOT NULL,
				label         VARCHAR(255)  NOT NULL,
				removed       BIGINT,
				PRIMARY KEY (changeid, name, digest),
				INDEX expired_idx(removed)
			)`,
		},
		MySQLDown: []string{
			`DROP TABLE exp_test_change`,
			`DROP TABLE exp_change`,
		},
	},

	// Use this is a template for more migration steps.
	// version x
	// {
	// 	MySQLUp: ,
	// 	MySQLDown: ,
	// },
}
<|endoftext|>


<commit_before>/*
Package hamt is the unifying package between the 32bit and 64bit implementations
of Hash Array Mapped Tries (HAMT). HAMT datastructure make an efficient hashed
map data structure. You can `import hamt "github.com/lleo/go-hamt"`
then instantiate either a hamt32 or hamt64 datastructure with the
`hamt.NewHamt32()` or `hamt.NewHamt64()` functions. Both datastructures have
the same exported API defined by the Hamt interface.

Given how wide a HAMT node is (either 32 or 64 nodes wide) HAMT datastructures
not very deep; either 6, for 32bit, or 10, for 64bit implementations, nodes
deep. This neans HAMTs are effectively O(1) for Search, Insertions, and
Deletions.

Both 32 and 64 bit implementations of HAMTs are of fixed depth is because they
are [Tries](https://en.wikipedia.org/wiki/Trie). The key of a Trie is split
into n-number smaller indecies and each node from the root uses each successive
index.

In the case of a this HAMT implementation the key is hashed into a 30 or 60 bit
number. In the case of the stringkey we take the []byte slice of the string
and feed it to hash.fnv.New32() or New64() hash generator. Since these
generate 32 and 64 bit hash values respectively and we need 30 and 60 bit
values, we use the [xor-fold technique](http://www.isthe.com/chongo/tech/comp/fnv/index.html#xor-fold)
to "fold" the high 2 or 4 bits of the 32 and 64 bit hash values into 30 and
60 bit values for our needs.

We want 30 and 60 bit values because they split nicely into six 5bit and ten
6bit values respectively. Each of these 5 and 6 bit values become the indexies
of our Trie nodes with a maximum depth of 6 or 10 respectively. Further 5 bits
indexes into a 32 entry table nodes for 32 bit HAMTs and 6 bit index into 64
entry table nodes for 64 bit HAMTs; isn't that symmetrical :).

For a this HAMT implementation, when key/value pair must be created, deleted,
or changed the key is hashed into a 30 or 60 bit value (described above) and
that hash30 or hash60 value represents a path of 5 or 6 bit values to place a
leaf containing the key, value pair. For a Get() or Del() operation we lookup
the deepest node along that pate that is not-nil. For a Put() operation we
lookup the deepest location that is nil and not beyond the lenth of the path.

You may implement your own Key type by implementeding the Key interface
defined in "github.com/lleo/go-hamt/key" or you may used the example
StringKey interface described in "github.com/lleo/go-hamt/stringkey".
*/
package hamt

import (
	"github.com/lleo/go-hamt/hamt32"
	"github.com/lleo/go-hamt/hamt64"
	"github.com/lleo/go-hamt/key"
)

// Hamt interface defines all behavior for implementations of the
// Hash Array Mapped Trie datastructures in hammt32/ and hamt64/.
type Hamt interface {
	Get(key.Key) (interface{}, bool)
	Put(key.Key, interface{}) bool
	Del(key.Key) (interface{}, bool)
	IsEmpty() bool
	String() string
}

// NewHamt32 ...
func NewHamt32() Hamt {
	//return hamt32.NewHamt()
	return hamt32.New(hamt32.HybridTables)
}

// NewHamt64 ...
func NewHamt64() Hamt {
	return hamt64.New(hamt64.HybridTables)
}
<commit_msg>duplicated the table configuration options; don't worry I added a test to make sure it is always the same as in the hamt32 library<commit_after>/*
Package hamt is the unifying package between the 32bit and 64bit implementations
of Hash Array Mapped Tries (HAMT). HAMT datastructure make an efficient hashed
map data structure. You can `import hamt "github.com/lleo/go-hamt"`
then instantiate either a hamt32 or hamt64 datastructure with the
`hamt.NewHamt32()` or `hamt.NewHamt64()` functions. Both datastructures have
the same exported API defined by the Hamt interface.

Given how wide a HAMT node is (either 32 or 64 nodes wide) HAMT datastructures
not very deep; either 6, for 32bit, or 10, for 64bit implementations, nodes
deep. This neans HAMTs are effectively O(1) for Search, Insertions, and
Deletions.

Both 32 and 64 bit implementations of HAMTs are of fixed depth is because they
are [Tries](https://en.wikipedia.org/wiki/Trie). The key of a Trie is split
into n-number smaller indecies and each node from the root uses each successive
index.

In the case of a this HAMT implementation the key is hashed into a 30 or 60 bit
number. In the case of the stringkey we take the []byte slice of the string
and feed it to hash.fnv.New32() or New64() hash generator. Since these
generate 32 and 64 bit hash values respectively and we need 30 and 60 bit
values, we use the [xor-fold technique](http://www.isthe.com/chongo/tech/comp/fnv/index.html#xor-fold)
to "fold" the high 2 or 4 bits of the 32 and 64 bit hash values into 30 and
60 bit values for our needs.

We want 30 and 60 bit values because they split nicely into six 5bit and ten
6bit values respectively. Each of these 5 and 6 bit values become the indexies
of our Trie nodes with a maximum depth of 6 or 10 respectively. Further 5 bits
indexes into a 32 entry table nodes for 32 bit HAMTs and 6 bit index into 64
entry table nodes for 64 bit HAMTs; isn't that symmetrical :).

For a this HAMT implementation, when key/value pair must be created, deleted,
or changed the key is hashed into a 30 or 60 bit value (described above) and
that hash30 or hash60 value represents a path of 5 or 6 bit values to place a
leaf containing the key, value pair. For a Get() or Del() operation we lookup
the deepest node along that pate that is not-nil. For a Put() operation we
lookup the deepest location that is nil and not beyond the lenth of the path.

You may implement your own Key type by implementeding the Key interface
defined in "github.com/lleo/go-hamt/key" or you may used the example
StringKey interface described in "github.com/lleo/go-hamt/stringkey".
*/
package hamt

import (
	"github.com/lleo/go-hamt/hamt32"
	"github.com/lleo/go-hamt/hamt64"
	"github.com/lleo/go-hamt/key"
)

// Configuration contants to be passed to `hamt64.New(int) *Hamt`.
// WARNING!!! Duplicated code with both hamt32 and hamt64. Must have
// test to guarantee they stay in lock step.
const (
	// HybridTables indicates the structure should use compressedTable
	// initially, then upgrad to fullTable when appropriate.
	HybridTables = iota
	// CompTablesOnly indicates the structure should use compressedTables ONLY.
	// This was intended just save space, but also seems to be faster; CPU cache
	// locality maybe?
	CompTablesOnly
	// FullTableOnly indicates the structure should use fullTables ONLY.
	// This was intended to be for speed, as compressed tables use a software
	// bitCount function to access individual cells. Turns out, not so much.
	FullTablesOnly
)

// TableOptionName is a pedantic lookup table; given the configuration option
// it maps to the configuration option's name.
var TableOptionName = make(map[int]string, 3)

func init() {
	TableOptionName[0] = "HybridTables"
	TableOptionName[1] = "CompTablesOnly"
	TableOptionName[2] = "FullTablesOnly"
}

// Hamt interface defines all behavior for implementations of the
// Hash Array Mapped Trie datastructures in hammt32/ and hamt64/.
type Hamt interface {
	Get(key.Key) (interface{}, bool)
	Put(key.Key, interface{}) bool
	Del(key.Key) (interface{}, bool)
	IsEmpty() bool
	String() string
}

// NewHamt32 ...
func NewHamt32() Hamt {
	//return hamt32.NewHamt()
	return hamt32.New(hamt32.HybridTables)
}

// NewHamt64 ...
func NewHamt64() Hamt {
	return hamt64.New(hamt64.HybridTables)
}
<|endoftext|>