<commit_before>package main

import (
	"context"
	"fmt"
	"sort"
)

const (
	evalInf = 50000
)

// SearchPosition searches a Position to the specified depth via iterative deepening
// and returns the search results.
func SearchPosition(ctx context.Context, pos Position, depth int) Results {
	var results Results
	for d := 1; d <= depth; d++ {
		_, results = negamax(ctx, pos, results, Window{-evalInf, evalInf}, d, true, make([]int, d+1))
		if ctx.Err() != nil {
			break
		}
	}
	return results
}

// negamax recursively searches a Position to the specified depth and returns the evaluation score
// relative to the side to move and the search results. It employs alpha-beta pruning outside of
// the specified Window. If recommended is zero length, negamax will generate and search all legal
// moves; if recommended moves are provided, they must all be legal, and only they will be searched.
func negamax(ctx context.Context, pos Position, recommended Results, w Window, depth int, allowCutoff bool, counters []int) (bestScore RelScore, results Results) {
	counters[0]++

	if len(recommended) == 0 {
		moves := Candidates(pos) // pseudo-legal
		recommended = make(Results, 0, len(moves))
		for _, m := range moves {
			if !IsLegal(Make(pos, m)) {
				continue
			}
			recommended = append(recommended, Result{move: m})
		}
		if len(recommended) == 0 { // checkmate or stalemate
			if IsCheck(pos) {
				bestScore = -evalInf
			}
			return
		}
		if depth == 0 {
			bestScore = Eval(pos).Rel(pos.ToMove)
			return
		}
	}
	// Invariant: len(recommended) > 0 and recommended contains only legal moves

	results = make(Results, 0, len(recommended))
	defer func() { results.SortFor(pos.ToMove) }()

	for _, r := range recommended {
		score, cont := negamax(ctx, Make(pos, r.move), Results{}, w.Neg(), depth-1, allowCutoff, counters[1:])
		score *= -1

		results = results.Update(Result{move: r.move, score: score.Abs(pos.ToMove), depth: depth - 1, cont: cont})

		if depth >= 3 && ctx.Err() != nil {
			break
		}

		var constrained, ok bool
		w, constrained, ok = w.Constrain(score)
		if constrained {
			// improved lower bound
		}
		if !ok && allowCutoff {
			break
		}
	}

	return w.alpha, results
}

// IsPseudoLegal returns whether a Move is pseudo-legal in a Position.
// A move is pseudo-legal if the square to be moved from contains the specified piece
// and the piece is capable of moving to the target square if doing so would not put the king in check.
func IsPseudoLegal(pos Position, move Move) bool {
	for _, m := range PieceMoves[move.Piece](pos) {
		if m == move {
			return true
		}
	}
	return false
}

// IsLegal returns whether a Position is legal.
// A position is illegal if the king of the side that just moved is in check.
func IsLegal(pos Position) bool {
	return !IsAttacked(pos, pos.KingSquare[pos.Opp()], pos.ToMove)
}

// IsCheck returns whether the king of the side to move is in check.
func IsCheck(pos Position) bool {
	return IsAttacked(pos, pos.KingSquare[pos.ToMove], pos.Opp())
}

// IsTerminal returns whether or not a Position is checkmate or stalemate.
// A position is checkmate or stalemate if the side to move has no legal moves.
func IsTerminal(pos Position) bool { return !anyLegal(pos, Candidates(pos)) }

// anyLegal returns whether any of the given Moves are legal in the given Position.
func anyLegal(pos Position, moves []Move) bool {
	for _, m := range moves {
		if IsLegal(Make(pos, m)) {
			return true
		}
	}
	return false
}

// A Result holds a searched Move along with its evaluated Score,
// the search depth, and the continuation Results of the search.
type Result struct {
	move  Move
	score Score
	depth int
	cont  Results
}

// String returns a string representation of r, including its principal variation.
func (r Result) String() string {
	return fmt.Sprintf("%v (%v) %v", r.score, r.depth, r.PV())
}

// PV returns a string representation of r's principal variation.
func (r Result) PV() string {
	if r.depth == 0 || len(r.cont) == 0 {
		return LongAlgebraic(r.move)
	}
	return LongAlgebraic(r.move) + " " + r.cont[0].PV()
}

// A Results contains the results of a search. Results satisfies sort.Interface.
// A Results should be sorted by the function generating it before it is returned.
type Results []Result

func (rs Results) Len() int      { return len(rs) }
func (rs Results) Swap(i, j int) { rs[i], rs[j] = rs[j], rs[i] }

// Less reports whether the Result with index i should sort before the Result with index j.
// It sorts first by depth and then by score.
func (rs Results) Less(i, j int) bool {
	return rs[i].depth < rs[j].depth || rs[i].depth == rs[j].depth && rs[i].score < rs[j].score
}

// SortFor sorts r beginning with the best move for c.
func (rs Results) SortFor(c Color) {
	if c == White {
		// highest score first
		sort.Sort(sort.Reverse(rs))
	} else {
		sort.Sort(rs)
	}
}

// Update looks for a Result in rs with the same Move as r and replaces it with r,
// or appends r if there is none, and returns the possibly updated slice.
func (rs Results) Update(r Result) Results {
	for i := range rs {
		if rs[i].move == r.move {
			rs[i] = r
			return rs
		}
	}
	return append(rs, r)
}

// String returns a string representation of all Result values in r.
func (rs Results) String() string {
	var s string
	for _, r := range rs {
		s += fmt.Sprintf("%v\n", r.String())
	}
	return s
}

// A Window represents the bounds of a position's evaluation.
type Window struct{ alpha, beta RelScore }

// Constrain updates the lower bound of w, if applicable, and returns the updated window,
// whether the lower bound was changed, and whether the returned Window remains valid.
func (w Window) Constrain(n RelScore) (c Window, constrained bool, ok bool) {
	if n <= w.alpha {
		return w, false, true
	}
	return Window{n, w.beta}, true, n <= w.beta
}

// Neg returns the additive inverse of w.
func (w Window) Neg() Window { return Window{-w.beta, -w.alpha} }
<commit_msg>use previous search results<commit_after>package main

import (
	"context"
	"fmt"
	"sort"
)

const (
	evalInf = 50000
)

// SearchPosition searches a Position to the specified depth via iterative deepening
// and returns the search results.
func SearchPosition(ctx context.Context, pos Position, depth int) Results {
	var results Results
	for d := 1; d <= depth; d++ {
		_, results = negamax(ctx, pos, results, Window{-evalInf, evalInf}, d, true, make([]int, d+1))
		if ctx.Err() != nil {
			break
		}
	}
	return results
}

// negamax recursively searches a Position to the specified depth and returns the evaluation score
// relative to the side to move and the search results. It employs alpha-beta pruning outside of
// the specified Window. If recommended is zero length, negamax will generate and search all legal
// moves; if recommended moves are provided, they must all be legal, and only they will be searched.
func negamax(ctx context.Context, pos Position, recommended Results, w Window, depth int, allowCutoff bool, counters []int) (bestScore RelScore, results Results) {
	counters[0]++

	if len(recommended) == 0 {
		moves := Candidates(pos) // pseudo-legal
		recommended = make(Results, 0, len(moves))
		for _, m := range moves {
			if !IsLegal(Make(pos, m)) {
				continue
			}
			recommended = append(recommended, Result{move: m})
		}
		if len(recommended) == 0 { // checkmate or stalemate
			if IsCheck(pos) {
				bestScore = -evalInf
			}
			return
		}
		if depth == 0 {
			bestScore = Eval(pos).Rel(pos.ToMove)
			return
		}
	}
	// Invariant: len(recommended) > 0 and recommended contains only legal moves

	results = recommended
	defer func() { results.SortFor(pos.ToMove) }()

	for _, r := range recommended {
		score, cont := negamax(ctx, Make(pos, r.move), r.cont, w.Neg(), depth-1, allowCutoff, counters[1:])
		score *= -1

		results = results.Update(Result{move: r.move, score: score.Abs(pos.ToMove), depth: depth - 1, cont: cont})

		if depth >= 3 && ctx.Err() != nil {
			break
		}

		var constrained, ok bool
		w, constrained, ok = w.Constrain(score)
		if constrained {
			// improved lower bound
		}
		if !ok && allowCutoff {
			break
		}
	}

	return w.alpha, results
}

// IsPseudoLegal returns whether a Move is pseudo-legal in a Position.
// A move is pseudo-legal if the square to be moved from contains the specified piece
// and the piece is capable of moving to the target square if doing so would not put the king in check.
func IsPseudoLegal(pos Position, move Move) bool {
	for _, m := range PieceMoves[move.Piece](pos) {
		if m == move {
			return true
		}
	}
	return false
}

// IsLegal returns whether a Position is legal.
// A position is illegal if the king of the side that just moved is in check.
func IsLegal(pos Position) bool {
	return !IsAttacked(pos, pos.KingSquare[pos.Opp()], pos.ToMove)
}

// IsCheck returns whether the king of the side to move is in check.
func IsCheck(pos Position) bool {
	return IsAttacked(pos, pos.KingSquare[pos.ToMove], pos.Opp())
}

// IsTerminal returns whether or not a Position is checkmate or stalemate.
// A position is checkmate or stalemate if the side to move has no legal moves.
func IsTerminal(pos Position) bool { return !anyLegal(pos, Candidates(pos)) }

// anyLegal returns whether any of the given Moves are legal in the given Position.
func anyLegal(pos Position, moves []Move) bool {
	for _, m := range moves {
		if IsLegal(Make(pos, m)) {
			return true
		}
	}
	return false
}

// A Result holds a searched Move along with its evaluated Score,
// the search depth, and the continuation Results of the search.
type Result struct {
	move  Move
	score Score
	depth int
	cont  Results
}

// String returns a string representation of r, including its principal variation.
func (r Result) String() string {
	return fmt.Sprintf("%v (%v) %v", r.score, r.depth, r.PV())
}

// PV returns a string representation of r's principal variation.
func (r Result) PV() string {
	if r.depth == 0 || len(r.cont) == 0 {
		return LongAlgebraic(r.move)
	}
	return LongAlgebraic(r.move) + " " + r.cont[0].PV()
}

// A Results contains the results of a search. Results satisfies sort.Interface.
// A Results should be sorted by the function generating it before it is returned.
type Results []Result

func (rs Results) Len() int      { return len(rs) }
func (rs Results) Swap(i, j int) { rs[i], rs[j] = rs[j], rs[i] }

// Less reports whether the Result with index i should sort before the Result with index j.
// It sorts first by depth and then by score.
func (rs Results) Less(i, j int) bool {
	return rs[i].depth < rs[j].depth || rs[i].depth == rs[j].depth && rs[i].score < rs[j].score
}

// SortFor sorts r beginning with the best move for c.
// TODO: Correctly sort by depth for Black
func (rs Results) SortFor(c Color) {
	if c == White {
		// highest score first
		sort.Sort(sort.Reverse(rs))
	} else {
		sort.Sort(rs)
	}
}

// Update looks for a Result in rs with the same Move as r and replaces it with r,
// or appends r if there is none, and returns the possibly updated slice.
func (rs Results) Update(r Result) Results {
	for i := range rs {
		if rs[i].move == r.move {
			rs[i] = r
			return rs
		}
	}
	return append(rs, r)
}

// String returns a string representation of all Result values in r.
func (rs Results) String() string {
	var s string
	for _, r := range rs {
		s += fmt.Sprintf("%v\n", r.String())
	}
	return s
}

// A Window represents the bounds of a position's evaluation.
type Window struct{ alpha, beta RelScore }

// Constrain updates the lower bound of w, if applicable, and returns the updated window,
// whether the lower bound was changed, and whether the returned Window remains valid.
func (w Window) Constrain(n RelScore) (c Window, constrained bool, ok bool) {
	if n <= w.alpha {
		return w, false, true
	}
	return Window{n, w.beta}, true, n <= w.beta
}

// Neg returns the additive inverse of w.
func (w Window) Neg() Window { return Window{-w.beta, -w.alpha} }
<|endoftext|>


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

package gonp

import (
	"bytes"
	"fmt"
	"io"
)

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

const (
	// limit of cordinate size
	DefaultRouteSize = 2000000
)

// SesType is manipulaton type
type SesType int

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

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

// Type constraints for element in SES
type Elem interface {
	// int32 overlaps rune
	~rune | ~string | ~byte | ~int | ~int8 | ~int16 | ~int64 | ~float32 | ~float64
}

// SesElem is element of SES
type SesElem[T Elem] struct {
	e    T
	t    SesType
	aIdx int
	bIdx int
}

// Diff is context for calculating difference between a and b
type Diff[T Elem] struct {
	a, b           []T
	m, n           int
	ed             int
	lcs            []T
	ses            []SesElem[T]
	reverse        bool
	path           []int
	onlyEd         bool
	pointWithRoute []PointWithRoute
	contextSize    int
	routeSize      int
}

// New is initializer of Diff
func New[T Elem](a, b []T) *Diff[T] {
	diff := new(Diff[T])
	m, n := len(a), len(b)
	reverse := false
	if m >= n {
		a, b = b, a
		m, n = n, m
		reverse = true
	}
	diff.a, diff.b = a, b
	diff.m, diff.n = m, n
	diff.reverse = reverse
	diff.onlyEd = false
	diff.contextSize = DefaultContextSize
	diff.routeSize = DefaultRouteSize
	return diff
}

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

// SetContextSize sets the context size of unified format difference
func (diff *Diff[T]) SetContextSize(n int) {
	diff.contextSize = n
}

// SetRouteSize sets the context size of unified format difference
func (diff *Diff[T]) SetRouteSize(n int) {
	diff.routeSize = n
}

// GetElem is getter of element of SES
func (e *SesElem[T]) GetElem() T {
	return e.e
}

// GetType is getter of manipulation type of SES
func (e *SesElem[T]) GetType() SesType {
	return e.t
}

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

// Lcs returns LCS (Longest Common Subsequence) between a and b
func (diff *Diff[T]) Lcs() []T {
	return diff.lcs
}

// Ses return SES (Shortest Edit Script) between a and b
func (diff *Diff[T]) Ses() []SesElem[T] {
	return diff.ses
}

// PrintSes prints shortest edit script between a and b
func (diff *Diff[T]) PrintSes() {
	fmt.Print(diff.SprintSes())
}

// SprintSes returns string about shortest edit script between a and b
func (diff *Diff[T]) SprintSes() string {
	var buf bytes.Buffer
	diff.FprintSes(&buf)
	return buf.String()
}

// FprintSes emit about shortest edit script between a and b to w
func (diff *Diff[T]) FprintSes(w io.Writer) {
	for _, e := range diff.ses {
		switch e.t {
		case SesDelete:
			fmt.Fprintf(w, "-%v\n", e.e)
		case SesAdd:
			fmt.Fprintf(w, "+%v\n", e.e)
		case SesCommon:
			fmt.Fprintf(w, " %v\n", e.e)
		}
	}
}

// Compose composes diff between a and b
func (diff *Diff[T]) Compose() {
ONP:
	fp := make([]int, diff.m+diff.n+3)
	diff.path = make([]int, diff.m+diff.n+3)
	diff.pointWithRoute = make([]PointWithRoute, 0)

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

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

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

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

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

		if fp[delta+offset] >= diff.n || len(diff.pointWithRoute) > diff.routeSize {
			diff.ed = delta + 2*p
			break
		}
	}

	if diff.onlyEd {
		return
	}

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

	if !diff.recordSeq(epc) {
		goto ONP
	}
}

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

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

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

	if !diff.onlyEd {
		diff.path[k+offset] = len(diff.pointWithRoute)
		diff.pointWithRoute = append(diff.pointWithRoute, PointWithRoute{x: x, y: y, r: r})
	}

	return y
}

func (diff *Diff[T]) recordSeq(epc []Point) bool {
	x, y := 1, 1
	px, py := 0, 0
	for i := len(epc) - 1; i >= 0; i-- {
		for (px < epc[i].x) || (py < epc[i].y) {
			if (epc[i].y - epc[i].x) > (py - px) {
				if diff.reverse {
					diff.ses = append(diff.ses, SesElem[T]{e: diff.b[py], t: SesDelete, aIdx: y, bIdx: 0})
				} else {
					diff.ses = append(diff.ses, SesElem[T]{e: diff.b[py], t: SesAdd, aIdx: 0, bIdx: y})
				}
				y++
				py++
			} else if epc[i].y-epc[i].x < py-px {
				if diff.reverse {
					diff.ses = append(diff.ses, SesElem[T]{e: diff.a[px], t: SesAdd, aIdx: 0, bIdx: x})
				} else {
					diff.ses = append(diff.ses, SesElem[T]{e: diff.a[px], t: SesDelete, aIdx: x, bIdx: 0})

				}
				x++
				px++
			} else {
				if diff.reverse {
					diff.lcs = append(diff.lcs, diff.b[py])
					diff.ses = append(diff.ses, SesElem[T]{e: diff.b[py], t: SesCommon, aIdx: y, bIdx: x})
				} else {
					diff.lcs = append(diff.lcs, diff.a[px])
					diff.ses = append(diff.ses, SesElem[T]{e: diff.a[px], t: SesCommon, aIdx: x, bIdx: y})
				}
				x++
				y++
				px++
				py++
			}
		}
	}

	if x > diff.m && y > diff.n {
		// all recording succeeded
	} else {
		a := diff.a[x-1:]
		b := diff.b[y-1:]
		m := len(a)
		n := len(b)
		reverse := false
		if m >= n {
			a, b = b, a
			m, n = n, m
			reverse = true
		}
		diff.a, diff.b = a, b
		diff.m, diff.n = m, n
		diff.reverse = reverse
		return false
	}

	return true
}

func max(x, y int) int {
	if x < y {
		return y
	}
	return x
}
<commit_msg>bugfix: fixed recording invalid SES when route size exeeds.<commit_after>// The algorithm implemented here is based on "An O(NP) Sequence Comparison Algorithm"
// by described by Sun Wu, Udi Manber and Gene Myers

package gonp

import (
	"bytes"
	"fmt"
	"io"
)

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

const (
	// limit of cordinate size
	DefaultRouteSize = 2000000
)

// SesType is manipulaton type
type SesType int

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

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

// Type constraints for element in SES
type Elem interface {
	// int32 overlaps rune
	~rune | ~string | ~byte | ~int | ~int8 | ~int16 | ~int64 | ~float32 | ~float64
}

// SesElem is element of SES
type SesElem[T Elem] struct {
	e    T
	t    SesType
	aIdx int
	bIdx int
}

// Diff is context for calculating difference between a and b
type Diff[T Elem] struct {
	a, b           []T
	m, n           int
	ed             int
	lcs            []T
	ses            []SesElem[T]
	reverse        bool
	path           []int
	onlyEd         bool
	pointWithRoute []PointWithRoute
	contextSize    int
	routeSize      int
}

// New is initializer of Diff
func New[T Elem](a, b []T) *Diff[T] {
	diff := new(Diff[T])
	m, n := len(a), len(b)
	reverse := false
	if m >= n {
		a, b = b, a
		m, n = n, m
		reverse = true
	}
	diff.a, diff.b = a, b
	diff.m, diff.n = m, n
	diff.reverse = reverse
	diff.onlyEd = false
	diff.contextSize = DefaultContextSize
	diff.routeSize = DefaultRouteSize
	return diff
}

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

// SetContextSize sets the context size of unified format difference
func (diff *Diff[T]) SetContextSize(n int) {
	diff.contextSize = n
}

// SetRouteSize sets the context size of unified format difference
func (diff *Diff[T]) SetRouteSize(n int) {
	diff.routeSize = n
}

// GetElem is getter of element of SES
func (e *SesElem[T]) GetElem() T {
	return e.e
}

// GetType is getter of manipulation type of SES
func (e *SesElem[T]) GetType() SesType {
	return e.t
}

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

// Lcs returns LCS (Longest Common Subsequence) between a and b
func (diff *Diff[T]) Lcs() []T {
	return diff.lcs
}

// Ses return SES (Shortest Edit Script) between a and b
func (diff *Diff[T]) Ses() []SesElem[T] {
	return diff.ses
}

// PrintSes prints shortest edit script between a and b
func (diff *Diff[T]) PrintSes() {
	fmt.Print(diff.SprintSes())
}

// SprintSes returns string about shortest edit script between a and b
func (diff *Diff[T]) SprintSes() string {
	var buf bytes.Buffer
	diff.FprintSes(&buf)
	return buf.String()
}

// FprintSes emit about shortest edit script between a and b to w
func (diff *Diff[T]) FprintSes(w io.Writer) {
	for _, e := range diff.ses {
		switch e.t {
		case SesDelete:
			fmt.Fprintf(w, "-%v\n", e.e)
		case SesAdd:
			fmt.Fprintf(w, "+%v\n", e.e)
		case SesCommon:
			fmt.Fprintf(w, " %v\n", e.e)
		}
	}
}

// Compose composes diff between a and b
func (diff *Diff[T]) Compose() {
ONP:
	fp := make([]int, diff.m+diff.n+3)
	diff.path = make([]int, diff.m+diff.n+3)
	diff.pointWithRoute = make([]PointWithRoute, 0)

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

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

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

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

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

		if fp[delta+offset] >= diff.n || len(diff.pointWithRoute) > diff.routeSize {
			diff.ed = delta + 2*p
			break
		}
	}

	if diff.onlyEd {
		return
	}

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

	if !diff.recordSeq(epc) {
		goto ONP
	}
}

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

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

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

	if !diff.onlyEd {
		diff.path[k+offset] = len(diff.pointWithRoute)
		diff.pointWithRoute = append(diff.pointWithRoute, PointWithRoute{x: x, y: y, r: r})
	}

	return y
}

func (diff *Diff[T]) recordSeq(epc []Point) bool {
	x, y := 1, 1
	px, py := 0, 0
	for i := len(epc) - 1; i >= 0; i-- {
		for (px < epc[i].x) || (py < epc[i].y) {
			if (epc[i].y - epc[i].x) > (py - px) {
				if diff.reverse {
					diff.ses = append(diff.ses, SesElem[T]{e: diff.b[py], t: SesDelete, aIdx: y, bIdx: 0})
				} else {
					diff.ses = append(diff.ses, SesElem[T]{e: diff.b[py], t: SesAdd, aIdx: 0, bIdx: y})
				}
				y++
				py++
			} else if epc[i].y-epc[i].x < py-px {
				if diff.reverse {
					diff.ses = append(diff.ses, SesElem[T]{e: diff.a[px], t: SesAdd, aIdx: 0, bIdx: x})
				} else {
					diff.ses = append(diff.ses, SesElem[T]{e: diff.a[px], t: SesDelete, aIdx: x, bIdx: 0})

				}
				x++
				px++
			} else {
				if diff.reverse {
					diff.lcs = append(diff.lcs, diff.b[py])
					diff.ses = append(diff.ses, SesElem[T]{e: diff.b[py], t: SesCommon, aIdx: y, bIdx: x})
				} else {
					diff.lcs = append(diff.lcs, diff.a[px])
					diff.ses = append(diff.ses, SesElem[T]{e: diff.a[px], t: SesCommon, aIdx: x, bIdx: y})
				}
				x++
				y++
				px++
				py++
			}
		}
	}

	if x > diff.m && y > diff.n {
		// all recording succeeded
	} else {
		diff.a = diff.a[x-1:]
		diff.b = diff.b[y-1:]
		diff.m = len(diff.a)
		diff.n = len(diff.b)
		return false
	}

	return true
}

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


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

// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris
// +build cgo

package user

import (
	"fmt"
	"strconv"
	"strings"
	"syscall"
	"unsafe"
)

/*
#cgo solaris CFLAGS: -D_POSIX_PTHREAD_SEMANTICS
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <stdlib.h>

static int mygetpwuid_r(int uid, struct passwd *pwd,
	char *buf, size_t buflen, struct passwd **result) {
	return getpwuid_r(uid, pwd, buf, buflen, result);
}

static int mygetpwnam_r(const char *name, struct passwd *pwd,
	char *buf, size_t buflen, struct passwd **result) {
	return getpwnam_r(name, pwd, buf, buflen, result);
}

static int mygetgrgid_r(int gid, struct group *grp,
	char *buf, size_t buflen, struct group **result) {
 return getgrgid_r(gid, grp, buf, buflen, result);
}
*/
import "C"

func current() (*User, error) {
	return lookupUnixUid(syscall.Getuid())
}

func lookupUser(username string) (*User, error) {
	var pwd C.struct_passwd
	var result *C.struct_passwd
	nameC := C.CString(username)
	defer C.free(unsafe.Pointer(nameC))

	buf := alloc(userBuffer)
	defer buf.free()

	err := retryWithBuffer(buf, func() syscall.Errno {
		// mygetpwnam_r is a wrapper around getpwnam_r to avoid
		// passing a size_t to getpwnam_r, because for unknown
		// reasons passing a size_t to getpwnam_r doesn't work on
		// Solaris.
		return syscall.Errno(C.mygetpwnam_r(nameC,
			&pwd,
			(*C.char)(buf.ptr),
			C.size_t(buf.size),
			&result))
	})
	if err != nil {
		return nil, fmt.Errorf("user: lookup username %s: %v", username, err)
	}
	if result == nil {
		return nil, UnknownUserError(username)
	}
	return buildUser(&pwd), err
}

func lookupUserId(uid string) (*User, error) {
	i, e := strconv.Atoi(uid)
	if e != nil {
		return nil, e
	}
	return lookupUnixUid(i)
}

func lookupUnixUid(uid int) (*User, error) {
	var pwd C.struct_passwd
	var result *C.struct_passwd

	buf := alloc(userBuffer)
	defer buf.free()

	err := retryWithBuffer(buf, func() syscall.Errno {
		// mygetpwuid_r is a wrapper around getpwuid_r to
		// to avoid using uid_t because C.uid_t(uid) for
		// unknown reasons doesn't work on linux.
		return syscall.Errno(C.mygetpwuid_r(C.int(uid),
			&pwd,
			(*C.char)(buf.ptr),
			C.size_t(buf.size),
			&result))
	})
	if err != nil {
		return nil, fmt.Errorf("user: lookup userid %d: %v", uid, err)
	}
	if result == nil {
		return nil, UnknownUserIdError(uid)
	}
	return buildUser(&pwd), nil
}

func buildUser(pwd *C.struct_passwd) *User {
	u := &User{
		Uid:      strconv.Itoa(int(pwd.pw_uid)),
		Gid:      strconv.Itoa(int(pwd.pw_gid)),
		Username: C.GoString(pwd.pw_name),
		Name:     C.GoString(pwd.pw_gecos),
		HomeDir:  C.GoString(pwd.pw_dir),
	}
	// The pw_gecos field isn't quite standardized. Some docs
	// say: "It is expected to be a comma separated list of
	// personal data where the first item is the full name of the
	// user."
	if i := strings.Index(u.Name, ","); i >= 0 {
		u.Name = u.Name[:i]
	}
	return u
}

func currentGroup() (*Group, error) {
	return lookupUnixGid(syscall.Getgid())
}

func lookupGroup(groupname string) (*Group, error) {
	var grp C.struct_group
	var result *C.struct_group

	buf := alloc(groupBuffer)
	defer buf.free()
	cname := C.CString(groupname)
	defer C.free(unsafe.Pointer(cname))

	err := retryWithBuffer(buf, func() syscall.Errno {
		return syscall.Errno(C.getgrnam_r(cname,
			&grp,
			(*C.char)(buf.ptr),
			C.size_t(buf.size),
			&result))
	})
	if err != nil {
		return nil, fmt.Errorf("user: lookup groupname %s: %v", groupname, err)
	}
	if result == nil {
		return nil, UnknownGroupError(groupname)
	}
	return buildGroup(&grp), nil
}

func lookupGroupId(gid string) (*Group, error) {
	i, e := strconv.Atoi(gid)
	if e != nil {
		return nil, e
	}
	return lookupUnixGid(i)
}

func lookupUnixGid(gid int) (*Group, error) {
	var grp C.struct_group
	var result *C.struct_group

	buf := alloc(groupBuffer)
	defer buf.free()

	err := retryWithBuffer(buf, func() syscall.Errno {
		// mygetgrgid_r is a wrapper around getgrgid_r to
		// to avoid using gid_t because C.gid_t(gid) for
		// unknown reasons doesn't work on linux.
		return syscall.Errno(C.mygetgrgid_r(C.int(gid),
			&grp,
			(*C.char)(buf.ptr),
			C.size_t(buf.size),
			&result))
	})
	if err != nil {
		return nil, fmt.Errorf("user: lookup groupid %d: %v", gid, err)
	}
	if result == nil {
		return nil, UnknownGroupIdError(strconv.Itoa(gid))
	}
	return buildGroup(&grp), nil
}

func buildGroup(grp *C.struct_group) *Group {
	g := &Group{
		Gid:  strconv.Itoa(int(grp.gr_gid)),
		Name: C.GoString(grp.gr_name),
	}
	return g
}

type bufferKind C.int

const (
	userBuffer  = bufferKind(C._SC_GETPW_R_SIZE_MAX)
	groupBuffer = bufferKind(C._SC_GETGR_R_SIZE_MAX)
)

func (k bufferKind) initialSize() C.size_t {
	sz := C.sysconf(C.int(k))
	if sz == -1 {
		// DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX.
		// Additionally, not all Linux systems have it, either. For
		// example, the musl libc returns -1.
		return 1024
	}
	if !isSizeReasonable(int64(sz)) {
		// Truncate.  If this truly isn't enough, retryWithBuffer will error on the first run.
		return maxBufferSize
	}
	return C.size_t(sz)
}

type memBuffer struct {
	ptr  unsafe.Pointer
	size C.size_t
}

func alloc(kind bufferKind) *memBuffer {
	sz := kind.initialSize()
	return &memBuffer{
		ptr:  C.malloc(sz),
		size: sz,
	}
}

func (mb *memBuffer) resize(newSize C.size_t) {
	mb.ptr = C.realloc(mb.ptr, newSize)
	mb.size = newSize
}

func (mb *memBuffer) free() {
	C.free(mb.ptr)
}

// retryWithBuffer repeatedly calls f(), increasing the size of the
// buffer each time, until f succeeds, fails with a non-ERANGE error,
// or the buffer exceeds a reasonable limit.
func retryWithBuffer(buf *memBuffer, f func() syscall.Errno) error {
	for {
		errno := f()
		if errno == 0 {
			return nil
		} else if errno != syscall.ERANGE {
			return errno
		}
		newSize := buf.size * 2
		if !isSizeReasonable(int64(newSize)) {
			return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize)
		}
		buf.resize(newSize)
	}
}

const maxBufferSize = 1 << 20

func isSizeReasonable(sz int64) bool {
	return sz > 0 && sz <= maxBufferSize
}
<commit_msg>os/user: wrap getgrnam_r to fix type issues<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris
// +build cgo

package user

import (
	"fmt"
	"strconv"
	"strings"
	"syscall"
	"unsafe"
)

/*
#cgo solaris CFLAGS: -D_POSIX_PTHREAD_SEMANTICS
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <stdlib.h>

static int mygetpwuid_r(int uid, struct passwd *pwd,
	char *buf, size_t buflen, struct passwd **result) {
	return getpwuid_r(uid, pwd, buf, buflen, result);
}

static int mygetpwnam_r(const char *name, struct passwd *pwd,
	char *buf, size_t buflen, struct passwd **result) {
	return getpwnam_r(name, pwd, buf, buflen, result);
}

static int mygetgrgid_r(int gid, struct group *grp,
	char *buf, size_t buflen, struct group **result) {
 return getgrgid_r(gid, grp, buf, buflen, result);
}

static int mygetgrnam_r(const char *name, struct group *grp,
	char *buf, size_t buflen, struct group **result) {
 return getgrnam_r(name, grp, buf, buflen, result);
}
*/
import "C"

func current() (*User, error) {
	return lookupUnixUid(syscall.Getuid())
}

func lookupUser(username string) (*User, error) {
	var pwd C.struct_passwd
	var result *C.struct_passwd
	nameC := C.CString(username)
	defer C.free(unsafe.Pointer(nameC))

	buf := alloc(userBuffer)
	defer buf.free()

	err := retryWithBuffer(buf, func() syscall.Errno {
		// mygetpwnam_r is a wrapper around getpwnam_r to avoid
		// passing a size_t to getpwnam_r, because for unknown
		// reasons passing a size_t to getpwnam_r doesn't work on
		// Solaris.
		return syscall.Errno(C.mygetpwnam_r(nameC,
			&pwd,
			(*C.char)(buf.ptr),
			C.size_t(buf.size),
			&result))
	})
	if err != nil {
		return nil, fmt.Errorf("user: lookup username %s: %v", username, err)
	}
	if result == nil {
		return nil, UnknownUserError(username)
	}
	return buildUser(&pwd), err
}

func lookupUserId(uid string) (*User, error) {
	i, e := strconv.Atoi(uid)
	if e != nil {
		return nil, e
	}
	return lookupUnixUid(i)
}

func lookupUnixUid(uid int) (*User, error) {
	var pwd C.struct_passwd
	var result *C.struct_passwd

	buf := alloc(userBuffer)
	defer buf.free()

	err := retryWithBuffer(buf, func() syscall.Errno {
		// mygetpwuid_r is a wrapper around getpwuid_r to
		// to avoid using uid_t because C.uid_t(uid) for
		// unknown reasons doesn't work on linux.
		return syscall.Errno(C.mygetpwuid_r(C.int(uid),
			&pwd,
			(*C.char)(buf.ptr),
			C.size_t(buf.size),
			&result))
	})
	if err != nil {
		return nil, fmt.Errorf("user: lookup userid %d: %v", uid, err)
	}
	if result == nil {
		return nil, UnknownUserIdError(uid)
	}
	return buildUser(&pwd), nil
}

func buildUser(pwd *C.struct_passwd) *User {
	u := &User{
		Uid:      strconv.Itoa(int(pwd.pw_uid)),
		Gid:      strconv.Itoa(int(pwd.pw_gid)),
		Username: C.GoString(pwd.pw_name),
		Name:     C.GoString(pwd.pw_gecos),
		HomeDir:  C.GoString(pwd.pw_dir),
	}
	// The pw_gecos field isn't quite standardized. Some docs
	// say: "It is expected to be a comma separated list of
	// personal data where the first item is the full name of the
	// user."
	if i := strings.Index(u.Name, ","); i >= 0 {
		u.Name = u.Name[:i]
	}
	return u
}

func currentGroup() (*Group, error) {
	return lookupUnixGid(syscall.Getgid())
}

func lookupGroup(groupname string) (*Group, error) {
	var grp C.struct_group
	var result *C.struct_group

	buf := alloc(groupBuffer)
	defer buf.free()
	cname := C.CString(groupname)
	defer C.free(unsafe.Pointer(cname))

	err := retryWithBuffer(buf, func() syscall.Errno {
		return syscall.Errno(C.mygetgrnam_r(cname,
			&grp,
			(*C.char)(buf.ptr),
			C.size_t(buf.size),
			&result))
	})
	if err != nil {
		return nil, fmt.Errorf("user: lookup groupname %s: %v", groupname, err)
	}
	if result == nil {
		return nil, UnknownGroupError(groupname)
	}
	return buildGroup(&grp), nil
}

func lookupGroupId(gid string) (*Group, error) {
	i, e := strconv.Atoi(gid)
	if e != nil {
		return nil, e
	}
	return lookupUnixGid(i)
}

func lookupUnixGid(gid int) (*Group, error) {
	var grp C.struct_group
	var result *C.struct_group

	buf := alloc(groupBuffer)
	defer buf.free()

	err := retryWithBuffer(buf, func() syscall.Errno {
		// mygetgrgid_r is a wrapper around getgrgid_r to
		// to avoid using gid_t because C.gid_t(gid) for
		// unknown reasons doesn't work on linux.
		return syscall.Errno(C.mygetgrgid_r(C.int(gid),
			&grp,
			(*C.char)(buf.ptr),
			C.size_t(buf.size),
			&result))
	})
	if err != nil {
		return nil, fmt.Errorf("user: lookup groupid %d: %v", gid, err)
	}
	if result == nil {
		return nil, UnknownGroupIdError(strconv.Itoa(gid))
	}
	return buildGroup(&grp), nil
}

func buildGroup(grp *C.struct_group) *Group {
	g := &Group{
		Gid:  strconv.Itoa(int(grp.gr_gid)),
		Name: C.GoString(grp.gr_name),
	}
	return g
}

type bufferKind C.int

const (
	userBuffer  = bufferKind(C._SC_GETPW_R_SIZE_MAX)
	groupBuffer = bufferKind(C._SC_GETGR_R_SIZE_MAX)
)

func (k bufferKind) initialSize() C.size_t {
	sz := C.sysconf(C.int(k))
	if sz == -1 {
		// DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX.
		// Additionally, not all Linux systems have it, either. For
		// example, the musl libc returns -1.
		return 1024
	}
	if !isSizeReasonable(int64(sz)) {
		// Truncate.  If this truly isn't enough, retryWithBuffer will error on the first run.
		return maxBufferSize
	}
	return C.size_t(sz)
}

type memBuffer struct {
	ptr  unsafe.Pointer
	size C.size_t
}

func alloc(kind bufferKind) *memBuffer {
	sz := kind.initialSize()
	return &memBuffer{
		ptr:  C.malloc(sz),
		size: sz,
	}
}

func (mb *memBuffer) resize(newSize C.size_t) {
	mb.ptr = C.realloc(mb.ptr, newSize)
	mb.size = newSize
}

func (mb *memBuffer) free() {
	C.free(mb.ptr)
}

// retryWithBuffer repeatedly calls f(), increasing the size of the
// buffer each time, until f succeeds, fails with a non-ERANGE error,
// or the buffer exceeds a reasonable limit.
func retryWithBuffer(buf *memBuffer, f func() syscall.Errno) error {
	for {
		errno := f()
		if errno == 0 {
			return nil
		} else if errno != syscall.ERANGE {
			return errno
		}
		newSize := buf.size * 2
		if !isSizeReasonable(int64(newSize)) {
			return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize)
		}
		buf.resize(newSize)
	}
}

const maxBufferSize = 1 << 20

func isSizeReasonable(sz int64) bool {
	return sz > 0 && sz <= maxBufferSize
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Ihsan Junaidi Ibrahim <ihsan@n3labs.my>
 */

package main

import (
	"flag"
	"fmt"
	"github.com/garyburd/redigo/redis"
	"os"
)

type SessionKey struct {
	MsgIdNext    string
	ServerIdNext string

	NServerAll      int
	NServerEnabled  int
	NServerDisabled int
	NServerActive   int
	NServerInactive int
}

const (
	APPNAME = "rdbtool"
	APPVER  = "0.1"
)

var rdb redis.Conn

func flush(db int) (err error) {
	if _, err = rdb.Do("select", db); err != nil {
		return
	}

	if _, err = rdb.Do("flushdb"); err != nil {
		return
	}

	return
}

func checkRedis(url, pw string) (err error) {
	if rdb == nil {
		rdb, err = redis.Dial("tcp", url)
		if err != nil {
			return
		}

		if _, err = rdb.Do("auth", pw); err != nil {
			return
		}
	}

	return
}

func main() {
	var (
		err      error
		debug    bool
		help     bool
		redisUrl = "dev1.int.n3labs.my:6379"
		redisPw  = "d0raemon"
		redisDb  int
	)

	flag.BoolVar(&debug, "d", true, "Debug mode")
	flag.BoolVar(&help, "h", false, "Display usage")
	flag.Parse()

	if help {
		usage()
	}

	if len(flag.Args()) > 3 {
		usage()
	}

	err = checkRedis(redisUrl, redisPw)
	if err != nil {
		fatal(err.Error())
	}
	defer rdb.Close()

	f := flag.Args()
	if f[0] == "session" {
		redisDb = 1
		switch f[1] {
		case "flush":
			err = flush(redisDb)
		case "reset":
			err = resetSession(redisDb)
		case "dump":
			err = dumpSession(redisDb)
		case "stat":
			err = statSession(redisDb)

		default:
			fatal("Invalid command")
		}
	} else if f[0] == "user" {
		redisDb = 2
		switch f[1] {
		case "flush":
			err = flush(redisDb)
		case "reset":
			err = resetUser(redisDb)
		case "dump":
			err = dumpUser(redisDb)
		case "exist":
			err = existUser(redisDb, f[2])

		default:
			fatal("Invalid command")
		}
	} else {
		fatal("Invalid operation")
	}

	if err != nil {
		fatal(err.Error())
	}
}

func fatal(a string, v ...interface{}) {
	var n string

	if len(v) != 0 {
		n = fmt.Sprintf(a, v...)
	} else {
		n = a
	}

	fmt.Fprintf(os.Stderr, "Fatal: "+n+"\n")
	os.Exit(1)
}

func usage() {
	str := fmt.Sprintf("%v-%v\nusage: %v [-d] [-h] op cmd arg\n", APPNAME,
		APPVER, APPNAME)

	fmt.Fprintf(os.Stderr, str)
	os.Exit(1)
}
<commit_msg>Update main.go<commit_after>/*
 * Copyright (c) 2013 Ihsan Junaidi Ibrahim <ihsan.junaidi@gmail.com>
 */

package main

import (
	"flag"
	"fmt"
	"github.com/garyburd/redigo/redis"
	"os"
)

type SessionKey struct {
	MsgIdNext    string
	ServerIdNext string

	NServerAll      int
	NServerEnabled  int
	NServerDisabled int
	NServerActive   int
	NServerInactive int
}

const (
	APPNAME = "rdbtool"
	APPVER  = "0.1"
)

var rdb redis.Conn

func flush(db int) (err error) {
	if _, err = rdb.Do("select", db); err != nil {
		return
	}

	if _, err = rdb.Do("flushdb"); err != nil {
		return
	}

	return
}

func checkRedis(url, pw string) (err error) {
	if rdb == nil {
		rdb, err = redis.Dial("tcp", url)
		if err != nil {
			return
		}

		if _, err = rdb.Do("auth", pw); err != nil {
			return
		}
	}

	return
}

func main() {
	var (
		err      error
		debug    bool
		help     bool
		redisUrl = "dev1.int.n3labs.my:6379"
		redisPw  = "d0raemon"
		redisDb  int
	)

	flag.BoolVar(&debug, "d", true, "Debug mode")
	flag.BoolVar(&help, "h", false, "Display usage")
	flag.Parse()

	if help {
		usage()
	}

	if len(flag.Args()) > 3 {
		usage()
	}

	err = checkRedis(redisUrl, redisPw)
	if err != nil {
		fatal(err.Error())
	}
	defer rdb.Close()

	f := flag.Args()
	if f[0] == "session" {
		redisDb = 1
		switch f[1] {
		case "flush":
			err = flush(redisDb)
		case "reset":
			err = resetSession(redisDb)
		case "dump":
			err = dumpSession(redisDb)
		case "stat":
			err = statSession(redisDb)

		default:
			fatal("Invalid command")
		}
	} else if f[0] == "user" {
		redisDb = 2
		switch f[1] {
		case "flush":
			err = flush(redisDb)
		case "reset":
			err = resetUser(redisDb)
		case "dump":
			err = dumpUser(redisDb)
		case "exist":
			err = existUser(redisDb, f[2])

		default:
			fatal("Invalid command")
		}
	} else {
		fatal("Invalid operation")
	}

	if err != nil {
		fatal(err.Error())
	}
}

func fatal(a string, v ...interface{}) {
	var n string

	if len(v) != 0 {
		n = fmt.Sprintf(a, v...)
	} else {
		n = a
	}

	fmt.Fprintf(os.Stderr, "Fatal: "+n+"\n")
	os.Exit(1)
}

func usage() {
	str := fmt.Sprintf("%v-%v\nusage: %v [-d] [-h] op cmd arg\n", APPNAME,
		APPVER, APPNAME)

	fmt.Fprintf(os.Stderr, str)
	os.Exit(1)
}
<|endoftext|>


<commit_before>package docker

import (
  "github.com/ViBiOh/dashboard/auth"
)

const adminUser = `admin`
const multiAppUser = `multi`

func isAdmin(user *auth.User) bool {
  if user != nil {
    return user.role == adminUser
  }
  return false
}

func isMultiApp(user *auth.User) bool {
  if user != nil {
    return user.role == multiAppUser || user.role == adminUser
  }
  return false
}
<commit_msg>Create user.go<commit_after>package docker

import (
  "github.com/ViBiOh/dashboard/auth"
)

const adminUser = `admin`
const multiAppUser = `multi`

func isAdmin(user *auth.User) bool {
  if user != nil {
    return user.role == adminUser
  }
  return false
}

func isMultiApp(user *auth.User) bool {
  if user != nil {
    return user.role == multiAppUser || user.role == adminUser
  }
  return false
}

func isAllowed(loggedUser *User, containerID string) (bool, error) {
	if !isAdmin(loggedUser) {
		container, err := inspectContainer(string(containerID))
		if err != nil {
			return false, err
		}

		owner, ok := container.Config.Labels[ownerLabel]
		if !ok || owner != loggedUser.username {
			return false, nil
		}
	}

	return true, nil
}
<|endoftext|>


<commit_before>package main

import (
  "github.com/elazarl/goproxy"
  "log"
  "net/http"
)

func main() {
  proxy := goproxy.NewProxyHttpServer()
  proxy.Verbose = true
  log.Fatal(http.ListenAndServe(":8384", proxy))
}
<commit_msg>Add check for *.docker.io<commit_after>package main

import (
  "github.com/elazarl/goproxy"
  "net/http"
  "regexp"
)

func main() {
  proxy := goproxy.NewProxyHttpServer()
  proxy.OnRequest().DoFunc(
    func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
      match, _ := regexp.MatchString("^*.docker.io$", r.URL.Host)
      if match == false {
        return r, goproxy.NewResponse(r, goproxy.ContentTypeText, http.StatusForbidden, "Don't use it for other proxy!")
      } else {
        return r, nil
      }
    })
  proxy.Verbose = true
  http.ListenAndServe(":8384", proxy)
}
<|endoftext|>


<commit_before>package mint

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"fmt"
	"io"
	"sync"
)

const (
	sequenceNumberLen = 8       // sequence number length
	recordHeaderLen   = 5       // record header length
	maxFragmentLen    = 1 << 14 // max number of bytes in a record
)

// struct {
//     ContentType type;
//     ProtocolVersion record_version = { 3, 1 };    /* TLS v1.x */
//     uint16 length;
//     opaque fragment[TLSPlaintext.length];
// } TLSPlaintext;
type tlsPlaintext struct {
	// Omitted: record_version (static)
	// Omitted: length         (computed from fragment)
	contentType recordType
	fragment    []byte
}

type recordLayer struct {
	sync.Mutex

	conn     io.ReadWriter // The underlying connection
	buffer   []byte        // The next record to send
	nextData []byte        // The next record to send

	ivLength int         // Length of the seq and nonce fields
	seq      []byte      // Zero-padded sequence number
	nonce    []byte      // Buffer for per-record nonces
	cipher   cipher.AEAD // AEAD cipher
}

func newRecordLayer(conn io.ReadWriter) *recordLayer {
	r := recordLayer{}
	r.conn = conn
	r.ivLength = 0
	return &r
}

func (r *recordLayer) Rekey(suite cipherSuite, key []byte, iv []byte) error {
	switch suite {
	case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
		TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
		TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
		TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
		TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
		TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
		TLS_PSK_WITH_AES_128_GCM_SHA256,
		TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256,
		TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384,
		TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
		TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
		params := cipherSuiteMap[suite]

		if len(key) != params.keyLen || len(iv) != params.ivLen {
			return fmt.Errorf("tls.rekey: Crypto parameters are the wrong size")
		}

		aes, _ := aes.NewCipher(key)
		gcm, _ := cipher.NewGCMWithNonceSize(aes, len(iv))
		r.cipher = gcm
		r.ivLength = len(iv)
	default:
		return fmt.Errorf("tls.rekey: Unsupported ciphersuite: %x", suite)
	}

	r.seq = bytes.Repeat([]byte{0}, r.ivLength)
	r.nonce = make([]byte, r.ivLength)
	copy(r.nonce, iv)
	return nil
}

func (r *recordLayer) incrementSequenceNumber() {
	if r.ivLength == 0 {
		return
	}

	for i := r.ivLength - 1; i > r.ivLength-sequenceNumberLen; i-- {
		r.seq[i]++
		r.nonce[i] ^= (r.seq[i] - 1) ^ r.seq[i]
		if r.seq[i] != 0 {
			return
		}
	}

	// Not allowed to let sequence number wrap.
	// Instead, must renegotiate before it does.
	// Not likely enough to bother.
	panic("TLS: sequence number wraparound")
}

func (r *recordLayer) encrypt(pt *tlsPlaintext, padLen int) *tlsPlaintext {
	// Expand the fragment to hold contentType, padding, and overhead
	originalLen := len(pt.fragment)
	plaintextLen := originalLen + 1 + padLen
	ciphertextLen := plaintextLen + r.cipher.Overhead()

	// Assemble the revised plaintext
	out := &tlsPlaintext{
		contentType: recordTypeApplicationData,
		fragment:    make([]byte, ciphertextLen),
	}
	copy(out.fragment, pt.fragment)
	out.fragment[originalLen] = byte(pt.contentType)
	for i := 1; i <= padLen; i++ {
		out.fragment[originalLen+i] = 0
	}

	// Encrypt the fragment
	payload := out.fragment[:plaintextLen]
	r.cipher.Seal(payload[:0], r.nonce, payload, nil)
	return out
}

func (r *recordLayer) decrypt(pt *tlsPlaintext) (*tlsPlaintext, int, error) {
	decryptLen := len(pt.fragment) - r.cipher.Overhead()
	out := &tlsPlaintext{
		contentType: pt.contentType,
		fragment:    make([]byte, decryptLen),
	}

	// Decrypt
	_, err := r.cipher.Open(out.fragment[:0], r.nonce, pt.fragment, nil)
	if err != nil {
		return nil, 0, fmt.Errorf("tls.record.decrypt: AEAD decrypt failed")
	}

	// Find the padding boundary
	padLen := 0
	for ; padLen < decryptLen+1 && out.fragment[decryptLen-padLen-1] == 0; padLen++ {
	}

	// Transfer the content type
	newLen := decryptLen - padLen - 1
	out.contentType = recordType(out.fragment[newLen])

	// Truncate the message to remove contentType, padding, overhead
	out.fragment = out.fragment[:newLen]
	return out, padLen, nil
}

func (r *recordLayer) readFullBuffer(data []byte) error {
	buffer := make([]byte, cap(data)+recordHeaderLen)

	var index int
	copy(buffer, r.nextData)
	index = len(r.nextData)

	for {
		m, err := r.conn.Read(buffer[index:])
		if m+index >= cap(data) {
			// TODO(bradfitz,agl): slightly suspicious
			// that we're throwing away r.Read's err here.
			copy(data[:cap(data)], buffer)
			r.nextData = buffer[cap(data) : m+index]
			return nil
		}
		if err != nil {
			return err
		}
		index = index + m
	}
}

func (r *recordLayer) ReadRecord() (*tlsPlaintext, error) {
	pt := &tlsPlaintext{}
	header := make([]byte, recordHeaderLen)
	err := r.readFullBuffer(header)
	if err != nil {
		return nil, err
	}

	// Validate content type
	switch recordType(header[0]) {
	default:
		return nil, fmt.Errorf("tls.record: Unknown content type %02x", header[0])
	case recordTypeAlert, recordTypeHandshake, recordTypeApplicationData:
		pt.contentType = recordType(header[0])
	}

	// Validate version
	if !allowWrongVersionNumber && (header[1] != 0x03 || header[2] != 0x01) {
		return nil, fmt.Errorf("tls.record: Invalid version %02x%02x", header[1], header[2])
	}

	// Validate size < max
	size := (int(header[3]) << 8) + int(header[4])
	if size > maxFragmentLen {
		return nil, fmt.Errorf("tls.record: Record size too big")
	}

	// Attempt to read fragment
	pt.fragment = make([]byte, size)
	err = r.readFullBuffer(pt.fragment[:0])
	if err != nil {
		return nil, err
	}

	// Attempt to decrypt fragment
	if r.cipher != nil {
		pt, _, err = r.decrypt(pt)
		if err != nil {
			return nil, err
		}
	}

	logf(logTypeIO, "recordLayer.ReadRecord [%d] [%x]", pt.contentType, pt.fragment)

	r.incrementSequenceNumber()
	return pt, nil
}

func (r *recordLayer) WriteRecord(pt *tlsPlaintext) error {
	return r.WriteRecordWithPadding(pt, 0)
}

func (r *recordLayer) WriteRecordWithPadding(pt *tlsPlaintext, padLen int) error {
	if r.cipher != nil {
		pt = r.encrypt(pt, padLen)
	} else if padLen > 0 {
		return fmt.Errorf("tls.record: Padding can only be done on encrypted records")
	}

	if len(pt.fragment) > maxFragmentLen {
		return fmt.Errorf("tls.record: Record size too big")
	}

	length := len(pt.fragment)
	header := []byte{byte(pt.contentType), 0x03, 0x01, byte(length >> 8), byte(length)}
	record := append(header, pt.fragment...)

	logf(logTypeIO, "recordLayer.WriteRecord [%d] [%x]", pt.contentType, pt.fragment)

	r.incrementSequenceNumber()
	_, err := r.conn.Write(record)
	return err
}
<commit_msg>Enforce different length checks for plaintext vs. ciphertext<commit_after>package mint

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"fmt"
	"io"
	"sync"
)

const (
	sequenceNumberLen = 8       // sequence number length
	recordHeaderLen   = 5       // record header length
	maxFragmentLen    = 1 << 14 // max number of bytes in a record
)

// struct {
//     ContentType type;
//     ProtocolVersion record_version = { 3, 1 };    /* TLS v1.x */
//     uint16 length;
//     opaque fragment[TLSPlaintext.length];
// } TLSPlaintext;
type tlsPlaintext struct {
	// Omitted: record_version (static)
	// Omitted: length         (computed from fragment)
	contentType recordType
	fragment    []byte
}

type recordLayer struct {
	sync.Mutex

	conn     io.ReadWriter // The underlying connection
	buffer   []byte        // The next record to send
	nextData []byte        // The next record to send

	ivLength int         // Length of the seq and nonce fields
	seq      []byte      // Zero-padded sequence number
	nonce    []byte      // Buffer for per-record nonces
	cipher   cipher.AEAD // AEAD cipher
}

func newRecordLayer(conn io.ReadWriter) *recordLayer {
	r := recordLayer{}
	r.conn = conn
	r.ivLength = 0
	return &r
}

func (r *recordLayer) Rekey(suite cipherSuite, key []byte, iv []byte) error {
	switch suite {
	case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
		TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
		TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
		TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
		TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
		TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
		TLS_PSK_WITH_AES_128_GCM_SHA256,
		TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256,
		TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384,
		TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
		TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
		params := cipherSuiteMap[suite]

		if len(key) != params.keyLen || len(iv) != params.ivLen {
			return fmt.Errorf("tls.rekey: Crypto parameters are the wrong size")
		}

		aes, _ := aes.NewCipher(key)
		gcm, _ := cipher.NewGCMWithNonceSize(aes, len(iv))
		r.cipher = gcm
		r.ivLength = len(iv)
	default:
		return fmt.Errorf("tls.rekey: Unsupported ciphersuite: %x", suite)
	}

	r.seq = bytes.Repeat([]byte{0}, r.ivLength)
	r.nonce = make([]byte, r.ivLength)
	copy(r.nonce, iv)
	return nil
}

func (r *recordLayer) incrementSequenceNumber() {
	if r.ivLength == 0 {
		return
	}

	for i := r.ivLength - 1; i > r.ivLength-sequenceNumberLen; i-- {
		r.seq[i]++
		r.nonce[i] ^= (r.seq[i] - 1) ^ r.seq[i]
		if r.seq[i] != 0 {
			return
		}
	}

	// Not allowed to let sequence number wrap.
	// Instead, must renegotiate before it does.
	// Not likely enough to bother.
	panic("TLS: sequence number wraparound")
}

func (r *recordLayer) encrypt(pt *tlsPlaintext, padLen int) *tlsPlaintext {
	// Expand the fragment to hold contentType, padding, and overhead
	originalLen := len(pt.fragment)
	plaintextLen := originalLen + 1 + padLen
	ciphertextLen := plaintextLen + r.cipher.Overhead()

	// Assemble the revised plaintext
	out := &tlsPlaintext{
		contentType: recordTypeApplicationData,
		fragment:    make([]byte, ciphertextLen),
	}
	copy(out.fragment, pt.fragment)
	out.fragment[originalLen] = byte(pt.contentType)
	for i := 1; i <= padLen; i++ {
		out.fragment[originalLen+i] = 0
	}

	// Encrypt the fragment
	payload := out.fragment[:plaintextLen]
	r.cipher.Seal(payload[:0], r.nonce, payload, nil)
	return out
}

func (r *recordLayer) decrypt(pt *tlsPlaintext) (*tlsPlaintext, int, error) {
	decryptLen := len(pt.fragment) - r.cipher.Overhead()
	out := &tlsPlaintext{
		contentType: pt.contentType,
		fragment:    make([]byte, decryptLen),
	}

	// Decrypt
	_, err := r.cipher.Open(out.fragment[:0], r.nonce, pt.fragment, nil)
	if err != nil {
		return nil, 0, fmt.Errorf("tls.record.decrypt: AEAD decrypt failed")
	}

	// Find the padding boundary
	padLen := 0
	for ; padLen < decryptLen+1 && out.fragment[decryptLen-padLen-1] == 0; padLen++ {
	}

	// Transfer the content type
	newLen := decryptLen - padLen - 1
	out.contentType = recordType(out.fragment[newLen])

	// Truncate the message to remove contentType, padding, overhead
	out.fragment = out.fragment[:newLen]
	return out, padLen, nil
}

func (r *recordLayer) readFullBuffer(data []byte) error {
	buffer := make([]byte, cap(data)+recordHeaderLen)

	var index int
	copy(buffer, r.nextData)
	index = len(r.nextData)

	for {
		m, err := r.conn.Read(buffer[index:])
		if m+index >= cap(data) {
			// TODO(bradfitz,agl): slightly suspicious
			// that we're throwing away r.Read's err here.
			copy(data[:cap(data)], buffer)
			r.nextData = buffer[cap(data) : m+index]
			return nil
		}
		if err != nil {
			return err
		}
		index = index + m
	}
}

func (r *recordLayer) ReadRecord() (*tlsPlaintext, error) {
	pt := &tlsPlaintext{}
	header := make([]byte, recordHeaderLen)
	err := r.readFullBuffer(header)
	if err != nil {
		return nil, err
	}

	// Validate content type
	switch recordType(header[0]) {
	default:
		return nil, fmt.Errorf("tls.record: Unknown content type %02x", header[0])
	case recordTypeAlert, recordTypeHandshake, recordTypeApplicationData:
		pt.contentType = recordType(header[0])
	}

	// Validate version
	if !allowWrongVersionNumber && (header[1] != 0x03 || header[2] != 0x01) {
		return nil, fmt.Errorf("tls.record: Invalid version %02x%02x", header[1], header[2])
	}

	// Validate size < max
	size := (int(header[3]) << 8) + int(header[4])
	if size > maxFragmentLen+256 {
		return nil, fmt.Errorf("tls.record: Ciphertext size too big")
	}

	// Attempt to read fragment
	pt.fragment = make([]byte, size)
	err = r.readFullBuffer(pt.fragment[:0])
	if err != nil {
		return nil, err
	}

	// Attempt to decrypt fragment
	if r.cipher != nil {
		pt, _, err = r.decrypt(pt)
		if err != nil {
			return nil, err
		}
	}

	// Check that plaintext length is not too long
	if len(pt.fragment) > maxFragmentLen {
		return nil, fmt.Errorf("tls.record: Plaintext size too big")
	}

	logf(logTypeIO, "recordLayer.ReadRecord [%d] [%x]", pt.contentType, pt.fragment)

	r.incrementSequenceNumber()
	return pt, nil
}

func (r *recordLayer) WriteRecord(pt *tlsPlaintext) error {
	return r.WriteRecordWithPadding(pt, 0)
}

func (r *recordLayer) WriteRecordWithPadding(pt *tlsPlaintext, padLen int) error {
	if r.cipher != nil {
		pt = r.encrypt(pt, padLen)
	} else if padLen > 0 {
		return fmt.Errorf("tls.record: Padding can only be done on encrypted records")
	}

	if len(pt.fragment) > maxFragmentLen {
		return fmt.Errorf("tls.record: Record size too big")
	}

	length := len(pt.fragment)
	header := []byte{byte(pt.contentType), 0x03, 0x01, byte(length >> 8), byte(length)}
	record := append(header, pt.fragment...)

	logf(logTypeIO, "recordLayer.WriteRecord [%d] [%x]", pt.contentType, pt.fragment)

	r.incrementSequenceNumber()
	_, err := r.conn.Write(record)
	return err
}
<|endoftext|>


<commit_before>package aspect

import (
	"fmt"
	"strings"
)

// Selectable is an interface that allows both tables and columns to be
// selected. It is implemented by TableElem and ColumnElem.
type Selectable interface {
	Selectable() []ColumnElem
}

// SelectStmt is the internal representation of an SQL SELECT statement.
type SelectStmt struct {
	tables  []*TableElem
	columns []ColumnElem
	joins   []JoinStmt
	cond    Clause
	groupBy []ColumnElem
	order   []OrderedColumn
	limit   int
	offset  int
	err     error
}

// TableExists checks if a table already exists in the SELECT statement.
func (stmt SelectStmt) TableExists(name string) bool {
	for _, table := range stmt.tables {
		if table.Name == name {
			return true
		}
	}
	return false
}

func (stmt SelectStmt) CompileTables(d Dialect, params *Parameters) []string {
	names := make([]string, len(stmt.tables))
	for i, table := range stmt.tables {
		names[i] = table.Compile(d, params)
	}
	return names
}

func (stmt SelectStmt) CompileColumns(d Dialect, params *Parameters) []string {
	names := make([]string, len(stmt.columns))
	for i, c := range stmt.columns {
		names[i], _ = c.Compile(d, params)
	}
	return names
}

// String outputs the parameter-less SELECT statement in a neutral dialect.
func (stmt SelectStmt) String() string {
	compiled, _ := stmt.Compile(&defaultDialect{}, Params())
	return compiled
}

// Compile outputs the SELECT statement using the given dialect and parameters.
// An error may be returned because of a pre-existing error or because
// an error occurred during compilation.
func (stmt SelectStmt) Compile(d Dialect, params *Parameters) (string, error) {
	if stmt.err != nil {
		return "", stmt.err
	}
	compiled := fmt.Sprintf(
		"SELECT %s FROM %s",
		strings.Join(stmt.CompileColumns(d, params), ", "),
		strings.Join(stmt.CompileTables(d, params), ", "),
	)
	if stmt.joins != nil && len(stmt.joins) > 0 {
		for _, join := range stmt.joins {
			jc, err := join.Compile(d, params)
			if err != nil {
				return "", err
			}
			compiled += jc
		}
	}
	if stmt.cond != nil {
		cc, err := stmt.cond.Compile(d, params)
		if err != nil {
			return "", err
		}
		compiled += fmt.Sprintf(" WHERE %s", cc)
	}
	if stmt.groupBy != nil && len(stmt.groupBy) > 0 {
		groupBy := make([]string, len(stmt.groupBy))
		for i, column := range stmt.groupBy {
			// TODO Errors!
			groupBy[i], _ = column.Compile(d, params)
		}
		compiled += fmt.Sprintf(" GROUP BY %s", strings.Join(groupBy, ", "))
	}
	if stmt.order != nil && len(stmt.order) > 0 {
		order := make([]string, len(stmt.order))
		for i, column := range stmt.order {
			// TODO Errors!
			order[i], _ = column.Compile(d, params)
		}
		compiled += fmt.Sprintf(" ORDER BY %s", strings.Join(order, ", "))
	}
	if stmt.limit != 0 {
		compiled += fmt.Sprintf(" LIMIT %d", stmt.limit)
	}
	if stmt.offset != 0 {
		compiled += fmt.Sprintf(" OFFSET %d", stmt.offset)
	}
	return compiled, nil
}

// Join adds a JOIN ... ON to the SELECT statement. The second column parameter
// will be used as the join table and will be removed from the statement's
// FROM selection.
// TODO A smarter result for determining the JOIN table.
func (stmt SelectStmt) Join(pre, post ColumnElem) SelectStmt {
	// Get the table of the pre element
	preTable := pre.Table()

	// If the preTable is not in the current select statement, add it
	if !stmt.TableExists(preTable.Name) {
		stmt.tables = append(stmt.tables, preTable)
	}

	// Get the table of the post element
	postTable := post.Table()

	// Remove the post table from the list of selected tables
	for i, t := range stmt.tables {
		if t == postTable {
			stmt.tables = append(stmt.tables[:i], stmt.tables[i+1:]...)
			break
		}
	}

	// Add the join structure to the select
	if stmt.joins == nil {
		stmt.joins = make([]JoinStmt, 0)
	}
	stmt.joins = append(
		stmt.joins,
		JoinStmt{method: "JOIN", pre: pre, post: post, table: postTable},
	)
	return stmt
}

// Where adds a conditional clause to the SELECT statement. Only one WHERE
// is allowed per statement. Additional calls to Where will overwrite the
// existing WHERE clause.
func (stmt SelectStmt) Where(cond Clause) SelectStmt {
	stmt.cond = cond
	return stmt
}

// GroupBy adds a GROUP BY to the SELECT statement. Only one GROUP BY
// is allowed per statement. Additional calls to GroupBy will overwrite the
// existing GROUP BY clause.
func (stmt SelectStmt) GroupBy(cs ...ColumnElem) SelectStmt {
	groupBy := make([]ColumnElem, len(cs))
	// Since columns may be given without an ordering method, perform the
	// orderable conversion whether or not it is already ordered
	for i, column := range cs {
		groupBy[i] = column
	}
	stmt.groupBy = groupBy
	return stmt
}

// OrderBy adds an ORDER BY to the SELECT statement. Only one ORDER BY
// is allowed per statement. Additional calls to OrderBy will overwrite the
// existing ORDER BY clause.
func (stmt SelectStmt) OrderBy(params ...Orderable) SelectStmt {
	order := make([]OrderedColumn, len(params))
	// Since columns may be given without an ordering method, perform the
	// orderable conversion whether or not it is already ordered
	for i, column := range params {
		order[i] = column.Orderable()
	}
	stmt.order = order
	return stmt
}

// Limit adds a LIMIT to the SELECT statement. Only one LIMIT is allowed per // statement. Additional calls to Limit will overwrite the existing LIMIT
// clause.
func (stmt SelectStmt) Limit(limit int) SelectStmt {
	stmt.limit = limit
	return stmt
}

// Offset adds an OFFSET to the SELECT statement. Only one OFFSET is allowed
// per statement. Additional calls to Offset will overwrite the existing
// OFFSET clause.
func (stmt SelectStmt) Offset(offset int) SelectStmt {
	stmt.offset = offset
	return stmt
}

// Select generates a new SELECT statement from the given columns and tables.
func Select(selection Selectable, ss ...Selectable) (stmt SelectStmt) {
	columns := make([]ColumnElem, 0)
	for _, s := range append([]Selectable{selection}, ss...) {
		if s == nil {
			stmt.err = fmt.Errorf("aspect: insert received a nil selectable - do the columns or tables you selected exist?")
			return
		}
		columns = append(columns, s.Selectable()...)
	}

	for _, column := range columns {
		// Adding a bad column will pass a zero-initialized ColumnElem and
		// since blank column names are invalid SQL we can reject them
		if column.Name() == "" {
			stmt.err = fmt.Errorf("aspect: selected column does not exist")
			return
		}
		stmt.columns = append(stmt.columns, column)

		// Add the table to the stmt tables if it does not already exist
		if !stmt.TableExists(column.Table().Name) {
			stmt.tables = append(stmt.tables, column.Table())
		}
	}
	return
}

// SelectExcept generates a new SELECT statement from the given table except
// for the given columns.
func SelectExcept(table *TableElem, exceptions ...ColumnElem) SelectStmt {
	// TODO This func should proxy to Select in case behavior changes
	stmt := SelectStmt{
		tables: []*TableElem{table},
	}
	columns := table.Selectable()
	// Remove the exceptions
	// TODO Some set operations would be nice here
	for _, exception := range exceptions {
		for i, column := range columns {
			// There should only be one column matching the exception per table
			if exception == column {
				columns = append(columns[:i], columns[i+1:]...)
				break
			}
		}
	}
	stmt.columns = columns
	return stmt
}
<commit_msg>Removed single selection parameter requirement of SELECT statements<commit_after>package aspect

import (
	"fmt"
	"strings"
)

// Selectable is an interface that allows both tables and columns to be
// selected. It is implemented by TableElem and ColumnElem.
type Selectable interface {
	Selectable() []ColumnElem
}

// SelectStmt is the internal representation of an SQL SELECT statement.
type SelectStmt struct {
	tables  []*TableElem
	columns []ColumnElem
	joins   []JoinStmt
	cond    Clause
	groupBy []ColumnElem
	order   []OrderedColumn
	limit   int
	offset  int
	err     error
}

// TableExists checks if a table already exists in the SELECT statement.
func (stmt SelectStmt) TableExists(name string) bool {
	for _, table := range stmt.tables {
		if table.Name == name {
			return true
		}
	}
	return false
}

func (stmt SelectStmt) CompileTables(d Dialect, params *Parameters) []string {
	names := make([]string, len(stmt.tables))
	for i, table := range stmt.tables {
		names[i] = table.Compile(d, params)
	}
	return names
}

func (stmt SelectStmt) CompileColumns(d Dialect, params *Parameters) []string {
	names := make([]string, len(stmt.columns))
	for i, c := range stmt.columns {
		names[i], _ = c.Compile(d, params)
	}
	return names
}

// String outputs the parameter-less SELECT statement in a neutral dialect.
func (stmt SelectStmt) String() string {
	compiled, _ := stmt.Compile(&defaultDialect{}, Params())
	return compiled
}

// Compile outputs the SELECT statement using the given dialect and parameters.
// An error may be returned because of a pre-existing error or because
// an error occurred during compilation.
func (stmt SelectStmt) Compile(d Dialect, params *Parameters) (string, error) {
	if stmt.err != nil {
		return "", stmt.err
	}
	compiled := fmt.Sprintf(
		"SELECT %s FROM %s",
		strings.Join(stmt.CompileColumns(d, params), ", "),
		strings.Join(stmt.CompileTables(d, params), ", "),
	)
	if stmt.joins != nil && len(stmt.joins) > 0 {
		for _, join := range stmt.joins {
			jc, err := join.Compile(d, params)
			if err != nil {
				return "", err
			}
			compiled += jc
		}
	}
	if stmt.cond != nil {
		cc, err := stmt.cond.Compile(d, params)
		if err != nil {
			return "", err
		}
		compiled += fmt.Sprintf(" WHERE %s", cc)
	}
	if stmt.groupBy != nil && len(stmt.groupBy) > 0 {
		groupBy := make([]string, len(stmt.groupBy))
		for i, column := range stmt.groupBy {
			// TODO Errors!
			groupBy[i], _ = column.Compile(d, params)
		}
		compiled += fmt.Sprintf(" GROUP BY %s", strings.Join(groupBy, ", "))
	}
	if stmt.order != nil && len(stmt.order) > 0 {
		order := make([]string, len(stmt.order))
		for i, column := range stmt.order {
			// TODO Errors!
			order[i], _ = column.Compile(d, params)
		}
		compiled += fmt.Sprintf(" ORDER BY %s", strings.Join(order, ", "))
	}
	if stmt.limit != 0 {
		compiled += fmt.Sprintf(" LIMIT %d", stmt.limit)
	}
	if stmt.offset != 0 {
		compiled += fmt.Sprintf(" OFFSET %d", stmt.offset)
	}
	return compiled, nil
}

// Join adds a JOIN ... ON to the SELECT statement. The second column parameter
// will be used as the join table and will be removed from the statement's
// FROM selection.
// TODO A smarter result for determining the JOIN table.
func (stmt SelectStmt) Join(pre, post ColumnElem) SelectStmt {
	// Get the table of the pre element
	preTable := pre.Table()

	// If the preTable is not in the current select statement, add it
	if !stmt.TableExists(preTable.Name) {
		stmt.tables = append(stmt.tables, preTable)
	}

	// Get the table of the post element
	postTable := post.Table()

	// Remove the post table from the list of selected tables
	for i, t := range stmt.tables {
		if t == postTable {
			stmt.tables = append(stmt.tables[:i], stmt.tables[i+1:]...)
			break
		}
	}

	// Add the join structure to the select
	if stmt.joins == nil {
		stmt.joins = make([]JoinStmt, 0)
	}
	stmt.joins = append(
		stmt.joins,
		JoinStmt{method: "JOIN", pre: pre, post: post, table: postTable},
	)
	return stmt
}

// Where adds a conditional clause to the SELECT statement. Only one WHERE
// is allowed per statement. Additional calls to Where will overwrite the
// existing WHERE clause.
func (stmt SelectStmt) Where(cond Clause) SelectStmt {
	stmt.cond = cond
	return stmt
}

// GroupBy adds a GROUP BY to the SELECT statement. Only one GROUP BY
// is allowed per statement. Additional calls to GroupBy will overwrite the
// existing GROUP BY clause.
func (stmt SelectStmt) GroupBy(cs ...ColumnElem) SelectStmt {
	groupBy := make([]ColumnElem, len(cs))
	// Since columns may be given without an ordering method, perform the
	// orderable conversion whether or not it is already ordered
	for i, column := range cs {
		groupBy[i] = column
	}
	stmt.groupBy = groupBy
	return stmt
}

// OrderBy adds an ORDER BY to the SELECT statement. Only one ORDER BY
// is allowed per statement. Additional calls to OrderBy will overwrite the
// existing ORDER BY clause.
func (stmt SelectStmt) OrderBy(params ...Orderable) SelectStmt {
	order := make([]OrderedColumn, len(params))
	// Since columns may be given without an ordering method, perform the
	// orderable conversion whether or not it is already ordered
	for i, column := range params {
		order[i] = column.Orderable()
	}
	stmt.order = order
	return stmt
}

// Limit adds a LIMIT to the SELECT statement. Only one LIMIT is allowed per // statement. Additional calls to Limit will overwrite the existing LIMIT
// clause.
func (stmt SelectStmt) Limit(limit int) SelectStmt {
	stmt.limit = limit
	return stmt
}

// Offset adds an OFFSET to the SELECT statement. Only one OFFSET is allowed
// per statement. Additional calls to Offset will overwrite the existing
// OFFSET clause.
func (stmt SelectStmt) Offset(offset int) SelectStmt {
	stmt.offset = offset
	return stmt
}

// Select generates a new SELECT statement from the given columns and tables.
func Select(selections ...Selectable) (stmt SelectStmt) {
	columns := make([]ColumnElem, 0)
	for _, selection := range selections {
		if selection == nil {
			stmt.err = fmt.Errorf("aspect: insert received a nil selectable - do the columns or tables you selected exist?")
			return
		}
		columns = append(columns, selection.Selectable()...)
	}

	if len(columns) < 1 {
		stmt.err = fmt.Errorf("aspect: must select at least one column")
		return
	}

	for _, column := range columns {
		// Adding a bad column will pass a zero-initialized ColumnElem and
		// since blank column names are invalid SQL we can reject them
		if column.Name() == "" {
			stmt.err = fmt.Errorf("aspect: selected column does not exist")
			return
		}
		stmt.columns = append(stmt.columns, column)

		// Add the table to the stmt tables if it does not already exist
		if !stmt.TableExists(column.Table().Name) {
			stmt.tables = append(stmt.tables, column.Table())
		}
	}
	return
}

// SelectExcept generates a new SELECT statement from the given table except
// for the given columns.
func SelectExcept(table *TableElem, exceptions ...ColumnElem) SelectStmt {
	// TODO This func should proxy to Select in case behavior changes
	stmt := SelectStmt{
		tables: []*TableElem{table},
	}
	columns := table.Selectable()
	// Remove the exceptions
	// TODO Some set operations would be nice here
	for _, exception := range exceptions {
		for i, column := range columns {
			// There should only be one column matching the exception per table
			if exception == column {
				columns = append(columns[:i], columns[i+1:]...)
				break
			}
		}
	}
	stmt.columns = columns
	return stmt
}
<|endoftext|>


<commit_before>// Copyright 2013 Alex Lockwood. All rights reserved.

// Google Cloud Messaging for application servers implemented using the
// Go programming language.
package gcm

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"time"
)

const (
	// GcmSendEndpoint is the endpoint for sending messages to the GCM server.
	const GcmSendEndpoint = "https://android.googleapis.com/gcm/send"
	// Initial delay before first retry, without jitter.
	backoffInitialDelay = 1000
	// Maximum delay before a retry.
	maxBackoffDelay = 1024000
)

// Sender abstracts the interaction between the application server and the
// GCM server. The developer must obtain an API key from the Google APIs
// Console page and pass it to the Sender so that it can perform authorized
// requests on the application server's behalf. To send a message to one or
// more devices use the Sender's Send or SendNoRetry methods.
//
// If the Http field is nil, a zeroed http.Client will be allocated and used
// to send messages (i.e. new(http.Client)). If your application server
// runs on Google AppEngine, you MUST use the "appengine/urlfetch" package
// to create the *http.Client as follows:
//
//	func handler(w http.ResponseWriter, r *http.Request) {
//		/* ... */
//
//		c := appengine.NewContext(r)
//		client := urlfetch.Client(c)
//		sender := &gcm.Sender{ApiKey: apiKey, Http: client}
//
//		/* ... */
//	}
type Sender struct {
	ApiKey string
	Http   *http.Client
}

// SendNoRetry sends a message to the GCM server without retrying in case of
// service unavailability. A non-nil error is returned if a non-recoverable
// error occurs (i.e. if the response status is not "200 OK").
func (s *Sender) SendNoRetry(msg *Message) (*Response, error) {
	if err := checkSender(s); err != nil {
		return nil, err
	} else if err := checkMessage(msg); err != nil {
		return nil, err
	}

	data, err := json.Marshal(msg)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", GcmSendEndpoint, bytes.NewBuffer(data))
	if err != nil {
		return nil, err
	}
	req.Header.Add("Authorization", fmt.Sprintf("key=%s", s.ApiKey))
	req.Header.Add("Content-Type", "application/json")

	resp, err := s.Http.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("%d error: %s", resp.StatusCode, resp.Status)
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	response := new(Response)
	err = json.Unmarshal(body, response)
	return response, err
}

// Send sends a message to the GCM server, retrying in case of service
// unavailability. A non-nil error is returned if a non-recoverable
// error occurs (i.e. if the response status is not "200 OK").
//
// Note that messages are retried using exponential backoff, and as a
// result, this method may block for several seconds.
func (s *Sender) Send(msg *Message, retries int) (*Response, error) {
	if err := checkSender(s); err != nil {
		return nil, err
	} else if err := checkMessage(msg); err != nil {
		return nil, err
	} else if retries < 0 {
		return nil, errors.New("'retries' must not be negative.")
	}

	// Send the message for the first time
	resp, err := s.SendNoRetry(msg)
	if err != nil {
		return nil, err
	} else if resp.Failure == 0 || retries == 0 {
		return resp, nil
	}

	// One or more messages failed to send
	var regIds = msg.RegistrationIDs
	var allResults = make(map[string]Result, len(regIds))
	var backoff = backoffInitialDelay
	for i := 0; updateStatus(msg, resp, allResults) > 0 || i < retries; i++ {
		sleepTime := backoff/2 + rand.Intn(backoff)
		time.Sleep(time.Duration(sleepTime) * time.Millisecond)
		backoff = min(2*backoff, maxBackoffDelay)
		if resp, err = s.SendNoRetry(msg); err != nil {
			return nil, err
		}
	}

	// Bring the message back to its original state
	msg.RegistrationIDs = regIds

	// Create a Response containing the overall results
	var success, failure, canonicalIds int
	var finalResults = make([]Result, len(regIds))
	for i := 0; i < len(regIds); i++ {
		result, _ := allResults[regIds[i]]
		finalResults[i] = result
		if result.MessageID != "" {
			if result.RegistrationID != "" {
				canonicalIds++
			}
			success++
		} else {
			failure++
		}
	}

	return &Response{
		// return the most recent multicast id
		MulticastID:  resp.MulticastID,
		Success:      success,
		Failure:      failure,
		CanonicalIDs: canonicalIds,
		Results:      finalResults,
	}, nil
}

// updateStatus updates the status of the messages sent to devices and
// returns the number of recoverable errors that could be retried.
func updateStatus(msg *Message, resp *Response, allResults map[string]Result) int {
	var unsentRegIds = make([]string, 0, resp.Failure)
	for i := 0; i < len(resp.Results); i++ {
		regId := msg.RegistrationIDs[i]
		allResults[regId] = resp.Results[i]
		if resp.Results[i].Error == "Unavailable" {
			unsentRegIds = append(unsentRegIds, regId)
		}
	}
	msg.RegistrationIDs = unsentRegIds
	return len(unsentRegIds)
}

// min returns the smaller of two integers. For exciting religious wars
// about why this wasn't included in the "math" package, see this thread:
// https://groups.google.com/d/topic/golang-nuts/dbyqx_LGUxM/discussion
func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

// checkSender returns an error if the sender is not well-formed and
// initializes a zeroed http.Client if one has not been provided.
func checkSender(sender *Sender) error {
	if sender.ApiKey == "" {
		return errors.New("The sender's API key must not be empty.")
	}
	if sender.Http == nil {
		sender.Http = new(http.Client)
	}
	return nil
}

// checkMessage returns an error if the message is not well-formed.
func checkMessage(msg *Message) error {
	if msg == nil {
		return errors.New("The message must not be nil.")
	} else if msg.RegistrationIDs == nil {
		return errors.New("The essage's RegistrationIDs field must not be nil.")
	} else if len(msg.RegistrationIDs) == 0 {
		return errors.New("The message must specify at least one registration ID.")
	} else if len(msg.RegistrationIDs) > 1000 {
		return errors.New("The message may specify at most 1000 registration IDs.")
	} else if msg.TimeToLive < 0 || 2419200 < msg.TimeToLive {
		return errors.New("The message's TimeToLive field must be an integer " +
			"between 0 and 2419200 (4 weeks).")
	}
	return nil
}
<commit_msg>go fmt<commit_after>// Copyright 2013 Alex Lockwood. All rights reserved.

// Google Cloud Messaging for application servers implemented using the
// Go programming language.
package gcm

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"time"
)

const (
	// GcmSendEndpoint is the endpoint for sending messages to the GCM server.
	GcmSendEndpoint = "https://android.googleapis.com/gcm/send"
	// Initial delay before first retry, without jitter.
	backoffInitialDelay = 1000
	// Maximum delay before a retry.
	maxBackoffDelay = 1024000
)

// Sender abstracts the interaction between the application server and the
// GCM server. The developer must obtain an API key from the Google APIs
// Console page and pass it to the Sender so that it can perform authorized
// requests on the application server's behalf. To send a message to one or
// more devices use the Sender's Send or SendNoRetry methods.
//
// If the Http field is nil, a zeroed http.Client will be allocated and used
// to send messages (i.e. new(http.Client)). If your application server
// runs on Google AppEngine, you MUST use the "appengine/urlfetch" package
// to create the *http.Client as follows:
//
//	func handler(w http.ResponseWriter, r *http.Request) {
//		/* ... */
//
//		c := appengine.NewContext(r)
//		client := urlfetch.Client(c)
//		sender := &gcm.Sender{ApiKey: apiKey, Http: client}
//
//		/* ... */
//	}
type Sender struct {
	ApiKey string
	Http   *http.Client
}

// SendNoRetry sends a message to the GCM server without retrying in case of
// service unavailability. A non-nil error is returned if a non-recoverable
// error occurs (i.e. if the response status is not "200 OK").
func (s *Sender) SendNoRetry(msg *Message) (*Response, error) {
	if err := checkSender(s); err != nil {
		return nil, err
	} else if err := checkMessage(msg); err != nil {
		return nil, err
	}

	data, err := json.Marshal(msg)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", GcmSendEndpoint, bytes.NewBuffer(data))
	if err != nil {
		return nil, err
	}
	req.Header.Add("Authorization", fmt.Sprintf("key=%s", s.ApiKey))
	req.Header.Add("Content-Type", "application/json")

	resp, err := s.Http.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("%d error: %s", resp.StatusCode, resp.Status)
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	response := new(Response)
	err = json.Unmarshal(body, response)
	return response, err
}

// Send sends a message to the GCM server, retrying in case of service
// unavailability. A non-nil error is returned if a non-recoverable
// error occurs (i.e. if the response status is not "200 OK").
//
// Note that messages are retried using exponential backoff, and as a
// result, this method may block for several seconds.
func (s *Sender) Send(msg *Message, retries int) (*Response, error) {
	if err := checkSender(s); err != nil {
		return nil, err
	} else if err := checkMessage(msg); err != nil {
		return nil, err
	} else if retries < 0 {
		return nil, errors.New("'retries' must not be negative.")
	}

	// Send the message for the first time
	resp, err := s.SendNoRetry(msg)
	if err != nil {
		return nil, err
	} else if resp.Failure == 0 || retries == 0 {
		return resp, nil
	}

	// One or more messages failed to send
	var regIds = msg.RegistrationIDs
	var allResults = make(map[string]Result, len(regIds))
	var backoff = backoffInitialDelay
	for i := 0; updateStatus(msg, resp, allResults) > 0 || i < retries; i++ {
		sleepTime := backoff/2 + rand.Intn(backoff)
		time.Sleep(time.Duration(sleepTime) * time.Millisecond)
		backoff = min(2*backoff, maxBackoffDelay)
		if resp, err = s.SendNoRetry(msg); err != nil {
			return nil, err
		}
	}

	// Bring the message back to its original state
	msg.RegistrationIDs = regIds

	// Create a Response containing the overall results
	var success, failure, canonicalIds int
	var finalResults = make([]Result, len(regIds))
	for i := 0; i < len(regIds); i++ {
		result, _ := allResults[regIds[i]]
		finalResults[i] = result
		if result.MessageID != "" {
			if result.RegistrationID != "" {
				canonicalIds++
			}
			success++
		} else {
			failure++
		}
	}

	return &Response{
		// return the most recent multicast id
		MulticastID:  resp.MulticastID,
		Success:      success,
		Failure:      failure,
		CanonicalIDs: canonicalIds,
		Results:      finalResults,
	}, nil
}

// updateStatus updates the status of the messages sent to devices and
// returns the number of recoverable errors that could be retried.
func updateStatus(msg *Message, resp *Response, allResults map[string]Result) int {
	var unsentRegIds = make([]string, 0, resp.Failure)
	for i := 0; i < len(resp.Results); i++ {
		regId := msg.RegistrationIDs[i]
		allResults[regId] = resp.Results[i]
		if resp.Results[i].Error == "Unavailable" {
			unsentRegIds = append(unsentRegIds, regId)
		}
	}
	msg.RegistrationIDs = unsentRegIds
	return len(unsentRegIds)
}

// min returns the smaller of two integers. For exciting religious wars
// about why this wasn't included in the "math" package, see this thread:
// https://groups.google.com/d/topic/golang-nuts/dbyqx_LGUxM/discussion
func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

// checkSender returns an error if the sender is not well-formed and
// initializes a zeroed http.Client if one has not been provided.
func checkSender(sender *Sender) error {
	if sender.ApiKey == "" {
		return errors.New("The sender's API key must not be empty.")
	}
	if sender.Http == nil {
		sender.Http = new(http.Client)
	}
	return nil
}

// checkMessage returns an error if the message is not well-formed.
func checkMessage(msg *Message) error {
	if msg == nil {
		return errors.New("The message must not be nil.")
	} else if msg.RegistrationIDs == nil {
		return errors.New("The essage's RegistrationIDs field must not be nil.")
	} else if len(msg.RegistrationIDs) == 0 {
		return errors.New("The message must specify at least one registration ID.")
	} else if len(msg.RegistrationIDs) > 1000 {
		return errors.New("The message may specify at most 1000 registration IDs.")
	} else if msg.TimeToLive < 0 || 2419200 < msg.TimeToLive {
		return errors.New("The message's TimeToLive field must be an integer " +
			"between 0 and 2419200 (4 weeks).")
	}
	return nil
}
<|endoftext|>


<commit_before>package color

import (
	"sync"
	"unicode"
)

// see doc.go for an explanation of these
const (
	errInvalid = "%!h(INVALID)"
	errMissing = "%!h(MISSING)"
	errBadAttr = "%!h(BADATTR)"
)

// highlighter holds the state of the scanner.
type highlighter struct {
	s       string // string being scanned
	pos     int    // position in s
	buf     buffer // result
	attrs   buffer // attributes of current verb
	enabled bool
}

// Highlight replaces the highlight verbs in s with their appropriate
// control sequences and then returns the resulting string.
// This is a low-level function that only scans highlight verbs. The color.Printf functions
// are the intended user functions as they wrap around the fmt.Printf functions,
// which handle the rest. Only use this for performance reasons.
func Highlight(s string) string {
	hl := getHighlighter(s)
	hl.enabled = true
	hl.run()
	return string(hl.free())
}

// RemoveVerbs removes all highlight verbs in s
func RemoveVerbs(s string) string {
	hl := getHighlighter(s)
	hl.enabled = false
	hl.run()
	return string(hl.free())
}

// highlighterPool reuses highlighter objects to avoid allocations per invocation.
var highlighterPool = sync.Pool{
	New: func() interface{} {
		hl := new(highlighter)
		// initial capacities avoid constant reallocation during growth.
		hl.buf = make([]byte, 0, 30)
		hl.attrs = make([]byte, 0, 10)
		return hl
	},
}

// getHighlighter returns a new initialized highlighter from the pool.
func getHighlighter(s string) (hl *highlighter) {
	hl = highlighterPool.Get().(*highlighter)
	hl.s = s
	return
}

// free resets the highlighter and returns the buffer.
func (hl *highlighter) free() (b []byte) {
	b = hl.buf
	hl.buf.reset()
	hl.pos = 0
	highlighterPool.Put(hl)
	return
}

// stateFn represents the state of the scanner as a function that returns the next state.
type stateFn func(*highlighter) stateFn

// run runs the state machine for the highlighter.
func (hl *highlighter) run() {
	for state := scanText; state != nil; {
		state = state(hl)
	}
}

const eof = -1

// get returns the current rune.
func (hl *highlighter) get() rune {
	if hl.pos >= len(hl.s) {
		return eof
	}
	return rune(hl.s[hl.pos])
}

// writeAttrs writes a control sequence derived from h.attrs[1:] to h.buf.
func (hl *highlighter) writeAttrs() {
	if !hl.enabled {
		return
	}
	hl.buf.writeString(csi)
	hl.buf.write(hl.attrs[1:])
	hl.buf.writeByte('m')
}

// writePrev writes n previous characters to the buffer.
func (hl *highlighter) writePrev(n int) {
	hl.buf.writeString(hl.s[n:hl.pos])
}

// scanText scans until the next highlight or reset verb.
func scanText(hl *highlighter) stateFn {
	// previous position
	ppos := hl.pos
	for {
		if r := hl.get(); r == eof {
			if ppos < hl.pos {
				hl.writePrev(ppos)
			}
			return nil
		} else if r == '%' {
			if ppos < hl.pos {
				hl.writePrev(ppos)
			}
			break
		}
		hl.pos++
	}
	hl.pos++
	switch hl.get() {
	case 'r':
		hl.pos++
		return verbReset
	case 'h':
		hl.pos += 2
		return scanHighlight
	case eof:
		// no need to writePrev, we know it was '%
		hl.buf.writeByte('%')
		return nil
	}
	hl.pos++
	hl.writePrev(hl.pos - 2)
	return scanText
}

// verbReset writes the reset verb with the reset control sequence.
func verbReset(hl *highlighter) stateFn {
	hl.attrs.writeString(attrs["reset"])
	hl.writeAttrs()
	hl.attrs.reset()
	return scanText
}

// scanHighlight scans the highlight verb for attributes,
// then writes a control sequence derived from said attributes to the buffer.
func scanHighlight(hl *highlighter) stateFn {
	r := hl.get()
	switch {
	case r == 'f':
		return scanColor256(hl, preFg256)
	case r == 'b':
		return scanColor256(hl, preBg256)
	case unicode.IsLetter(r):
		return scanAttribute(hl, 0)
	case r == '+':
		hl.pos++
		return scanHighlight
	case r == ']':
		if len(hl.attrs) != 0 {
			hl.writeAttrs()
		} else {
			hl.buf.writeString(errMissing)
		}
		hl.attrs.reset()
		hl.pos++
		return scanText
	default:
		return abortHighlight(hl, errInvalid)
	}
}

// scanAttribute scans a named attribute
func scanAttribute(hl *highlighter, off int) stateFn {
	start := hl.pos - off
	for unicode.IsLetter(hl.get()) {
		hl.pos++
	}
	if a, ok := attrs[hl.s[start:hl.pos]]; ok {
		hl.attrs.writeString(a)
	} else {
		return abortHighlight(hl, errBadAttr)
	}
	return scanHighlight
}

// abortHighlight writes a error to the buffer and
// then skips to the end of the highlight verb.
func abortHighlight(hl *highlighter, msg string) stateFn {
	hl.buf.writeString(msg)
	hl.attrs.reset()
	for {
		switch hl.get() {
		case ']':
			hl.pos++
			return scanText
		case eof:
			return nil
		}
		hl.pos++
	}
}

// scanColor256 scans a 256 color attribute
func scanColor256(hl *highlighter, pre string) stateFn {
	hl.pos++
	if hl.get() != 'g' {
		return scanAttribute(hl, 1)
	}
	hl.pos++
	if !unicode.IsNumber(hl.get()) {
		return scanAttribute(hl, 2)
	}
	start := hl.pos
	hl.pos++
	for unicode.IsNumber(hl.get()) {
		hl.pos++
	}
	hl.attrs.writeString(pre)
	hl.attrs.writeString(hl.s[start:hl.pos])
	return scanHighlight
}
<commit_msg>unexport removeVerbs<commit_after>package color

import (
	"sync"
	"unicode"
)

// see doc.go for an explanation of these
const (
	errInvalid = "%!h(INVALID)"
	errMissing = "%!h(MISSING)"
	errBadAttr = "%!h(BADATTR)"
)

// highlighter holds the state of the scanner.
type highlighter struct {
	s       string // string being scanned
	pos     int    // position in s
	buf     buffer // result
	attrs   buffer // attributes of current verb
	enabled bool
}

// Highlight replaces the highlight verbs in s with their appropriate
// control sequences and then returns the resulting string.
// This is a low-level function that only scans highlight verbs. The color.Printf functions
// are the intended user functions as they wrap around the fmt.Printf functions,
// which handle the rest. Only use this for performance reasons.
func Highlight(s string) string {
	hl := getHighlighter(s)
	hl.enabled = true
	hl.run()
	return string(hl.free())
}

// removeVerbs removes all highlight verbs in s
func removeVerbs(s string) string {
	hl := getHighlighter(s)
	hl.enabled = false
	hl.run()
	return string(hl.free())
}

// highlighterPool reuses highlighter objects to avoid allocations per invocation.
var highlighterPool = sync.Pool{
	New: func() interface{} {
		hl := new(highlighter)
		// initial capacities avoid constant reallocation during growth.
		hl.buf = make([]byte, 0, 30)
		hl.attrs = make([]byte, 0, 10)
		return hl
	},
}

// getHighlighter returns a new initialized highlighter from the pool.
func getHighlighter(s string) (hl *highlighter) {
	hl = highlighterPool.Get().(*highlighter)
	hl.s = s
	return
}

// free resets the highlighter and returns the buffer.
func (hl *highlighter) free() (b []byte) {
	b = hl.buf
	hl.buf.reset()
	hl.pos = 0
	highlighterPool.Put(hl)
	return
}

// stateFn represents the state of the scanner as a function that returns the next state.
type stateFn func(*highlighter) stateFn

// run runs the state machine for the highlighter.
func (hl *highlighter) run() {
	for state := scanText; state != nil; {
		state = state(hl)
	}
}

const eof = -1

// get returns the current rune.
func (hl *highlighter) get() rune {
	if hl.pos >= len(hl.s) {
		return eof
	}
	return rune(hl.s[hl.pos])
}

// writeAttrs writes a control sequence derived from h.attrs[1:] to h.buf.
func (hl *highlighter) writeAttrs() {
	if !hl.enabled {
		return
	}
	hl.buf.writeString(csi)
	hl.buf.write(hl.attrs[1:])
	hl.buf.writeByte('m')
}

// writePrev writes n previous characters to the buffer.
func (hl *highlighter) writePrev(n int) {
	hl.buf.writeString(hl.s[n:hl.pos])
}

// scanText scans until the next highlight or reset verb.
func scanText(hl *highlighter) stateFn {
	// previous position
	ppos := hl.pos
	for {
		if r := hl.get(); r == eof {
			if ppos < hl.pos {
				hl.writePrev(ppos)
			}
			return nil
		} else if r == '%' {
			if ppos < hl.pos {
				hl.writePrev(ppos)
			}
			break
		}
		hl.pos++
	}
	hl.pos++
	switch hl.get() {
	case 'r':
		hl.pos++
		return verbReset
	case 'h':
		hl.pos += 2
		return scanHighlight
	case eof:
		// no need to writePrev, we know it was '%
		hl.buf.writeByte('%')
		return nil
	}
	hl.pos++
	hl.writePrev(hl.pos - 2)
	return scanText
}

// verbReset writes the reset verb with the reset control sequence.
func verbReset(hl *highlighter) stateFn {
	hl.attrs.writeString(attrs["reset"])
	hl.writeAttrs()
	hl.attrs.reset()
	return scanText
}

// scanHighlight scans the highlight verb for attributes,
// then writes a control sequence derived from said attributes to the buffer.
func scanHighlight(hl *highlighter) stateFn {
	r := hl.get()
	switch {
	case r == 'f':
		return scanColor256(hl, preFg256)
	case r == 'b':
		return scanColor256(hl, preBg256)
	case unicode.IsLetter(r):
		return scanAttribute(hl, 0)
	case r == '+':
		hl.pos++
		return scanHighlight
	case r == ']':
		if len(hl.attrs) != 0 {
			hl.writeAttrs()
		} else {
			hl.buf.writeString(errMissing)
		}
		hl.attrs.reset()
		hl.pos++
		return scanText
	default:
		return abortHighlight(hl, errInvalid)
	}
}

// scanAttribute scans a named attribute
func scanAttribute(hl *highlighter, off int) stateFn {
	start := hl.pos - off
	for unicode.IsLetter(hl.get()) {
		hl.pos++
	}
	if a, ok := attrs[hl.s[start:hl.pos]]; ok {
		hl.attrs.writeString(a)
	} else {
		return abortHighlight(hl, errBadAttr)
	}
	return scanHighlight
}

// abortHighlight writes a error to the buffer and
// then skips to the end of the highlight verb.
func abortHighlight(hl *highlighter, msg string) stateFn {
	hl.buf.writeString(msg)
	hl.attrs.reset()
	for {
		switch hl.get() {
		case ']':
			hl.pos++
			return scanText
		case eof:
			return nil
		}
		hl.pos++
	}
}

// scanColor256 scans a 256 color attribute
func scanColor256(hl *highlighter, pre string) stateFn {
	hl.pos++
	if hl.get() != 'g' {
		return scanAttribute(hl, 1)
	}
	hl.pos++
	if !unicode.IsNumber(hl.get()) {
		return scanAttribute(hl, 2)
	}
	start := hl.pos
	hl.pos++
	for unicode.IsNumber(hl.get()) {
		hl.pos++
	}
	hl.attrs.writeString(pre)
	hl.attrs.writeString(hl.s[start:hl.pos])
	return scanHighlight
}
<|endoftext|>