<commit_before>package prompt

import (
	"math"
)

// Render to render prompt information from state of Buffer.
type Render struct {
	out                ConsoleWriter
	prefix             string
	livePrefixCallback func() (prefix string, useLivePrefix bool)
	title              string
	row                uint16
	col                uint16
	// colors,
	prefixTextColor              Color
	prefixBGColor                Color
	inputTextColor               Color
	inputBGColor                 Color
	previewSuggestionTextColor   Color
	previewSuggestionBGColor     Color
	suggestionTextColor          Color
	suggestionBGColor            Color
	selectedSuggestionTextColor  Color
	selectedSuggestionBGColor    Color
	descriptionTextColor         Color
	descriptionBGColor           Color
	selectedDescriptionTextColor Color
	selectedDescriptionBGColor   Color
	scrollbarThumbColor          Color
	scrollbarBGColor             Color
}

// Setup to initialize console output.
func (r *Render) Setup() {
	if r.title != "" {
		r.out.SetTitle(r.title)
		r.out.Flush()
	}
}

// getCurrentPrefix to get current prefix.
// If live-prefix is enabled, return live-prefix.
func (r *Render) getCurrentPrefix() string {
	if prefix, ok := r.livePrefixCallback(); ok {
		return prefix
	}
	return r.prefix
}

func (r *Render) renderPrefix() {
	r.out.SetColor(r.prefixTextColor, r.prefixBGColor, false)
	r.out.WriteStr(r.getCurrentPrefix())
	r.out.SetColor(DefaultColor, DefaultColor, false)
}

// TearDown to clear title and erasing.
func (r *Render) TearDown() {
	r.out.ClearTitle()
	r.out.EraseDown()
	r.out.Flush()
}

func (r *Render) prepareArea(lines int) {
	for i := 0; i < lines; i++ {
		r.out.ScrollDown()
	}
	for i := 0; i < lines; i++ {
		r.out.ScrollUp()
	}
	return
}

// UpdateWinSize called when window size is changed.
func (r *Render) UpdateWinSize(ws *WinSize) {
	r.row = ws.Row
	r.col = ws.Col
	return
}

func (r *Render) renderWindowTooSmall() {
	r.out.CursorGoTo(0, 0)
	r.out.EraseScreen()
	r.out.SetColor(DarkRed, White, false)
	r.out.WriteStr("Your console window is too small...")
	r.out.Flush()
	return
}

func (r *Render) renderCompletion(buf *Buffer, completions *CompletionManager) {
	windowHeight := len(completions.tmp)
	if windowHeight > int(completions.max) {
		windowHeight = int(completions.max)
	}
	contentHeight := len(completions.tmp)

	fractionVisible := float64(windowHeight) / float64(contentHeight)
	fractionAbove := float64(completions.verticalScroll) / float64(contentHeight)

	scrollbarHeight := int(math.Min(float64(windowHeight), math.Max(1, float64(windowHeight)*fractionVisible)))
	scrollbarTop := int(float64(windowHeight) * fractionAbove)

	isScrollThumb := func(row int) bool {
		return scrollbarTop <= row && row <= scrollbarTop+scrollbarHeight
	}

	suggestions := completions.GetSuggestions()
	if l := len(completions.GetSuggestions()); l == 0 {
		return
	}

	prefix := r.getCurrentPrefix()
	formatted, width := formatSuggestions(
		suggestions,
		int(r.col)-len(prefix)-1, // -1 means a width of scrollbar
	)
	formatted = formatted[completions.verticalScroll : completions.verticalScroll+windowHeight]
	l := len(formatted)
	r.prepareArea(windowHeight)

	// +1 means a width of scrollbar.
	d := (len(prefix) + len(buf.Document().TextBeforeCursor()) + 1) % int(r.col)
	if d == 0 { // the cursor is on right end.
		r.out.CursorBackward(width)
	} else if d+width > int(r.col) {
		r.out.CursorBackward(d + width - int(r.col))
	}

	selected := completions.selected - completions.verticalScroll

	r.out.SetColor(White, Cyan, false)
	for i := 0; i < l; i++ {
		r.out.CursorDown(1)
		if i == selected {
			r.out.SetColor(r.selectedSuggestionTextColor, r.selectedSuggestionBGColor, true)
		} else {
			r.out.SetColor(r.suggestionTextColor, r.suggestionBGColor, false)
		}
		r.out.WriteStr(formatted[i].Text)

		if i == selected {
			r.out.SetColor(r.selectedDescriptionTextColor, r.selectedDescriptionBGColor, false)
		} else {
			r.out.SetColor(r.descriptionTextColor, r.descriptionBGColor, false)
		}
		r.out.WriteStr(formatted[i].Description)

		if isScrollThumb(i) {
			r.out.SetColor(DefaultColor, r.scrollbarThumbColor, false)
		} else {
			r.out.SetColor(DefaultColor, r.scrollbarBGColor, false)
		}
		r.out.WriteStr(" ")
		// +1 means a width of scrollbar.
		r.out.CursorBackward(width + 1)
	}
	if d == 0 && len(prefix)+len(buf.Text()) != 0 { // the cursor is on right end.
		// DON'T CURSOR DOWN HERE. Because the line doesn't erase properly.
		r.out.CursorForward(width + 1)
	} else if d+width > int(r.col) {
		r.out.CursorForward(d + width - int(r.col))
	}

	r.out.CursorUp(l)
	r.out.SetColor(DefaultColor, DefaultColor, false)
	return
}

// Render renders to the console.
func (r *Render) Render(buffer *Buffer, completion *CompletionManager) {
	line := buffer.Text()
	prefix := r.getCurrentPrefix()

	if r.col > 0 {
		// Erasing
		r.out.CursorBackward(int(r.col) + len(line) + len(prefix))
		r.out.EraseDown()

		// prepare area
		h := ((len(prefix) + len(line)) / int(r.col)) + 1 + int(completion.max)
		if h > int(r.row) || completionMargin > int(r.col) {
			r.renderWindowTooSmall()
			return
		}
	}

	// Rendering
	r.renderPrefix()

	r.out.SetColor(r.inputTextColor, r.inputBGColor, false)
	r.out.WriteStr(line)
	r.out.SetColor(DefaultColor, DefaultColor, false)
	r.out.CursorBackward(len([]rune(line)) - buffer.CursorPosition)
	r.renderCompletion(buffer, completion)
	if suggest, ok := completion.GetSelectedSuggestion(); ok {
		r.out.CursorBackward(len([]rune(buffer.Document().GetWordBeforeCursor())))
		r.out.SetColor(r.previewSuggestionTextColor, r.previewSuggestionBGColor, false)
		r.out.WriteStr(suggest.Text)
		r.out.SetColor(DefaultColor, DefaultColor, false)
	}
	r.out.Flush()
}

// BreakLine to break line.
func (r *Render) BreakLine(buffer *Buffer) {
	// CR
	prefix := r.getCurrentPrefix()
	r.out.CursorBackward(int(r.col) + len(buffer.Text()) + len(prefix))
	// Erasing and Render
	r.out.EraseDown()
	r.renderPrefix()
	r.out.SetColor(r.inputTextColor, r.inputBGColor, false)
	r.out.WriteStr(buffer.Document().Text + "\n")
	r.out.SetColor(DefaultColor, DefaultColor, false)
	r.out.Flush()
}
<commit_msg>Add comment of the case TIOCGWINSZ returns 0,0<commit_after>package prompt

import (
	"math"
)

// Render to render prompt information from state of Buffer.
type Render struct {
	out                ConsoleWriter
	prefix             string
	livePrefixCallback func() (prefix string, useLivePrefix bool)
	title              string
	row                uint16
	col                uint16
	// colors,
	prefixTextColor              Color
	prefixBGColor                Color
	inputTextColor               Color
	inputBGColor                 Color
	previewSuggestionTextColor   Color
	previewSuggestionBGColor     Color
	suggestionTextColor          Color
	suggestionBGColor            Color
	selectedSuggestionTextColor  Color
	selectedSuggestionBGColor    Color
	descriptionTextColor         Color
	descriptionBGColor           Color
	selectedDescriptionTextColor Color
	selectedDescriptionBGColor   Color
	scrollbarThumbColor          Color
	scrollbarBGColor             Color
}

// Setup to initialize console output.
func (r *Render) Setup() {
	if r.title != "" {
		r.out.SetTitle(r.title)
		r.out.Flush()
	}
}

// getCurrentPrefix to get current prefix.
// If live-prefix is enabled, return live-prefix.
func (r *Render) getCurrentPrefix() string {
	if prefix, ok := r.livePrefixCallback(); ok {
		return prefix
	}
	return r.prefix
}

func (r *Render) renderPrefix() {
	r.out.SetColor(r.prefixTextColor, r.prefixBGColor, false)
	r.out.WriteStr(r.getCurrentPrefix())
	r.out.SetColor(DefaultColor, DefaultColor, false)
}

// TearDown to clear title and erasing.
func (r *Render) TearDown() {
	r.out.ClearTitle()
	r.out.EraseDown()
	r.out.Flush()
}

func (r *Render) prepareArea(lines int) {
	for i := 0; i < lines; i++ {
		r.out.ScrollDown()
	}
	for i := 0; i < lines; i++ {
		r.out.ScrollUp()
	}
	return
}

// UpdateWinSize called when window size is changed.
func (r *Render) UpdateWinSize(ws *WinSize) {
	r.row = ws.Row
	r.col = ws.Col
	return
}

func (r *Render) renderWindowTooSmall() {
	r.out.CursorGoTo(0, 0)
	r.out.EraseScreen()
	r.out.SetColor(DarkRed, White, false)
	r.out.WriteStr("Your console window is too small...")
	r.out.Flush()
	return
}

func (r *Render) renderCompletion(buf *Buffer, completions *CompletionManager) {
	windowHeight := len(completions.tmp)
	if windowHeight > int(completions.max) {
		windowHeight = int(completions.max)
	}
	contentHeight := len(completions.tmp)

	fractionVisible := float64(windowHeight) / float64(contentHeight)
	fractionAbove := float64(completions.verticalScroll) / float64(contentHeight)

	scrollbarHeight := int(math.Min(float64(windowHeight), math.Max(1, float64(windowHeight)*fractionVisible)))
	scrollbarTop := int(float64(windowHeight) * fractionAbove)

	isScrollThumb := func(row int) bool {
		return scrollbarTop <= row && row <= scrollbarTop+scrollbarHeight
	}

	suggestions := completions.GetSuggestions()
	if l := len(completions.GetSuggestions()); l == 0 {
		return
	}

	prefix := r.getCurrentPrefix()
	formatted, width := formatSuggestions(
		suggestions,
		int(r.col)-len(prefix)-1, // -1 means a width of scrollbar
	)
	formatted = formatted[completions.verticalScroll : completions.verticalScroll+windowHeight]
	l := len(formatted)
	r.prepareArea(windowHeight)

	// +1 means a width of scrollbar.
	d := (len(prefix) + len(buf.Document().TextBeforeCursor()) + 1) % int(r.col)
	if d == 0 { // the cursor is on right end.
		r.out.CursorBackward(width)
	} else if d+width > int(r.col) {
		r.out.CursorBackward(d + width - int(r.col))
	}

	selected := completions.selected - completions.verticalScroll

	r.out.SetColor(White, Cyan, false)
	for i := 0; i < l; i++ {
		r.out.CursorDown(1)
		if i == selected {
			r.out.SetColor(r.selectedSuggestionTextColor, r.selectedSuggestionBGColor, true)
		} else {
			r.out.SetColor(r.suggestionTextColor, r.suggestionBGColor, false)
		}
		r.out.WriteStr(formatted[i].Text)

		if i == selected {
			r.out.SetColor(r.selectedDescriptionTextColor, r.selectedDescriptionBGColor, false)
		} else {
			r.out.SetColor(r.descriptionTextColor, r.descriptionBGColor, false)
		}
		r.out.WriteStr(formatted[i].Description)

		if isScrollThumb(i) {
			r.out.SetColor(DefaultColor, r.scrollbarThumbColor, false)
		} else {
			r.out.SetColor(DefaultColor, r.scrollbarBGColor, false)
		}
		r.out.WriteStr(" ")
		// +1 means a width of scrollbar.
		r.out.CursorBackward(width + 1)
	}
	if d == 0 && len(prefix)+len(buf.Text()) != 0 { // the cursor is on right end.
		// DON'T CURSOR DOWN HERE. Because the line doesn't erase properly.
		r.out.CursorForward(width + 1)
	} else if d+width > int(r.col) {
		r.out.CursorForward(d + width - int(r.col))
	}

	r.out.CursorUp(l)
	r.out.SetColor(DefaultColor, DefaultColor, false)
	return
}

// Render renders to the console.
func (r *Render) Render(buffer *Buffer, completion *CompletionManager) {
	line := buffer.Text()
	prefix := r.getCurrentPrefix()

	// In situations where a psuedo tty is allocated (e.g. within a docker container),
	// window size via TIOCGWINSZ is not immediately available and will result in 0,0 dimensions.
	if r.col > 0 {
		// Erasing
		r.out.CursorBackward(int(r.col) + len(line) + len(prefix))
		r.out.EraseDown()

		// prepare area
		h := ((len(prefix) + len(line)) / int(r.col)) + 1 + int(completion.max)
		if h > int(r.row) || completionMargin > int(r.col) {
			r.renderWindowTooSmall()
			return
		}
	}

	// Rendering
	r.renderPrefix()

	r.out.SetColor(r.inputTextColor, r.inputBGColor, false)
	r.out.WriteStr(line)
	r.out.SetColor(DefaultColor, DefaultColor, false)
	r.out.CursorBackward(len([]rune(line)) - buffer.CursorPosition)
	r.renderCompletion(buffer, completion)
	if suggest, ok := completion.GetSelectedSuggestion(); ok {
		r.out.CursorBackward(len([]rune(buffer.Document().GetWordBeforeCursor())))
		r.out.SetColor(r.previewSuggestionTextColor, r.previewSuggestionBGColor, false)
		r.out.WriteStr(suggest.Text)
		r.out.SetColor(DefaultColor, DefaultColor, false)
	}
	r.out.Flush()
}

// BreakLine to break line.
func (r *Render) BreakLine(buffer *Buffer) {
	// CR
	prefix := r.getCurrentPrefix()
	r.out.CursorBackward(int(r.col) + len(buffer.Text()) + len(prefix))
	// Erasing and Render
	r.out.EraseDown()
	r.renderPrefix()
	r.out.SetColor(r.inputTextColor, r.inputBGColor, false)
	r.out.WriteStr(buffer.Document().Text + "\n")
	r.out.SetColor(DefaultColor, DefaultColor, false)
	r.out.Flush()
}
<|endoftext|>


<commit_before>// Copyright 2015 Dorival de Moraes Pedroso. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package goga

import (
	"bytes"

	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/io"
	"github.com/cpmech/gosl/rnd"
)

func TexDocumentStart() (buf *bytes.Buffer) {
	buf = new(bytes.Buffer)
	io.Ff(buf, `\documentclass[a4paper]{article}
\usepackage{mydefaults}
\usepackage[margin=1.5cm,footskip=0.5cm]{geometry}
\title{GOGA Report}
\author{Dorival Pedroso}
\begin{document}
`)
	return
}

func TexDocumentEnd(buf *bytes.Buffer) {
	io.Ff(buf, `\end{document}`)
}

func TexSingleObjTableStart(buf *bytes.Buffer, ntrials int) {
	io.Ff(buf, `\begin{table} \centering
\caption{Constrained single objective problems: Results}
\begin{tabular}[c]{cccc} \toprule
P & settings & results & histogram ($N_{trials}=%d$) \\ \hline
`, ntrials)
}

func TexSingleObjTableEnd(buf *bytes.Buffer) {
	io.Ff(buf, `\end{tabular}
\label{tab:singleobj}
\end{table}`)
}

func (o *Optimiser) TexSingleObjTableItem(buf *bytes.Buffer, problem int, fref float64, fmtFave, fmtFdev, fmtHist, fmtX string) {
	hlen := 28
	SortByOva(o.Solutions, 0)
	best := o.Solutions[0]
	fmin, fave, fmax, fdev, F := o.StatMinProb(0, 20, fref, false)
	io.Ff(buf, `%d
&
{$\!\begin{aligned}
    N_{sol}        & = %d \ACR
	N_{cpu}        & = %d \ACR
	t_{max}        & = %d \ACR
	\Delta t_{exc} & = %d \ACR
	N_{eval}       & = %d
\end{aligned}$}
&
{$\!\begin{aligned}
    f_{min}  &= `+fmtFave+`  \ACR
    f_{ave}  &= {\bf `+fmtFave+`}  \ACR
             &\phantom{=}( `+fmtFave+`) \ACR
    f_{max}  &= `+fmtFave+`  \ACR
    f_{dev}  &= `+fmtFdev+`
\end{aligned}$}
&
\begin{minipage}{7cm} \scriptsize
\begin{verbatim}
%s
\end{verbatim}
\end{minipage} \\
\multicolumn{4}{c}{$X_{best}$=`+fmtX+`} \\
`, problem, o.Nsol, o.Ncpu, o.Tf, o.DtExc, o.Nfeval, fmin, fave, fref, fmax, fdev,
		rnd.BuildTextHist(nice_num(fmin-0.05), nice_num(fmax+0.05), 11, F, fmtHist, hlen),
		best.Flt)
}

func TexWrite(fnkey string, buf *bytes.Buffer, dorun bool) {
	tex := fnkey + ".tex"
	io.WriteFileVD("/tmp/goga", tex, buf)
	if dorun {
		_, err := io.RunCmd(true, "pdflatex", "-interaction=batchmode", "-halt-on-error", "-output-directory=/tmp/goga/", tex)
		if err != nil {
			chk.Panic("%v", err)
		}
		io.PfBlue("file </tmp/goga/%s.pdf> generated\n", fnkey)
	}
}

// nice_num returns a truncated float
func nice_num(x float64) float64 {
	s := io.Sf("%.2f", x)
	return io.Atof(s)
}
<commit_msg>tex report improved with multiple tables<commit_after>// Copyright 2015 Dorival de Moraes Pedroso. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package goga

import (
	"bytes"

	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/io"
	"github.com/cpmech/gosl/rnd"
)

func TexDocumentStart() (buf *bytes.Buffer) {
	buf = new(bytes.Buffer)
	io.Ff(buf, `\documentclass[a4paper]{article}

\usepackage{mydefaults}
\usepackage[margin=1.5cm,footskip=0.5cm]{geometry}

\title{GOGA Report}
\author{Dorival Pedroso}

\begin{document}
`)
	return
}

func TexDocumentEnd(buf *bytes.Buffer) {
	io.Ff(buf, `
\end{document}`)
}

func TexSingleObjTableStart(buf *bytes.Buffer, ntrials int) {
	io.Ff(buf, `
\begin{table} \centering
\caption{Constrained single objective problems: Results}
\begin{tabular}[c]{cccc} \toprule
P & settings & results & histogram ($N_{trials}=%d$) \\ \hline
`, ntrials)
}

func TexSingleObjTableEnd(buf *bytes.Buffer) {
	io.Ff(buf, `\end{tabular}
\label{tab:singleobj}
\end{table}`)
}

func TexSingleObjTableItem(o *Optimiser, buf *bytes.Buffer, problem int, fref float64, fmtFave, fmtFdev, fmtHist, fmtX string) {
	hlen := 28
	SortByOva(o.Solutions, 0)
	best := o.Solutions[0]
	fmin, fave, fmax, fdev, F := o.StatMinProb(0, 20, fref, false)
	io.Ff(buf, `%d
&
{$\!\begin{aligned}
    N_{sol}        & = %d \ACR
	N_{cpu}        & = %d \ACR
	t_{max}        & = %d \ACR
	\Delta t_{exc} & = %d \ACR
	N_{eval}       & = %d
\end{aligned}$}
&
{$\!\begin{aligned}
    f_{min}  &= `+fmtFave+`  \ACR
    f_{ave}  &= {\bf `+fmtFave+`}  \ACR
             &\phantom{=}( `+fmtFave+`) \ACR
    f_{max}  &= `+fmtFave+`  \ACR
    f_{dev}  &= `+fmtFdev+`
\end{aligned}$}
&
\begin{minipage}{7cm} \scriptsize
\begin{verbatim}
%s
\end{verbatim}
\end{minipage} \\
\multicolumn{4}{c}{$X_{best}$=`+fmtX+`} \\
`, problem, o.Nsol, o.Ncpu, o.Tf, o.DtExc, o.Nfeval, fmin, fave, fref, fmax, fdev,
		rnd.BuildTextHist(nice_num(fmin-0.05), nice_num(fmax+0.05), 11, F, fmtHist, hlen),
		best.Flt)
}

func TexWrite(dirout, fnkey string, buf *bytes.Buffer, dorun bool) {
	tex := fnkey + ".tex"
	io.WriteFileVD(dirout, tex, buf)
	if dorun {
		_, err := io.RunCmd(true, "pdflatex", "-interaction=batchmode", "-halt-on-error", "-output-directory=/tmp/goga/", tex)
		if err != nil {
			chk.Panic("%v", err)
		}
		io.PfBlue("file <%s/%s.pdf> generated\n", dirout, fnkey)
	}
}

// TexReport produces TeX report
//  nRowPerTab -- number of rows per table
func TexReport(dirout, fnkey string, ntrials, nRowPerTab int, opts []*Optimiser, frefs []float64, fmtHist []string, dorun bool) {
	nprob := len(opts)
	if nRowPerTab < 1 {
		chk.Panic("number of rows per table must be greater than 0")
	}
	chk.IntAssert(len(frefs), nprob)
	chk.IntAssert(len(fmtHist), nprob)
	buf := TexDocumentStart()
	for i, opt := range opts {
		if i%nRowPerTab == 0 {
			if i > 0 {
				io.Ff(buf, `\bottomrule`)
				TexSingleObjTableEnd(buf) // end previous table
				io.Ff(buf, "\n")
			}
			TexSingleObjTableStart(buf, ntrials) // begin new table
		} else {
			if i > 0 {
				io.Ff(buf, `\hline`)
			}
		}
		TexSingleObjTableItem(opt, buf, i+1, frefs[i], "%.8f", "%.9f", fmtHist[i], "%.2f")
	}
	io.Ff(buf, `\bottomrule`)
	TexSingleObjTableEnd(buf) // end previous table
	io.Ff(buf, "\n")
	TexDocumentEnd(buf)
	TexWrite(dirout, fnkey, buf, dorun)
}

// nice_num returns a truncated float
func nice_num(x float64) float64 {
	s := io.Sf("%.2f", x)
	return io.Atof(s)
}
<|endoftext|>


<commit_before>package magick

// #include <magick/api.h>
// #include "bridge.h"
// #include "resize.h"
import "C"

type Filter C.FilterTypes

const (
	FPoint     Filter = C.PointFilter
	FBox       Filter = C.BoxFilter
	FTriangle  Filter = C.TriangleFilter
	FHermite   Filter = C.HermiteFilter
	FHanning   Filter = C.HanningFilter
	FHamming   Filter = C.HammingFilter
	FBlackman  Filter = C.BlackmanFilter
	FGaussian  Filter = C.GaussianFilter
	FQuadratic Filter = C.QuadraticFilter
	FCubic     Filter = C.CubicFilter
	FCatrom    Filter = C.CatromFilter
	FMitchell  Filter = C.MitchellFilter
	FLanczos   Filter = C.LanczosFilter
	FBessel    Filter = C.BesselFilter
	FSinc      Filter = C.SincFilter
)

// Magnify is a convenience method that scales an image proportionally to twice its size.
func (im *Image) Magnify() (*Image, error) {
	return im.applyFunc("magnifying", C.ImageFunc(C.MagnifyImage))
}

// Minify is a convenience method that scales an image proportionally to half its size.
func (im *Image) Minify() (*Image, error) {
	return im.applyFunc("minifying", C.ImageFunc(C.MinifyImage))
}

// ResizeBlur returns a new image resized to the given dimensions using the provided
// filter and blur factor (where > 1 is blurry, < 1 is sharp).
func (im *Image) ResizeBlur(width, height int, filter Filter, blur float64) (*Image, error) {
	var data C.ResizeData
	data.columns = C.ulong(width)
	data.rows = C.ulong(height)
	data.filter = C.FilterTypes(filter)
	data.blur = C.double(blur)
	return im.applyDataFunc("resizing", C.ImageDataFunc(C.resizeImage), &data)
}

// Resize works like ResizeBlur, but sets the blur to 1
func (im *Image) Resize(width, height int, filter Filter) (*Image, error) {
	return im.ResizeBlur(width, height, filter, 1)
}

func (im *Image) sizeFunc(what string, width, height int, f C.ImageDataFunc) (*Image, error) {
	var s C.SizeData
	s.columns = C.ulong(width)
	s.rows = C.ulong(height)
	return im.applyDataFunc(what, f, &s)
}

// Sample scales an image to the desired dimensions with pixel sampling.
// Unlike other scaling methods, this method does not introduce any
// additional color into the scaled image.
func (im *Image) Sample(width, height int) (*Image, error) {
	return im.sizeFunc("sampling", width, height, C.ImageDataFunc(C.sampleImage))
}

// Scale changes the size of an image to the given dimensions.
func (im *Image) Scale(width, height int) (*Image, error) {
	return im.sizeFunc("scaling", width, height, C.ImageDataFunc(C.scaleImage))
}

// Thumbnail changes the size of an image to the given dimensions. This
// method was designed by Bob Friesenhahn as a low cost thumbnail generator.
func (im *Image) Thumbnail(width, height int) (*Image, error) {
	return im.sizeFunc("thumbnailing", width, height, C.ImageDataFunc(C.thumbnailImage))
}
<commit_msg>Add support for resizing proportionally by setting a dimension to < 0<commit_after>package magick

// #include <magick/api.h>
// #include "bridge.h"
// #include "resize.h"
import "C"

import (
	"fmt"
)

type Filter C.FilterTypes

const (
	FPoint     Filter = C.PointFilter
	FBox       Filter = C.BoxFilter
	FTriangle  Filter = C.TriangleFilter
	FHermite   Filter = C.HermiteFilter
	FHanning   Filter = C.HanningFilter
	FHamming   Filter = C.HammingFilter
	FBlackman  Filter = C.BlackmanFilter
	FGaussian  Filter = C.GaussianFilter
	FQuadratic Filter = C.QuadraticFilter
	FCubic     Filter = C.CubicFilter
	FCatrom    Filter = C.CatromFilter
	FMitchell  Filter = C.MitchellFilter
	FLanczos   Filter = C.LanczosFilter
	FBessel    Filter = C.BesselFilter
	FSinc      Filter = C.SincFilter
)

// Magnify is a convenience method that scales an image proportionally to twice its size.
func (im *Image) Magnify() (*Image, error) {
	return im.applyFunc("magnifying", C.ImageFunc(C.MagnifyImage))
}

// Minify is a convenience method that scales an image proportionally to half its size.
func (im *Image) Minify() (*Image, error) {
	return im.applyFunc("minifying", C.ImageFunc(C.MinifyImage))
}

// ResizeBlur returns a new image resized to the given dimensions using the provided
// filter and blur factor (where > 1 is blurry, < 1 is sharp). If width or height is
// < 0, it's calculated proportionally to the other dimension. If both of them are < 0,
// an error is returned.
func (im *Image) ResizeBlur(width, height int, filter Filter, blur float64) (*Image, error) {
	var data C.ResizeData
	if width < 0 {
		if height < 0 {
			return nil, fmt.Errorf("invalid resize %dx%d", width, height)
		}
		h := float64(im.Height())
		var ratio float64
		if h != 0 {
			ratio = float64(im.Width()) / h
		}
		width = int(float64(height) * ratio)
	}
	if height < 0 {
		if width < 0 {
			return nil, fmt.Errorf("invalid resize %dx%d", width, height)
		}
		var ratio float64
		w := float64(im.Width())
		if w != 0 {
			ratio = float64(im.Height()) / w
		}
		height = int(float64(width) * ratio)
	}
	data.columns = C.ulong(width)
	data.rows = C.ulong(height)
	data.filter = C.FilterTypes(filter)
	data.blur = C.double(blur)
	return im.applyDataFunc("resizing", C.ImageDataFunc(C.resizeImage), &data)
}

// Resize works like ResizeBlur, but sets the blur to 1
func (im *Image) Resize(width, height int, filter Filter) (*Image, error) {
	return im.ResizeBlur(width, height, filter, 1)
}

func (im *Image) sizeFunc(what string, width, height int, f C.ImageDataFunc) (*Image, error) {
	var s C.SizeData
	s.columns = C.ulong(width)
	s.rows = C.ulong(height)
	return im.applyDataFunc(what, f, &s)
}

// Sample scales an image to the desired dimensions with pixel sampling.
// Unlike other scaling methods, this method does not introduce any
// additional color into the scaled image.
func (im *Image) Sample(width, height int) (*Image, error) {
	return im.sizeFunc("sampling", width, height, C.ImageDataFunc(C.sampleImage))
}

// Scale changes the size of an image to the given dimensions.
func (im *Image) Scale(width, height int) (*Image, error) {
	return im.sizeFunc("scaling", width, height, C.ImageDataFunc(C.scaleImage))
}

// Thumbnail changes the size of an image to the given dimensions. This
// method was designed by Bob Friesenhahn as a low cost thumbnail generator.
func (im *Image) Thumbnail(width, height int) (*Image, error) {
	return im.sizeFunc("thumbnailing", width, height, C.ImageDataFunc(C.thumbnailImage))
}
<|endoftext|>


<commit_before>package main

import (
	"html/template"
	"io/ioutil"
	"os/exec"
	"strings"

	"github.com/buaazp/fasthttprouter"
	"github.com/shurcooL/github_flavored_markdown"
	"github.com/valyala/fasthttp"
)

const EmptyPageText = "Page does not exist. Click on the Go button above to create it."

type Page struct {
	Title  string
	Source string
	Body   template.HTML
}

type H map[string]interface{} // a standard hash map

func Router() *fasthttprouter.Router {
	router := fasthttprouter.New()
	router.ServeFiles("/assets/*filepath", "assets")

	// / root path
	router.GET("/", func(ctx *fasthttp.RequestCtx) {
		ctx.Redirect("/docs/Main_page", fasthttp.StatusMovedPermanently)
	})

	// /edit launch edit mode from top bar
	router.GET("/edit", func(ctx *fasthttp.RequestCtx) {
		term := string(ctx.FormValue("term"))
		redirection := "/docs/"+term+"/edit"
		if term == "" {
			redirection = "/docs"
		}
		ctx.Redirect(redirection, fasthttp.StatusMovedPermanently)
	})

	// /search find pages
	router.GET("/search", func(ctx *fasthttp.RequestCtx) {
		term := string(ctx.FormValue("term"))
		if term == "" {
			ctx.Redirect("/docs", fasthttp.StatusMovedPermanently)
			return
		}
		files, _ := ioutil.ReadDir("pages/")
		var pages []string
		for _, f := range files {
			title := f.Name()
			if strings.Contains(strings.ToLower(title), strings.ToLower(term)) {
				pages = append(pages, title)
			}
		}

		var content = map[string]string{}
		results, _ := exec.Command("bash", "-c", "cd pages; grep '"+term+"' *").Output() // TODO case insensitive search
		for _, result := range strings.Split(string(results), "\n") {
			matches := strings.Split(result, ":")
			page := matches[0]
			if page == "" {
				continue
			}
			line := strings.Join(matches[1:], "\n")
			content[page] += strings.TrimSpace(line) + "\n"
		}

		page := Page{"Main page", "", ""}
		render(ctx, "search", H{
			"page":    page,
			"pages":   pages,   // pages with search term in name
			"content": content, // pages with search term in content
			"term":    term,
			"title":   "Main page",
		})
	})

	// /docs display all pages
	router.GET("/docs", func(ctx *fasthttp.RequestCtx) {
		files, _ := ioutil.ReadDir("pages/")
		var pages []string
		for _, f := range files {
			title := f.Name()
			if title[0] != '.' && title != "Main_page" {
				pages = append(pages, title)
			}
		}

		page := Page{
			"All",
			"",
			"",
		}
		render(ctx, "show_all", H{
			"page":  page,
			"pages": pages,
			"title": "All",
		})
	})

	// /docs/:title display page
	router.GET("/docs/:title", func(ctx *fasthttp.RequestCtx) {
		title := pageTitle(ctx)
		if title == "All" {
			ctx.Redirect("/docs", fasthttp.StatusMovedPermanently)
			return
		}
		page := loadPage(title)
		term := ""
		if page.Source == EmptyPageText {
			term = title
		}
		render(ctx, "show", H{
			"page":  page,
			"title": strings.ReplaceAll(title, "_", " "),
			"term":  term,
		})
	})

	// /docs/:title/edit edit page
	router.GET("/docs/:title/edit", func(ctx *fasthttp.RequestCtx) {
		title := pageTitle(ctx)
		page := loadPage(title)
		render(ctx, "edit", H{
			"page":  page,
			"title": strings.ReplaceAll(title, "_", " "),
		})
	})

	// /docs/:title/edit edit page action
	router.POST("/docs/:title/edit", func(ctx *fasthttp.RequestCtx) {
		title := pageTitle(ctx)
		source := ctx.FormValue("source")
		ioutil.WriteFile("pages/"+title+".mw.html.md", []byte(source), 0644)

		ctx.Redirect("/docs/"+title, fasthttp.StatusMovedPermanently)
	})

	// /docs/:title/preview preview page action
	router.POST("/docs/:title/preview", func(ctx *fasthttp.RequestCtx) {
		title := pageTitle(ctx)
		source := ctx.FormValue("source")
		page := Page{
			title,
			string(source),
			template.HTML(github_flavored_markdown.Markdown(source)),
		}

		render(ctx, "edit", H{
			"page":  page,
			"title": strings.ReplaceAll(title, "_", " "),
		})
	})

	return router
}

func render(ctx *fasthttp.RequestCtx, view string, attributes H) {
	ctx.SetContentType("text/html")
	t := template.Must(template.ParseFiles("views/layout.html", "views/"+view+".html"))
	t.Execute(ctx, attributes)
}

func loadPage(title string) Page {
	source, err := ioutil.ReadFile("pages/" + title)
	if err != nil {
		source = []byte(EmptyPageText)
	}

	page := Page{
		title,
		string(source),
		template.HTML(github_flavored_markdown.Markdown(source)),
	}
	return page
}

func pageTitle(ctx *fasthttp.RequestCtx) string {
	title, _ := ctx.UserValue("title").(string)
	if title == "" {
		title = "Main_page"
	}
	return title
}
<commit_msg>Extract redirect function<commit_after>package main

import (
	"html/template"
	"io/ioutil"
	"os/exec"
	"strings"

	"github.com/buaazp/fasthttprouter"
	"github.com/shurcooL/github_flavored_markdown"
	"github.com/valyala/fasthttp"
)

const EmptyPageText = "Page does not exist. Click on the Go button above to create it."

type Page struct {
	Title  string
	Source string
	Body   template.HTML
}

type H map[string]interface{} // a standard hash map

func Router() *fasthttprouter.Router {
	router := fasthttprouter.New()
	router.ServeFiles("/assets/*filepath", "assets")

	// / root path
	router.GET("/", func(ctx *fasthttp.RequestCtx) {
		redirect(ctx, "/docs/Main_page")
	})

	// /edit launch edit mode from top bar
	router.GET("/edit", func(ctx *fasthttp.RequestCtx) {
		term := string(ctx.FormValue("term"))
		url := "/docs/" + term + "/edit"
		if term == "" {
			url = "/docs"
		}
		redirect(ctx, url)
	})

	// /search find pages
	router.GET("/search", func(ctx *fasthttp.RequestCtx) {
		term := string(ctx.FormValue("term"))
		if term == "" {
			redirect(ctx, "/docs")
			return
		}
		files, _ := ioutil.ReadDir("pages/")
		var pages []string
		for _, f := range files {
			title := f.Name()
			if strings.Contains(strings.ToLower(title), strings.ToLower(term)) {
				pages = append(pages, title)
			}
		}

		var content = map[string]string{}
		results, _ := exec.Command("bash", "-c", "cd pages; grep '"+term+"' *").Output() // TODO case insensitive search
		for _, result := range strings.Split(string(results), "\n") {
			matches := strings.Split(result, ":")
			page := matches[0]
			if page == "" {
				continue
			}
			line := strings.Join(matches[1:], "\n")
			content[page] += strings.TrimSpace(line) + "\n"
		}

		page := Page{"Main page", "", ""}
		render(ctx, "search", H{
			"page":    page,
			"pages":   pages,   // pages with search term in name
			"content": content, // pages with search term in content
			"term":    term,
			"title":   "Main page",
		})
	})

	// /docs display all pages
	router.GET("/docs", func(ctx *fasthttp.RequestCtx) {
		files, _ := ioutil.ReadDir("pages/")
		var pages []string
		for _, f := range files {
			title := f.Name()
			if title[0] != '.' && title != "Main_page" {
				pages = append(pages, title)
			}
		}

		page := Page{
			"All",
			"",
			"",
		}
		render(ctx, "show_all", H{
			"page":  page,
			"pages": pages,
			"title": "All",
		})
	})

	// /docs/:title display page
	router.GET("/docs/:title", func(ctx *fasthttp.RequestCtx) {
		title := pageTitle(ctx)
		if title == "All" {
			redirect(ctx, "/docs")
			return
		}
		page := loadPage(title)
		term := ""
		if page.Source == EmptyPageText {
			term = title
		}
		render(ctx, "show", H{
			"page":  page,
			"title": strings.ReplaceAll(title, "_", " "),
			"term":  term,
		})
	})

	// /docs/:title/edit edit page
	router.GET("/docs/:title/edit", func(ctx *fasthttp.RequestCtx) {
		title := pageTitle(ctx)
		page := loadPage(title)
		render(ctx, "edit", H{
			"page":  page,
			"title": strings.ReplaceAll(title, "_", " "),
		})
	})

	// /docs/:title/edit edit page action
	router.POST("/docs/:title/edit", func(ctx *fasthttp.RequestCtx) {
		title := pageTitle(ctx)
		source := ctx.FormValue("source")
		ioutil.WriteFile("pages/"+title+".mw.html.md", []byte(source), 0644)

		redirect(ctx, "/docs/"+title)
	})

	// /docs/:title/preview preview page action
	router.POST("/docs/:title/preview", func(ctx *fasthttp.RequestCtx) {
		title := pageTitle(ctx)
		source := ctx.FormValue("source")
		page := Page{
			title,
			string(source),
			template.HTML(github_flavored_markdown.Markdown(source)),
		}

		render(ctx, "edit", H{
			"page":  page,
			"title": strings.ReplaceAll(title, "_", " "),
		})
	})

	return router
}

func redirect(ctx *fasthttp.RequestCtx, url string) {
	ctx.Redirect(url, fasthttp.StatusMovedPermanently)
}

func render(ctx *fasthttp.RequestCtx, view string, attributes H) {
	ctx.SetContentType("text/html")
	t := template.Must(template.ParseFiles("views/layout.html", "views/"+view+".html"))
	t.Execute(ctx, attributes)
}

func loadPage(title string) Page {
	source, err := ioutil.ReadFile("pages/" + title)
	if err != nil {
		source = []byte(EmptyPageText)
	}

	page := Page{
		title,
		string(source),
		template.HTML(github_flavored_markdown.Markdown(source)),
	}
	return page
}

func pageTitle(ctx *fasthttp.RequestCtx) string {
	title, _ := ctx.UserValue("title").(string)
	if title == "" {
		title = "Main_page"
	}
	return title
}
<|endoftext|>


<commit_before>package muxy

import (
	"fmt"
	"net/http"
	"net/url"
	"strings"
)

// New creates a new Router.
func New() *Router {
	r := &Router{
		matcher:         newPathMatcher(),
		namedRoutes:     map[string]*Route{},
		NotFoundHandler: http.NotFound,
	}
	r.router = r
	return r
}

// Router matches the URL of incoming requests against
// registered routes and calls the appropriate handler.
type Router struct {
	router          *Router
	pattern         string
	name            string
	middleware      []func(http.Handler) http.Handler
	matcher         matcher
	namedRoutes     map[string]*Route
	NotFoundHandler func(http.ResponseWriter, *http.Request)
	// ...
}

// Name sets the name prefix used for new routes.
func (r *Router) Name(name string) *Router {
	r.name = r.name + name
	return r
}

// Use sets the middleware used for new routes.
func (r *Router) Use(middleware ...func(http.Handler) http.Handler) *Router {
	r.middleware = append(r.middleware, middleware...)
	return r
}

// Route creates a new Route for the given pattern.
func (r *Router) Route(pattern string) *Route {
	route := r.router.route(r.pattern + pattern)
	route.NamePrefix = r.name
	route.Middleware = r.middleware
	return route
}

// route creates a new Route for the given pattern.
func (r *Router) route(pattern string) *Route {
	m, pm := newPattern(r.matcher, pattern)
	r.matcher = m
	if pm.leaf == nil {
		pm.leaf = newRoute(r, pattern)
	}
	if pm.leaf.Pattern != pattern {
		panic(fmt.Sprintf("mux: pattern %q has a registered equivalent %q", pattern, pm.leaf.Pattern))
	}
	return pm.leaf
}

// Sub creates a subrouter for the given pattern prefix.
func (r *Router) Sub(pattern string) *Router {
	return &Router{
		router:     r.router,
		pattern:    r.pattern + pattern,
		name:       r.name,
		middleware: r.middleware,
	}
}

// Vars returns the matched variables for the given request.
func (r *Router) Vars(req *http.Request) url.Values {
	// oooh... what a muxy trick!
	if route := r.router.match(req); route != nil {
		return route.vars(req)
	}
	return nil
}

// URL returns a URL segment for the given route name and variables.
func (r *Router) URL(name string, vars url.Values) string {
	if route, ok := r.router.namedRoutes[name]; ok {
		return route.url(vars)
	}
	return ""
}

// ServeHTTP dispatches to the handler whose pattern matches the request.
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	// not implemented...
	if route := r.router.match(req); route != nil {
		route.handler(req).ServeHTTP(w, req)
		return
	}
	r.NotFoundHandler(w, req)
}

// match returns the matched route for the given request.
func (r *Router) match(req *http.Request) *Route {
	u := req.URL
	if pm := r.matcher.match(u.Scheme, u.Host, u.Path[1:]); pm != nil {
		return pm.leaf
	}
	return nil
}

// -----------------------------------------------------------------------------

// newRoute creates a new Route.
func newRoute(r *Router, pattern string) *Route {
	return &Route{
		Router:   r,
		Pattern:  pattern,
		Handlers: map[string]func(http.ResponseWriter, *http.Request){},
	}
}

// Route stores a URL pattern to be matched and the handler to be served
// in case of a match, optionally mapping HTTP methods to different handlers.
type Route struct {
	Router     *Router
	Pattern    string
	NamePrefix string
	Middleware []func(http.Handler) http.Handler
	Handlers   map[string]func(http.ResponseWriter, *http.Request)
}

// Name defines the route name used for URL building.
func (r *Route) Name(name string) *Route {
	name = r.NamePrefix + name
	if _, ok := r.Router.namedRoutes[name]; ok {
		panic(fmt.Sprintf("mux: duplicated name %q", name))
	}
	r.Router.namedRoutes[name] = r
	return r
}

// Handle sets the given handler to be served for the optional request methods.
func (r *Route) Handle(handler func(http.ResponseWriter, *http.Request), methods ...string) *Route {
	if methods == nil {
		r.Handlers[""] = handler
	} else {
		for _, m := range methods {
			r.Handlers[m] = handler
		}
	}
	return r
}

// Below are convenience methods that map HTTP verbs to handlers, equivalent
// to call r.Handle(handler, "METHOD-NAME").

// Connect sets the given handler to be served for the request method CONNECT.
func (r *Route) Connect(handler func(http.ResponseWriter, *http.Request)) *Route {
	return r.Handle(handler, "CONNECT")
}

// Delete sets the given handler to be served for the request method DELETE.
func (r *Route) Delete(handler func(http.ResponseWriter, *http.Request)) *Route {
	return r.Handle(handler, "DELETE")
}

// Get sets the given handler to be served for the request method GET.
func (r *Route) Get(handler func(http.ResponseWriter, *http.Request)) *Route {
	return r.Handle(handler, "GET")
}

// Head sets the given handler to be served for the request method HEAD.
func (r *Route) Head(handler func(http.ResponseWriter, *http.Request)) *Route {
	return r.Handle(handler, "HEAD")
}

// Options sets the given handler to be served for the request method OPTIONS.
func (r *Route) Options(handler func(http.ResponseWriter, *http.Request)) *Route {
	return r.Handle(handler, "OPTIONS")
}

// PATCH sets the given handler to be served for the request method PATCH.
func (r *Route) Patch(handler func(http.ResponseWriter, *http.Request)) *Route {
	return r.Handle(handler, "PATCH")
}

// POST sets the given handler to be served for the request method POST.
func (r *Route) Post(handler func(http.ResponseWriter, *http.Request)) *Route {
	return r.Handle(handler, "POST")
}

// Put sets the given handler to be served for the request method PUT.
func (r *Route) Put(handler func(http.ResponseWriter, *http.Request)) *Route {
	return r.Handle(handler, "PUT")
}

// Trace sets the given handler to be served for the request method TRACE.
func (r *Route) Trace(handler func(http.ResponseWriter, *http.Request)) *Route {
	return r.Handle(handler, "TRACE")
}

// handler returns a handler for the request, applying registered middleware.
func (r *Route) handler(req *http.Request) http.Handler {
	var h http.Handler = http.HandlerFunc(r.methodHandler(req.Method))
	for i := len(r.Middleware) - 1; i >= 0; i-- {
		h = r.Middleware[i](h)
	}
	return h
}

// methodHandler returns the handler registered for the given HTTP method.
func (r *Route) methodHandler(method string) func(http.ResponseWriter, *http.Request) {
	if h, ok := r.Handlers[method]; ok {
		return h
	}
	switch method {
	case "HEAD":
		if h, ok := r.Handlers["GET"]; ok {
			return h
		}
	case "OPTIONS":
		return r.allowHandler(200)
	default:
		if h, ok := r.Handlers[""]; ok {
			return h
		}
	}
	return r.allowHandler(405)
}

// allowHandler returns a handler that sets a header with the given
// status code and allowed methods.
func (r *Route) allowHandler(code int) func(http.ResponseWriter, *http.Request) {
	allowed := []string{"OPTIONS"}
	for m, _ := range r.Handlers {
		if m != "" && m != "OPTIONS" {
			allowed = append(allowed, m)
		}
	}
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.Header().Set("Allow", strings.Join(allowed, ", "))
		w.WriteHeader(code)
		fmt.Fprintln(w, code, http.StatusText(code))
	}
}

// url returns a URL segment for the given variables.
func (r *Route) url(vars url.Values) string {
	// not implemented...
	return ""
}

// vars returns the matched variables for the given request.
func (r *Route) vars(req *http.Request) url.Values {
	// not implemented...
	return nil
}
<commit_msg>Fixed logic in Route.methodHandler().<commit_after>package muxy

import (
	"fmt"
	"net/http"
	"net/url"
	"strings"
)

// New creates a new Router.
func New() *Router {
	r := &Router{
		matcher:         newPathMatcher(),
		namedRoutes:     map[string]*Route{},
		NotFoundHandler: http.NotFound,
	}
	r.router = r
	return r
}

// Router matches the URL of incoming requests against
// registered routes and calls the appropriate handler.
type Router struct {
	router          *Router
	pattern         string
	name            string
	middleware      []func(http.Handler) http.Handler
	matcher         matcher
	namedRoutes     map[string]*Route
	NotFoundHandler func(http.ResponseWriter, *http.Request)
	// ...
}

// Name sets the name prefix used for new routes.
func (r *Router) Name(name string) *Router {
	r.name = r.name + name
	return r
}

// Use sets the middleware used for new routes.
func (r *Router) Use(middleware ...func(http.Handler) http.Handler) *Router {
	r.middleware = append(r.middleware, middleware...)
	return r
}

// Route creates a new Route for the given pattern.
func (r *Router) Route(pattern string) *Route {
	route := r.router.route(r.pattern + pattern)
	route.NamePrefix = r.name
	route.Middleware = r.middleware
	return route
}

// route creates a new Route for the given pattern.
func (r *Router) route(pattern string) *Route {
	m, pm := newPattern(r.matcher, pattern)
	r.matcher = m
	if pm.leaf == nil {
		pm.leaf = newRoute(r, pattern)
	}
	if pm.leaf.Pattern != pattern {
		panic(fmt.Sprintf("mux: pattern %q has a registered equivalent %q", pattern, pm.leaf.Pattern))
	}
	return pm.leaf
}

// Sub creates a subrouter for the given pattern prefix.
func (r *Router) Sub(pattern string) *Router {
	return &Router{
		router:     r.router,
		pattern:    r.pattern + pattern,
		name:       r.name,
		middleware: r.middleware,
	}
}

// Vars returns the matched variables for the given request.
func (r *Router) Vars(req *http.Request) url.Values {
	// oooh... what a muxy trick!
	if route := r.router.match(req); route != nil {
		return route.vars(req)
	}
	return nil
}

// URL returns a URL segment for the given route name and variables.
func (r *Router) URL(name string, vars url.Values) string {
	if route, ok := r.router.namedRoutes[name]; ok {
		return route.url(vars)
	}
	return ""
}

// ServeHTTP dispatches to the handler whose pattern matches the request.
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	// not implemented...
	if route := r.router.match(req); route != nil {
		route.handler(req).ServeHTTP(w, req)
		return
	}
	r.NotFoundHandler(w, req)
}

// match returns the matched route for the given request.
func (r *Router) match(req *http.Request) *Route {
	u := req.URL
	if pm := r.matcher.match(u.Scheme, u.Host, u.Path[1:]); pm != nil {
		return pm.leaf
	}
	return nil
}

// -----------------------------------------------------------------------------

// newRoute creates a new Route.
func newRoute(r *Router, pattern string) *Route {
	return &Route{
		Router:   r,
		Pattern:  pattern,
		Handlers: map[string]func(http.ResponseWriter, *http.Request){},
	}
}

// Route stores a URL pattern to be matched and the handler to be served
// in case of a match, optionally mapping HTTP methods to different handlers.
type Route struct {
	Router     *Router
	Pattern    string
	NamePrefix string
	Middleware []func(http.Handler) http.Handler
	Handlers   map[string]func(http.ResponseWriter, *http.Request)
}

// Name defines the route name used for URL building.
func (r *Route) Name(name string) *Route {
	name = r.NamePrefix + name
	if _, ok := r.Router.namedRoutes[name]; ok {
		panic(fmt.Sprintf("mux: duplicated name %q", name))
	}
	r.Router.namedRoutes[name] = r
	return r
}

// Handle sets the given handler to be served for the optional request methods.
func (r *Route) Handle(handler func(http.ResponseWriter, *http.Request), methods ...string) *Route {
	if methods == nil {
		r.Handlers[""] = handler
	} else {
		for _, m := range methods {
			r.Handlers[m] = handler
		}
	}
	return r
}

// Below are convenience methods that map HTTP verbs to handlers, equivalent
// to call r.Handle(handler, "METHOD-NAME").

// Connect sets the given handler to be served for the request method CONNECT.
func (r *Route) Connect(handler func(http.ResponseWriter, *http.Request)) *Route {
	return r.Handle(handler, "CONNECT")
}

// Delete sets the given handler to be served for the request method DELETE.
func (r *Route) Delete(handler func(http.ResponseWriter, *http.Request)) *Route {
	return r.Handle(handler, "DELETE")
}

// Get sets the given handler to be served for the request method GET.
func (r *Route) Get(handler func(http.ResponseWriter, *http.Request)) *Route {
	return r.Handle(handler, "GET")
}

// Head sets the given handler to be served for the request method HEAD.
func (r *Route) Head(handler func(http.ResponseWriter, *http.Request)) *Route {
	return r.Handle(handler, "HEAD")
}

// Options sets the given handler to be served for the request method OPTIONS.
func (r *Route) Options(handler func(http.ResponseWriter, *http.Request)) *Route {
	return r.Handle(handler, "OPTIONS")
}

// PATCH sets the given handler to be served for the request method PATCH.
func (r *Route) Patch(handler func(http.ResponseWriter, *http.Request)) *Route {
	return r.Handle(handler, "PATCH")
}

// POST sets the given handler to be served for the request method POST.
func (r *Route) Post(handler func(http.ResponseWriter, *http.Request)) *Route {
	return r.Handle(handler, "POST")
}

// Put sets the given handler to be served for the request method PUT.
func (r *Route) Put(handler func(http.ResponseWriter, *http.Request)) *Route {
	return r.Handle(handler, "PUT")
}

// Trace sets the given handler to be served for the request method TRACE.
func (r *Route) Trace(handler func(http.ResponseWriter, *http.Request)) *Route {
	return r.Handle(handler, "TRACE")
}

// handler returns a handler for the request, applying registered middleware.
func (r *Route) handler(req *http.Request) http.Handler {
	var h http.Handler = http.HandlerFunc(r.methodHandler(req.Method))
	for i := len(r.Middleware) - 1; i >= 0; i-- {
		h = r.Middleware[i](h)
	}
	return h
}

// methodHandler returns the handler registered for the given HTTP method.
func (r *Route) methodHandler(method string) func(http.ResponseWriter, *http.Request) {
	if h, ok := r.Handlers[method]; ok {
		return h
	}
	switch method {
	case "OPTIONS":
		return r.allowHandler(200)
	case "HEAD":
		if h, ok := r.Handlers["GET"]; ok {
			return h
		}
		fallthrough
	default:
		if h, ok := r.Handlers[""]; ok {
			return h
		}
	}
	return r.allowHandler(405)
}

// allowHandler returns a handler that sets a header with the given
// status code and allowed methods.
func (r *Route) allowHandler(code int) func(http.ResponseWriter, *http.Request) {
	allowed := []string{"OPTIONS"}
	for m, _ := range r.Handlers {
		if m != "" && m != "OPTIONS" {
			allowed = append(allowed, m)
		}
	}
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.Header().Set("Allow", strings.Join(allowed, ", "))
		w.WriteHeader(code)
		fmt.Fprintln(w, code, http.StatusText(code))
	}
}

// url returns a URL segment for the given variables.
func (r *Route) url(vars url.Values) string {
	// not implemented...
	return ""
}

// vars returns the matched variables for the given request.
func (r *Route) vars(req *http.Request) url.Values {
	// not implemented...
	return nil
}
<|endoftext|>


<commit_before>package air

import (
	"net/url"
	ppath "path"
	"strings"
	"sync"
)

// router is a registry of all registered routes.
type router struct {
	sync.Mutex

	a                    *Air
	routeTree            *routeNode
	registeredRoutes     map[string]bool
	maxRouteParams       int
	routeParamValuesPool *sync.Pool
}

// newRouter returns a new instance of the `router` with the a.
func newRouter(a *Air) *router {
	r := &router{
		a: a,
		routeTree: &routeNode{
			handlers: map[string]Handler{},
		},
		registeredRoutes: map[string]bool{},
	}
	r.routeParamValuesPool = &sync.Pool{
		New: func() interface{} {
			return make([]string, r.maxRouteParams)
		},
	}

	return r
}

// register registers a new route for the method and the path with the matching
// h in the r with the optional route-level gases.
func (r *router) register(method, path string, h Handler, gases ...Gas) {
	r.Lock()
	defer r.Unlock()

	if path == "" {
		panic("air: route path cannot be empty")
	} else if h == nil {
		panic("air: route handler cannot be nil")
	}

	path = ppath.Clean(path)
	path = url.PathEscape(path)
	path = strings.Replace(path, "%2F", "/", -1)
	path = strings.Replace(path, "%2A", "*", -1)
	if path[0] != '/' {
		panic("air: route path must start with /")
	} else if strings.Count(path, ":") > 1 {
		ps := strings.Split(path, "/")
		for _, p := range ps {
			if strings.Count(p, ":") > 1 {
				panic("air: adjacent param names in route " +
					"path must be separated by /")
			}
		}
	} else if strings.Contains(path, "*") {
		if strings.Count(path, "*") > 1 {
			panic("air: only one * is allowed in route path")
		} else if path[len(path)-1] != '*' {
			panic("air: * can only appear at end of route path")
		} else if strings.Contains(
			path[strings.LastIndex(path, "/"):],
			":",
		) {
			panic("air: adjacent param name and * in route path " +
				"must be separated by /")
		}
	}

	routeName := method + path
	for i, l := len(method), len(routeName); i < l; i++ {
		if routeName[i] == ':' {
			j := i + 1

			for ; i < l && routeName[i] != '/'; i++ {
			}

			routeName = routeName[:j] + routeName[i:]
			i, l = j, len(routeName)

			if i == l {
				break
			}
		}
	}

	if r.registeredRoutes[routeName] {
		panic("air: route already exists")
	} else {
		r.registeredRoutes[routeName] = true
	}

	rh := func(req *Request, res *Response) error {
		h := h
		for i := len(gases) - 1; i >= 0; i-- {
			h = gases[i](h)
		}

		return h(req, res)
	}

	paramNames := []string{}
	for i, l := 0, len(path); i < l; i++ {
		if path[i] == ':' {
			j := i + 1

			r.insert(
				method,
				path[:i],
				nil,
				routeNodeTypeStatic,
				nil,
			)

			for ; i < l && path[i] != '/'; i++ {
			}

			paramName := path[j:i]

			for _, pn := range paramNames {
				if pn == paramName {
					panic("air: route path cannot have " +
						"duplicate param names")
				}
			}

			paramNames = append(paramNames, paramName)
			path = path[:j] + path[i:]

			if i, l = j, len(path); i == l {
				r.insert(
					method,
					path,
					rh,
					routeNodeTypeParam,
					paramNames,
				)
				return
			}

			r.insert(
				method,
				path[:i],
				nil,
				routeNodeTypeParam,
				paramNames,
			)
		} else if path[i] == '*' {
			r.insert(
				method,
				path[:i],
				nil,
				routeNodeTypeStatic,
				nil,
			)
			paramNames = append(paramNames, "*")
			r.insert(
				method,
				path[:i+1],
				rh,
				routeNodeTypeAny,
				paramNames,
			)
			return
		}
	}

	r.insert(method, path, rh, routeNodeTypeStatic, paramNames)
}

// insert inserts a new route into the `r.routeTree`.
func (r *router) insert(
	method string,
	path string,
	h Handler,
	nt routeNodeType,
	paramNames []string,
) {
	if l := len(paramNames); l > r.maxRouteParams {
		r.maxRouteParams = l
	}

	var (
		s  = path        // Search
		cn = r.routeTree // Current node
		nn *routeNode    // Next node
		sl int           // Search length
		pl int           // Prefix length
		ll int           // LCP length
		ml int           // Minimum length of sl and pl
	)

	for {
		sl = len(s)
		pl = len(cn.prefix)
		ll = 0

		ml = pl
		if sl < ml {
			ml = sl
		}

		for ; ll < ml && s[ll] == cn.prefix[ll]; ll++ {
		}

		if ll == 0 { // At root node
			cn.label = s[0]
			cn.nType = nt
			cn.prefix = s
			cn.paramNames = paramNames
			if h != nil {
				cn.handlers[method] = h
			}
		} else if ll < pl { // Split node
			nn = &routeNode{
				label:      cn.prefix[ll],
				nType:      cn.nType,
				prefix:     cn.prefix[ll:],
				children:   cn.children,
				paramNames: cn.paramNames,
				handlers:   cn.handlers,
			}

			// Reset current node.
			cn.label = cn.prefix[0]
			cn.nType = routeNodeTypeStatic
			cn.prefix = cn.prefix[:ll]
			cn.children = []*routeNode{nn}
			cn.paramNames = nil
			cn.handlers = map[string]Handler{}

			if ll == sl { // At current node
				cn.nType = nt
				cn.paramNames = paramNames
				if h != nil {
					cn.handlers[method] = h
				}
			} else { // Create child node
				nn = &routeNode{
					label:      s[ll],
					nType:      nt,
					prefix:     s[ll:],
					paramNames: paramNames,
					handlers:   map[string]Handler{},
				}
				if h != nil {
					nn.handlers[method] = h
				}

				cn.children = append(cn.children, nn)
			}
		} else if ll < sl {
			s = s[ll:]
			if nn = cn.childByLabel(s[0]); nn != nil {
				// Go deeper.
				cn = nn
				continue
			}

			// Create child node.
			nn = &routeNode{
				label:      s[0],
				nType:      nt,
				prefix:     s,
				handlers:   map[string]Handler{},
				paramNames: paramNames,
			}
			if h != nil {
				nn.handlers[method] = h
			}

			cn.children = append(cn.children, nn)
		} else { // Node already exists
			if len(cn.paramNames) == 0 {
				cn.paramNames = paramNames
			}

			if h != nil {
				cn.handlers[method] = h
			}
		}

		break
	}
}

// route returns a handler registered for the req.
func (r *router) route(req *Request) Handler {
	var (
		s, _ = splitPathQuery(req.Path) // Search
		cn   = r.routeTree              // Current node
		nn   *routeNode                 // Next node
		nnt  routeNodeType              // Next node type
		sn   *routeNode                 // Saved node
		ss   string                     // Saved search
		sl   int                        // Search length
		pl   int                        // Prefix length
		ll   int                        // LCP length
		ml   int                        // Minimum length of sl and pl
		i    int                        // Index
		pi   int                        // Param index
	)

	// Search order: static route > param route > any route.
	for {
		if s == "" {
			break
		}

		for i, sl = 1, len(s); i < sl && s[i] == '/'; i++ {
		}

		s = s[i-1:]

		pl = 0
		ll = 0

		if cn.label != ':' {
			pl = len(cn.prefix)

			ml = pl
			if sl = len(s); sl < ml {
				ml = sl
			}

			for ; ll < ml && s[ll] == cn.prefix[ll]; ll++ {
			}
		}

		if ll != pl {
			goto Struggle
		}

		if s = s[ll:]; s == "" {
			if len(cn.handlers) == 0 {
				if cn.childByType(routeNodeTypeParam) != nil {
					goto Param
				}

				if cn.childByType(routeNodeTypeAny) != nil {
					goto Any
				}
			}

			break
		}

		// Static node.
		if nn = cn.child(s[0], routeNodeTypeStatic); nn != nil {
			// Save next.
			if l := len(cn.prefix); l > 0 && cn.prefix[l-1] == '/' {
				nnt = routeNodeTypeParam
				sn = cn
				ss = s
			}

			cn = nn

			continue
		}

		// Param node.
	Param:
		if nn = cn.childByType(routeNodeTypeParam); nn != nil {
			// Save next.
			if l := len(cn.prefix); l > 0 && cn.prefix[l-1] == '/' {
				nnt = routeNodeTypeAny
				sn = cn
				ss = s
			}

			cn = nn

			for i, sl = 0, len(s); i < sl && s[i] != '/'; i++ {
			}

			if req.routeParamValues == nil {
				req.routeParamValues = r.routeParamValuesPool.
					Get().([]string)
			}

			req.routeParamValues[pi] = s[:i]
			pi++

			s = s[i:]

			continue
		}

		// Any node.
	Any:
		if cn = cn.childByType(routeNodeTypeAny); cn != nil {
			if req.routeParamValues == nil {
				req.routeParamValues = r.routeParamValuesPool.
					Get().([]string)
			}

			req.routeParamValues[len(cn.paramNames)-1] = s

			break
		}

		// Struggle for the former node.
	Struggle:
		if sn != nil {
			cn = sn
			sn = nil
			s = ss

			switch nnt {
			case routeNodeTypeParam:
				goto Param
			case routeNodeTypeAny:
				goto Any
			}
		}

		return r.a.NotFoundHandler
	}

	if h := cn.handlers[req.Method]; h != nil {
		req.routeParamNames = cn.paramNames
		return h
	} else if len(cn.handlers) != 0 {
		return r.a.MethodNotAllowedHandler
	}

	return r.a.NotFoundHandler
}

// routeNode is the node of the route radix tree.
type routeNode struct {
	label      byte
	nType      routeNodeType
	prefix     string
	children   []*routeNode
	paramNames []string
	handlers   map[string]Handler
}

// child returns a child node of the rn by the l and the t.
func (rn *routeNode) child(l byte, t routeNodeType) *routeNode {
	for _, c := range rn.children {
		if c.label == l && c.nType == t {
			return c
		}
	}

	return nil
}

// childByLabel returns a child node of the rn by the l.
func (rn *routeNode) childByLabel(l byte) *routeNode {
	for _, c := range rn.children {
		if c.label == l {
			return c
		}
	}

	return nil
}

// childByType returns a child node of the rn by the t.
func (rn *routeNode) childByType(t routeNodeType) *routeNode {
	for _, c := range rn.children {
		if c.nType == t {
			return c
		}
	}

	return nil
}

// routeNodeType is the type of the `routeNode`.
type routeNodeType uint8

// The route node types.
const (
	routeNodeTypeStatic routeNodeType = iota
	routeNodeTypeParam
	routeNodeTypeAny
)

// splitPathQuery splits the p of the form "path?query" into path and query.
func splitPathQuery(p string) (path, query string) {
	i, l := 0, len(p)
	for ; i < l && p[i] != '?'; i++ {
	}

	if i < l {
		return p[:i], p[i+1:]
	}

	return p, ""
}
<commit_msg>refactor: refine `router#route()`<commit_after>package air

import (
	"net/url"
	ppath "path"
	"strings"
	"sync"
)

// router is a registry of all registered routes.
type router struct {
	sync.Mutex

	a                    *Air
	routeTree            *routeNode
	registeredRoutes     map[string]bool
	maxRouteParams       int
	routePalPool         *sync.Pool
	routeParamValuesPool *sync.Pool
}

// newRouter returns a new instance of the `router` with the a.
func newRouter(a *Air) *router {
	r := &router{
		a: a,
		routeTree: &routeNode{
			handlers: map[string]Handler{},
		},
		registeredRoutes: map[string]bool{},
		routePalPool: &sync.Pool{
			New: func() interface{} {
				return &routePal{}
			},
		},
	}
	r.routeParamValuesPool = &sync.Pool{
		New: func() interface{} {
			return make([]string, r.maxRouteParams)
		},
	}

	return r
}

// register registers a new route for the method and the path with the matching
// h in the r with the optional route-level gases.
func (r *router) register(method, path string, h Handler, gases ...Gas) {
	r.Lock()
	defer r.Unlock()

	if path == "" {
		panic("air: route path cannot be empty")
	} else if h == nil {
		panic("air: route handler cannot be nil")
	}

	path = ppath.Clean(path)
	path = url.PathEscape(path)
	path = strings.Replace(path, "%2F", "/", -1)
	path = strings.Replace(path, "%2A", "*", -1)
	if path[0] != '/' {
		panic("air: route path must start with /")
	} else if strings.Count(path, ":") > 1 {
		ps := strings.Split(path, "/")
		for _, p := range ps {
			if strings.Count(p, ":") > 1 {
				panic("air: adjacent param names in route " +
					"path must be separated by /")
			}
		}
	} else if strings.Contains(path, "*") {
		if strings.Count(path, "*") > 1 {
			panic("air: only one * is allowed in route path")
		} else if path[len(path)-1] != '*' {
			panic("air: * can only appear at end of route path")
		} else if strings.Contains(
			path[strings.LastIndex(path, "/"):],
			":",
		) {
			panic("air: adjacent param name and * in route path " +
				"must be separated by /")
		}
	}

	routeName := method + path
	for i, l := len(method), len(routeName); i < l; i++ {
		if routeName[i] == ':' {
			j := i + 1

			for ; i < l && routeName[i] != '/'; i++ {
			}

			routeName = routeName[:j] + routeName[i:]
			i, l = j, len(routeName)

			if i == l {
				break
			}
		}
	}

	if r.registeredRoutes[routeName] {
		panic("air: route already exists")
	} else {
		r.registeredRoutes[routeName] = true
	}

	rh := func(req *Request, res *Response) error {
		h := h
		for i := len(gases) - 1; i >= 0; i-- {
			h = gases[i](h)
		}

		return h(req, res)
	}

	paramNames := []string{}
	for i, l := 0, len(path); i < l; i++ {
		if path[i] == ':' {
			j := i + 1

			r.insert(
				method,
				path[:i],
				nil,
				routeNodeTypeStatic,
				nil,
			)

			for ; i < l && path[i] != '/'; i++ {
			}

			paramName := path[j:i]

			for _, pn := range paramNames {
				if pn == paramName {
					panic("air: route path cannot have " +
						"duplicate param names")
				}
			}

			paramNames = append(paramNames, paramName)
			path = path[:j] + path[i:]

			if i, l = j, len(path); i == l {
				r.insert(
					method,
					path,
					rh,
					routeNodeTypeParam,
					paramNames,
				)
				return
			}

			r.insert(
				method,
				path[:i],
				nil,
				routeNodeTypeParam,
				paramNames,
			)
		} else if path[i] == '*' {
			r.insert(
				method,
				path[:i],
				nil,
				routeNodeTypeStatic,
				nil,
			)
			paramNames = append(paramNames, "*")
			r.insert(
				method,
				path[:i+1],
				rh,
				routeNodeTypeAny,
				paramNames,
			)
			return
		}
	}

	r.insert(method, path, rh, routeNodeTypeStatic, paramNames)
}

// insert inserts a new route into the `r.routeTree`.
func (r *router) insert(
	method string,
	path string,
	h Handler,
	nt routeNodeType,
	paramNames []string,
) {
	if l := len(paramNames); l > r.maxRouteParams {
		r.maxRouteParams = l
	}

	var (
		s  = path        // Search
		cn = r.routeTree // Current node
		nn *routeNode    // Next node
		sl int           // Search length
		pl int           // Prefix length
		ll int           // LCP length
		ml int           // Minimum length of sl and pl
	)

	for {
		sl = len(s)
		pl = len(cn.prefix)
		ll = 0

		ml = pl
		if sl < ml {
			ml = sl
		}

		for ; ll < ml && s[ll] == cn.prefix[ll]; ll++ {
		}

		if ll == 0 { // At root node
			cn.label = s[0]
			cn.nType = nt
			cn.prefix = s
			cn.paramNames = paramNames
			if h != nil {
				cn.handlers[method] = h
			}
		} else if ll < pl { // Split node
			nn = &routeNode{
				label:      cn.prefix[ll],
				nType:      cn.nType,
				prefix:     cn.prefix[ll:],
				children:   cn.children,
				paramNames: cn.paramNames,
				handlers:   cn.handlers,
			}

			// Reset current node.
			cn.label = cn.prefix[0]
			cn.nType = routeNodeTypeStatic
			cn.prefix = cn.prefix[:ll]
			cn.children = []*routeNode{nn}
			cn.paramNames = nil
			cn.handlers = map[string]Handler{}

			if ll == sl { // At current node
				cn.nType = nt
				cn.paramNames = paramNames
				if h != nil {
					cn.handlers[method] = h
				}
			} else { // Create child node
				nn = &routeNode{
					label:      s[ll],
					nType:      nt,
					prefix:     s[ll:],
					paramNames: paramNames,
					handlers:   map[string]Handler{},
				}
				if h != nil {
					nn.handlers[method] = h
				}

				cn.children = append(cn.children, nn)
			}
		} else if ll < sl {
			s = s[ll:]
			if nn = cn.childByLabel(s[0]); nn != nil {
				// Go deeper.
				cn = nn
				continue
			}

			// Create child node.
			nn = &routeNode{
				label:      s[0],
				nType:      nt,
				prefix:     s,
				handlers:   map[string]Handler{},
				paramNames: paramNames,
			}
			if h != nil {
				nn.handlers[method] = h
			}

			cn.children = append(cn.children, nn)
		} else { // Node already exists
			if len(cn.paramNames) == 0 {
				cn.paramNames = paramNames
			}

			if h != nil {
				cn.handlers[method] = h
			}
		}

		break
	}
}

// route returns a handler registered for the req.
func (r *router) route(req *Request) Handler {
	p := r.routePalPool.Get().(*routePal)
	p.s, _ = splitPathQuery(req.Path)
	p.cn = r.routeTree
	p.nnt = routeNodeTypeStatic
	p.sn = nil
	p.ss = ""
	p.pi = 0

	// Search order: static route > param route > any route.
	for {
		if p.s == "" {
			break
		}

		p.i = 1
		p.sl = len(p.s)
		for p.i < p.sl && p.s[p.i] == '/' {
			p.i++
		}

		p.s = p.s[p.i-1:]

		p.pl = 0
		p.ll = 0

		if p.cn.label != ':' {
			p.pl = len(p.cn.prefix)

			p.ml = p.pl
			if p.sl = len(p.s); p.sl < p.ml {
				p.ml = p.sl
			}

			for p.ll < p.ml && p.s[p.ll] == p.cn.prefix[p.ll] {
				p.ll++
			}
		}

		if p.ll != p.pl {
			goto Struggle
		}

		if p.s = p.s[p.ll:]; p.s == "" {
			if len(p.cn.handlers) == 0 {
				if p.cn.childByType(routeNodeTypeParam) != nil {
					goto Param
				}

				if p.cn.childByType(routeNodeTypeAny) != nil {
					goto Any
				}
			}

			break
		}

		// Static node.
		if p.nn = p.cn.child(p.s[0], routeNodeTypeStatic); p.nn != nil {
			// Save next.
			if p.pl = len(p.cn.prefix); p.pl > 0 &&
				p.cn.prefix[p.pl-1] == '/' {
				p.nnt = routeNodeTypeParam
				p.sn = p.cn
				p.ss = p.s
			}

			p.cn = p.nn

			continue
		}

		// Param node.
	Param:
		if p.nn = p.cn.childByType(routeNodeTypeParam); p.nn != nil {
			// Save next.
			if p.pl = len(p.cn.prefix); p.pl > 0 &&
				p.cn.prefix[p.pl-1] == '/' {
				p.nnt = routeNodeTypeAny
				p.sn = p.cn
				p.ss = p.s
			}

			p.cn = p.nn

			p.i = 0
			p.sl = len(p.s)
			for p.i < p.sl && p.s[p.i] != '/' {
				p.i++
			}

			if req.routeParamValues == nil {
				req.routeParamValues = r.routeParamValuesPool.
					Get().([]string)
			}

			req.routeParamValues[p.pi] = p.s[:p.i]
			p.pi++

			p.s = p.s[p.i:]

			continue
		}

		// Any node.
	Any:
		if p.cn = p.cn.childByType(routeNodeTypeAny); p.cn != nil {
			if req.routeParamValues == nil {
				req.routeParamValues = r.routeParamValuesPool.
					Get().([]string)
			}

			req.routeParamValues[len(p.cn.paramNames)-1] = p.s

			break
		}

		// Struggle for the former node.
	Struggle:
		if p.sn != nil {
			p.cn = p.sn
			p.sn = nil
			p.s = p.ss

			switch p.nnt {
			case routeNodeTypeParam:
				goto Param
			case routeNodeTypeAny:
				goto Any
			}
		}

		r.routePalPool.Put(p)

		return r.a.NotFoundHandler
	}

	h := p.cn.handlers[req.Method]
	if h != nil {
		req.routeParamNames = p.cn.paramNames
	} else if len(p.cn.handlers) != 0 {
		h = r.a.MethodNotAllowedHandler
	} else {
		h = r.a.NotFoundHandler
	}

	r.routePalPool.Put(p)

	return h
}

// routeNode is the node of the route radix tree.
type routeNode struct {
	label      byte
	nType      routeNodeType
	prefix     string
	children   []*routeNode
	paramNames []string
	handlers   map[string]Handler
}

// child returns a child node of the rn by the l and the t.
func (rn *routeNode) child(l byte, t routeNodeType) *routeNode {
	for _, c := range rn.children {
		if c.label == l && c.nType == t {
			return c
		}
	}

	return nil
}

// childByLabel returns a child node of the rn by the l.
func (rn *routeNode) childByLabel(l byte) *routeNode {
	for _, c := range rn.children {
		if c.label == l {
			return c
		}
	}

	return nil
}

// childByType returns a child node of the rn by the t.
func (rn *routeNode) childByType(t routeNodeType) *routeNode {
	for _, c := range rn.children {
		if c.nType == t {
			return c
		}
	}

	return nil
}

// routeNodeType is the type of the `routeNode`.
type routeNodeType uint8

// The route node types.
const (
	routeNodeTypeStatic routeNodeType = iota
	routeNodeTypeParam
	routeNodeTypeAny
)

// routePal is the pal struct of the `router#route()`.
type routePal struct {
	s   string        // Search
	cn  *routeNode    // Current node
	nn  *routeNode    // Next node
	nnt routeNodeType // Next node type
	sn  *routeNode    // Saved node
	ss  string        // Saved search
	sl  int           // Search length
	pl  int           // Prefix length
	ll  int           // LCP length
	ml  int           // Minimum length of sl and pl
	i   int           // Index
	pi  int           // Param index
}

// splitPathQuery splits the p of the form "path?query" into path and query.
func splitPathQuery(p string) (path, query string) {
	i, l := 0, len(p)
	for ; i < l && p[i] != '?'; i++ {
	}

	if i < l {
		return p[:i], p[i+1:]
	}

	return p, ""
}
<|endoftext|>


<commit_before>package main

// Glen Newton
// glen.newton@gmail.com

import (
	"flag"
	"fmt"
	"log"
	"math/rand"
	"os"
	"os/exec"
	"runtime"
	"sort"
	"strings"
	"time"
)

func init() {
	flag.StringVar(&outputFormat, "outputFormat", "html", "Values: html|html2|json|txt|latex")
	flag.StringVar(&header, "header", "Installed Software", "gggg")
	flag.BoolVar(&doR, "R", false, "Find R packages")
}

var outputFormat string
var header string
var doR = false

type PackageInfo struct {
	Name      string
	IndexName string
	IsR       bool
	Tags      map[string]string
}

type RpmWriter interface {
	output(string, []string, []string, map[string]*PackageInfo, map[string]bool, map[string]*Node) error
}

func handleParameters() bool {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "%s: Output rpm info for rpms that have install components in certain directories\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "\t %s <args> <rootDir0>...<rootDirN>\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "\t default <rootDir>: /\n")
		fmt.Fprintf(os.Stderr, "Args:\n")
		flag.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\nExample:  %s -outputFormat=html /opt /usr/local\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "\nNote that the 'rpm' program (http://www.rpm.org/max-rpm/rpm.8.html) needs to be in your path\n\n")
	}

	flag.Parse()
	switch outputFormat {
	case "html":
		rpmWriter = new(HtmlOut)
	case "html2":
		rpmWriter = new(HtmlOut2)
	case "json":
		rpmWriter = new(JsonOut)

	case "txt":
		rpmWriter = new(TextOut)

	case "latex":
		rpmWriter = new(LaTeXOut)
	default:
		log.Print("Unknown/unsupported output format: " + outputFormat)
		return false
	}
	return true
}

var allRpmsExec = []string{"rpm", "-qa"}

var numFindOfInterestRpmsWorkers = 6
var numFindRpmTagsWorkers = 2

var rpmWriter RpmWriter

func main() {

	runtime.GOMAXPROCS(8)

	_, err := exec.LookPath(allRpmsExec[0])

	if err != nil {
		log.Fatal("'", allRpmsExec[0], "' is not in your path: needed to run; it is usually in '/usr/bin/rpm'")
	}

	if !handleParameters() {
		log.Fatal("Bad parameters")
	}

	rand.Seed(time.Now().Unix())

	var dirsOfInterest []string
	if len(flag.Args()) == 0 {
		dirsOfInterest = []string{"/"}
	} else {
		dirsOfInterest = flag.Args()
	}

	//fmt.Println(dirsOfInterest)

	rpmListChannel, err := makeRpmList()
	if err != nil {
		log.Fatal("jjjj ", err)
	}

	ofInterestChannel, ofInterestDoneChannel, err := findOfInterestRpms(dirsOfInterest, rpmListChannel)

	tagInfoChannel, tagInfoDoneChannel, err := findRpmTags(ofInterestChannel)

	packageInfoMap := make(map[string]*PackageInfo, 200)
	//packageInfoMap2 := make(map[string] PackageInfo, 200)
	groupSet := make(map[string]bool)

	var nodes map[string]*Node
	nodes = make(map[string]*Node)

	addResultsDoneChannel := addResultsToMap(packageInfoMap, groupSet, tagInfoChannel, nodes)

	for i := 0; i < numFindOfInterestRpmsWorkers; i++ {
		_ = <-ofInterestDoneChannel
	}
	close(ofInterestChannel)

	for i := 0; i < numFindRpmTagsWorkers; i++ {
		_ = <-tagInfoDoneChannel
	}
	close(tagInfoChannel)

	_ = <-addResultsDoneChannel

	var rpackageInfoMap map[string]*PackageInfo = nil
	if doR {
		rpackageInfoMap = findRPackages()
	}

	add(packageInfoMap, rpackageInfoMap)
	rpmWriter.output(header, dirsOfInterest, sortStringKeyMap(packageInfoMap), packageInfoMap, groupSet, nodes)
}

func xlog(m string) {
	fmt.Println(m)
}

func makeRpmList() (chan *PackageInfo, error) {
	rpmListChannel := make(chan *PackageInfo, 200)

	stringListChannel := runExec(allRpmsExec)

	go func() {
		for stringInfo := range stringListChannel {
			//fmt.Println("makeRpmList: [",  stringInfo.val, "] ", &stringInfo.val," ", stringInfo.done)
			packageInfo := new(PackageInfo)
			packageInfo.Name = stringInfo.val
			packageInfo.Tags = make(map[string]string)
			rpmListChannel <- packageInfo
		}
		close(rpmListChannel)
	}()
	return rpmListChannel, nil
}

func findOfInterestRpms(dirsOfInterest []string, rpmListChannel chan *PackageInfo) (chan *PackageInfo, chan bool, error) {
	ofInterestChannel := make(chan *PackageInfo, 100)
	doneChannel := make(chan bool, numFindOfInterestRpmsWorkers)

	for i := 0; i < numFindOfInterestRpmsWorkers; i++ {
		go func() {
			for packageInfo := range rpmListChannel {
				//fmt.Println("findOfInterestRpms ", packageInfo.Name)
				cmd := exec.Command(allRpmsExec[0], "-ql", packageInfo.Name)
				out, err := cmd.Output()

				if err != nil {
					log.Fatal("jjjj ", err)
				}

				lines := strings.Split(string(out), "\n")
				done := false
				for _, line := range lines {
					if done {
						break
					}
					for _, dir := range dirsOfInterest {
						if strings.Index(line, dir) == 0 {
							ofInterestChannel <- packageInfo
							done = true
						}
					}
				}
			}
			doneChannel <- true
		}()
	}
	return ofInterestChannel, doneChannel, nil
}

func findRpmTags(ofInterestChannel chan *PackageInfo) (chan *PackageInfo, chan bool, error) {
	tagInfoChannel := make(chan *PackageInfo, 200)
	doneChannel := make(chan bool, numFindRpmTagsWorkers)

	for i := 0; i < numFindRpmTagsWorkers; i++ {
		go func() {
			numTags := random(100, 400)
			tagBuffer := make([]*PackageInfo, numTags)
			count := 0
			for packageInfo := range ofInterestChannel {
				//fmt.Println("\t findRpmTags ", packageInfo.name)
				//fmt.Println("--",count," ", packageInfo.name)
				if count >= numTags {
					out := runCommand(tagBuffer, count)
					parseAndSend(count, tagBuffer, out, tagInfoChannel)
					//fmt.Println("+++ ", packageInfo.name, " --- ", out)
					count = 0
					tagInfoChannel <- packageInfo

				}
				tagBuffer[count] = packageInfo
				count = count + 1
			}
			if count > 0 {
				out := runCommand(tagBuffer, count)
				parseAndSend(count, tagBuffer, out, tagInfoChannel)
				//_ = runCommand(tagBuffer, count)
				//fmt.Println("+++ ", " --- ", out)
			}
			doneChannel <- true
		}()
	}
	return tagInfoChannel, doneChannel, nil
}

const tagSeparator = "--==--"

const recordSeparator = "|||"

var rpmFormat = "name:%{NAME}" + tagSeparator + "os:%{OS}" + tagSeparator + "version:%{VERSION}" + tagSeparator + "release:%{RELEASE}" + tagSeparator + "arch:%{ARCH}" + tagSeparator + "installtime:%{INSTALLTIME:date}" + tagSeparator + "group:%{GROUP}" + tagSeparator + "size:%{SIZE}" + tagSeparator + "license:%{LICENSE}" + tagSeparator + "sourcerpm:%{SOURCERPM}" + tagSeparator + "buildtime:%{BUILDTIME}" + tagSeparator + "buildhost:%{BUILDHOST}" + tagSeparator + "packager:%{PACKAGER}" + tagSeparator + "vendor:%{VENDOR}" + tagSeparator + "url:%{URL}" + tagSeparator + "summary:%{SUMMARY}" + tagSeparator + "description:%{DESCRIPTION}" + tagSeparator + "" + tagSeparator + "distribution:%{DISTRIBUTION}" + tagSeparator + "packager:%{PACKAGER}" + tagSeparator + "patch:%{PATCH}" + recordSeparator

func parseAndSend(count int, tagInfoBuffer []*PackageInfo, out string, tagInfoChannel chan *PackageInfo) {
	records := strings.Split(out, recordSeparator)
	if len(records) != count+1 {
		log.Fatal("jiijjj ")
	}
	for i := 0; i < count; i++ {
		tags := strings.Split(records[i], tagSeparator)
		for j := 0; j < len(tags); j++ {
			//fmt.Println("\t", j, " ", tags[j])
			parts := strings.SplitN(tags[j], ":", 2)
			if len(parts) == 2 {
				parts[0] = strings.Trim(parts[0], " ")
				parts[1] = strings.Trim(parts[1], " ")
				if len(parts[1]) > 0 && parts[1] != "(none)" {
					tagInfoBuffer[i].Tags[parts[0]] = parts[1]
				}
			}
		}
		tagInfoChannel <- tagInfoBuffer[i]
	}
}

func runCommand(tagBuffer []*PackageInfo, count int) string {
	args := []string{}
	args = append(args, "--qf")
	args = append(args, rpmFormat)
	args = append(args, "-q")
	for j := 0; j < count; j++ {
		args = append(args, tagBuffer[j].Name)
	}
	cmd := exec.Command("rpm", args...)
	out, err := cmd.Output()
	if err != nil {
		log.Fatal("help", err)
	}
	return string(out)
}

func makeArgs(cmd *exec.Cmd, tagBuffer []*PackageInfo, count int) {
	cmd.Args = make([]string, 3+count)
	cmd.Args[0] = "--qf"
	cmd.Args[1] = rpmFormat
	cmd.Args[2] = "-q"
	for i := 0; i < count; i++ {
		cmd.Args[3+i] = tagBuffer[i].Name
	}
}

func addResultsToMap(rpmMap map[string]*PackageInfo, groupSet map[string]bool, tagInfoChannel chan *PackageInfo, nodes map[string]*Node) chan bool {
	doneChannel := make(chan bool)
	go func() {
		for packageInfo := range tagInfoChannel {
			rpmMap[strings.ToLower(packageInfo.Name)] = packageInfo
			groupSet[packageInfo.Tags["group"]] = true
			//fmt.Println("Adding to map ", packageInfo.Name)
			extractHierarchy(packageInfo, nodes)
		}
		doneChannel <- true
	}()
	return doneChannel
}

func random(min, max int) int {
	return rand.Intn(max-min) + min
}

func sortStringKeyMap2(m map[string]*struct{}) []string {
	sm := make([]string, len(m))
	return sm
}

//func sortStringKeyMap(m map[string]struct{}) []string{
func sortStringKeyMap(m map[string]*PackageInfo) []string {
	sm := make([]string, len(m))
	i := 0
	for k, _ := range m {
		sm[i] = k
		i++
	}
	sort.Strings(sm)
	return sm
}

func sortLicenseCountMap(licenseCount map[string]int) []string {
	sm := make([]string, len(licenseCount))
	i := 0
	for k, _ := range licenseCount {
		sm[i] = k
		i++
	}
	sort.Strings(sm)
	return sm
}

type Pair struct {
	Key   string
	Value int
}

// From: https://groups.google.com/forum/#!topic/golang-nuts/FT7cjmcL7gw
type PairList []Pair

func (p PairList) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
func (p PairList) Len() int           { return len(p) }
func (p PairList) Less(i, j int) bool { return p[i].Value < p[j].Value }

// A function to turn a map into a PairList, then sort and return it.
func sortMapByValue(m map[string]int) PairList {
	pp := make(PairList, len(m))
	i := 0
	for k, v := range m {
		pp[i] = Pair{k, v}
	}
	sort.Sort(pp)
	return pp
}

func sortStringKeyNodeMap(m map[string]*Node) []string {
	sm := make([]string, len(m))
	i := 0
	for k, _ := range m {
		sm[i] = k
		i++
	}
	sort.Strings(sm)
	return sm
}

// Add the R packages to the RPM packages
func add(rpms map[string]*PackageInfo, rpacks map[string]*PackageInfo) {
	for k, v := range rpacks {
		rpms[k] = v
	}
}
<commit_msg>First branch commit: altered flags to include -outputFormat=exhibit<commit_after>package main

// Glen Newton
// glen.newton@gmail.com

import (
	"flag"
	"fmt"
	"log"
	"math/rand"
	"os"
	"os/exec"
	"runtime"
	"sort"
	"strings"
	"time"
)

func init() {
	flag.StringVar(&outputFormat, "outputFormat", "html", "Values: html|html2|json|txt|latex|exhibit")
	flag.StringVar(&header, "header", "Installed Software", "gggg")
	flag.BoolVar(&doR, "R", false, "Find R packages")
}

var outputFormat string
var header string
var doR = false

type PackageInfo struct {
	Name      string
	IndexName string
	IsR       bool
	Tags      map[string]string
}

type RpmWriter interface {
	output(string, []string, []string, map[string]*PackageInfo, map[string]bool, map[string]*Node) error
}

func handleParameters() bool {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "%s: Output rpm info for rpms that have install components in certain directories\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "\t %s <args> <rootDir0>...<rootDirN>\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "\t default <rootDir>: /\n")
		fmt.Fprintf(os.Stderr, "Args:\n")
		flag.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\nExample:  %s -outputFormat=html /opt /usr/local\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "\nNote that the 'rpm' program (http://www.rpm.org/max-rpm/rpm.8.html) needs to be in your path\n\n")
	}

	flag.Parse()
	switch outputFormat {
	case "html":
		rpmWriter = new(HtmlOut)
	case "html2":
		rpmWriter = new(HtmlOut2)
	case "json":
		rpmWriter = new(JsonOut)

	case "txt":
		rpmWriter = new(TextOut)

	case "latex":
		rpmWriter = new(LaTeXOut)
	default:
		log.Print("Unknown/unsupported output format: " + outputFormat)
		return false
	}
	return true
}

var allRpmsExec = []string{"rpm", "-qa"}

var numFindOfInterestRpmsWorkers = 6
var numFindRpmTagsWorkers = 2

var rpmWriter RpmWriter

func main() {

	runtime.GOMAXPROCS(8)

	_, err := exec.LookPath(allRpmsExec[0])

	if err != nil {
		log.Fatal("'", allRpmsExec[0], "' is not in your path: needed to run; it is usually in '/usr/bin/rpm'")
	}

	if !handleParameters() {
		log.Fatal("Bad parameters")
	}

	rand.Seed(time.Now().Unix())

	var dirsOfInterest []string
	if len(flag.Args()) == 0 {
		dirsOfInterest = []string{"/"}
	} else {
		dirsOfInterest = flag.Args()
	}

	//fmt.Println(dirsOfInterest)

	rpmListChannel, err := makeRpmList()
	if err != nil {
		log.Fatal("jjjj ", err)
	}

	ofInterestChannel, ofInterestDoneChannel, err := findOfInterestRpms(dirsOfInterest, rpmListChannel)

	tagInfoChannel, tagInfoDoneChannel, err := findRpmTags(ofInterestChannel)

	packageInfoMap := make(map[string]*PackageInfo, 200)
	//packageInfoMap2 := make(map[string] PackageInfo, 200)
	groupSet := make(map[string]bool)

	var nodes map[string]*Node
	nodes = make(map[string]*Node)

	addResultsDoneChannel := addResultsToMap(packageInfoMap, groupSet, tagInfoChannel, nodes)

	for i := 0; i < numFindOfInterestRpmsWorkers; i++ {
		_ = <-ofInterestDoneChannel
	}
	close(ofInterestChannel)

	for i := 0; i < numFindRpmTagsWorkers; i++ {
		_ = <-tagInfoDoneChannel
	}
	close(tagInfoChannel)

	_ = <-addResultsDoneChannel

	var rpackageInfoMap map[string]*PackageInfo = nil
	if doR {
		rpackageInfoMap = findRPackages()
	}

	add(packageInfoMap, rpackageInfoMap)
	rpmWriter.output(header, dirsOfInterest, sortStringKeyMap(packageInfoMap), packageInfoMap, groupSet, nodes)
}

func xlog(m string) {
	fmt.Println(m)
}

func makeRpmList() (chan *PackageInfo, error) {
	rpmListChannel := make(chan *PackageInfo, 200)

	stringListChannel := runExec(allRpmsExec)

	go func() {
		for stringInfo := range stringListChannel {
			//fmt.Println("makeRpmList: [",  stringInfo.val, "] ", &stringInfo.val," ", stringInfo.done)
			packageInfo := new(PackageInfo)
			packageInfo.Name = stringInfo.val
			packageInfo.Tags = make(map[string]string)
			rpmListChannel <- packageInfo
		}
		close(rpmListChannel)
	}()
	return rpmListChannel, nil
}

func findOfInterestRpms(dirsOfInterest []string, rpmListChannel chan *PackageInfo) (chan *PackageInfo, chan bool, error) {
	ofInterestChannel := make(chan *PackageInfo, 100)
	doneChannel := make(chan bool, numFindOfInterestRpmsWorkers)

	for i := 0; i < numFindOfInterestRpmsWorkers; i++ {
		go func() {
			for packageInfo := range rpmListChannel {
				//fmt.Println("findOfInterestRpms ", packageInfo.Name)
				cmd := exec.Command(allRpmsExec[0], "-ql", packageInfo.Name)
				out, err := cmd.Output()

				if err != nil {
					log.Fatal("jjjj ", err)
				}

				lines := strings.Split(string(out), "\n")
				done := false
				for _, line := range lines {
					if done {
						break
					}
					for _, dir := range dirsOfInterest {
						if strings.Index(line, dir) == 0 {
							ofInterestChannel <- packageInfo
							done = true
						}
					}
				}
			}
			doneChannel <- true
		}()
	}
	return ofInterestChannel, doneChannel, nil
}

func findRpmTags(ofInterestChannel chan *PackageInfo) (chan *PackageInfo, chan bool, error) {
	tagInfoChannel := make(chan *PackageInfo, 200)
	doneChannel := make(chan bool, numFindRpmTagsWorkers)

	for i := 0; i < numFindRpmTagsWorkers; i++ {
		go func() {
			numTags := random(100, 400)
			tagBuffer := make([]*PackageInfo, numTags)
			count := 0
			for packageInfo := range ofInterestChannel {
				//fmt.Println("\t findRpmTags ", packageInfo.name)
				//fmt.Println("--",count," ", packageInfo.name)
				if count >= numTags {
					out := runCommand(tagBuffer, count)
					parseAndSend(count, tagBuffer, out, tagInfoChannel)
					//fmt.Println("+++ ", packageInfo.name, " --- ", out)
					count = 0
					tagInfoChannel <- packageInfo

				}
				tagBuffer[count] = packageInfo
				count = count + 1
			}
			if count > 0 {
				out := runCommand(tagBuffer, count)
				parseAndSend(count, tagBuffer, out, tagInfoChannel)
				//_ = runCommand(tagBuffer, count)
				//fmt.Println("+++ ", " --- ", out)
			}
			doneChannel <- true
		}()
	}
	return tagInfoChannel, doneChannel, nil
}

const tagSeparator = "--==--"

const recordSeparator = "|||"

var rpmFormat = "name:%{NAME}" + tagSeparator + "os:%{OS}" + tagSeparator + "version:%{VERSION}" + tagSeparator + "release:%{RELEASE}" + tagSeparator + "arch:%{ARCH}" + tagSeparator + "installtime:%{INSTALLTIME:date}" + tagSeparator + "group:%{GROUP}" + tagSeparator + "size:%{SIZE}" + tagSeparator + "license:%{LICENSE}" + tagSeparator + "sourcerpm:%{SOURCERPM}" + tagSeparator + "buildtime:%{BUILDTIME}" + tagSeparator + "buildhost:%{BUILDHOST}" + tagSeparator + "packager:%{PACKAGER}" + tagSeparator + "vendor:%{VENDOR}" + tagSeparator + "url:%{URL}" + tagSeparator + "summary:%{SUMMARY}" + tagSeparator + "description:%{DESCRIPTION}" + tagSeparator + "" + tagSeparator + "distribution:%{DISTRIBUTION}" + tagSeparator + "packager:%{PACKAGER}" + tagSeparator + "patch:%{PATCH}" + recordSeparator

func parseAndSend(count int, tagInfoBuffer []*PackageInfo, out string, tagInfoChannel chan *PackageInfo) {
	records := strings.Split(out, recordSeparator)
	if len(records) != count+1 {
		log.Fatal("jiijjj ")
	}
	for i := 0; i < count; i++ {
		tags := strings.Split(records[i], tagSeparator)
		for j := 0; j < len(tags); j++ {
			//fmt.Println("\t", j, " ", tags[j])
			parts := strings.SplitN(tags[j], ":", 2)
			if len(parts) == 2 {
				parts[0] = strings.Trim(parts[0], " ")
				parts[1] = strings.Trim(parts[1], " ")
				if len(parts[1]) > 0 && parts[1] != "(none)" {
					tagInfoBuffer[i].Tags[parts[0]] = parts[1]
				}
			}
		}
		tagInfoChannel <- tagInfoBuffer[i]
	}
}

func runCommand(tagBuffer []*PackageInfo, count int) string {
	args := []string{}
	args = append(args, "--qf")
	args = append(args, rpmFormat)
	args = append(args, "-q")
	for j := 0; j < count; j++ {
		args = append(args, tagBuffer[j].Name)
	}
	cmd := exec.Command("rpm", args...)
	out, err := cmd.Output()
	if err != nil {
		log.Fatal("help", err)
	}
	return string(out)
}

func makeArgs(cmd *exec.Cmd, tagBuffer []*PackageInfo, count int) {
	cmd.Args = make([]string, 3+count)
	cmd.Args[0] = "--qf"
	cmd.Args[1] = rpmFormat
	cmd.Args[2] = "-q"
	for i := 0; i < count; i++ {
		cmd.Args[3+i] = tagBuffer[i].Name
	}
}

func addResultsToMap(rpmMap map[string]*PackageInfo, groupSet map[string]bool, tagInfoChannel chan *PackageInfo, nodes map[string]*Node) chan bool {
	doneChannel := make(chan bool)
	go func() {
		for packageInfo := range tagInfoChannel {
			rpmMap[strings.ToLower(packageInfo.Name)] = packageInfo
			groupSet[packageInfo.Tags["group"]] = true
			//fmt.Println("Adding to map ", packageInfo.Name)
			extractHierarchy(packageInfo, nodes)
		}
		doneChannel <- true
	}()
	return doneChannel
}

func random(min, max int) int {
	return rand.Intn(max-min) + min
}

func sortStringKeyMap2(m map[string]*struct{}) []string {
	sm := make([]string, len(m))
	return sm
}

//func sortStringKeyMap(m map[string]struct{}) []string{
func sortStringKeyMap(m map[string]*PackageInfo) []string {
	sm := make([]string, len(m))
	i := 0
	for k, _ := range m {
		sm[i] = k
		i++
	}
	sort.Strings(sm)
	return sm
}

func sortLicenseCountMap(licenseCount map[string]int) []string {
	sm := make([]string, len(licenseCount))
	i := 0
	for k, _ := range licenseCount {
		sm[i] = k
		i++
	}
	sort.Strings(sm)
	return sm
}

type Pair struct {
	Key   string
	Value int
}

// From: https://groups.google.com/forum/#!topic/golang-nuts/FT7cjmcL7gw
type PairList []Pair

func (p PairList) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
func (p PairList) Len() int           { return len(p) }
func (p PairList) Less(i, j int) bool { return p[i].Value < p[j].Value }

// A function to turn a map into a PairList, then sort and return it.
func sortMapByValue(m map[string]int) PairList {
	pp := make(PairList, len(m))
	i := 0
	for k, v := range m {
		pp[i] = Pair{k, v}
	}
	sort.Sort(pp)
	return pp
}

func sortStringKeyNodeMap(m map[string]*Node) []string {
	sm := make([]string, len(m))
	i := 0
	for k, _ := range m {
		sm[i] = k
		i++
	}
	sort.Strings(sm)
	return sm
}

// Add the R packages to the RPM packages
func add(rpms map[string]*PackageInfo, rpacks map[string]*PackageInfo) {
	for k, v := range rpacks {
		rpms[k] = v
	}
}
<|endoftext|>


<commit_before>// main of samples

package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/aliyun/aliyun-oss-go-sdk/sample"
)

// sampleMap contains all samples
var sampleMap = map[string]interface{}{
	"CreateBucketSample":    sample.CreateBucketSample,
	"NewBucketSample":       sample.NewBucketSample,
	"ListBucketsSample":     sample.ListBucketsSample,
	"BucketACLSample":       sample.BucketACLSample,
	"BucketLifecycleSample": sample.BucketLifecycleSample,
	"BucketRefererSample":   sample.BucketRefererSample,
	"BucketLoggingSample":   sample.BucketLoggingSample,
	"BucketCORSSample":      sample.BucketCORSSample,
	"ObjectACLSample":       sample.ObjectACLSample,
	"ObjectMetaSample":      sample.ObjectMetaSample,
	"ListObjectsSample":     sample.ListObjectsSample,
	"DeleteObjectSample":    sample.DeleteObjectSample,
	"AppendObjectSample":    sample.AppendObjectSample,
	"CopyObjectSample":      sample.CopyObjectSample,
	"PutObjectSample":       sample.PutObjectSample,
	"GetObjectSample":       sample.GetObjectSample,
	"CnameSample":           sample.CnameSample,
	"SignURLSample":         sample.SignURLSample,
	"ArchiveSample":         sample.ArchiveSample,
}

func main() {
<<<<<<< HEAD
	sample.CreateBucketSample()
	sample.NewBucketSample()
	sample.ListBucketsSample()
	sample.BucketACLSample()
	sample.BucketLifecycleSample()
	sample.BucketRefererSample()
	sample.BucketLoggingSample()
	sample.BucketCORSSample()

	sample.ObjectACLSample()
	sample.ObjectMetaSample()
	sample.ListObjectsSample()
	sample.DeleteObjectSample()
	sample.AppendObjectSample()
	sample.CopyObjectSample()
	sample.PutObjectSample()
	sample.GetObjectSample()

	sample.CnameSample()
	sample.SignURLSample()

	sample.ArchiveSample()
	sample.ObjectTaggingSample()

	fmt.Println("All samples completed")
=======
	var name string
	flag.StringVar(&name, "name", "", "Waiting for a sample of execution")
	flag.Parse()

	if len(name) <= 0 {
		fmt.Println("please enter your sample's name. like '-name CreateBucketSample'")
		os.Exit(-1)
	} else {
		if sampleMap[name] == nil {
			fmt.Println("the " + name + "is not exist.")
			os.Exit(-1)
		}
		sampleMap[name].(func())()
	}

	// sample.CreateBucketSample()
	// sample.NewBucketSample()
	// sample.ListBucketsSample()
	// sample.BucketACLSample()
	// sample.BucketLifecycleSample()
	// sample.BucketRefererSample()
	// sample.BucketLoggingSample()
	// sample.BucketCORSSample()

	// sample.ObjectACLSample()
	// sample.ObjectMetaSample()
	// sample.ListObjectsSample()
	// sample.DeleteObjectSample()
	// sample.AppendObjectSample()
	// sample.CopyObjectSample()
	// sample.PutObjectSample()
	// sample.GetObjectSample()

	// sample.CnameSample()
	// sample.SignURLSample()

	// sample.ArchiveSample()

	// fmt.Println("All samples completed")
>>>>>>> 86eaf1f... modify the sample main function, let the implementation of a single sample
}
<commit_msg>fix conflict<commit_after>// main of samples

package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/aliyun/aliyun-oss-go-sdk/sample"
)

// sampleMap contains all samples
var sampleMap = map[string]interface{}{
	"CreateBucketSample":    sample.CreateBucketSample,
	"NewBucketSample":       sample.NewBucketSample,
	"ListBucketsSample":     sample.ListBucketsSample,
	"BucketACLSample":       sample.BucketACLSample,
	"BucketLifecycleSample": sample.BucketLifecycleSample,
	"BucketRefererSample":   sample.BucketRefererSample,
	"BucketLoggingSample":   sample.BucketLoggingSample,
	"BucketCORSSample":      sample.BucketCORSSample,
	"ObjectACLSample":       sample.ObjectACLSample,
	"ObjectMetaSample":      sample.ObjectMetaSample,
	"ListObjectsSample":     sample.ListObjectsSample,
	"DeleteObjectSample":    sample.DeleteObjectSample,
	"AppendObjectSample":    sample.AppendObjectSample,
	"CopyObjectSample":      sample.CopyObjectSample,
	"PutObjectSample":       sample.PutObjectSample,
	"GetObjectSample":       sample.GetObjectSample,
	"CnameSample":           sample.CnameSample,
	"SignURLSample":         sample.SignURLSample,
	"ArchiveSample":         sample.ArchiveSample,
	"ObjectTaggingSample":   sample.ObjectTaggingSample,
}

func main() {
	var name string
	flag.StringVar(&name, "name", "", "Waiting for a sample of execution")
	flag.Parse()

	if len(name) <= 0 {
		fmt.Println("please enter your sample's name. like '-name CreateBucketSample'")
		os.Exit(-1)
	} else {
		if sampleMap[name] == nil {
			fmt.Println("the " + name + "is not exist.")
			os.Exit(-1)
		}
		sampleMap[name].(func())()
	}
}
<|endoftext|>